early-access version 1475
This commit is contained in:
		| @@ -1,7 +1,7 @@ | ||||
| yuzu emulator early access | ||||
| ============= | ||||
|  | ||||
| This is the source code for early-access 1474. | ||||
| This is the source code for early-access 1475. | ||||
|  | ||||
| ## Legal Notice | ||||
|  | ||||
|   | ||||
							
								
								
									
										6
									
								
								externals/dynarmic/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								externals/dynarmic/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							| @@ -97,6 +97,12 @@ else() | ||||
|         list(APPEND DYNARMIC_CXX_FLAGS | ||||
|              -Wfatal-errors) | ||||
|     endif() | ||||
|  | ||||
|     if (CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang") | ||||
|         # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. | ||||
|         # And this in turns limits the size of a std::array. | ||||
|         list(APPEND DYNARMIC_CXX_FLAGS -fbracket-depth=1024) | ||||
|     endif() | ||||
| endif() | ||||
|  | ||||
| # Arch detection | ||||
|   | ||||
							
								
								
									
										2
									
								
								externals/dynarmic/README.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								externals/dynarmic/README.md
									
									
									
									
										vendored
									
									
								
							| @@ -199,7 +199,7 @@ Legal | ||||
|  | ||||
| dynarmic is under a 0BSD license. See LICENSE.txt for more details. | ||||
|  | ||||
| dynarmic uses several other libraries, whose licenes are included below: | ||||
| dynarmic uses several other libraries, whose licenses are included below: | ||||
|  | ||||
| ### catch | ||||
|  | ||||
|   | ||||
							
								
								
									
										23
									
								
								externals/dynarmic/include/dynarmic/A32/arch_version.h
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										23
									
								
								externals/dynarmic/include/dynarmic/A32/arch_version.h
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| /* This file is part of the dynarmic project. | ||||
|  * Copyright (c) 2020 MerryMage | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| namespace Dynarmic { | ||||
| namespace A32 { | ||||
|  | ||||
| enum class ArchVersion { | ||||
|     v3, | ||||
|     v4, | ||||
|     v4T, | ||||
|     v5TE, | ||||
|     v6K, | ||||
|     v6T2, | ||||
|     v7, | ||||
|     v8, | ||||
| }; | ||||
|  | ||||
| } // namespace A32 | ||||
| } // namespace Dynarmic | ||||
| @@ -10,6 +10,7 @@ | ||||
| #include <cstdint> | ||||
| #include <memory> | ||||
|  | ||||
| #include <dynarmic/A32/arch_version.h> | ||||
| #include <dynarmic/optimization_flags.h> | ||||
|  | ||||
| namespace Dynarmic { | ||||
| @@ -105,6 +106,10 @@ struct UserConfig { | ||||
|     size_t processor_id = 0; | ||||
|     ExclusiveMonitor* global_monitor = nullptr; | ||||
|  | ||||
|     /// Select the architecture version to use. | ||||
|     /// There are minor behavioural differences between versions. | ||||
|     ArchVersion arch_version = ArchVersion::v8; | ||||
|  | ||||
|     /// This selects other optimizations than can't otherwise be disabled by setting other | ||||
|     /// configuration options. This includes: | ||||
|     /// - IR optimizations | ||||
|   | ||||
							
								
								
									
										10
									
								
								externals/dynarmic/include/dynarmic/A64/config.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								externals/dynarmic/include/dynarmic/A64/config.h
									
									
									
									
										vendored
									
									
								
							| @@ -68,6 +68,15 @@ enum class DataCacheOperation { | ||||
|     ZeroByVA, | ||||
| }; | ||||
|  | ||||
| enum class InstructionCacheOperation { | ||||
|     /// IC IVAU | ||||
|     InvalidateByVAToPoU, | ||||
|     /// IC IALLU | ||||
|     InvalidateAllToPoU, | ||||
|     /// IC IALLUIS | ||||
|     InvalidateAllToPoUInnerSharable | ||||
| }; | ||||
|  | ||||
| struct UserCallbacks { | ||||
|     virtual ~UserCallbacks() = default; | ||||
|  | ||||
| @@ -110,6 +119,7 @@ struct UserCallbacks { | ||||
|  | ||||
|     virtual void ExceptionRaised(VAddr pc, Exception exception) = 0; | ||||
|     virtual void DataCacheOperationRaised(DataCacheOperation /*op*/, VAddr /*value*/) {} | ||||
|     virtual void InstructionCacheOperationRaised(InstructionCacheOperation /*op*/, VAddr /*value*/) {} | ||||
|     virtual void InstructionSynchronizationBarrierRaised() {} | ||||
|  | ||||
|     // Timing-related callbacks | ||||
|   | ||||
							
								
								
									
										13
									
								
								externals/dynarmic/src/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								externals/dynarmic/src/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,6 @@ | ||||
| add_library(dynarmic | ||||
|     ../include/dynarmic/A32/a32.h | ||||
|     ../include/dynarmic/A32/arch_version.h | ||||
|     ../include/dynarmic/A32/config.h | ||||
|     ../include/dynarmic/A32/coprocessor.h | ||||
|     ../include/dynarmic/A32/coprocessor_util.h | ||||
| @@ -124,6 +125,8 @@ if ("A32" IN_LIST DYNARMIC_FRONTENDS) | ||||
|         frontend/A32/location_descriptor.cpp | ||||
|         frontend/A32/location_descriptor.h | ||||
|         frontend/A32/PSR.h | ||||
|         frontend/A32/translate/conditional_state.cpp | ||||
|         frontend/A32/translate/conditional_state.h | ||||
|         frontend/A32/translate/impl/asimd_load_store_structures.cpp | ||||
|         frontend/A32/translate/impl/asimd_misc.cpp | ||||
|         frontend/A32/translate/impl/asimd_one_reg_modified_immediate.cpp | ||||
| @@ -150,7 +153,14 @@ if ("A32" IN_LIST DYNARMIC_FRONTENDS) | ||||
|         frontend/A32/translate/impl/status_register_access.cpp | ||||
|         frontend/A32/translate/impl/synchronization.cpp | ||||
|         frontend/A32/translate/impl/thumb16.cpp | ||||
|         frontend/A32/translate/impl/thumb32.cpp | ||||
|         frontend/A32/translate/impl/thumb32_branch.cpp | ||||
|         frontend/A32/translate/impl/thumb32_control.cpp | ||||
|         frontend/A32/translate/impl/thumb32_data_processing_register.cpp | ||||
|         frontend/A32/translate/impl/thumb32_data_processing_modified_immediate.cpp | ||||
|         frontend/A32/translate/impl/thumb32_long_multiply.cpp | ||||
|         frontend/A32/translate/impl/thumb32_misc.cpp | ||||
|         frontend/A32/translate/impl/thumb32_multiply.cpp | ||||
|         frontend/A32/translate/impl/thumb32_parallel.cpp | ||||
|         frontend/A32/translate/impl/translate_arm.h | ||||
|         frontend/A32/translate/impl/translate_thumb.h | ||||
|         frontend/A32/translate/impl/vfp.cpp | ||||
| @@ -226,6 +236,7 @@ if ("A64" IN_LIST DYNARMIC_FRONTENDS) | ||||
|         frontend/A64/translate/impl/simd_two_register_misc.cpp | ||||
|         frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp | ||||
|         frontend/A64/translate/impl/sys_dc.cpp | ||||
|         frontend/A64/translate/impl/sys_ic.cpp | ||||
|         frontend/A64/translate/impl/system.cpp | ||||
|         frontend/A64/translate/impl/system_flag_format.cpp | ||||
|         frontend/A64/translate/impl/system_flag_manipulation.cpp | ||||
|   | ||||
| @@ -67,6 +67,10 @@ A32::LocationDescriptor A32EmitContext::Location() const { | ||||
|     return A32::LocationDescriptor{block.Location()}; | ||||
| } | ||||
|  | ||||
| A32::LocationDescriptor A32EmitContext::EndLocation() const { | ||||
|     return A32::LocationDescriptor{block.EndLocation()}; | ||||
| } | ||||
|  | ||||
| bool A32EmitContext::IsSingleStep() const { | ||||
|     return Location().SingleStepping(); | ||||
| } | ||||
| @@ -732,7 +736,7 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) { | ||||
|     auto args = ctx.reg_alloc.GetArgumentInfo(inst); | ||||
|     auto& arg = args[0]; | ||||
|  | ||||
|     const u32 upper_without_t = (ctx.Location().SetSingleStepping(false).UniqueHash() >> 32) & 0xFFFFFFFE; | ||||
|     const u32 upper_without_t = (ctx.EndLocation().SetSingleStepping(false).UniqueHash() >> 32) & 0xFFFFFFFE; | ||||
|  | ||||
|     // Pseudocode: | ||||
|     // if (new_pc & 1) { | ||||
| @@ -766,6 +770,15 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| void A32EmitX64::EmitA32UpdateUpperLocationDescriptor(A32EmitContext& ctx, IR::Inst*) { | ||||
|     for (auto& inst : ctx.block) { | ||||
|         if (inst.GetOpcode() == IR::Opcode::A32BXWritePC) { | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     EmitSetUpperLocationDescriptor(ctx.EndLocation(), ctx.Location()); | ||||
| } | ||||
|  | ||||
| void A32EmitX64::EmitA32CallSupervisor(A32EmitContext& ctx, IR::Inst* inst) { | ||||
|     ctx.reg_alloc.HostCall(nullptr); | ||||
|  | ||||
|   | ||||
| @@ -29,6 +29,7 @@ struct A32EmitContext final : public EmitContext { | ||||
|     A32EmitContext(const A32::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block); | ||||
|  | ||||
|     A32::LocationDescriptor Location() const; | ||||
|     A32::LocationDescriptor EndLocation() const; | ||||
|     bool IsSingleStep() const; | ||||
|     FP::FPCR FPCR(bool fpcr_controlled = true) const override; | ||||
|  | ||||
|   | ||||
| @@ -175,7 +175,7 @@ private: | ||||
|             PerformCacheInvalidation(); | ||||
|         } | ||||
|  | ||||
|         IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, [this](u32 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); }, {conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); | ||||
|         IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, [this](u32 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); }, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions}); | ||||
|         if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) { | ||||
|             Optimization::A32GetSetElimination(ir_block); | ||||
|             Optimization::DeadCodeElimination(ir_block); | ||||
|   | ||||
| @@ -647,10 +647,16 @@ void A64EmitX64::EmitA64ExceptionRaised(A64EmitContext& ctx, IR::Inst* inst) { | ||||
|  | ||||
| void A64EmitX64::EmitA64DataCacheOperationRaised(A64EmitContext& ctx, IR::Inst* inst) { | ||||
|     auto args = ctx.reg_alloc.GetArgumentInfo(inst); | ||||
|     ctx.reg_alloc.HostCall(nullptr, args[0], args[1]); | ||||
|     ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]); | ||||
|     Devirtualize<&A64::UserCallbacks::DataCacheOperationRaised>(conf.callbacks).EmitCall(code); | ||||
| } | ||||
|  | ||||
| void A64EmitX64::EmitA64InstructionCacheOperationRaised(A64EmitContext& ctx, IR::Inst* inst) { | ||||
|     auto args = ctx.reg_alloc.GetArgumentInfo(inst); | ||||
|     ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]); | ||||
|     Devirtualize<&A64::UserCallbacks::InstructionCacheOperationRaised>(conf.callbacks).EmitCall(code); | ||||
| } | ||||
|  | ||||
| void A64EmitX64::EmitA64DataSynchronizationBarrier(A64EmitContext&, IR::Inst*) { | ||||
|     code.mfence(); | ||||
| } | ||||
|   | ||||
							
								
								
									
										11
									
								
								externals/dynarmic/src/common/bit_util.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								externals/dynarmic/src/common/bit_util.h
									
									
									
									
										vendored
									
									
								
							| @@ -218,18 +218,23 @@ constexpr T RotateRight(T value, size_t amount) { | ||||
|     return static_cast<T>((x >> amount) | (x << (BitSize<T>() - amount))); | ||||
| } | ||||
|  | ||||
| constexpr u16 Swap16(u16 value) { | ||||
| constexpr u32 SwapHalves32(u32 value) { | ||||
|     return ((value & 0xFFFF0000U) >> 16) | | ||||
|            ((value & 0x0000FFFFU) << 16); | ||||
| } | ||||
|  | ||||
| constexpr u16 SwapBytes16(u16 value) { | ||||
|     return static_cast<u16>(u32{value} >> 8 | u32{value} << 8); | ||||
| } | ||||
|  | ||||
| constexpr u32 Swap32(u32 value) { | ||||
| constexpr u32 SwapBytes32(u32 value) { | ||||
|     return ((value & 0xFF000000U) >> 24) | | ||||
|            ((value & 0x00FF0000U) >>  8) | | ||||
|            ((value & 0x0000FF00U) <<  8) | | ||||
|            ((value & 0x000000FFU) << 24); | ||||
| } | ||||
|  | ||||
| constexpr u64 Swap64(u64 value) { | ||||
| constexpr u64 SwapBytes64(u64 value) { | ||||
|     return  ((value & 0xFF00000000000000ULL) >> 56) | | ||||
|             ((value & 0x00FF000000000000ULL) >> 40) | | ||||
|             ((value & 0x0000FF0000000000ULL) >> 24) | | ||||
|   | ||||
| @@ -54,20 +54,38 @@ std::string DisassembleX64(const void* begin, const void* end) { | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| std::string DisassembleAArch32([[maybe_unused]] u32 instruction, [[maybe_unused]] u64 pc) { | ||||
| std::string DisassembleAArch32([[maybe_unused]] bool is_thumb, [[maybe_unused]] u32 pc, [[maybe_unused]] const u8* instructions, [[maybe_unused]] size_t length) { | ||||
|     std::string result; | ||||
|  | ||||
| #ifdef DYNARMIC_USE_LLVM | ||||
|     LLVMInitializeARMTargetInfo(); | ||||
|     LLVMInitializeARMTargetMC(); | ||||
|     LLVMInitializeARMDisassembler(); | ||||
|     LLVMDisasmContextRef llvm_ctx = LLVMCreateDisasm("armv8-arm", nullptr, 0, nullptr, nullptr); | ||||
|     LLVMDisasmContextRef llvm_ctx = LLVMCreateDisasm(is_thumb ? "thumbv8-arm" : "armv8-arm", nullptr, 0, nullptr, nullptr); | ||||
|     LLVMSetDisasmOptions(llvm_ctx, LLVMDisassembler_Option_AsmPrinterVariant); | ||||
|  | ||||
|     char buffer[80]; | ||||
|     size_t inst_size = LLVMDisasmInstruction(llvm_ctx, (u8*)&instruction, sizeof(instruction), pc, buffer, sizeof(buffer)); | ||||
|     result = inst_size > 0 ? buffer : "<invalid instruction>"; | ||||
|     result += '\n'; | ||||
|     char buffer[1024]; | ||||
|     while (length) { | ||||
|         size_t inst_size = LLVMDisasmInstruction(llvm_ctx, const_cast<u8*>(instructions), length, pc, buffer, sizeof(buffer)); | ||||
|  | ||||
|         result += fmt::format("{:08x}    ", pc); | ||||
|         for (size_t i = 0; i < 4; i++) { | ||||
|             if (i < inst_size) { | ||||
|                 result += fmt::format("{:02x}", instructions[inst_size - i - 1]); | ||||
|             } else { | ||||
|                 result += "  "; | ||||
|             } | ||||
|         } | ||||
|         result += inst_size > 0 ? buffer : "<invalid instruction>"; | ||||
|         result += '\n'; | ||||
|  | ||||
|         if (inst_size == 0) inst_size = is_thumb ? 2 : 4; | ||||
|         if (length <= inst_size) break; | ||||
|  | ||||
|         pc += inst_size; | ||||
|         instructions += inst_size; | ||||
|         length -= inst_size; | ||||
|     } | ||||
|  | ||||
|     LLVMDisasmDispose(llvm_ctx); | ||||
| #else | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| namespace Dynarmic::Common { | ||||
|  | ||||
| std::string DisassembleX64(const void* pos, const void* end); | ||||
| std::string DisassembleAArch32(u32 instruction, u64 pc = 0); | ||||
| std::string DisassembleAArch32(bool is_thumb, u32 pc, const u8* instructions, size_t length); | ||||
| std::string DisassembleAArch64(u32 instruction, u64 pc = 0); | ||||
|  | ||||
| } // namespace Dynarmic::Common | ||||
|   | ||||
							
								
								
									
										27
									
								
								externals/dynarmic/src/frontend/A32/ITState.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								externals/dynarmic/src/frontend/A32/ITState.h
									
									
									
									
										vendored
									
									
								
							| @@ -22,34 +22,25 @@ public: | ||||
|     } | ||||
|  | ||||
|     IR::Cond Cond() const { | ||||
|         if (value == 0b00000000) { | ||||
|             return IR::Cond::AL; | ||||
|         } | ||||
|         return static_cast<IR::Cond>(Common::Bits<4, 7>(value)); | ||||
|     } | ||||
|     void Cond(IR::Cond cond) { | ||||
|         value = Common::ModifyBits<4, 7>(value, static_cast<u8>(cond)); | ||||
|     } | ||||
|  | ||||
|     u8 Mask() const { | ||||
|         return Common::Bits<0, 3>(value); | ||||
|     } | ||||
|     void Mask(u8 mask) { | ||||
|         value = Common::ModifyBits<0, 3>(value, mask); | ||||
|     } | ||||
|  | ||||
|     bool IsInITBlock() const { | ||||
|         return Mask() != 0b0000; | ||||
|         return Common::Bits<0, 3>(value) != 0b0000; | ||||
|     } | ||||
|  | ||||
|     bool IsLastInITBlock() const { | ||||
|         return Mask() == 0b1000; | ||||
|         return Common::Bits<0, 3>(value) == 0b1000; | ||||
|     } | ||||
|  | ||||
|     ITState Advance() const { | ||||
|         ITState result{*this}; | ||||
|         result.Mask(result.Mask() << 1); | ||||
|         if (result.Mask() == 0) { | ||||
|             return ITState{0}; | ||||
|         if (Common::Bits<0, 2>(value) == 0b000) { | ||||
|             return ITState{0b00000000}; | ||||
|         } | ||||
|         return result; | ||||
|         return ITState{Common::ModifyBits<0, 4>(value, static_cast<u8>(Common::Bits<0, 4>(value) << 1))}; | ||||
|     } | ||||
|  | ||||
|     u8 Value() const { | ||||
| @@ -57,7 +48,7 @@ public: | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     u8 value; | ||||
|     u8 value = 0; | ||||
| }; | ||||
|  | ||||
| inline bool operator==(ITState lhs, ITState rhs) { | ||||
|   | ||||
| @@ -23,104 +23,8 @@ template<typename V> | ||||
| std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16 instruction) { | ||||
|     static const std::vector<Thumb16Matcher<V>> table = { | ||||
|  | ||||
| #define INST(fn, name, bitstring) Decoder::detail::detail<Thumb16Matcher<V>>::GetMatcher(fn, name, bitstring) | ||||
|  | ||||
|         // Shift (immediate), add, subtract, move and compare instructions | ||||
|         INST(&V::thumb16_LSL_imm,        "LSL (imm)",                "00000vvvvvmmmddd"), | ||||
|         INST(&V::thumb16_LSR_imm,        "LSR (imm)",                "00001vvvvvmmmddd"), | ||||
|         INST(&V::thumb16_ASR_imm,        "ASR (imm)",                "00010vvvvvmmmddd"), | ||||
|         INST(&V::thumb16_ADD_reg_t1,     "ADD (reg, T1)",            "0001100mmmnnnddd"), | ||||
|         INST(&V::thumb16_SUB_reg,        "SUB (reg)",                "0001101mmmnnnddd"), | ||||
|         INST(&V::thumb16_ADD_imm_t1,     "ADD (imm, T1)",            "0001110vvvnnnddd"), | ||||
|         INST(&V::thumb16_SUB_imm_t1,     "SUB (imm, T1)",            "0001111vvvnnnddd"), | ||||
|         INST(&V::thumb16_MOV_imm,        "MOV (imm)",                "00100dddvvvvvvvv"), | ||||
|         INST(&V::thumb16_CMP_imm,        "CMP (imm)",                "00101nnnvvvvvvvv"), | ||||
|         INST(&V::thumb16_ADD_imm_t2,     "ADD (imm, T2)",            "00110dddvvvvvvvv"), | ||||
|         INST(&V::thumb16_SUB_imm_t2,     "SUB (imm, T2)",            "00111dddvvvvvvvv"), | ||||
|  | ||||
|          // Data-processing instructions | ||||
|         INST(&V::thumb16_AND_reg,        "AND (reg)",                "0100000000mmmddd"), | ||||
|         INST(&V::thumb16_EOR_reg,        "EOR (reg)",                "0100000001mmmddd"), | ||||
|         INST(&V::thumb16_LSL_reg,        "LSL (reg)",                "0100000010mmmddd"), | ||||
|         INST(&V::thumb16_LSR_reg,        "LSR (reg)",                "0100000011mmmddd"), | ||||
|         INST(&V::thumb16_ASR_reg,        "ASR (reg)",                "0100000100mmmddd"), | ||||
|         INST(&V::thumb16_ADC_reg,        "ADC (reg)",                "0100000101mmmddd"), | ||||
|         INST(&V::thumb16_SBC_reg,        "SBC (reg)",                "0100000110mmmddd"), | ||||
|         INST(&V::thumb16_ROR_reg,        "ROR (reg)",                "0100000111sssddd"), | ||||
|         INST(&V::thumb16_TST_reg,        "TST (reg)",                "0100001000mmmnnn"), | ||||
|         INST(&V::thumb16_RSB_imm,        "RSB (imm)",                "0100001001nnnddd"), | ||||
|         INST(&V::thumb16_CMP_reg_t1,     "CMP (reg, T1)",            "0100001010mmmnnn"), | ||||
|         INST(&V::thumb16_CMN_reg,        "CMN (reg)",                "0100001011mmmnnn"), | ||||
|         INST(&V::thumb16_ORR_reg,        "ORR (reg)",                "0100001100mmmddd"), | ||||
|         INST(&V::thumb16_MUL_reg,        "MUL (reg)",                "0100001101nnnddd"), | ||||
|         INST(&V::thumb16_BIC_reg,        "BIC (reg)",                "0100001110mmmddd"), | ||||
|         INST(&V::thumb16_MVN_reg,        "MVN (reg)",                "0100001111mmmddd"), | ||||
|  | ||||
|         // Special data instructions | ||||
|         INST(&V::thumb16_ADD_reg_t2,     "ADD (reg, T2)",            "01000100Dmmmmddd"), // v4T, Low regs: v6T2 | ||||
|         INST(&V::thumb16_CMP_reg_t2,     "CMP (reg, T2)",            "01000101Nmmmmnnn"), // v4T | ||||
|         INST(&V::thumb16_MOV_reg,        "MOV (reg)",                "01000110Dmmmmddd"), // v4T, Low regs: v6 | ||||
|  | ||||
|         // Store/Load single data item instructions | ||||
|         INST(&V::thumb16_LDR_literal,    "LDR (literal)",            "01001tttvvvvvvvv"), | ||||
|         INST(&V::thumb16_STR_reg,        "STR (reg)",                "0101000mmmnnnttt"), | ||||
|         INST(&V::thumb16_STRH_reg,       "STRH (reg)",               "0101001mmmnnnttt"), | ||||
|         INST(&V::thumb16_STRB_reg,       "STRB (reg)",               "0101010mmmnnnttt"), | ||||
|         INST(&V::thumb16_LDRSB_reg,      "LDRSB (reg)",              "0101011mmmnnnttt"), | ||||
|         INST(&V::thumb16_LDR_reg,        "LDR (reg)",                "0101100mmmnnnttt"), | ||||
|         INST(&V::thumb16_LDRH_reg,       "LDRH (reg)",               "0101101mmmnnnttt"), | ||||
|         INST(&V::thumb16_LDRB_reg,       "LDRB (reg)",               "0101110mmmnnnttt"), | ||||
|         INST(&V::thumb16_LDRSH_reg,      "LDRSH (reg)",              "0101111mmmnnnttt"), | ||||
|         INST(&V::thumb16_STR_imm_t1,     "STR (imm, T1)",            "01100vvvvvnnnttt"), | ||||
|         INST(&V::thumb16_LDR_imm_t1,     "LDR (imm, T1)",            "01101vvvvvnnnttt"), | ||||
|         INST(&V::thumb16_STRB_imm,       "STRB (imm)",               "01110vvvvvnnnttt"), | ||||
|         INST(&V::thumb16_LDRB_imm,       "LDRB (imm)",               "01111vvvvvnnnttt"), | ||||
|         INST(&V::thumb16_STRH_imm,       "STRH (imm)",               "10000vvvvvnnnttt"), | ||||
|         INST(&V::thumb16_LDRH_imm,       "LDRH (imm)",               "10001vvvvvnnnttt"), | ||||
|         INST(&V::thumb16_STR_imm_t2,     "STR (imm, T2)",            "10010tttvvvvvvvv"), | ||||
|         INST(&V::thumb16_LDR_imm_t2,     "LDR (imm, T2)",            "10011tttvvvvvvvv"), | ||||
|  | ||||
|         // Generate relative address instructions | ||||
|         INST(&V::thumb16_ADR,            "ADR",                      "10100dddvvvvvvvv"), | ||||
|         INST(&V::thumb16_ADD_sp_t1,      "ADD (SP plus imm, T1)",    "10101dddvvvvvvvv"), | ||||
|         INST(&V::thumb16_ADD_sp_t2,      "ADD (SP plus imm, T2)",    "101100000vvvvvvv"), // v4T | ||||
|         INST(&V::thumb16_SUB_sp,         "SUB (SP minus imm)",       "101100001vvvvvvv"), // v4T | ||||
|  | ||||
|         // Hint instructions | ||||
|         INST(&V::thumb16_NOP,            "NOP",                      "1011111100000000"), // v6T2 | ||||
|         INST(&V::thumb16_SEV,            "SEV",                      "1011111101000000"), // v7 | ||||
|         INST(&V::thumb16_SEVL,           "SEVL",                     "1011111101010000"), // v8 | ||||
|         INST(&V::thumb16_WFE,            "WFE",                      "1011111100100000"), // v7 | ||||
|         INST(&V::thumb16_WFI,            "WFI",                      "1011111100110000"), // v7 | ||||
|         INST(&V::thumb16_YIELD,          "YIELD",                    "1011111100010000"), // v7 | ||||
|  | ||||
|         // Miscellaneous 16-bit instructions | ||||
|         INST(&V::thumb16_SXTH,           "SXTH",                     "1011001000mmmddd"), // v6 | ||||
|         INST(&V::thumb16_SXTB,           "SXTB",                     "1011001001mmmddd"), // v6 | ||||
|         INST(&V::thumb16_UXTH,           "UXTH",                     "1011001010mmmddd"), // v6 | ||||
|         INST(&V::thumb16_UXTB,           "UXTB",                     "1011001011mmmddd"), // v6 | ||||
|         INST(&V::thumb16_PUSH,           "PUSH",                     "1011010Mxxxxxxxx"), // v4T | ||||
|         INST(&V::thumb16_POP,            "POP",                      "1011110Pxxxxxxxx"), // v4T | ||||
|         INST(&V::thumb16_SETEND,         "SETEND",                   "101101100101x000"), // v6 | ||||
|         INST(&V::thumb16_CPS,            "CPS",                      "10110110011m0aif"), // v6 | ||||
|         INST(&V::thumb16_REV,            "REV",                      "1011101000mmmddd"), // v6 | ||||
|         INST(&V::thumb16_REV16,          "REV16",                    "1011101001mmmddd"), // v6 | ||||
|         INST(&V::thumb16_REVSH,          "REVSH",                    "1011101011mmmddd"), // v6 | ||||
|         INST(&V::thumb16_BKPT,           "BKPT",                     "10111110xxxxxxxx"), // v5 | ||||
|  | ||||
|         // Store/Load multiple registers | ||||
|         INST(&V::thumb16_STMIA,          "STMIA",                    "11000nnnxxxxxxxx"), | ||||
|         INST(&V::thumb16_LDMIA,          "LDMIA",                    "11001nnnxxxxxxxx"), | ||||
|  | ||||
|         // Branch instructions | ||||
|         INST(&V::thumb16_BX,             "BX",                       "010001110mmmm000"), // v4T | ||||
|         INST(&V::thumb16_BLX_reg,        "BLX (reg)",                "010001111mmmm000"), // v5T | ||||
|         INST(&V::thumb16_CBZ_CBNZ,       "CBZ/CBNZ",                 "1011o0i1iiiiinnn"), // v6T2 | ||||
|         INST(&V::thumb16_UDF,            "UDF",                      "11011110--------"), | ||||
|         INST(&V::thumb16_SVC,            "SVC",                      "11011111xxxxxxxx"), | ||||
|         INST(&V::thumb16_B_t1,           "B (T1)",                   "1101ccccvvvvvvvv"), | ||||
|         INST(&V::thumb16_B_t2,           "B (T2)",                   "11100vvvvvvvvvvv"), | ||||
|  | ||||
| #define INST(fn, name, bitstring) Decoder::detail::detail<Thumb16Matcher<V>>::GetMatcher(&V::fn, name, bitstring), | ||||
| #include "thumb16.inc" | ||||
| #undef INST | ||||
|  | ||||
|     }; | ||||
|   | ||||
							
								
								
									
										98
									
								
								externals/dynarmic/src/frontend/A32/decoder/thumb16.inc
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										98
									
								
								externals/dynarmic/src/frontend/A32/decoder/thumb16.inc
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| // Shift (immediate) add, subtract, move and compare instructions | ||||
| INST(thumb16_LSL_imm,        "LSL (imm)",                "00000vvvvvmmmddd") | ||||
| INST(thumb16_LSR_imm,        "LSR (imm)",                "00001vvvvvmmmddd") | ||||
| INST(thumb16_ASR_imm,        "ASR (imm)",                "00010vvvvvmmmddd") | ||||
| INST(thumb16_ADD_reg_t1,     "ADD (reg, T1)",            "0001100mmmnnnddd") | ||||
| INST(thumb16_SUB_reg,        "SUB (reg)",                "0001101mmmnnnddd") | ||||
| INST(thumb16_ADD_imm_t1,     "ADD (imm, T1)",            "0001110vvvnnnddd") | ||||
| INST(thumb16_SUB_imm_t1,     "SUB (imm, T1)",            "0001111vvvnnnddd") | ||||
| INST(thumb16_MOV_imm,        "MOV (imm)",                "00100dddvvvvvvvv") | ||||
| INST(thumb16_CMP_imm,        "CMP (imm)",                "00101nnnvvvvvvvv") | ||||
| INST(thumb16_ADD_imm_t2,     "ADD (imm, T2)",            "00110dddvvvvvvvv") | ||||
| INST(thumb16_SUB_imm_t2,     "SUB (imm, T2)",            "00111dddvvvvvvvv") | ||||
|  | ||||
|  // Data-processing instructions | ||||
| INST(thumb16_AND_reg,        "AND (reg)",                "0100000000mmmddd") | ||||
| INST(thumb16_EOR_reg,        "EOR (reg)",                "0100000001mmmddd") | ||||
| INST(thumb16_LSL_reg,        "LSL (reg)",                "0100000010mmmddd") | ||||
| INST(thumb16_LSR_reg,        "LSR (reg)",                "0100000011mmmddd") | ||||
| INST(thumb16_ASR_reg,        "ASR (reg)",                "0100000100mmmddd") | ||||
| INST(thumb16_ADC_reg,        "ADC (reg)",                "0100000101mmmddd") | ||||
| INST(thumb16_SBC_reg,        "SBC (reg)",                "0100000110mmmddd") | ||||
| INST(thumb16_ROR_reg,        "ROR (reg)",                "0100000111sssddd") | ||||
| INST(thumb16_TST_reg,        "TST (reg)",                "0100001000mmmnnn") | ||||
| INST(thumb16_RSB_imm,        "RSB (imm)",                "0100001001nnnddd") | ||||
| INST(thumb16_CMP_reg_t1,     "CMP (reg, T1)",            "0100001010mmmnnn") | ||||
| INST(thumb16_CMN_reg,        "CMN (reg)",                "0100001011mmmnnn") | ||||
| INST(thumb16_ORR_reg,        "ORR (reg)",                "0100001100mmmddd") | ||||
| INST(thumb16_MUL_reg,        "MUL (reg)",                "0100001101nnnddd") | ||||
| INST(thumb16_BIC_reg,        "BIC (reg)",                "0100001110mmmddd") | ||||
| INST(thumb16_MVN_reg,        "MVN (reg)",                "0100001111mmmddd") | ||||
|  | ||||
| // Special data instructions | ||||
| INST(thumb16_ADD_reg_t2,     "ADD (reg, T2)",            "01000100Dmmmmddd") // v4T, Low regs: v6T2 | ||||
| INST(thumb16_CMP_reg_t2,     "CMP (reg, T2)",            "01000101Nmmmmnnn") // v4T | ||||
| INST(thumb16_MOV_reg,        "MOV (reg)",                "01000110Dmmmmddd") // v4T, Low regs: v6 | ||||
|  | ||||
| // Store/Load single data item instructions | ||||
| INST(thumb16_LDR_literal,    "LDR (literal)",            "01001tttvvvvvvvv") | ||||
| INST(thumb16_STR_reg,        "STR (reg)",                "0101000mmmnnnttt") | ||||
| INST(thumb16_STRH_reg,       "STRH (reg)",               "0101001mmmnnnttt") | ||||
| INST(thumb16_STRB_reg,       "STRB (reg)",               "0101010mmmnnnttt") | ||||
| INST(thumb16_LDRSB_reg,      "LDRSB (reg)",              "0101011mmmnnnttt") | ||||
| INST(thumb16_LDR_reg,        "LDR (reg)",                "0101100mmmnnnttt") | ||||
| INST(thumb16_LDRH_reg,       "LDRH (reg)",               "0101101mmmnnnttt") | ||||
| INST(thumb16_LDRB_reg,       "LDRB (reg)",               "0101110mmmnnnttt") | ||||
| INST(thumb16_LDRSH_reg,      "LDRSH (reg)",              "0101111mmmnnnttt") | ||||
| INST(thumb16_STR_imm_t1,     "STR (imm, T1)",            "01100vvvvvnnnttt") | ||||
| INST(thumb16_LDR_imm_t1,     "LDR (imm, T1)",            "01101vvvvvnnnttt") | ||||
| INST(thumb16_STRB_imm,       "STRB (imm)",               "01110vvvvvnnnttt") | ||||
| INST(thumb16_LDRB_imm,       "LDRB (imm)",               "01111vvvvvnnnttt") | ||||
| INST(thumb16_STRH_imm,       "STRH (imm)",               "10000vvvvvnnnttt") | ||||
| INST(thumb16_LDRH_imm,       "LDRH (imm)",               "10001vvvvvnnnttt") | ||||
| INST(thumb16_STR_imm_t2,     "STR (imm, T2)",            "10010tttvvvvvvvv") | ||||
| INST(thumb16_LDR_imm_t2,     "LDR (imm, T2)",            "10011tttvvvvvvvv") | ||||
|  | ||||
| // Generate relative address instructions | ||||
| INST(thumb16_ADR,            "ADR",                      "10100dddvvvvvvvv") | ||||
| INST(thumb16_ADD_sp_t1,      "ADD (SP plus imm, T1)",    "10101dddvvvvvvvv") | ||||
| INST(thumb16_ADD_sp_t2,      "ADD (SP plus imm, T2)",    "101100000vvvvvvv") // v4T | ||||
| INST(thumb16_SUB_sp,         "SUB (SP minus imm)",       "101100001vvvvvvv") // v4T | ||||
|  | ||||
| // Hint instructions | ||||
| INST(thumb16_SEV,            "SEV",                      "1011111101000000") // v7 | ||||
| INST(thumb16_SEVL,           "SEVL",                     "1011111101010000") // v8 | ||||
| INST(thumb16_WFE,            "WFE",                      "1011111100100000") // v7 | ||||
| INST(thumb16_WFI,            "WFI",                      "1011111100110000") // v7 | ||||
| INST(thumb16_YIELD,          "YIELD",                    "1011111100010000") // v7 | ||||
| INST(thumb16_NOP,            "NOP",                      "10111111----0000") // v7 | ||||
|  | ||||
| // IT instruction | ||||
| INST(thumb16_IT,             "IT",                       "10111111iiiiiiii") // v7 | ||||
|  | ||||
| // Miscellaneous 16-bit instructions | ||||
| INST(thumb16_SXTH,           "SXTH",                     "1011001000mmmddd") // v6 | ||||
| INST(thumb16_SXTB,           "SXTB",                     "1011001001mmmddd") // v6 | ||||
| INST(thumb16_UXTH,           "UXTH",                     "1011001010mmmddd") // v6 | ||||
| INST(thumb16_UXTB,           "UXTB",                     "1011001011mmmddd") // v6 | ||||
| INST(thumb16_PUSH,           "PUSH",                     "1011010Mxxxxxxxx") // v4T | ||||
| INST(thumb16_POP,            "POP",                      "1011110Pxxxxxxxx") // v4T | ||||
| INST(thumb16_SETEND,         "SETEND",                   "101101100101x000") // v6 | ||||
| INST(thumb16_CPS,            "CPS",                      "10110110011m0aif") // v6 | ||||
| INST(thumb16_REV,            "REV",                      "1011101000mmmddd") // v6 | ||||
| INST(thumb16_REV16,          "REV16",                    "1011101001mmmddd") // v6 | ||||
| INST(thumb16_REVSH,          "REVSH",                    "1011101011mmmddd") // v6 | ||||
| INST(thumb16_BKPT,           "BKPT",                     "10111110xxxxxxxx") // v5 | ||||
|  | ||||
| // Store/Load multiple registers | ||||
| INST(thumb16_STMIA,          "STMIA",                    "11000nnnxxxxxxxx") | ||||
| INST(thumb16_LDMIA,          "LDMIA",                    "11001nnnxxxxxxxx") | ||||
|  | ||||
| // Branch instructions | ||||
| INST(thumb16_BX,             "BX",                       "010001110mmmm000") // v4T | ||||
| INST(thumb16_BLX_reg,        "BLX (reg)",                "010001111mmmm000") // v5T | ||||
| INST(thumb16_CBZ_CBNZ,       "CBZ/CBNZ",                 "1011o0i1iiiiinnn") // v6T2 | ||||
| INST(thumb16_UDF,            "UDF",                      "11011110--------") | ||||
| INST(thumb16_SVC,            "SVC",                      "11011111xxxxxxxx") | ||||
| INST(thumb16_B_t1,           "B (T1)",                   "1101ccccvvvvvvvv") | ||||
| INST(thumb16_B_t2,           "B (T2)",                   "11100vvvvvvvvvvv") | ||||
| @@ -22,325 +22,8 @@ template<typename V> | ||||
| std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32 instruction) { | ||||
|     static const std::vector<Thumb32Matcher<V>> table = { | ||||
|  | ||||
| #define INST(fn, name, bitstring) Decoder::detail::detail<Thumb32Matcher<V>>::GetMatcher(fn, name, bitstring) | ||||
|  | ||||
|         // Load/Store Multiple | ||||
|         //INST(&V::thumb32_SRS_1,          "SRS",                      "1110100000-0--------------------"), | ||||
|         //INST(&V::thumb32_RFE_2,          "RFE",                      "1110100000-1--------------------"), | ||||
|         //INST(&V::thumb32_STMIA,          "STMIA/STMEA",              "1110100010-0--------------------"), | ||||
|         //INST(&V::thumb32_POP,            "POP",                      "1110100010111101----------------"), | ||||
|         //INST(&V::thumb32_LDMIA,          "LDMIA/LDMFD",              "1110100010-1--------------------"), | ||||
|         //INST(&V::thumb32_PUSH,           "PUSH",                     "1110100100101101----------------"), | ||||
|         //INST(&V::thumb32_STMDB,          "STMDB/STMFD",              "1110100100-0--------------------"), | ||||
|         //INST(&V::thumb32_LDMDB,          "LDMDB/LDMEA",              "1110100100-1--------------------"), | ||||
|         //INST(&V::thumb32_SRS_1,          "SRS",                      "1110100110-0--------------------"), | ||||
|         //INST(&V::thumb32_RFE_2,          "RFE",                      "1110100110-1--------------------"), | ||||
|  | ||||
|         // Load/Store Dual, Load/Store Exclusive, Table Branch | ||||
|         //INST(&V::thumb32_STREX,          "STREX",                    "111010000100--------------------"), | ||||
|         //INST(&V::thumb32_LDREX,          "LDREX",                    "111010000101--------------------"), | ||||
|         //INST(&V::thumb32_STRD_imm_1,     "STRD (imm)",               "11101000-110--------------------"), | ||||
|         //INST(&V::thumb32_STRD_imm_2,     "STRD (imm)",               "11101001-1-0--------------------"), | ||||
|         //INST(&V::thumb32_LDRD_imm_1,     "LDRD (lit)",               "11101000-1111111----------------"), | ||||
|         //INST(&V::thumb32_LDRD_imm_2,     "LDRD (lit)",               "11101001-1-11111----------------"), | ||||
|         //INST(&V::thumb32_LDRD_imm_1,     "LDRD (imm)",               "11101000-111--------------------"), | ||||
|         //INST(&V::thumb32_LDRD_imm_2,     "LDRD (imm)",               "11101001-1-1--------------------"), | ||||
|         //INST(&V::thumb32_STREXB,         "STREXB",                   "111010001100------------0100----"), | ||||
|         //INST(&V::thumb32_STREXH,         "STREXH",                   "111010001100------------0101----"), | ||||
|         //INST(&V::thumb32_STREXD,         "STREXD",                   "111010001100------------0111----"), | ||||
|         //INST(&V::thumb32_TBB,            "TBB",                      "111010001101------------0000----"), | ||||
|         //INST(&V::thumb32_TBH,            "TBH",                      "111010001101------------0001----"), | ||||
|         //INST(&V::thumb32_LDREXB,         "LDREXB",                   "111010001101------------0100----"), | ||||
|         //INST(&V::thumb32_LDREXH,         "LDREXH",                   "111010001101------------0101----"), | ||||
|         //INST(&V::thumb32_LDREXD,         "LDREXD",                   "111010001101------------0111----"), | ||||
|  | ||||
|         // Data Processing (Shifted Register) | ||||
|         //INST(&V::thumb32_TST_reg,        "TST (reg)",                "111010100001--------1111--------"), | ||||
|         //INST(&V::thumb32_AND_reg,        "AND (reg)",                "11101010000---------------------"), | ||||
|         //INST(&V::thumb32_BIC_reg,        "BIC (reg)",                "11101010001---------------------"), | ||||
|         //INST(&V::thumb32_MOV_reg,        "MOV (reg)",                "11101010010-1111-000----0000----"), | ||||
|         //INST(&V::thumb32_LSL_imm,        "LSL (imm)",                "11101010010-1111----------00----"), | ||||
|         //INST(&V::thumb32_LSR_imm,        "LSR (imm)",                "11101010010-1111----------01----"), | ||||
|         //INST(&V::thumb32_ASR_imm,        "ASR (imm)",                "11101010010-1111----------10----"), | ||||
|         //INST(&V::thumb32_RRX,            "RRX",                      "11101010010-1111-000----0011----"), | ||||
|         //INST(&V::thumb32_ROR_imm,        "ROR (imm)",                "11101010010-1111----------11----"), | ||||
|         //INST(&V::thumb32_ORR_reg,        "ORR (reg)",                "11101010010---------------------"), | ||||
|         //INST(&V::thumb32_MVN_reg,        "MVN (reg)",                "11101010011-1111----------------"), | ||||
|         //INST(&V::thumb32_ORN_reg,        "ORN (reg)",                "11101010011---------------------"), | ||||
|         //INST(&V::thumb32_TEQ_reg,        "TEQ (reg)",                "111010101001--------1111--------"), | ||||
|         //INST(&V::thumb32_EOR_reg,        "EOR (reg)",                "11101010100---------------------"), | ||||
|         //INST(&V::thumb32_PKH,            "PKH",                      "11101010110---------------------"), | ||||
|         //INST(&V::thumb32_CMN_reg,        "CMN (reg)",                "111010110001--------1111--------"), | ||||
|         //INST(&V::thumb32_ADD_reg,        "ADD (reg)",                "11101011000---------------------"), | ||||
|         //INST(&V::thumb32_ADC_reg,        "ADC (reg)",                "11101011010---------------------"), | ||||
|         //INST(&V::thumb32_SBC_reg,        "SBC (reg)",                "11101011011---------------------"), | ||||
|         //INST(&V::thumb32_CMP_reg,        "CMP (reg)",                "111010111011--------1111--------"), | ||||
|         //INST(&V::thumb32_SUB_reg,        "SUB (reg)",                "11101011101---------------------"), | ||||
|         //INST(&V::thumb32_RSB_reg,        "RSB (reg)",                "11101011110---------------------"), | ||||
|  | ||||
|         // Data Processing (Modified Immediate) | ||||
|         //INST(&V::thumb32_TST_imm,        "TST (imm)",                "11110-000001----0---1111--------"), | ||||
|         //INST(&V::thumb32_AND_imm,        "AND (imm)",                "11110-00000-----0---------------"), | ||||
|         //INST(&V::thumb32_BIC_imm,        "BIC (imm)",                "11110-00001-----0---------------"), | ||||
|         //INST(&V::thumb32_MOV_imm,        "MOV (imm)",                "11110000010-11110---------------"), | ||||
|         //INST(&V::thumb32_ORR_imm,        "ORR (imm)",                "11110-00010-----0---------------"), | ||||
|         //INST(&V::thumb32_MVN_imm,        "MVN (imm)",                "11110000011-11110---------------"), | ||||
|         //INST(&V::thumb32_ORN_imm,        "ORN (imm)",                "11110-00011-----0---------------"), | ||||
|         //INST(&V::thumb32_TEQ_imm,        "TEQ (imm)",                "11110-001001----0---1111--------"), | ||||
|         //INST(&V::thumb32_EOR_imm,        "EOR (imm)",                "11110-00100-----0---------------"), | ||||
|         //INST(&V::thumb32_CMN_imm,        "CMN (imm)",                "11110-010001----0---1111--------"), | ||||
|         //INST(&V::thumb32_ADD_imm_1,      "ADD (imm)",                "11110-01000-----0---------------"), | ||||
|         //INST(&V::thumb32_ADC_imm,        "ADC (imm)",                "11110-01010-----0---------------"), | ||||
|         //INST(&V::thumb32_SBC_imm,        "SBC (imm)",                "11110-01011-----0---------------"), | ||||
|         //INST(&V::thumb32_CMP_imm,        "CMP (imm)",                "11110-011011----0---1111--------"), | ||||
|         //INST(&V::thumb32_SUB_imm_1,      "SUB (imm)",                "11110-01101-----0---------------"), | ||||
|         //INST(&V::thumb32_RSB_imm,        "RSB (imm)",                "11110-01110-----0---------------"), | ||||
|  | ||||
|         // Data Processing (Plain Binary Immediate) | ||||
|         //INST(&V::thumb32_ADR,            "ADR",                      "11110-10000011110---------------"), | ||||
|         //INST(&V::thumb32_ADD_imm_2,      "ADD (imm)",                "11110-100000----0---------------"), | ||||
|         //INST(&V::thumb32_MOVW_imm,       "MOVW (imm)",               "11110-100100----0---------------"), | ||||
|         //INST(&V::thumb32_ADR,            "ADR",                      "11110-10101011110---------------"), | ||||
|         //INST(&V::thumb32_SUB_imm_2,      "SUB (imm)",                "11110-101010----0---------------"), | ||||
|         //INST(&V::thumb32_MOVT,           "MOVT",                     "11110-101100----0---------------"), | ||||
|         //INST(&V::thumb32_SSAT,           "SSAT",                     "11110-110000----0---------------"), | ||||
|         //INST(&V::thumb32_SSAT16,         "SSAT16",                   "11110-110010----0000----00------"), | ||||
|         //INST(&V::thumb32_SSAT,           "SSAT",                     "11110-110010----0---------------"), | ||||
|         //INST(&V::thumb32_SBFX,           "SBFX",                     "11110-110100----0---------------"), | ||||
|         //INST(&V::thumb32_BFC,            "BFC",                      "11110-11011011110---------------"), | ||||
|         //INST(&V::thumb32_BFI,            "BFI",                      "11110-110110----0---------------"), | ||||
|         //INST(&V::thumb32_USAT,           "USAT",                     "11110-111000----0---------------"), | ||||
|         //INST(&V::thumb32_USAT16,         "USAT16",                   "11110-111010----0000----00------"), | ||||
|         //INST(&V::thumb32_USAT,           "USAT",                     "11110-111010----0---------------"), | ||||
|         //INST(&V::thumb32_UBFX,           "UBFX",                     "11110-111100----0---------------"), | ||||
|  | ||||
|         // Branches and Miscellaneous Control | ||||
|         //INST(&V::thumb32_MSR_banked,     "MSR (banked)",             "11110011100-----10-0------1-----"), | ||||
|         //INST(&V::thumb32_MSR_reg_1,      "MSR (reg)",                "111100111001----10-0------0-----"), | ||||
|         //INST(&V::thumb32_MSR_reg_2,      "MSR (reg)",                "111100111000----10-0--01--0-----"), | ||||
|         //INST(&V::thumb32_MSR_reg_3,      "MSR (reg)",                "111100111000----10-0--1---0-----"), | ||||
|         //INST(&V::thumb32_MSR_reg_4,      "MSR (reg)",                "111100111000----10-0--00--0-----"), | ||||
|  | ||||
|         //INST(&V::thumb32_NOP,            "NOP",                      "111100111010----10-0-00000000000"), | ||||
|         //INST(&V::thumb32_YIELD,          "YIELD",                    "111100111010----10-0-00000000001"), | ||||
|         //INST(&V::thumb32_WFE,            "WFE",                      "111100111010----10-0-00000000010"), | ||||
|         //INST(&V::thumb32_WFI,            "WFI",                      "111100111010----10-0-00000000011"), | ||||
|         //INST(&V::thumb32_SEV,            "SEV",                      "111100111010----10-0-00000000100"), | ||||
|         //INST(&V::thumb32_SEVL,           "SEVL",                     "111100111010----10-0-00000000101"), | ||||
|         //INST(&V::thumb32_DBG,            "DBG",                      "111100111010----10-0-0001111----"), | ||||
|         //INST(&V::thumb32_CPS,            "CPS",                      "111100111010----10-0------------"), | ||||
|  | ||||
|         //INST(&V::thumb32_ENTERX,         "ENTERX",                   "111100111011----10-0----0001----"), | ||||
|         //INST(&V::thumb32_LEAVEX,         "LEAVEX",                   "111100111011----10-0----0000----"), | ||||
|         //INST(&V::thumb32_CLREX,          "CLREX",                    "111100111011----10-0----0010----"), | ||||
|         //INST(&V::thumb32_DSB,            "DSB",                      "111100111011----10-0----0100----"), | ||||
|         //INST(&V::thumb32_DMB,            "DMB",                      "111100111011----10-0----0101----"), | ||||
|         //INST(&V::thumb32_ISB,            "ISB",                      "111100111011----10-0----0110----"), | ||||
|  | ||||
|         //INST(&V::thumb32_BXJ,            "BXJ",                      "111100111100----1000111100000000"), | ||||
|         //INST(&V::thumb32_ERET,           "ERET",                     "11110011110111101000111100000000"), | ||||
|         //INST(&V::thumb32_SUBS_pc_lr,     "SUBS PC, LR",              "111100111101111010001111--------"), | ||||
|  | ||||
|         //INST(&V::thumb32_MRS_banked,     "MRS (banked)",             "11110011111-----10-0------1-----"), | ||||
|         //INST(&V::thumb32_MRS_reg_1,      "MRS (reg)",                "111100111111----10-0------0-----"), | ||||
|         //INST(&V::thumb32_MRS_reg_2,      "MRS (reg)",                "111100111110----10-0------0-----"), | ||||
|         //INST(&V::thumb32_HVC,            "HVC",                      "111101111110----1000------------"), | ||||
|         //INST(&V::thumb32_SMC,            "SMC",                      "111101111111----1000000000000000"), | ||||
|         //INST(&V::thumb32_UDF,            "UDF",                      "111101111111----1010------------"), | ||||
|  | ||||
|         //INST(&V::thumb32_BL,             "BL",                       "11110-----------11-1------------"), | ||||
|         //INST(&V::thumb32_BLX,            "BLX",                      "11110-----------11-0------------"), | ||||
|         //INST(&V::thumb32_B,              "B",                        "11110-----------10-1------------"), | ||||
|         //INST(&V::thumb32_B_cond,         "B (cond)",                 "11110-----------10-0------------"), | ||||
|  | ||||
|         // Store Single Data Item | ||||
|         //INST(&V::thumb32_STRB_imm_1,     "STRB (imm)",               "111110000000--------1--1--------"), | ||||
|         //INST(&V::thumb32_STRB_imm_2,     "STRB (imm)",               "111110000000--------1100--------"), | ||||
|         //INST(&V::thumb32_STRB_imm_3,     "STRB (imm)",               "111110001000--------------------"), | ||||
|         //INST(&V::thumb32_STRBT,          "STRBT",                    "111110000000--------1110--------"), | ||||
|         //INST(&V::thumb32_STRB,           "STRB (reg)",               "111110000000--------000000------"), | ||||
|         //INST(&V::thumb32_STRH_imm_1,     "STRH (imm)",               "111110000010--------1--1--------"), | ||||
|         //INST(&V::thumb32_STRH_imm_2,     "STRH (imm)",               "111110000010--------1100--------"), | ||||
|         //INST(&V::thumb32_STRH_imm_3,     "STRH (imm)",               "111110001010--------------------"), | ||||
|         //INST(&V::thumb32_STRHT,          "STRHT",                    "111110000010--------1110--------"), | ||||
|         //INST(&V::thumb32_STRH,           "STRH (reg)",               "111110000010--------000000------"), | ||||
|         //INST(&V::thumb32_STR_imm_1,      "STR (imm)",                "111110000100--------1--1--------"), | ||||
|         //INST(&V::thumb32_STR_imm_2,      "STR (imm)",                "111110000100--------1100--------"), | ||||
|         //INST(&V::thumb32_STR_imm_3,      "STR (imm)",                "111110001100--------------------"), | ||||
|         //INST(&V::thumb32_STRT,           "STRT",                     "111110000100--------1110--------"), | ||||
|         //INST(&V::thumb32_STR_reg,        "STR (reg)",                "111110000100--------000000------"), | ||||
|  | ||||
|         // Load Byte and Memory Hints | ||||
|         //INST(&V::thumb32_PLD_lit,        "PLD (lit)",                "11111000-00111111111------------"), | ||||
|         //INST(&V::thumb32_PLD_reg,        "PLD (reg)",                "111110000001----1111000000------"), | ||||
|         //INST(&V::thumb32_PLD_imm8,       "PLD (imm8)",               "1111100000-1----11111100--------"), | ||||
|         //INST(&V::thumb32_PLD_imm12,      "PLD (imm12)",              "111110001001----1111------------"), | ||||
|         //INST(&V::thumb32_PLI_lit,        "PLI (lit)",                "11111001-00111111111------------"), | ||||
|         //INST(&V::thumb32_PLI_reg,        "PLI (reg)",                "111110010001----1111000000------"), | ||||
|         //INST(&V::thumb32_PLI_imm8,       "PLI (imm8)",               "111110010001----11111100--------"), | ||||
|         //INST(&V::thumb32_PLI_imm12,      "PLI (imm12)",              "111110011001----1111------------"), | ||||
|         //INST(&V::thumb32_LDRB_lit,       "LDRB (lit)",               "11111000-0011111----------------"), | ||||
|         //INST(&V::thumb32_LDRB_reg,       "LDRB (reg)",               "111110000001--------000000------"), | ||||
|         //INST(&V::thumb32_LDRBT,          "LDRBT",                    "111110000001--------1110--------"), | ||||
|         //INST(&V::thumb32_LDRB_imm8,      "LDRB (imm8)",              "111110000001--------1-----------"), | ||||
|         //INST(&V::thumb32_LDRB_imm12,     "LDRB (imm12)",             "111110001001--------------------"), | ||||
|         //INST(&V::thumb32_LDRSB_lit,      "LDRSB (lit)",              "11111001-0011111----------------"), | ||||
|         //INST(&V::thumb32_LDRSB_reg,      "LDRSB (reg)",              "111110010001--------000000------"), | ||||
|         //INST(&V::thumb32_LDRSBT,         "LDRSBT",                   "111110010001--------1110--------"), | ||||
|         //INST(&V::thumb32_LDRSB_imm8,     "LDRSB (imm8)",             "111110010001--------1-----------"), | ||||
|         //INST(&V::thumb32_LDRSB_imm12,    "LDRSB (imm12)",            "111110011001--------------------"), | ||||
|  | ||||
|         // Load Halfword and Memory Hints | ||||
|         //INST(&V::thumb32_LDRH_lit,       "LDRH (lit)",               "11111000-0111111----------------"), | ||||
|         //INST(&V::thumb32_LDRH_reg,       "LDRH (reg)",               "111110000011--------000000------"), | ||||
|         //INST(&V::thumb32_LDRHT,          "LDRHT",                    "111110000011--------1110--------"), | ||||
|         //INST(&V::thumb32_LDRH_imm8,      "LDRH (imm8)",              "111110000011--------1-----------"), | ||||
|         //INST(&V::thumb32_LDRH_imm12,     "LDRH (imm12)",             "111110001011--------------------"), | ||||
|         //INST(&V::thumb32_LDRSH_lit,      "LDRSH (lit)",              "11111001-0111111----------------"), | ||||
|         //INST(&V::thumb32_LDRSH_reg,      "LDRSH (reg)",              "111110010011--------000000------"), | ||||
|         //INST(&V::thumb32_LDRSHT,         "LDRSHT",                   "111110010011--------1110--------"), | ||||
|         //INST(&V::thumb32_LDRSH_imm8,     "LDRSH (imm8)",             "111110010011--------1-----------"), | ||||
|         //INST(&V::thumb32_LDRSH_imm12,    "LDRSH (imm12)",            "111110011011--------------------"), | ||||
|         //INST(&V::thumb32_NOP,            "NOP",                      "111110010011----1111000000------"), | ||||
|         //INST(&V::thumb32_NOP,            "NOP",                      "111110010011----11111100--------"), | ||||
|         //INST(&V::thumb32_NOP,            "NOP",                      "11111001-01111111111------------"), | ||||
|         //INST(&V::thumb32_NOP,            "NOP",                      "111110011011----1111------------"), | ||||
|  | ||||
|         // Load Word | ||||
|         //INST(&V::thumb32_LDR_lit,        "LDR (lit)",                "11111000-1011111----------------"), | ||||
|         //INST(&V::thumb32_LDRT,           "LDRT",                     "111110000101--------1110--------"), | ||||
|         //INST(&V::thumb32_LDR_reg,        "LDR (reg)",                "111110000101--------000000------"), | ||||
|         //INST(&V::thumb32_LDR_imm8,       "LDR (imm8)",               "111110000101--------1-----------"), | ||||
|         //INST(&V::thumb32_LDR_imm12,      "LDR (imm12)",              "111110001101--------------------"), | ||||
|  | ||||
|         // Undefined | ||||
|         //INST(&V::thumb32_UDF,            "UDF",                      "1111100--111--------------------"), | ||||
|  | ||||
|         // Data Processing (register) | ||||
|         //INST(&V::thumb32_LSL_reg,        "LSL (reg)",                "11111010000-----1111----0000----"), | ||||
|         //INST(&V::thumb32_LSR_reg,        "LSR (reg)",                "11111010001-----1111----0000----"), | ||||
|         //INST(&V::thumb32_ASR_reg,        "ASR (reg)",                "11111010010-----1111----0000----"), | ||||
|         //INST(&V::thumb32_ROR_reg,        "ROR (reg)",                "11111010011-----1111----0000----"), | ||||
|         //INST(&V::thumb32_SXTH,           "SXTH",                     "11111010000011111111----1-------"), | ||||
|         //INST(&V::thumb32_SXTAH,          "SXTAH",                    "111110100000----1111----1-------"), | ||||
|         //INST(&V::thumb32_UXTH,           "UXTH",                     "11111010000111111111----1-------"), | ||||
|         //INST(&V::thumb32_UXTAH,          "UXTAH",                    "111110100001----1111----1-------"), | ||||
|         //INST(&V::thumb32_SXTB16,         "SXTB16",                   "11111010001011111111----1-------"), | ||||
|         //INST(&V::thumb32_SXTAB16,        "SXTAB16",                  "111110100010----1111----1-------"), | ||||
|         //INST(&V::thumb32_UXTB16,         "UXTB16",                   "11111010001111111111----1-------"), | ||||
|         //INST(&V::thumb32_UXTAB16,        "UXTAB16",                  "111110100011----1111----1-------"), | ||||
|         //INST(&V::thumb32_SXTB,           "SXTB",                     "11111010010011111111----1-------"), | ||||
|         //INST(&V::thumb32_SXTAB,          "SXTAB",                    "111110100100----1111----1-------"), | ||||
|         //INST(&V::thumb32_UXTB,           "UXTB",                     "11111010010111111111----1-------"), | ||||
|         //INST(&V::thumb32_UXTAB,          "UXTAB",                    "111110100101----1111----1-------"), | ||||
|  | ||||
|         // Parallel Addition and Subtraction (signed) | ||||
|         //INST(&V::thumb32_SADD16,         "SADD16",                   "111110101001----1111----0000----"), | ||||
|         //INST(&V::thumb32_SASX,           "SASX",                     "111110101010----1111----0000----"), | ||||
|         //INST(&V::thumb32_SSAX,           "SSAX",                     "111110101110----1111----0000----"), | ||||
|         //INST(&V::thumb32_SSUB16,         "SSUB16",                   "111110101101----1111----0000----"), | ||||
|         //INST(&V::thumb32_SADD8,          "SADD8",                    "111110101000----1111----0000----"), | ||||
|         //INST(&V::thumb32_SSUB8,          "SSUB8",                    "111110101100----1111----0000----"), | ||||
|         //INST(&V::thumb32_QADD16,         "QADD16",                   "111110101001----1111----0001----"), | ||||
|         //INST(&V::thumb32_QASX,           "QASX",                     "111110101010----1111----0001----"), | ||||
|         //INST(&V::thumb32_QSAX,           "QSAX",                     "111110101110----1111----0001----"), | ||||
|         //INST(&V::thumb32_QSUB16,         "QSUB16",                   "111110101101----1111----0001----"), | ||||
|         //INST(&V::thumb32_QADD8,          "QADD8",                    "111110101000----1111----0001----"), | ||||
|         //INST(&V::thumb32_QSUB8,          "QSUB8",                    "111110101100----1111----0001----"), | ||||
|         //INST(&V::thumb32_SHADD16,        "SHADD16",                  "111110101001----1111----0010----"), | ||||
|         //INST(&V::thumb32_SHASX,          "SHASX",                    "111110101010----1111----0010----"), | ||||
|         //INST(&V::thumb32_SHSAX,          "SHSAX",                    "111110101110----1111----0010----"), | ||||
|         //INST(&V::thumb32_SHSUB16,        "SHSUB16",                  "111110101101----1111----0010----"), | ||||
|         //INST(&V::thumb32_SHADD8,         "SHADD8",                   "111110101000----1111----0010----"), | ||||
|         //INST(&V::thumb32_SHSUB8,         "SHSUB8",                   "111110101100----1111----0010----"), | ||||
|  | ||||
|         // Parallel Addition and Subtraction (unsigned) | ||||
|         //INST(&V::thumb32_UADD16,         "UADD16",                   "111110101001----1111----0100----"), | ||||
|         //INST(&V::thumb32_UASX,           "UASX",                     "111110101010----1111----0100----"), | ||||
|         //INST(&V::thumb32_USAX,           "USAX",                     "111110101110----1111----0100----"), | ||||
|         //INST(&V::thumb32_USUB16,         "USUB16",                   "111110101101----1111----0100----"), | ||||
|         //INST(&V::thumb32_UADD8,          "UADD8",                    "111110101000----1111----0100----"), | ||||
|         //INST(&V::thumb32_USUB8,          "USUB8",                    "111110101100----1111----0100----"), | ||||
|         //INST(&V::thumb32_UQADD16,        "UQADD16",                  "111110101001----1111----0101----"), | ||||
|         //INST(&V::thumb32_UQASX,          "UQASX",                    "111110101010----1111----0101----"), | ||||
|         //INST(&V::thumb32_UQSAX,          "UQSAX",                    "111110101110----1111----0101----"), | ||||
|         //INST(&V::thumb32_UQSUB16,        "UQSUB16",                  "111110101101----1111----0101----"), | ||||
|         //INST(&V::thumb32_UQADD8,         "UQADD8",                   "111110101000----1111----0101----"), | ||||
|         //INST(&V::thumb32_UQSUB8,         "UQSUB8",                   "111110101100----1111----0101----"), | ||||
|         //INST(&V::thumb32_UHADD16,        "UHADD16",                  "111110101001----1111----0110----"), | ||||
|         //INST(&V::thumb32_UHASX,          "UHASX",                    "111110101010----1111----0110----"), | ||||
|         //INST(&V::thumb32_UHSAX,          "UHSAX",                    "111110101110----1111----0110----"), | ||||
|         //INST(&V::thumb32_UHSUB16,        "UHSUB16",                  "111110101101----1111----0110----"), | ||||
|         //INST(&V::thumb32_UHADD8,         "UHADD8",                   "111110101000----1111----0110----"), | ||||
|         //INST(&V::thumb32_UHSUB8,         "UHSUB8",                   "111110101100----1111----0110----"), | ||||
|  | ||||
|         // Miscellaneous Operations | ||||
|         //INST(&V::thumb32_QADD,           "QADD",                     "111110101000----1111----1000----"), | ||||
|         //INST(&V::thumb32_QDADD,          "QDADD",                    "111110101000----1111----1001----"), | ||||
|         //INST(&V::thumb32_QSUB,           "QSUB",                     "111110101000----1111----1010----"), | ||||
|         //INST(&V::thumb32_QDSUB,          "QDSUB",                    "111110101000----1111----1011----"), | ||||
|         //INST(&V::thumb32_REV,            "REV",                      "111110101001----1111----1000----"), | ||||
|         //INST(&V::thumb32_REV16,          "REV16",                    "111110101001----1111----1001----"), | ||||
|         //INST(&V::thumb32_RBIT,           "RBIT",                     "111110101001----1111----1010----"), | ||||
|         //INST(&V::thumb32_REVSH,          "REVSH",                    "111110101001----1111----1011----"), | ||||
|         //INST(&V::thumb32_SEL,            "SEL",                      "111110101010----1111----1000----"), | ||||
|         //INST(&V::thumb32_CLZ,            "CLZ",                      "111110101011----1111----1000----"), | ||||
|  | ||||
|         // Multiply, Multiply Accumulate, and Absolute Difference | ||||
|         //INST(&V::thumb32_MUL,            "MUL",                      "111110110000----1111----0000----"), | ||||
|         //INST(&V::thumb32_MLA,            "MLA",                      "111110110000------------0000----"), | ||||
|         //INST(&V::thumb32_MLS,            "MLS",                      "111110110000------------0001----"), | ||||
|         //INST(&V::thumb32_SMULXY,         "SMULXY",                   "111110110001----1111----00------"), | ||||
|         //INST(&V::thumb32_SMLAXY,         "SMLAXY",                   "111110110001------------00------"), | ||||
|         //INST(&V::thumb32_SMUAD,          "SMUAD",                    "111110110010----1111----000-----"), | ||||
|         //INST(&V::thumb32_SMLAD,          "SMLAD",                    "111110110010------------000-----"), | ||||
|         //INST(&V::thumb32_SMULWY,         "SMULWY",                   "111110110011----1111----000-----"), | ||||
|         //INST(&V::thumb32_SMLAWY,         "SMLAWY",                   "111110110011------------000-----"), | ||||
|         //INST(&V::thumb32_SMUSD,          "SMUSD",                    "111110110100----1111----000-----"), | ||||
|         //INST(&V::thumb32_SMLSD,          "SMLSD",                    "111110110100------------000-----"), | ||||
|         //INST(&V::thumb32_SMMUL,          "SMMUL",                    "111110110101----1111----000-----"), | ||||
|         //INST(&V::thumb32_SMMLA,          "SMMLA",                    "111110110101------------000-----"), | ||||
|         //INST(&V::thumb32_SMMLS,          "SMMLS",                    "111110110110------------000-----"), | ||||
|         //INST(&V::thumb32_USAD8,          "USAD8",                    "111110110111----1111----0000----"), | ||||
|         //INST(&V::thumb32_USADA8,         "USADA8",                   "111110110111------------0000----"), | ||||
|  | ||||
|         // Long Multiply, Long Multiply Accumulate, and Divide | ||||
|         //INST(&V::thumb32_SMULL,          "SMULL",                    "111110111000------------0000----"), | ||||
|         //INST(&V::thumb32_SDIV,           "SDIV",                     "111110111001------------1111----"), | ||||
|         //INST(&V::thumb32_UMULL,          "UMULL",                    "111110111010------------0000----"), | ||||
|         //INST(&V::thumb32_UDIV,           "UDIV",                     "111110111011------------1111----"), | ||||
|         //INST(&V::thumb32_SMLAL,          "SMLAL",                    "111110111100------------0000----"), | ||||
|         //INST(&V::thumb32_SMLALXY,        "SMLALXY",                  "111110111100------------10------"), | ||||
|         //INST(&V::thumb32_SMLALD,         "SMLALD",                   "111110111100------------110-----"), | ||||
|         //INST(&V::thumb32_SMLSLD,         "SMLSLD",                   "111110111101------------110-----"), | ||||
|         //INST(&V::thumb32_UMLAL,          "UMLAL",                    "111110111110------------0000----"), | ||||
|         //INST(&V::thumb32_UMAAL,          "UMAAL",                    "111110111110------------0110----"), | ||||
|  | ||||
|         // Coprocessor | ||||
|         //INST(&V::thumb32_MCRR2,          "MCRR2",                    "111111000100--------------------"), | ||||
|         //INST(&V::thumb32_MCRR,           "MCRR",                     "111011000100--------------------"), | ||||
|         //INST(&V::thumb32_STC2,           "STC2",                     "1111110----0--------------------"), | ||||
|         //INST(&V::thumb32_STC,            "STC",                      "1110110----0--------------------"), | ||||
|         //INST(&V::thumb32_MRRC2,          "MRRC2",                    "111111000101--------------------"), | ||||
|         //INST(&V::thumb32_MRRC,           "MRRC",                     "111011000101--------------------"), | ||||
|         //INST(&V::thumb32_LDC2_lit,       "LDC2 (lit)",               "1111110----11111----------------"), | ||||
|         //INST(&V::thumb32_LDC_lit,        "LDC (lit)",                "1110110----11111----------------"), | ||||
|         //INST(&V::thumb32_LDC2_imm,       "LDC2 (imm)",               "1111110----1--------------------"), | ||||
|         //INST(&V::thumb32_LDC_imm,        "LDC (imm)",                "1110110----1--------------------"), | ||||
|         //INST(&V::thumb32_CDP2,           "CDP2",                     "11111110-------------------0----"), | ||||
|         //INST(&V::thumb32_CDP,            "CDP",                      "11101110-------------------0----"), | ||||
|         //INST(&V::thumb32_MCR2,           "MCR2",                     "11111110---0---------------1----"), | ||||
|         //INST(&V::thumb32_MCR,            "MCR",                      "11101110---0---------------1----"), | ||||
|         //INST(&V::thumb32_MRC2,           "MRC2",                     "11111110---1---------------1----"), | ||||
|         //INST(&V::thumb32_MRC,            "MRC",                      "11101110---1---------------1----"), | ||||
|  | ||||
|         // Branch instructions | ||||
|         INST(&V::thumb32_BL_imm,         "BL (imm)",                 "11110vvvvvvvvvvv11111vvvvvvvvvvv"), // v4T | ||||
|         INST(&V::thumb32_BLX_imm,        "BLX (imm)",                "11110vvvvvvvvvvv11101vvvvvvvvvvv"), // v5T | ||||
|  | ||||
|         // Misc instructions | ||||
|         INST(&V::thumb32_UDF,            "UDF",                      "111101111111----1010------------"), // v6T2 | ||||
|  | ||||
| #define INST(fn, name, bitstring) Decoder::detail::detail<Thumb32Matcher<V>>::GetMatcher(&V::fn, name, bitstring), | ||||
| #include "thumb32.inc" | ||||
| #undef INST | ||||
|  | ||||
|     }; | ||||
|   | ||||
							
								
								
									
										307
									
								
								externals/dynarmic/src/frontend/A32/decoder/thumb32.inc
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										307
									
								
								externals/dynarmic/src/frontend/A32/decoder/thumb32.inc
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,307 @@ | ||||
| // Load/Store Multiple | ||||
| //INST(thumb32_SRS_1,          "SRS",                      "1110100000-0--------------------") | ||||
| //INST(thumb32_RFE_2,          "RFE",                      "1110100000-1--------------------") | ||||
| //INST(thumb32_STMIA,          "STMIA/STMEA",              "1110100010-0--------------------") | ||||
| //INST(thumb32_POP,            "POP",                      "1110100010111101----------------") | ||||
| //INST(thumb32_LDMIA,          "LDMIA/LDMFD",              "1110100010-1--------------------") | ||||
| //INST(thumb32_PUSH,           "PUSH",                     "1110100100101101----------------") | ||||
| //INST(thumb32_STMDB,          "STMDB/STMFD",              "1110100100-0--------------------") | ||||
| //INST(thumb32_LDMDB,          "LDMDB/LDMEA",              "1110100100-1--------------------") | ||||
| //INST(thumb32_SRS_1,          "SRS",                      "1110100110-0--------------------") | ||||
| //INST(thumb32_RFE_2,          "RFE",                      "1110100110-1--------------------") | ||||
|  | ||||
| // Load/Store Dual, Load/Store Exclusive, Table Branch | ||||
| //INST(thumb32_STREX,          "STREX",                    "111010000100--------------------") | ||||
| //INST(thumb32_LDREX,          "LDREX",                    "111010000101--------------------") | ||||
| //INST(thumb32_STRD_imm_1,     "STRD (imm)",               "11101000-110--------------------") | ||||
| //INST(thumb32_STRD_imm_2,     "STRD (imm)",               "11101001-1-0--------------------") | ||||
| //INST(thumb32_LDRD_imm_1,     "LDRD (lit)",               "11101000-1111111----------------") | ||||
| //INST(thumb32_LDRD_imm_2,     "LDRD (lit)",               "11101001-1-11111----------------") | ||||
| //INST(thumb32_LDRD_imm_1,     "LDRD (imm)",               "11101000-111--------------------") | ||||
| //INST(thumb32_LDRD_imm_2,     "LDRD (imm)",               "11101001-1-1--------------------") | ||||
| //INST(thumb32_STREXB,         "STREXB",                   "111010001100------------0100----") | ||||
| //INST(thumb32_STREXH,         "STREXH",                   "111010001100------------0101----") | ||||
| //INST(thumb32_STREXD,         "STREXD",                   "111010001100------------0111----") | ||||
| //INST(thumb32_TBB,            "TBB",                      "111010001101------------0000----") | ||||
| //INST(thumb32_TBH,            "TBH",                      "111010001101------------0001----") | ||||
| //INST(thumb32_LDREXB,         "LDREXB",                   "111010001101------------0100----") | ||||
| //INST(thumb32_LDREXH,         "LDREXH",                   "111010001101------------0101----") | ||||
| //INST(thumb32_LDREXD,         "LDREXD",                   "111010001101------------0111----") | ||||
|  | ||||
| // Data Processing (Shifted Register) | ||||
| //INST(thumb32_TST_reg,        "TST (reg)",                "111010100001--------1111--------") | ||||
| //INST(thumb32_AND_reg,        "AND (reg)",                "11101010000---------------------") | ||||
| //INST(thumb32_BIC_reg,        "BIC (reg)",                "11101010001---------------------") | ||||
| //INST(thumb32_MOV_reg,        "MOV (reg)",                "11101010010-1111-000----0000----") | ||||
| //INST(thumb32_LSL_imm,        "LSL (imm)",                "11101010010-1111----------00----") | ||||
| //INST(thumb32_LSR_imm,        "LSR (imm)",                "11101010010-1111----------01----") | ||||
| //INST(thumb32_ASR_imm,        "ASR (imm)",                "11101010010-1111----------10----") | ||||
| //INST(thumb32_RRX,            "RRX",                      "11101010010-1111-000----0011----") | ||||
| //INST(thumb32_ROR_imm,        "ROR (imm)",                "11101010010-1111----------11----") | ||||
| //INST(thumb32_ORR_reg,        "ORR (reg)",                "11101010010---------------------") | ||||
| //INST(thumb32_MVN_reg,        "MVN (reg)",                "11101010011-1111----------------") | ||||
| //INST(thumb32_ORN_reg,        "ORN (reg)",                "11101010011---------------------") | ||||
| //INST(thumb32_TEQ_reg,        "TEQ (reg)",                "111010101001--------1111--------") | ||||
| //INST(thumb32_EOR_reg,        "EOR (reg)",                "11101010100---------------------") | ||||
| //INST(thumb32_PKH,            "PKH",                      "11101010110---------------------") | ||||
| //INST(thumb32_CMN_reg,        "CMN (reg)",                "111010110001--------1111--------") | ||||
| //INST(thumb32_ADD_reg,        "ADD (reg)",                "11101011000---------------------") | ||||
| //INST(thumb32_ADC_reg,        "ADC (reg)",                "11101011010---------------------") | ||||
| //INST(thumb32_SBC_reg,        "SBC (reg)",                "11101011011---------------------") | ||||
| //INST(thumb32_CMP_reg,        "CMP (reg)",                "111010111011--------1111--------") | ||||
| //INST(thumb32_SUB_reg,        "SUB (reg)",                "11101011101---------------------") | ||||
| //INST(thumb32_RSB_reg,        "RSB (reg)",                "11101011110---------------------") | ||||
|  | ||||
| // Data Processing (Modified Immediate) | ||||
| INST(thumb32_TST_imm,        "TST (imm)",                "11110v000001nnnn0vvv1111vvvvvvvv") | ||||
| INST(thumb32_AND_imm,        "AND (imm)",                "11110v00000Snnnn0vvvddddvvvvvvvv") | ||||
| INST(thumb32_BIC_imm,        "BIC (imm)",                "11110v00001Snnnn0vvvddddvvvvvvvv") | ||||
| INST(thumb32_MOV_imm,        "MOV (imm)",                "11110v00010S11110vvvddddvvvvvvvv") | ||||
| INST(thumb32_ORR_imm,        "ORR (imm)",                "11110v00010Snnnn0vvvddddvvvvvvvv") | ||||
| //INST(thumb32_MVN_imm,        "MVN (imm)",                "11110000011-11110---------------") | ||||
| //INST(thumb32_ORN_imm,        "ORN (imm)",                "11110-00011-----0---------------") | ||||
| //INST(thumb32_TEQ_imm,        "TEQ (imm)",                "11110-001001----0---1111--------") | ||||
| //INST(thumb32_EOR_imm,        "EOR (imm)",                "11110-00100-----0---------------") | ||||
| //INST(thumb32_CMN_imm,        "CMN (imm)",                "11110-010001----0---1111--------") | ||||
| //INST(thumb32_ADD_imm_1,      "ADD (imm)",                "11110-01000-----0---------------") | ||||
| //INST(thumb32_ADC_imm,        "ADC (imm)",                "11110-01010-----0---------------") | ||||
| //INST(thumb32_SBC_imm,        "SBC (imm)",                "11110-01011-----0---------------") | ||||
| //INST(thumb32_CMP_imm,        "CMP (imm)",                "11110-011011----0---1111--------") | ||||
| //INST(thumb32_SUB_imm_1,      "SUB (imm)",                "11110-01101-----0---------------") | ||||
| //INST(thumb32_RSB_imm,        "RSB (imm)",                "11110-01110-----0---------------") | ||||
|  | ||||
| // Data Processing (Plain Binary Immediate) | ||||
| //INST(thumb32_ADR,            "ADR",                      "11110-10000011110---------------") | ||||
| //INST(thumb32_ADD_imm_2,      "ADD (imm)",                "11110-100000----0---------------") | ||||
| //INST(thumb32_MOVW_imm,       "MOVW (imm)",               "11110-100100----0---------------") | ||||
| //INST(thumb32_ADR,            "ADR",                      "11110-10101011110---------------") | ||||
| //INST(thumb32_SUB_imm_2,      "SUB (imm)",                "11110-101010----0---------------") | ||||
| //INST(thumb32_MOVT,           "MOVT",                     "11110-101100----0---------------") | ||||
| //INST(thumb32_SSAT,           "SSAT",                     "11110-110000----0---------------") | ||||
| //INST(thumb32_SSAT16,         "SSAT16",                   "11110-110010----0000----00------") | ||||
| //INST(thumb32_SSAT,           "SSAT",                     "11110-110010----0---------------") | ||||
| //INST(thumb32_SBFX,           "SBFX",                     "11110-110100----0---------------") | ||||
| //INST(thumb32_BFC,            "BFC",                      "11110-11011011110---------------") | ||||
| //INST(thumb32_BFI,            "BFI",                      "11110-110110----0---------------") | ||||
| //INST(thumb32_USAT,           "USAT",                     "11110-111000----0---------------") | ||||
| //INST(thumb32_USAT16,         "USAT16",                   "11110-111010----0000----00------") | ||||
| //INST(thumb32_USAT,           "USAT",                     "11110-111010----0---------------") | ||||
| //INST(thumb32_UBFX,           "UBFX",                     "11110-111100----0---------------") | ||||
|  | ||||
| // Branches and Miscellaneous Control | ||||
| //INST(thumb32_MSR_banked,     "MSR (banked)",             "11110011100-----10-0------1-----") | ||||
| //INST(thumb32_MSR_reg_1,      "MSR (reg)",                "111100111001----10-0------0-----") | ||||
| //INST(thumb32_MSR_reg_2,      "MSR (reg)",                "111100111000----10-0--01--0-----") | ||||
| //INST(thumb32_MSR_reg_3,      "MSR (reg)",                "111100111000----10-0--1---0-----") | ||||
| //INST(thumb32_MSR_reg_4,      "MSR (reg)",                "111100111000----10-0--00--0-----") | ||||
|  | ||||
| //INST(thumb32_NOP,            "NOP",                      "111100111010----10-0-00000000000") | ||||
| //INST(thumb32_YIELD,          "YIELD",                    "111100111010----10-0-00000000001") | ||||
| //INST(thumb32_WFE,            "WFE",                      "111100111010----10-0-00000000010") | ||||
| //INST(thumb32_WFI,            "WFI",                      "111100111010----10-0-00000000011") | ||||
| //INST(thumb32_SEV,            "SEV",                      "111100111010----10-0-00000000100") | ||||
| //INST(thumb32_SEVL,           "SEVL",                     "111100111010----10-0-00000000101") | ||||
| //INST(thumb32_DBG,            "DBG",                      "111100111010----10-0-0001111----") | ||||
| //INST(thumb32_CPS,            "CPS",                      "111100111010----10-0------------") | ||||
|  | ||||
| //INST(thumb32_ENTERX,         "ENTERX",                   "111100111011----10-0----0001----") | ||||
| //INST(thumb32_LEAVEX,         "LEAVEX",                   "111100111011----10-0----0000----") | ||||
| //INST(thumb32_CLREX,          "CLREX",                    "111100111011----10-0----0010----") | ||||
| //INST(thumb32_DSB,            "DSB",                      "111100111011----10-0----0100----") | ||||
| //INST(thumb32_DMB,            "DMB",                      "111100111011----10-0----0101----") | ||||
| //INST(thumb32_ISB,            "ISB",                      "111100111011----10-0----0110----") | ||||
|  | ||||
| //INST(thumb32_BXJ,            "BXJ",                      "111100111100----1000111100000000") | ||||
| //INST(thumb32_ERET,           "ERET",                     "11110011110111101000111100000000") | ||||
| //INST(thumb32_SUBS_pc_lr,     "SUBS PC, LR",              "111100111101111010001111--------") | ||||
|  | ||||
| //INST(thumb32_MRS_banked,     "MRS (banked)",             "11110011111-----10-0------1-----") | ||||
| //INST(thumb32_MRS_reg_1,      "MRS (reg)",                "111100111111----10-0------0-----") | ||||
| //INST(thumb32_MRS_reg_2,      "MRS (reg)",                "111100111110----10-0------0-----") | ||||
| //INST(thumb32_HVC,            "HVC",                      "111101111110----1000------------") | ||||
| //INST(thumb32_SMC,            "SMC",                      "111101111111----1000000000000000") | ||||
| INST(thumb32_UDF,            "UDF",                      "111101111111----1010------------") // v6T2 | ||||
|  | ||||
| // Branch instructions | ||||
| INST(thumb32_BL_imm,         "BL (imm)",                 "11110Svvvvvvvvvv11j1jvvvvvvvvvvv") // v4T | ||||
| INST(thumb32_BLX_imm,        "BLX (imm)",                "11110Svvvvvvvvvv11j0jvvvvvvvvvvv") // v5T | ||||
| //INST(thumb32_B,              "B",                        "11110-----------10-1------------") | ||||
| //INST(thumb32_B_cond,         "B (cond)",                 "11110-----------10-0------------") | ||||
|  | ||||
| // Store Single Data Item | ||||
| //INST(thumb32_STRB_imm_1,     "STRB (imm)",               "111110000000--------1--1--------") | ||||
| //INST(thumb32_STRB_imm_2,     "STRB (imm)",               "111110000000--------1100--------") | ||||
| //INST(thumb32_STRB_imm_3,     "STRB (imm)",               "111110001000--------------------") | ||||
| //INST(thumb32_STRBT,          "STRBT",                    "111110000000--------1110--------") | ||||
| //INST(thumb32_STRB,           "STRB (reg)",               "111110000000--------000000------") | ||||
| //INST(thumb32_STRH_imm_1,     "STRH (imm)",               "111110000010--------1--1--------") | ||||
| //INST(thumb32_STRH_imm_2,     "STRH (imm)",               "111110000010--------1100--------") | ||||
| //INST(thumb32_STRH_imm_3,     "STRH (imm)",               "111110001010--------------------") | ||||
| //INST(thumb32_STRHT,          "STRHT",                    "111110000010--------1110--------") | ||||
| //INST(thumb32_STRH,           "STRH (reg)",               "111110000010--------000000------") | ||||
| //INST(thumb32_STR_imm_1,      "STR (imm)",                "111110000100--------1--1--------") | ||||
| //INST(thumb32_STR_imm_2,      "STR (imm)",                "111110000100--------1100--------") | ||||
| //INST(thumb32_STR_imm_3,      "STR (imm)",                "111110001100--------------------") | ||||
| //INST(thumb32_STRT,           "STRT",                     "111110000100--------1110--------") | ||||
| //INST(thumb32_STR_reg,        "STR (reg)",                "111110000100--------000000------") | ||||
|  | ||||
| // Load Byte and Memory Hints | ||||
| //INST(thumb32_PLD_lit,        "PLD (lit)",                "11111000-00111111111------------") | ||||
| //INST(thumb32_PLD_reg,        "PLD (reg)",                "111110000001----1111000000------") | ||||
| //INST(thumb32_PLD_imm8,       "PLD (imm8)",               "1111100000-1----11111100--------") | ||||
| //INST(thumb32_PLD_imm12,      "PLD (imm12)",              "111110001001----1111------------") | ||||
| //INST(thumb32_PLI_lit,        "PLI (lit)",                "11111001-00111111111------------") | ||||
| //INST(thumb32_PLI_reg,        "PLI (reg)",                "111110010001----1111000000------") | ||||
| //INST(thumb32_PLI_imm8,       "PLI (imm8)",               "111110010001----11111100--------") | ||||
| //INST(thumb32_PLI_imm12,      "PLI (imm12)",              "111110011001----1111------------") | ||||
| //INST(thumb32_LDRB_lit,       "LDRB (lit)",               "11111000-0011111----------------") | ||||
| //INST(thumb32_LDRB_reg,       "LDRB (reg)",               "111110000001--------000000------") | ||||
| //INST(thumb32_LDRBT,          "LDRBT",                    "111110000001--------1110--------") | ||||
| //INST(thumb32_LDRB_imm8,      "LDRB (imm8)",              "111110000001--------1-----------") | ||||
| //INST(thumb32_LDRB_imm12,     "LDRB (imm12)",             "111110001001--------------------") | ||||
| //INST(thumb32_LDRSB_lit,      "LDRSB (lit)",              "11111001-0011111----------------") | ||||
| //INST(thumb32_LDRSB_reg,      "LDRSB (reg)",              "111110010001--------000000------") | ||||
| //INST(thumb32_LDRSBT,         "LDRSBT",                   "111110010001--------1110--------") | ||||
| //INST(thumb32_LDRSB_imm8,     "LDRSB (imm8)",             "111110010001--------1-----------") | ||||
| //INST(thumb32_LDRSB_imm12,    "LDRSB (imm12)",            "111110011001--------------------") | ||||
|  | ||||
| // Load Halfword and Memory Hints | ||||
| //INST(thumb32_LDRH_lit,       "LDRH (lit)",               "11111000-0111111----------------") | ||||
| //INST(thumb32_LDRH_reg,       "LDRH (reg)",               "111110000011--------000000------") | ||||
| //INST(thumb32_LDRHT,          "LDRHT",                    "111110000011--------1110--------") | ||||
| //INST(thumb32_LDRH_imm8,      "LDRH (imm8)",              "111110000011--------1-----------") | ||||
| //INST(thumb32_LDRH_imm12,     "LDRH (imm12)",             "111110001011--------------------") | ||||
| //INST(thumb32_LDRSH_lit,      "LDRSH (lit)",              "11111001-0111111----------------") | ||||
| //INST(thumb32_LDRSH_reg,      "LDRSH (reg)",              "111110010011--------000000------") | ||||
| //INST(thumb32_LDRSHT,         "LDRSHT",                   "111110010011--------1110--------") | ||||
| //INST(thumb32_LDRSH_imm8,     "LDRSH (imm8)",             "111110010011--------1-----------") | ||||
| //INST(thumb32_LDRSH_imm12,    "LDRSH (imm12)",            "111110011011--------------------") | ||||
| //INST(thumb32_NOP,            "NOP",                      "111110010011----1111000000------") | ||||
| //INST(thumb32_NOP,            "NOP",                      "111110010011----11111100--------") | ||||
| //INST(thumb32_NOP,            "NOP",                      "11111001-01111111111------------") | ||||
| //INST(thumb32_NOP,            "NOP",                      "111110011011----1111------------") | ||||
|  | ||||
| // Load Word | ||||
| //INST(thumb32_LDR_lit,        "LDR (lit)",                "11111000-1011111----------------") | ||||
| //INST(thumb32_LDRT,           "LDRT",                     "111110000101--------1110--------") | ||||
| //INST(thumb32_LDR_reg,        "LDR (reg)",                "111110000101--------000000------") | ||||
| //INST(thumb32_LDR_imm8,       "LDR (imm8)",               "111110000101--------1-----------") | ||||
| //INST(thumb32_LDR_imm12,      "LDR (imm12)",              "111110001101--------------------") | ||||
|  | ||||
| // Data Processing (register) | ||||
| //INST(thumb32_LSL_reg,        "LSL (reg)",                "11111010000-----1111----0000----") | ||||
| //INST(thumb32_LSR_reg,        "LSR (reg)",                "11111010001-----1111----0000----") | ||||
| //INST(thumb32_ASR_reg,        "ASR (reg)",                "11111010010-----1111----0000----") | ||||
| //INST(thumb32_ROR_reg,        "ROR (reg)",                "11111010011-----1111----0000----") | ||||
| INST(thumb32_SXTH,           "SXTH",                     "11111010000011111111dddd10rrmmmm") | ||||
| INST(thumb32_SXTAH,          "SXTAH",                    "111110100000nnnn1111dddd10rrmmmm") | ||||
| INST(thumb32_UXTH,           "UXTH",                     "11111010000111111111dddd10rrmmmm") | ||||
| INST(thumb32_UXTAH,          "UXTAH",                    "111110100001nnnn1111dddd10rrmmmm") | ||||
| INST(thumb32_SXTB16,         "SXTB16",                   "11111010001011111111dddd10rrmmmm") | ||||
| INST(thumb32_SXTAB16,        "SXTAB16",                  "111110100010nnnn1111dddd10rrmmmm") | ||||
| INST(thumb32_UXTB16,         "UXTB16",                   "11111010001111111111dddd10rrmmmm") | ||||
| INST(thumb32_UXTAB16,        "UXTAB16",                  "111110100011nnnn1111dddd10rrmmmm") | ||||
| INST(thumb32_SXTB,           "SXTB",                     "11111010010011111111dddd10rrmmmm") | ||||
| INST(thumb32_SXTAB,          "SXTAB",                    "111110100100nnnn1111dddd10rrmmmm") | ||||
| INST(thumb32_UXTB,           "UXTB",                     "11111010010111111111dddd10rrmmmm") | ||||
| INST(thumb32_UXTAB,          "UXTAB",                    "111110100101nnnn1111dddd10rrmmmm") | ||||
|  | ||||
| // Parallel Addition and Subtraction (signed) | ||||
| INST(thumb32_SADD16,         "SADD16",                   "111110101001nnnn1111dddd0000mmmm") | ||||
| INST(thumb32_SASX,           "SASX",                     "111110101010nnnn1111dddd0000mmmm") | ||||
| INST(thumb32_SSAX,           "SSAX",                     "111110101110nnnn1111dddd0000mmmm") | ||||
| INST(thumb32_SSUB16,         "SSUB16",                   "111110101101nnnn1111dddd0000mmmm") | ||||
| INST(thumb32_SADD8,          "SADD8",                    "111110101000nnnn1111dddd0000mmmm") | ||||
| INST(thumb32_SSUB8,          "SSUB8",                    "111110101100nnnn1111dddd0000mmmm") | ||||
| INST(thumb32_QADD16,         "QADD16",                   "111110101001nnnn1111dddd0001mmmm") | ||||
| INST(thumb32_QASX,           "QASX",                     "111110101010nnnn1111dddd0001mmmm") | ||||
| INST(thumb32_QSAX,           "QSAX",                     "111110101110nnnn1111dddd0001mmmm") | ||||
| INST(thumb32_QSUB16,         "QSUB16",                   "111110101101nnnn1111dddd0001mmmm") | ||||
| INST(thumb32_QADD8,          "QADD8",                    "111110101000nnnn1111dddd0001mmmm") | ||||
| INST(thumb32_QSUB8,          "QSUB8",                    "111110101100nnnn1111dddd0001mmmm") | ||||
| INST(thumb32_SHADD16,        "SHADD16",                  "111110101001nnnn1111dddd0010mmmm") | ||||
| INST(thumb32_SHASX,          "SHASX",                    "111110101010nnnn1111dddd0010mmmm") | ||||
| INST(thumb32_SHSAX,          "SHSAX",                    "111110101110nnnn1111dddd0010mmmm") | ||||
| INST(thumb32_SHSUB16,        "SHSUB16",                  "111110101101nnnn1111dddd0010mmmm") | ||||
| INST(thumb32_SHADD8,         "SHADD8",                   "111110101000nnnn1111dddd0010mmmm") | ||||
| INST(thumb32_SHSUB8,         "SHSUB8",                   "111110101100nnnn1111dddd0010mmmm") | ||||
|  | ||||
| // Parallel Addition and Subtraction (unsigned) | ||||
| INST(thumb32_UADD16,         "UADD16",                   "111110101001nnnn1111dddd0100mmmm") | ||||
| INST(thumb32_UASX,           "UASX",                     "111110101010nnnn1111dddd0100mmmm") | ||||
| INST(thumb32_USAX,           "USAX",                     "111110101110nnnn1111dddd0100mmmm") | ||||
| INST(thumb32_USUB16,         "USUB16",                   "111110101101nnnn1111dddd0100mmmm") | ||||
| INST(thumb32_UADD8,          "UADD8",                    "111110101000nnnn1111dddd0100mmmm") | ||||
| INST(thumb32_USUB8,          "USUB8",                    "111110101100nnnn1111dddd0100mmmm") | ||||
| INST(thumb32_UQADD16,        "UQADD16",                  "111110101001nnnn1111dddd0101mmmm") | ||||
| INST(thumb32_UQASX,          "UQASX",                    "111110101010nnnn1111dddd0101mmmm") | ||||
| INST(thumb32_UQSAX,          "UQSAX",                    "111110101110nnnn1111dddd0101mmmm") | ||||
| INST(thumb32_UQSUB16,        "UQSUB16",                  "111110101101nnnn1111dddd0101mmmm") | ||||
| INST(thumb32_UQADD8,         "UQADD8",                   "111110101000nnnn1111dddd0101mmmm") | ||||
| INST(thumb32_UQSUB8,         "UQSUB8",                   "111110101100nnnn1111dddd0101mmmm") | ||||
| INST(thumb32_UHADD16,        "UHADD16",                  "111110101001nnnn1111dddd0110mmmm") | ||||
| INST(thumb32_UHASX,          "UHASX",                    "111110101010nnnn1111dddd0110mmmm") | ||||
| INST(thumb32_UHSAX,          "UHSAX",                    "111110101110nnnn1111dddd0110mmmm") | ||||
| INST(thumb32_UHSUB16,        "UHSUB16",                  "111110101101nnnn1111dddd0110mmmm") | ||||
| INST(thumb32_UHADD8,         "UHADD8",                   "111110101000nnnn1111dddd0110mmmm") | ||||
| INST(thumb32_UHSUB8,         "UHSUB8",                   "111110101100nnnn1111dddd0110mmmm") | ||||
|  | ||||
| // Miscellaneous Operations | ||||
| INST(thumb32_QADD,           "QADD",                     "111110101000nnnn1111dddd1000mmmm") | ||||
| INST(thumb32_QDADD,          "QDADD",                    "111110101000nnnn1111dddd1001mmmm") | ||||
| INST(thumb32_QSUB,           "QSUB",                     "111110101000nnnn1111dddd1010mmmm") | ||||
| INST(thumb32_QDSUB,          "QDSUB",                    "111110101000nnnn1111dddd1011mmmm") | ||||
| INST(thumb32_REV,            "REV",                      "111110101001nnnn1111dddd1000mmmm") | ||||
| INST(thumb32_REV16,          "REV16",                    "111110101001nnnn1111dddd1001mmmm") | ||||
| INST(thumb32_RBIT,           "RBIT",                     "111110101001nnnn1111dddd1010mmmm") | ||||
| INST(thumb32_REVSH,          "REVSH",                    "111110101001nnnn1111dddd1011mmmm") | ||||
| INST(thumb32_SEL,            "SEL",                      "111110101010nnnn1111dddd1000mmmm") | ||||
| INST(thumb32_CLZ,            "CLZ",                      "111110101011nnnn1111dddd1000mmmm") | ||||
|  | ||||
| // Multiply, Multiply Accumulate, and Absolute Difference | ||||
| INST(thumb32_MUL,            "MUL",                      "111110110000nnnn1111dddd0000mmmm") | ||||
| INST(thumb32_MLA,            "MLA",                      "111110110000nnnnaaaadddd0000mmmm") | ||||
| INST(thumb32_MLS,            "MLS",                      "111110110000nnnnaaaadddd0001mmmm") | ||||
| INST(thumb32_SMULXY,         "SMULXY",                   "111110110001nnnn1111dddd00NMmmmm") | ||||
| INST(thumb32_SMLAXY,         "SMLAXY",                   "111110110001nnnnaaaadddd00NMmmmm") | ||||
| INST(thumb32_SMUAD,          "SMUAD",                    "111110110010nnnn1111dddd000Mmmmm") | ||||
| INST(thumb32_SMLAD,          "SMLAD",                    "111110110010nnnnaaaadddd000Xmmmm") | ||||
| INST(thumb32_SMULWY,         "SMULWY",                   "111110110011nnnn1111dddd000Mmmmm") | ||||
| INST(thumb32_SMLAWY,         "SMLAWY",                   "111110110011nnnnaaaadddd000Mmmmm") | ||||
| INST(thumb32_SMUSD,          "SMUSD",                    "111110110100nnnn1111dddd000Mmmmm") | ||||
| INST(thumb32_SMLSD,          "SMLSD",                    "111110110100nnnnaaaadddd000Xmmmm") | ||||
| INST(thumb32_SMMUL,          "SMMUL",                    "111110110101nnnn1111dddd000Rmmmm") | ||||
| INST(thumb32_SMMLA,          "SMMLA",                    "111110110101nnnnaaaadddd000Rmmmm") | ||||
| INST(thumb32_SMMLS,          "SMMLS",                    "111110110110nnnnaaaadddd000Rmmmm") | ||||
| INST(thumb32_USAD8,          "USAD8",                    "111110110111nnnn1111dddd0000mmmm") | ||||
| INST(thumb32_USADA8,         "USADA8",                   "111110110111nnnnaaaadddd0000mmmm") | ||||
|  | ||||
| // Long Multiply, Long Multiply Accumulate, and Divide | ||||
| INST(thumb32_SMULL,          "SMULL",                    "111110111000nnnnllllhhhh0000mmmm") | ||||
| INST(thumb32_SDIV,           "SDIV",                     "111110111001nnnn1111dddd1111mmmm") | ||||
| INST(thumb32_UMULL,          "UMULL",                    "111110111010nnnnllllhhhh0000mmmm") | ||||
| INST(thumb32_UDIV,           "UDIV",                     "111110111011nnnn1111dddd1111mmmm") | ||||
| INST(thumb32_SMLAL,          "SMLAL",                    "111110111100nnnnllllhhhh0000mmmm") | ||||
| INST(thumb32_SMLALXY,        "SMLALXY",                  "111110111100nnnnllllhhhh10NMmmmm") | ||||
| INST(thumb32_SMLALD,         "SMLALD",                   "111110111100nnnnllllhhhh110Mmmmm") | ||||
| INST(thumb32_SMLSLD,         "SMLSLD",                   "111110111101nnnnllllhhhh110Mmmmm") | ||||
| INST(thumb32_UMLAL,          "UMLAL",                    "111110111110nnnnllllhhhh0000mmmm") | ||||
| INST(thumb32_UMAAL,          "UMAAL",                    "111110111110nnnnllllhhhh0110mmmm") | ||||
|  | ||||
| // Coprocessor | ||||
| //INST(thumb32_MCRR2,          "MCRR2",                    "111111000100--------------------") | ||||
| //INST(thumb32_MCRR,           "MCRR",                     "111011000100--------------------") | ||||
| //INST(thumb32_STC2,           "STC2",                     "1111110----0--------------------") | ||||
| //INST(thumb32_STC,            "STC",                      "1110110----0--------------------") | ||||
| //INST(thumb32_MRRC2,          "MRRC2",                    "111111000101--------------------") | ||||
| //INST(thumb32_MRRC,           "MRRC",                     "111011000101--------------------") | ||||
| //INST(thumb32_LDC2_lit,       "LDC2 (lit)",               "1111110----11111----------------") | ||||
| //INST(thumb32_LDC_lit,        "LDC (lit)",                "1110110----11111----------------") | ||||
| //INST(thumb32_LDC2_imm,       "LDC2 (imm)",               "1111110----1--------------------") | ||||
| //INST(thumb32_LDC_imm,        "LDC (imm)",                "1110110----1--------------------") | ||||
| //INST(thumb32_CDP2,           "CDP2",                     "11111110-------------------0----") | ||||
| //INST(thumb32_CDP,            "CDP",                      "11101110-------------------0----") | ||||
| //INST(thumb32_MCR2,           "MCR2",                     "11111110---0---------------1----") | ||||
| //INST(thumb32_MCR,            "MCR",                      "11101110---0---------------1----") | ||||
| //INST(thumb32_MRC2,           "MRC2",                     "11111110---1---------------1----") | ||||
| //INST(thumb32_MRC,            "MRC",                      "11101110---1---------------1----") | ||||
| @@ -269,6 +269,25 @@ public: | ||||
|         return "yield"; | ||||
|     } | ||||
|  | ||||
|     std::string thumb16_IT(Imm<8> imm8) { | ||||
|         const Cond firstcond = imm8.Bits<4, 7, Cond>(); | ||||
|         const bool firstcond0 = imm8.Bit<4>(); | ||||
|         const auto [x, y, z] = [&]{ | ||||
|             if (imm8.Bits<0, 3>() == 0b1000) { | ||||
|                 return std::make_tuple("", "", ""); | ||||
|             } | ||||
|             if (imm8.Bits<0, 2>() == 0b100) { | ||||
|                 return std::make_tuple(imm8.Bit<3>() == firstcond0 ? "t" : "e", "", ""); | ||||
|             } | ||||
|             if (imm8.Bits<0, 1>() == 0b10) { | ||||
|                 return std::make_tuple(imm8.Bit<3>() == firstcond0 ? "t" : "e", imm8.Bit<2>() == firstcond0 ? "t" : "e", ""); | ||||
|             } | ||||
|             // Sanity note: Here imm8.Bit<0>() is guaranteed to be == 1. (imm8 can never be 0bxxxx0000) | ||||
|             return std::make_tuple(imm8.Bit<3>() == firstcond0 ? "t" : "e", imm8.Bit<2>() == firstcond0 ? "t" : "e", imm8.Bit<1>() == firstcond0 ? "t" : "e"); | ||||
|         }(); | ||||
|         return fmt::format("it{}{}{} {}", x, y, z, firstcond); | ||||
|     } | ||||
|  | ||||
|     std::string thumb16_SXTH(Reg m, Reg d) { | ||||
|         return fmt::format("sxth {}, {}", d, m); | ||||
|     } | ||||
|   | ||||
| @@ -8,10 +8,32 @@ | ||||
| #include "frontend/A32/types.h" | ||||
| #include "frontend/ir/opcodes.h" | ||||
|  | ||||
| #include <dynarmic/A32/arch_version.h> | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
|  | ||||
| using Opcode = IR::Opcode; | ||||
|  | ||||
| size_t IREmitter::ArchVersion() const { | ||||
|     switch (arch_version) { | ||||
|     case ArchVersion::v3: | ||||
|         return 3; | ||||
|     case ArchVersion::v4: | ||||
|     case ArchVersion::v4T: | ||||
|         return 4; | ||||
|     case ArchVersion::v5TE: | ||||
|         return 5; | ||||
|     case ArchVersion::v6K: | ||||
|     case ArchVersion::v6T2: | ||||
|         return 6; | ||||
|     case ArchVersion::v7: | ||||
|         return 7; | ||||
|     case ArchVersion::v8: | ||||
|         return 8; | ||||
|     } | ||||
|     UNREACHABLE(); | ||||
| } | ||||
|  | ||||
| u32 IREmitter::PC() const { | ||||
|     const u32 offset = current_location.TFlag() ? 4 : 8; | ||||
|     return current_location.PC() + offset; | ||||
| @@ -68,12 +90,16 @@ void IREmitter::SetVector(ExtReg reg, const IR::U128& value) { | ||||
|  | ||||
| void IREmitter::ALUWritePC(const IR::U32& value) { | ||||
|     // This behaviour is ARM version-dependent. | ||||
|     // The below implementation is for ARMv6k | ||||
|     BranchWritePC(value); | ||||
|     if (ArchVersion() >= 7 && !current_location.TFlag()) { | ||||
|         BXWritePC(value); | ||||
|     } else { | ||||
|         BranchWritePC(value); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void IREmitter::BranchWritePC(const IR::U32& value) { | ||||
|     if (!current_location.TFlag()) { | ||||
|         // Note that for ArchVersion() < 6, this is UNPREDICTABLE when value<1:0> != 0b00 | ||||
|         const auto new_pc = And(value, Imm32(0xFFFFFFFC)); | ||||
|         Inst(Opcode::A32SetRegister, IR::Value(A32::Reg::PC), new_pc); | ||||
|     } else { | ||||
| @@ -88,8 +114,15 @@ void IREmitter::BXWritePC(const IR::U32& value) { | ||||
|  | ||||
| void IREmitter::LoadWritePC(const IR::U32& value) { | ||||
|     // This behaviour is ARM version-dependent. | ||||
|     // The below implementation is for ARMv6k | ||||
|     BXWritePC(value); | ||||
|     if (ArchVersion() >= 5) { | ||||
|         BXWritePC(value); | ||||
|     } else { | ||||
|         BranchWritePC(value); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void IREmitter::UpdateUpperLocationDescriptor() { | ||||
|     Inst(Opcode::A32UpdateUpperLocationDescriptor); | ||||
| } | ||||
|  | ||||
| void IREmitter::CallSupervisor(const IR::U32& value) { | ||||
|   | ||||
| @@ -14,6 +14,7 @@ | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
|  | ||||
| enum class ArchVersion; | ||||
| enum class CoprocReg; | ||||
| enum class Exception; | ||||
| enum class ExtReg; | ||||
| @@ -26,10 +27,12 @@ enum class Reg; | ||||
|  */ | ||||
| class IREmitter : public IR::IREmitter { | ||||
| public: | ||||
|     explicit IREmitter(IR::Block& block, LocationDescriptor descriptor) : IR::IREmitter(block), current_location(descriptor) {} | ||||
|     IREmitter(IR::Block& block, LocationDescriptor descriptor, ArchVersion arch_version) : IR::IREmitter(block), current_location(descriptor), arch_version(arch_version) {} | ||||
|  | ||||
|     LocationDescriptor current_location; | ||||
|  | ||||
|     size_t ArchVersion() const; | ||||
|  | ||||
|     u32 PC() const; | ||||
|     u32 AlignPC(size_t alignment) const; | ||||
|  | ||||
| @@ -44,6 +47,7 @@ public: | ||||
|     void BranchWritePC(const IR::U32& value); | ||||
|     void BXWritePC(const IR::U32& value); | ||||
|     void LoadWritePC(const IR::U32& value); | ||||
|     void UpdateUpperLocationDescriptor(); | ||||
|  | ||||
|     void CallSupervisor(const IR::U32& value); | ||||
|     void ExceptionRaised(Exception exception); | ||||
| @@ -99,6 +103,9 @@ public: | ||||
|     IR::U64 CoprocGetTwoWords(size_t coproc_no, bool two, size_t opc, CoprocReg CRm); | ||||
|     void CoprocLoadWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option); | ||||
|     void CoprocStoreWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option); | ||||
|  | ||||
| private: | ||||
|     enum ArchVersion arch_version; | ||||
| }; | ||||
|  | ||||
| } // namespace Dynarmic::A32 | ||||
|   | ||||
| @@ -88,13 +88,17 @@ public: | ||||
|         return LocationDescriptor(arm_pc, cpsr, A32::FPSCR{new_fpscr & FPSCR_MODE_MASK}, single_stepping); | ||||
|     } | ||||
|  | ||||
|     LocationDescriptor AdvanceIT() const { | ||||
|     LocationDescriptor SetIT(ITState new_it) const { | ||||
|         PSR new_cpsr = cpsr; | ||||
|         new_cpsr.IT(new_cpsr.IT().Advance()); | ||||
|         new_cpsr.IT(new_it); | ||||
|  | ||||
|         return LocationDescriptor(arm_pc, new_cpsr, fpscr, single_stepping); | ||||
|     } | ||||
|  | ||||
|     LocationDescriptor AdvanceIT() const { | ||||
|         return SetIT(IT().Advance()); | ||||
|     } | ||||
|  | ||||
|     LocationDescriptor SetSingleStepping(bool new_single_stepping) const { | ||||
|         return LocationDescriptor(arm_pc, cpsr, fpscr, new_single_stepping); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										79
									
								
								externals/dynarmic/src/frontend/A32/translate/conditional_state.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										79
									
								
								externals/dynarmic/src/frontend/A32/translate/conditional_state.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| /* This file is part of the dynarmic project. | ||||
|  * Copyright (c) 2020 MerryMage | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #include <algorithm> | ||||
|  | ||||
| #include <dynarmic/A32/config.h> | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "frontend/A32/ir_emitter.h" | ||||
| #include "frontend/A32/translate/conditional_state.h" | ||||
| #include "frontend/ir/cond.h" | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
|  | ||||
| bool CondCanContinue(ConditionalState cond_state, const A32::IREmitter& ir) { | ||||
|     ASSERT_MSG(cond_state != ConditionalState::Break, "Should never happen."); | ||||
|  | ||||
|     if (cond_state == ConditionalState::None) | ||||
|         return true; | ||||
|  | ||||
|     // TODO: This is more conservative than necessary. | ||||
|     return std::all_of(ir.block.begin(), ir.block.end(), [](const IR::Inst& inst) { return !inst.WritesToCPSR(); }); | ||||
| } | ||||
|  | ||||
| bool IsConditionPassed(IR::Cond cond, ConditionalState& cond_state, A32::IREmitter& ir, int instruction_size) { | ||||
|     ASSERT_MSG(cond_state != ConditionalState::Break, | ||||
|                "This should never happen. We requested a break but that wasn't honored."); | ||||
|  | ||||
|     if (cond == IR::Cond::NV) { | ||||
|         // NV conditional is obsolete | ||||
|         ir.ExceptionRaised(Exception::UnpredictableInstruction); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (cond_state == ConditionalState::Translating) { | ||||
|         if (ir.block.ConditionFailedLocation() != ir.current_location || cond == IR::Cond::AL) { | ||||
|             cond_state = ConditionalState::Trailing; | ||||
|         } else { | ||||
|             if (cond == ir.block.GetCondition()) { | ||||
|                 ir.block.SetConditionFailedLocation(ir.current_location.AdvancePC(instruction_size).AdvanceIT()); | ||||
|                 ir.block.ConditionFailedCycleCount()++; | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             // cond has changed, abort | ||||
|             cond_state = ConditionalState::Break; | ||||
|             ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location}); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (cond == IR::Cond::AL) { | ||||
|         // Everything is fine with the world | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     // non-AL cond | ||||
|  | ||||
|     if (!ir.block.empty()) { | ||||
|         // We've already emitted instructions. Quit for now, we'll make a new block here later. | ||||
|         cond_state = ConditionalState::Break; | ||||
|         ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location}); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // We've not emitted instructions yet. | ||||
|     // We'll emit one instruction, and set the block-entry conditional appropriately. | ||||
|  | ||||
|     cond_state = ConditionalState::Translating; | ||||
|     ir.block.SetCondition(cond); | ||||
|     ir.block.SetConditionFailedLocation(ir.current_location.AdvancePC(instruction_size).AdvanceIT()); | ||||
|     ir.block.ConditionFailedCycleCount() = ir.block.CycleCount() + 1; | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| } // namespace Dynarmic::A32 | ||||
							
								
								
									
										32
									
								
								externals/dynarmic/src/frontend/A32/translate/conditional_state.h
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										32
									
								
								externals/dynarmic/src/frontend/A32/translate/conditional_state.h
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| /* This file is part of the dynarmic project. | ||||
|  * Copyright (c) 2020 MerryMage | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace Dynarmic::IR { | ||||
| enum class Cond; | ||||
| } // namespace Dynarmic::IR | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
|  | ||||
| class IREmitter; | ||||
|  | ||||
| enum class ConditionalState { | ||||
|     /// We haven't met any conditional instructions yet. | ||||
|     None, | ||||
|     /// Current instruction is a conditional. This marks the end of this basic block. | ||||
|     Break, | ||||
|     /// This basic block is made up solely of conditional instructions. | ||||
|     Translating, | ||||
|     /// This basic block is made up of conditional instructions followed by unconditional instructions. | ||||
|     Trailing, | ||||
| }; | ||||
|  | ||||
| bool CondCanContinue(ConditionalState cond_state, const A32::IREmitter& ir); | ||||
| bool IsConditionPassed(IR::Cond cond, ConditionalState& cond_state, A32::IREmitter& ir, int instruction_size); | ||||
|  | ||||
| } // namespace Dynarmic::A32 | ||||
| @@ -12,13 +12,19 @@ namespace Dynarmic::A32 { | ||||
| // LSLS <Rd>, <Rm>, #<imm5> | ||||
| bool ThumbTranslatorVisitor::thumb16_LSL_imm(Imm<5> imm5, Reg m, Reg d) { | ||||
|     const u8 shift_n = imm5.ZeroExtend<u8>(); | ||||
|     if (shift_n == 0 && ir.current_location.IT().IsInITBlock()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto cpsr_c = ir.GetCFlag(); | ||||
|     const auto result = ir.LogicalShiftLeft(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -29,9 +35,11 @@ bool ThumbTranslatorVisitor::thumb16_LSR_imm(Imm<5> imm5, Reg m, Reg d) { | ||||
|     const auto result = ir.LogicalShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -42,9 +50,11 @@ bool ThumbTranslatorVisitor::thumb16_ASR_imm(Imm<5> imm5, Reg m, Reg d) { | ||||
|     const auto result = ir.ArithmeticShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -52,11 +62,14 @@ bool ThumbTranslatorVisitor::thumb16_ASR_imm(Imm<5> imm5, Reg m, Reg d) { | ||||
| // Note that it is not possible to encode Rd == R15. | ||||
| bool ThumbTranslatorVisitor::thumb16_ADD_reg_t1(Reg m, Reg n, Reg d) { | ||||
|     const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0)); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     ir.SetVFlag(result.overflow); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|         ir.SetVFlag(result.overflow); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -64,11 +77,14 @@ bool ThumbTranslatorVisitor::thumb16_ADD_reg_t1(Reg m, Reg n, Reg d) { | ||||
| // Note that it is not possible to encode Rd == R15. | ||||
| bool ThumbTranslatorVisitor::thumb16_SUB_reg(Reg m, Reg n, Reg d) { | ||||
|     const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(1)); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     ir.SetVFlag(result.overflow); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|         ir.SetVFlag(result.overflow); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -79,10 +95,12 @@ bool ThumbTranslatorVisitor::thumb16_ADD_imm_t1(Imm<3> imm3, Reg n, Reg d) { | ||||
|     const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(0)); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     ir.SetVFlag(result.overflow); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|         ir.SetVFlag(result.overflow); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -93,10 +111,12 @@ bool ThumbTranslatorVisitor::thumb16_SUB_imm_t1(Imm<3> imm3, Reg n, Reg d) { | ||||
|     const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1)); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     ir.SetVFlag(result.overflow); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|         ir.SetVFlag(result.overflow); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -107,8 +127,10 @@ bool ThumbTranslatorVisitor::thumb16_MOV_imm(Reg d, Imm<8> imm8) { | ||||
|     const auto result = ir.Imm32(imm32); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|     ir.SetZFlag(ir.IsZero(result)); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|         ir.SetZFlag(ir.IsZero(result)); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -133,10 +155,12 @@ bool ThumbTranslatorVisitor::thumb16_ADD_imm_t2(Reg d_n, Imm<8> imm8) { | ||||
|     const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(0)); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     ir.SetVFlag(result.overflow); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|         ir.SetVFlag(result.overflow); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -149,10 +173,12 @@ bool ThumbTranslatorVisitor::thumb16_SUB_imm_t2(Reg d_n, Imm<8> imm8) { | ||||
|     const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1)); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     ir.SetVFlag(result.overflow); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|         ir.SetVFlag(result.overflow); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -164,8 +190,10 @@ bool ThumbTranslatorVisitor::thumb16_AND_reg(Reg m, Reg d_n) { | ||||
|     const auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|     ir.SetZFlag(ir.IsZero(result)); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|         ir.SetZFlag(ir.IsZero(result)); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -177,8 +205,10 @@ bool ThumbTranslatorVisitor::thumb16_EOR_reg(Reg m, Reg d_n) { | ||||
|     const auto result = ir.Eor(ir.GetRegister(n), ir.GetRegister(m)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|     ir.SetZFlag(ir.IsZero(result)); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|         ir.SetZFlag(ir.IsZero(result)); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -191,9 +221,11 @@ bool ThumbTranslatorVisitor::thumb16_LSL_reg(Reg m, Reg d_n) { | ||||
|     const auto result_carry = ir.LogicalShiftLeft(ir.GetRegister(n), shift_n, apsr_c); | ||||
|  | ||||
|     ir.SetRegister(d, result_carry.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result_carry.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result_carry.result)); | ||||
|     ir.SetCFlag(result_carry.carry); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result_carry.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result_carry.result)); | ||||
|         ir.SetCFlag(result_carry.carry); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -206,9 +238,11 @@ bool ThumbTranslatorVisitor::thumb16_LSR_reg(Reg m, Reg d_n) { | ||||
|     const auto result = ir.LogicalShiftRight(ir.GetRegister(n), shift_n, cpsr_c); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -221,9 +255,11 @@ bool ThumbTranslatorVisitor::thumb16_ASR_reg(Reg m, Reg d_n) { | ||||
|     const auto result = ir.ArithmeticShiftRight(ir.GetRegister(n), shift_n, cpsr_c); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -236,10 +272,12 @@ bool ThumbTranslatorVisitor::thumb16_ADC_reg(Reg m, Reg d_n) { | ||||
|     const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), aspr_c); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     ir.SetVFlag(result.overflow); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|         ir.SetVFlag(result.overflow); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -252,10 +290,12 @@ bool ThumbTranslatorVisitor::thumb16_SBC_reg(Reg m, Reg d_n) { | ||||
|     const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), aspr_c); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     ir.SetVFlag(result.overflow); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|         ir.SetVFlag(result.overflow); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -268,9 +308,11 @@ bool ThumbTranslatorVisitor::thumb16_ROR_reg(Reg m, Reg d_n) { | ||||
|     const auto result = ir.RotateRight(ir.GetRegister(n), shift_n, cpsr_c); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -287,10 +329,12 @@ bool ThumbTranslatorVisitor::thumb16_TST_reg(Reg m, Reg n) { | ||||
| bool ThumbTranslatorVisitor::thumb16_RSB_imm(Reg n, Reg d) { | ||||
|     const auto result = ir.SubWithCarry(ir.Imm32(0), ir.GetRegister(n), ir.Imm1(1)); | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|     ir.SetZFlag(ir.IsZero(result.result)); | ||||
|     ir.SetCFlag(result.carry); | ||||
|     ir.SetVFlag(result.overflow); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result.result)); | ||||
|         ir.SetZFlag(ir.IsZero(result.result)); | ||||
|         ir.SetCFlag(result.carry); | ||||
|         ir.SetVFlag(result.overflow); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -322,8 +366,10 @@ bool ThumbTranslatorVisitor::thumb16_ORR_reg(Reg m, Reg d_n) { | ||||
|     const auto result = ir.Or(ir.GetRegister(m), ir.GetRegister(n)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|     ir.SetZFlag(ir.IsZero(result)); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|         ir.SetZFlag(ir.IsZero(result)); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -335,8 +381,10 @@ bool ThumbTranslatorVisitor::thumb16_MUL_reg(Reg n, Reg d_m) { | ||||
|     const auto result = ir.Mul(ir.GetRegister(m), ir.GetRegister(n)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|     ir.SetZFlag(ir.IsZero(result)); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|         ir.SetZFlag(ir.IsZero(result)); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -348,8 +396,10 @@ bool ThumbTranslatorVisitor::thumb16_BIC_reg(Reg m, Reg d_n) { | ||||
|     const auto result = ir.And(ir.GetRegister(n), ir.Not(ir.GetRegister(m))); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|     ir.SetZFlag(ir.IsZero(result)); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|         ir.SetZFlag(ir.IsZero(result)); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -357,9 +407,12 @@ bool ThumbTranslatorVisitor::thumb16_BIC_reg(Reg m, Reg d_n) { | ||||
| // Rd cannot encode R15. | ||||
| bool ThumbTranslatorVisitor::thumb16_MVN_reg(Reg m, Reg d) { | ||||
|     const auto result = ir.Not(ir.GetRegister(m)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|     ir.SetZFlag(ir.IsZero(result)); | ||||
|     if (!ir.current_location.IT().IsInITBlock()) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|         ir.SetZFlag(ir.IsZero(result)); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| @@ -367,13 +420,17 @@ bool ThumbTranslatorVisitor::thumb16_MVN_reg(Reg m, Reg d) { | ||||
| bool ThumbTranslatorVisitor::thumb16_ADD_reg_t2(bool d_n_hi, Reg m, Reg d_n_lo) { | ||||
|     const Reg d_n = d_n_hi ? (d_n_lo + 8) : d_n_lo; | ||||
|     const Reg n = d_n; | ||||
|     const Reg d = d_n; | ||||
|     if (n == Reg::PC && m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|     if (d == Reg::PC && ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const Reg d = d_n; | ||||
|     const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0)); | ||||
|     if (d == Reg::PC) { | ||||
|         ir.UpdateUpperLocationDescriptor(); | ||||
|         ir.ALUWritePC(result.result); | ||||
|         // Return to dispatch as we can't predict what PC is going to be. Stop compilation. | ||||
|         ir.SetTerm(IR::Term::FastDispatchHint{}); | ||||
| @@ -405,9 +462,14 @@ bool ThumbTranslatorVisitor::thumb16_CMP_reg_t2(bool n_hi, Reg m, Reg n_lo) { | ||||
| // MOV <Rd>, <Rm> | ||||
| bool ThumbTranslatorVisitor::thumb16_MOV_reg(bool d_hi, Reg m, Reg d_lo) { | ||||
|     const Reg d = d_hi ? (d_lo + 8) : d_lo; | ||||
|     if (d == Reg::PC && ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto result = ir.GetRegister(m); | ||||
|  | ||||
|     if (d == Reg::PC) { | ||||
|         ir.UpdateUpperLocationDescriptor(); | ||||
|         ir.ALUWritePC(result); | ||||
|         ir.SetTerm(IR::Term::FastDispatchHint{}); | ||||
|         return false; | ||||
| @@ -635,11 +697,6 @@ bool ThumbTranslatorVisitor::thumb16_SUB_sp(Imm<7> imm7) { | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| // NOP<c> | ||||
| bool ThumbTranslatorVisitor::thumb16_NOP() { | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| // SEV<c> | ||||
| bool ThumbTranslatorVisitor::thumb16_SEV() { | ||||
|     if (!options.hook_hint_instructions) { | ||||
| @@ -680,6 +737,26 @@ bool ThumbTranslatorVisitor::thumb16_YIELD() { | ||||
|     return RaiseException(Exception::Yield); | ||||
| } | ||||
|  | ||||
| // NOP<c> | ||||
| bool ThumbTranslatorVisitor::thumb16_NOP() { | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| // IT{<x>{<y>{<z>}}} <cond> | ||||
| bool ThumbTranslatorVisitor::thumb16_IT(Imm<8> imm8) { | ||||
|     ASSERT_MSG((imm8.Bits<0, 3>() != 0b0000), "Decode Error"); | ||||
|     if (imm8.Bits<4, 7>() == 0b1111 || (imm8.Bits<4, 7>() == 0b1110 && Common::BitCount(imm8.Bits<0, 3>()) != 1)) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|     if (ir.current_location.IT().IsInITBlock()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto next_location = ir.current_location.AdvancePC(2).SetIT(ITState{imm8.ZeroExtend<u8>()}); | ||||
|     ir.SetTerm(IR::Term::LinkBlockFast{next_location}); | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| // SXTH <Rd>, <Rm> | ||||
| // Rd cannot encode R15. | ||||
| bool ThumbTranslatorVisitor::thumb16_SXTH(Reg m, Reg d) { | ||||
| @@ -761,6 +838,7 @@ bool ThumbTranslatorVisitor::thumb16_POP(bool P, RegList reg_list) { | ||||
|     if (Common::Bit<15>(reg_list)) { | ||||
|         // TODO(optimization): Possible location for an RSB pop. | ||||
|         const auto data = ir.ReadMemory32(address); | ||||
|         ir.UpdateUpperLocationDescriptor(); | ||||
|         ir.LoadWritePC(data); | ||||
|         address = ir.Add(address, ir.Imm32(4)); | ||||
|         ir.SetRegister(Reg::SP, address); | ||||
| @@ -774,6 +852,10 @@ bool ThumbTranslatorVisitor::thumb16_POP(bool P, RegList reg_list) { | ||||
|  | ||||
| // SETEND <endianness> | ||||
| bool ThumbTranslatorVisitor::thumb16_SETEND(bool E) { | ||||
|     if (ir.current_location.IT().IsInITBlock()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     if (E == ir.current_location.EFlag()) { | ||||
|         return true; | ||||
|     } | ||||
| @@ -822,6 +904,8 @@ bool ThumbTranslatorVisitor::thumb16_REVSH(Reg m, Reg d) { | ||||
| // BKPT #<imm8> | ||||
| bool ThumbTranslatorVisitor::thumb16_BKPT([[maybe_unused]] Imm<8> imm8) { | ||||
|     ir.ExceptionRaised(Exception::Breakpoint); | ||||
|     ir.UpdateUpperLocationDescriptor(); | ||||
|     ir.LoadWritePC(ir.Imm32(ir.current_location.PC())); | ||||
|     ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}}); | ||||
|     return false; | ||||
| } | ||||
| @@ -873,15 +957,18 @@ bool ThumbTranslatorVisitor::thumb16_LDMIA(Reg n, RegList reg_list) { | ||||
|  | ||||
| // CB{N}Z <Rn>, <label> | ||||
| bool ThumbTranslatorVisitor::thumb16_CBZ_CBNZ(bool nonzero, Imm<1> i, Imm<5> imm5, Reg n) { | ||||
|     if (ir.current_location.IT().IsInITBlock()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const u32 imm = concatenate(i, imm5, Imm<1>{0}).ZeroExtend(); | ||||
|     const IR::U32 rn = ir.GetRegister(n); | ||||
|  | ||||
|     ir.SetCheckBit(ir.IsZero(rn)); | ||||
|  | ||||
|     const auto [cond_pass, cond_fail] = [this, imm, nonzero] { | ||||
|         const u32 target = ir.PC() + imm; | ||||
|         const auto skip = IR::Term::LinkBlock{ir.current_location.AdvancePC(2)}; | ||||
|         const auto branch = IR::Term::LinkBlock{ir.current_location.AdvancePC(target)}; | ||||
|         const auto branch = IR::Term::LinkBlock{ir.current_location.AdvancePC(imm + 4)}; | ||||
|  | ||||
|         if (nonzero) { | ||||
|             return std::make_pair(skip, branch); | ||||
| @@ -890,7 +977,7 @@ bool ThumbTranslatorVisitor::thumb16_CBZ_CBNZ(bool nonzero, Imm<1> i, Imm<5> imm | ||||
|         } | ||||
|     }(); | ||||
|  | ||||
|      ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail}); | ||||
|     ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail}); | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| @@ -900,6 +987,11 @@ bool ThumbTranslatorVisitor::thumb16_UDF() { | ||||
|  | ||||
| // BX <Rm> | ||||
| bool ThumbTranslatorVisitor::thumb16_BX(Reg m) { | ||||
|     if (ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     ir.UpdateUpperLocationDescriptor(); | ||||
|     ir.BXWritePC(ir.GetRegister(m)); | ||||
|     if (m == Reg::R14) | ||||
|         ir.SetTerm(IR::Term::PopRSBHint{}); | ||||
| @@ -910,7 +1002,12 @@ bool ThumbTranslatorVisitor::thumb16_BX(Reg m) { | ||||
|  | ||||
| // BLX <Rm> | ||||
| bool ThumbTranslatorVisitor::thumb16_BLX_reg(Reg m) { | ||||
|     if (ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     ir.PushRSB(ir.current_location.AdvancePC(2)); | ||||
|     ir.UpdateUpperLocationDescriptor(); | ||||
|     ir.BXWritePC(ir.GetRegister(m)); | ||||
|     ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 2) | 1)); | ||||
|     ir.SetTerm(IR::Term::FastDispatchHint{}); | ||||
| @@ -920,6 +1017,7 @@ bool ThumbTranslatorVisitor::thumb16_BLX_reg(Reg m) { | ||||
| // SVC #<imm8> | ||||
| bool ThumbTranslatorVisitor::thumb16_SVC(Imm<8> imm8) { | ||||
|     const u32 imm32 = imm8.ZeroExtend(); | ||||
|     ir.UpdateUpperLocationDescriptor(); | ||||
|     ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 2)); | ||||
|     ir.PushRSB(ir.current_location.AdvancePC(2)); | ||||
|     ir.CallSupervisor(ir.Imm32(imm32)); | ||||
| @@ -929,13 +1027,17 @@ bool ThumbTranslatorVisitor::thumb16_SVC(Imm<8> imm8) { | ||||
|  | ||||
| // B<cond> <label> | ||||
| bool ThumbTranslatorVisitor::thumb16_B_t1(Cond cond, Imm<8> imm8) { | ||||
|     if (ir.current_location.IT().IsInITBlock()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     if (cond == Cond::AL) { | ||||
|         return thumb16_UDF(); | ||||
|     } | ||||
|  | ||||
|     const s32 imm32 = static_cast<s32>((imm8.SignExtend<u32>() << 1) + 4); | ||||
|     const auto then_location = ir.current_location.AdvancePC(imm32); | ||||
|     const auto else_location = ir.current_location.AdvancePC(2); | ||||
|     const auto then_location = ir.current_location.AdvancePC(imm32).AdvanceIT(); | ||||
|     const auto else_location = ir.current_location.AdvancePC(2).AdvanceIT(); | ||||
|  | ||||
|     ir.SetTerm(IR::Term::If{cond, IR::Term::LinkBlock{then_location}, IR::Term::LinkBlock{else_location}}); | ||||
|     return false; | ||||
| @@ -943,8 +1045,12 @@ bool ThumbTranslatorVisitor::thumb16_B_t1(Cond cond, Imm<8> imm8) { | ||||
|  | ||||
| // B <label> | ||||
| bool ThumbTranslatorVisitor::thumb16_B_t2(Imm<11> imm11) { | ||||
|     if (ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const s32 imm32 = static_cast<s32>((imm11.SignExtend<u32>() << 1) + 4); | ||||
|     const auto next_location = ir.current_location.AdvancePC(imm32); | ||||
|     const auto next_location = ir.current_location.AdvancePC(imm32).AdvanceIT(); | ||||
|  | ||||
|     ir.SetTerm(IR::Term::LinkBlock{next_location}); | ||||
|     return false; | ||||
|   | ||||
							
								
								
									
										55
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_branch.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										55
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_branch.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| /* This file is part of the dynarmic project. | ||||
|  * Copyright (c) 2016 MerryMage | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #include "frontend/A32/translate/impl/translate_thumb.h" | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
|  | ||||
| // BL <label> | ||||
| bool ThumbTranslatorVisitor::thumb32_BL_imm(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> j2, Imm<11> lo) { | ||||
|     const Imm<1> i1{j1 == S}; | ||||
|     const Imm<1> i2{j2 == S}; | ||||
|  | ||||
|     if (ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     ir.PushRSB(ir.current_location.AdvancePC(4)); | ||||
|     ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1)); | ||||
|  | ||||
|     const s32 imm32 = static_cast<s32>((concatenate(S, i1, i2, hi, lo).SignExtend<u32>() << 1) + 4); | ||||
|     const auto new_location = ir.current_location | ||||
|                                 .AdvancePC(imm32) | ||||
|                                 .AdvanceIT(); | ||||
|     ir.SetTerm(IR::Term::LinkBlock{new_location}); | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| // BLX <label> | ||||
| bool ThumbTranslatorVisitor::thumb32_BLX_imm(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> j2, Imm<11> lo) { | ||||
|     const Imm<1> i1{j1 == S}; | ||||
|     const Imm<1> i2{j2 == S}; | ||||
|  | ||||
|     if (ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     if (lo.Bit<0>()) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     ir.PushRSB(ir.current_location.AdvancePC(4)); | ||||
|     ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1)); | ||||
|  | ||||
|     const s32 imm32 = static_cast<s32>(concatenate(S, i1, i2, hi, lo).SignExtend<u32>() << 1); | ||||
|     const auto new_location = ir.current_location | ||||
|                                 .SetPC(ir.AlignPC(4) + imm32) | ||||
|                                 .SetTFlag(false) | ||||
|                                 .AdvanceIT(); | ||||
|     ir.SetTerm(IR::Term::LinkBlock{new_location}); | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| } // namespace Dynarmic::A32 | ||||
							
								
								
									
										14
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_control.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										14
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_control.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| /* This file is part of the dynarmic project. | ||||
|  * Copyright (c) 2016 MerryMage | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #include "frontend/A32/translate/impl/translate_thumb.h" | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UDF() { | ||||
|     return thumb16_UDF(); | ||||
| } | ||||
|  | ||||
| } // namespace Dynarmic::A32 | ||||
							
								
								
									
										94
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_data_processing_modified_immediate.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										94
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_data_processing_modified_immediate.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| /* This file is part of the dynarmic project. | ||||
|  * Copyright (c) 2021 MerryMage | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #include "frontend/A32/translate/impl/translate_thumb.h" | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_TST_imm(Imm<1> i, Reg n, Imm<3> imm3, Imm<8> imm8) { | ||||
|     if (n == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto imm_carry = ThumbExpandImm_C(i, imm3, imm8, ir.GetCFlag()); | ||||
|     const auto result = ir.And(ir.GetRegister(n), ir.Imm32(imm_carry.imm32)); | ||||
|  | ||||
|     ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|     ir.SetZFlag(ir.IsZero(result)); | ||||
|     ir.SetCFlag(imm_carry.carry); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_AND_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Reg d, Imm<8> imm8) { | ||||
|     ASSERT_MSG(!(d == Reg::PC && S), "Decode error"); | ||||
|     if ((d == Reg::PC && !S) || n == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto imm_carry = ThumbExpandImm_C(i, imm3, imm8, ir.GetCFlag()); | ||||
|     const auto result = ir.And(ir.GetRegister(n), ir.Imm32(imm_carry.imm32)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     if (S) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|         ir.SetZFlag(ir.IsZero(result)); | ||||
|         ir.SetCFlag(imm_carry.carry); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_BIC_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Reg d, Imm<8> imm8) { | ||||
|     if (d == Reg::PC || n == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto imm_carry = ThumbExpandImm_C(i, imm3, imm8, ir.GetCFlag()); | ||||
|     const auto result = ir.And(ir.GetRegister(n), ir.Not(ir.Imm32(imm_carry.imm32))); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     if (S) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|         ir.SetZFlag(ir.IsZero(result)); | ||||
|         ir.SetCFlag(imm_carry.carry); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_MOV_imm(Imm<1> i, bool S, Imm<3> imm3, Reg d, Imm<8> imm8) { | ||||
|     if (d == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto imm_carry = ThumbExpandImm_C(i, imm3, imm8, ir.GetCFlag()); | ||||
|     const auto result = ir.Imm32(imm_carry.imm32); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     if (S) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|         ir.SetZFlag(ir.IsZero(result)); | ||||
|         ir.SetCFlag(imm_carry.carry); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_ORR_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Reg d, Imm<8> imm8) { | ||||
|     ASSERT_MSG(n != Reg::PC, "Decode error"); | ||||
|     if (d == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto imm_carry = ThumbExpandImm_C(i, imm3, imm8, ir.GetCFlag()); | ||||
|     const auto result = ir.Or(ir.GetRegister(n), ir.Imm32(imm_carry.imm32)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     if (S) { | ||||
|         ir.SetNFlag(ir.MostSignificantBit(result)); | ||||
|         ir.SetZFlag(ir.IsZero(result)); | ||||
|         ir.SetCFlag(imm_carry.carry); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| } // namespace Dynarmic::A32 | ||||
							
								
								
									
										169
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_data_processing_register.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										169
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_data_processing_register.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| /* This file is part of the dynarmic project. | ||||
|  * Copyright (c) 2021 MerryMage | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #include "frontend/A32/translate/impl/translate_thumb.h" | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
| static IR::U32 Rotate(A32::IREmitter& ir, Reg m, SignExtendRotation rotate) { | ||||
|     const u8 rotate_by = static_cast<u8>(static_cast<size_t>(rotate) * 8); | ||||
|     return ir.RotateRight(ir.GetRegister(m), ir.Imm8(rotate_by), ir.Imm1(0)).result; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SXTB(Reg d, SignExtendRotation rotate, Reg m) { | ||||
|     if (d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto rotated = Rotate(ir, m, rotate); | ||||
|     const auto result = ir.SignExtendByteToWord(ir.LeastSignificantByte(rotated)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SXTB16(Reg d, SignExtendRotation rotate, Reg m) { | ||||
|     if (d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto rotated = Rotate(ir, m, rotate); | ||||
|     const auto low_byte = ir.And(rotated, ir.Imm32(0x00FF00FF)); | ||||
|     const auto sign_bit = ir.And(rotated, ir.Imm32(0x00800080)); | ||||
|     const auto result = ir.Or(low_byte, ir.Mul(sign_bit, ir.Imm32(0x1FE))); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SXTAB(Reg n, Reg d, SignExtendRotation rotate, Reg m) { | ||||
|     if (d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto rotated = Rotate(ir, m, rotate); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.Add(reg_n, ir.SignExtendByteToWord(ir.LeastSignificantByte(rotated))); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SXTAB16(Reg n, Reg d, SignExtendRotation rotate, Reg m) { | ||||
|     if (d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto rotated = Rotate(ir, m, rotate); | ||||
|     const auto low_byte = ir.And(rotated, ir.Imm32(0x00FF00FF)); | ||||
|     const auto sign_bit = ir.And(rotated, ir.Imm32(0x00800080)); | ||||
|     const auto addend = ir.Or(low_byte, ir.Mul(sign_bit, ir.Imm32(0x1FE))); | ||||
|     const auto result = ir.PackedAddU16(addend, ir.GetRegister(n)).result; | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SXTH(Reg d, SignExtendRotation rotate, Reg m) { | ||||
|     if (d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto rotated = Rotate(ir, m, rotate); | ||||
|     const auto result = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(rotated)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SXTAH(Reg n, Reg d, SignExtendRotation rotate, Reg m) { | ||||
|     if (d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto rotated = Rotate(ir, m, rotate); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.Add(reg_n, ir.SignExtendHalfToWord(ir.LeastSignificantHalf(rotated))); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UXTB(Reg d, SignExtendRotation rotate, Reg m) { | ||||
|     if (d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto rotated = Rotate(ir, m, rotate); | ||||
|     const auto result = ir.ZeroExtendByteToWord(ir.LeastSignificantByte(rotated)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UXTB16(Reg d, SignExtendRotation rotate, Reg m) { | ||||
|     if (d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto rotated = Rotate(ir, m, rotate); | ||||
|     const auto result = ir.And(rotated, ir.Imm32(0x00FF00FF)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UXTAB(Reg n, Reg d, SignExtendRotation rotate, Reg m) { | ||||
|     if (d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto rotated = Rotate(ir, m, rotate); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.Add(reg_n, ir.ZeroExtendByteToWord(ir.LeastSignificantByte(rotated))); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UXTAB16(Reg n, Reg d, SignExtendRotation rotate, Reg m) { | ||||
|     if (d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto rotated = Rotate(ir, m, rotate); | ||||
|     auto result = ir.And(rotated, ir.Imm32(0x00FF00FF)); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     result = ir.PackedAddU16(reg_n, result).result; | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UXTH(Reg d, SignExtendRotation rotate, Reg m) { | ||||
|     if (d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto rotated = Rotate(ir, m, rotate); | ||||
|     const auto result = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(rotated)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UXTAH(Reg n, Reg d, SignExtendRotation rotate, Reg m) { | ||||
|     if (d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto rotated = Rotate(ir, m, rotate); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.Add(reg_n, ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(rotated))); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| } // namespace Dynarmic::A32 | ||||
							
								
								
									
										222
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_long_multiply.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										222
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_long_multiply.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,222 @@ | ||||
| /* This file is part of the dynarmic project. | ||||
|  * Copyright (c) 2021 MerryMage | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #include "frontend/A32/translate/impl/translate_thumb.h" | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
| namespace { | ||||
| using DivideFunction = IR::U32U64 (IREmitter::*)(const IR::U32U64&, const IR::U32U64&); | ||||
|  | ||||
| bool DivideOperation(ThumbTranslatorVisitor& v, Reg d, Reg m, Reg n, DivideFunction fn) { | ||||
|     if (d == Reg::PC || m == Reg::PC || n == Reg::PC) { | ||||
|         return v.UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 operand1 = v.ir.GetRegister(n); | ||||
|     const IR::U32 operand2 = v.ir.GetRegister(m); | ||||
|     const IR::U32 result = (v.ir.*fn)(operand1, operand2); | ||||
|  | ||||
|     v.ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
| } // Anonymous namespace | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SDIV(Reg n, Reg d, Reg m) { | ||||
|     return DivideOperation(*this, d, m, n, &IREmitter::SignedDiv); | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMLAL(Reg n, Reg dLo, Reg dHi, Reg m) { | ||||
|     if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     if (dHi == dLo) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); | ||||
|     const auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); | ||||
|     const auto product = ir.Mul(n64, m64); | ||||
|     const auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); | ||||
|     const auto result = ir.Add(product, addend); | ||||
|     const auto lo = ir.LeastSignificantWord(result); | ||||
|     const auto hi = ir.MostSignificantWord(result).result; | ||||
|  | ||||
|     ir.SetRegister(dLo, lo); | ||||
|     ir.SetRegister(dHi, hi); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMLALD(Reg n, Reg dLo, Reg dHi, bool M, Reg m) { | ||||
|     if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     if (dHi == dLo) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 n32 = ir.GetRegister(n); | ||||
|     const IR::U32 m32 = ir.GetRegister(m); | ||||
|     const IR::U32 n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); | ||||
|     const IR::U32 n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|  | ||||
|     IR::U32 m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); | ||||
|     IR::U32 m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|     if (M) { | ||||
|         std::swap(m_lo, m_hi); | ||||
|     } | ||||
|  | ||||
|     const IR::U64 product_lo = ir.SignExtendWordToLong(ir.Mul(n_lo, m_lo)); | ||||
|     const IR::U64 product_hi = ir.SignExtendWordToLong(ir.Mul(n_hi, m_hi)); | ||||
|     const auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); | ||||
|     const auto result = ir.Add(ir.Add(product_lo, product_hi), addend); | ||||
|  | ||||
|     ir.SetRegister(dLo, ir.LeastSignificantWord(result)); | ||||
|     ir.SetRegister(dHi, ir.MostSignificantWord(result).result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMLALXY(Reg n, Reg dLo, Reg dHi, bool N, bool M, Reg m) { | ||||
|     if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     if (dHi == dLo) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 n32 = ir.GetRegister(n); | ||||
|     const IR::U32 m32 = ir.GetRegister(m); | ||||
|     const IR::U32 n16 = N ? ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result | ||||
|                           : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); | ||||
|     const IR::U32 m16 = M ? ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result | ||||
|                           : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); | ||||
|     const IR::U64 product = ir.SignExtendWordToLong(ir.Mul(n16, m16)); | ||||
|     const auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); | ||||
|     const auto result = ir.Add(product, addend); | ||||
|  | ||||
|     ir.SetRegister(dLo, ir.LeastSignificantWord(result)); | ||||
|     ir.SetRegister(dHi, ir.MostSignificantWord(result).result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMLSLD(Reg n, Reg dLo, Reg dHi, bool M, Reg m) { | ||||
|     if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     if (dHi == dLo) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 n32 = ir.GetRegister(n); | ||||
|     const IR::U32 m32 = ir.GetRegister(m); | ||||
|     const IR::U32 n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); | ||||
|     const IR::U32 n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|  | ||||
|     IR::U32 m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); | ||||
|     IR::U32 m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|     if (M) { | ||||
|         std::swap(m_lo, m_hi); | ||||
|     } | ||||
|  | ||||
|     const IR::U64 product_lo = ir.SignExtendWordToLong(ir.Mul(n_lo, m_lo)); | ||||
|     const IR::U64 product_hi = ir.SignExtendWordToLong(ir.Mul(n_hi, m_hi)); | ||||
|     const auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); | ||||
|     const auto result = ir.Add(ir.Sub(product_lo, product_hi), addend); | ||||
|  | ||||
|     ir.SetRegister(dLo, ir.LeastSignificantWord(result)); | ||||
|     ir.SetRegister(dHi, ir.MostSignificantWord(result).result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMULL(Reg n, Reg dLo, Reg dHi, Reg m) { | ||||
|     if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     if (dHi == dLo) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); | ||||
|     const auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); | ||||
|     const auto result = ir.Mul(n64, m64); | ||||
|     const auto lo = ir.LeastSignificantWord(result); | ||||
|     const auto hi = ir.MostSignificantWord(result).result; | ||||
|  | ||||
|     ir.SetRegister(dLo, lo); | ||||
|     ir.SetRegister(dHi, hi); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UDIV(Reg n, Reg d, Reg m) { | ||||
|     return DivideOperation(*this, d, m, n, &IREmitter::UnsignedDiv); | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UMLAL(Reg n, Reg dLo, Reg dHi, Reg m) { | ||||
|     if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     if (dHi == dLo) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n)); | ||||
|     const auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m)); | ||||
|     const auto product = ir.Mul(n64, m64); | ||||
|     const auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi)); | ||||
|     const auto result = ir.Add(product, addend); | ||||
|     const auto lo = ir.LeastSignificantWord(result); | ||||
|     const auto hi = ir.MostSignificantWord(result).result; | ||||
|  | ||||
|     ir.SetRegister(dLo, lo); | ||||
|     ir.SetRegister(dHi, hi); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UMULL(Reg n, Reg dLo, Reg dHi, Reg m) { | ||||
|     if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     if (dHi == dLo) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n)); | ||||
|     const auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m)); | ||||
|     const auto result = ir.Mul(n64, m64); | ||||
|     const auto lo = ir.LeastSignificantWord(result); | ||||
|     const auto hi = ir.MostSignificantWord(result).result; | ||||
|  | ||||
|     ir.SetRegister(dLo, lo); | ||||
|     ir.SetRegister(dHi, hi); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UMAAL(Reg n, Reg dLo, Reg dHi, Reg m) { | ||||
|     if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     if (dHi == dLo) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto lo64 = ir.ZeroExtendWordToLong(ir.GetRegister(dLo)); | ||||
|     const auto hi64 = ir.ZeroExtendWordToLong(ir.GetRegister(dHi)); | ||||
|     const auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n)); | ||||
|     const auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m)); | ||||
|     const auto result = ir.Add(ir.Add(ir.Mul(n64, m64), hi64), lo64); | ||||
|  | ||||
|     ir.SetRegister(dLo, ir.LeastSignificantWord(result)); | ||||
|     ir.SetRegister(dHi, ir.MostSignificantWord(result).result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| } // namespace Dynarmic::A32 | ||||
							
								
								
									
										157
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_misc.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										157
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_misc.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| /* This file is part of the dynarmic project. | ||||
|  * Copyright (c) 2016 MerryMage | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #include "frontend/A32/translate/impl/translate_thumb.h" | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_CLZ(Reg n, Reg d, Reg m) { | ||||
|     if (m != n || d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto result = ir.CountLeadingZeros(reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_QADD(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.SignedSaturatedAdd(reg_m, reg_n); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.OrQFlag(result.overflow); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_QDADD(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto doubled_n = ir.SignedSaturatedAdd(reg_n, reg_n); | ||||
|     ir.OrQFlag(doubled_n.overflow); | ||||
|  | ||||
|     const auto result = ir.SignedSaturatedAdd(reg_m, doubled_n.result); | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.OrQFlag(result.overflow); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_QDSUB(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto doubled_n = ir.SignedSaturatedAdd(reg_n, reg_n); | ||||
|     ir.OrQFlag(doubled_n.overflow); | ||||
|  | ||||
|     const auto result = ir.SignedSaturatedSub(reg_m, doubled_n.result); | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.OrQFlag(result.overflow); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_QSUB(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.SignedSaturatedSub(reg_m, reg_n); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.OrQFlag(result.overflow); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_RBIT(Reg n, Reg d, Reg m) { | ||||
|     if (m != n || d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 swapped = ir.ByteReverseWord(ir.GetRegister(m)); | ||||
|  | ||||
|     // ((x & 0xF0F0F0F0) >> 4) | ((x & 0x0F0F0F0F) << 4) | ||||
|     const IR::U32 first_lsr = ir.LogicalShiftRight(ir.And(swapped, ir.Imm32(0xF0F0F0F0)), ir.Imm8(4)); | ||||
|     const IR::U32 first_lsl = ir.LogicalShiftLeft(ir.And(swapped, ir.Imm32(0x0F0F0F0F)), ir.Imm8(4)); | ||||
|     const IR::U32 corrected = ir.Or(first_lsl, first_lsr); | ||||
|  | ||||
|     // ((x & 0x88888888) >> 3) | ((x & 0x44444444) >> 1) | | ||||
|     // ((x & 0x22222222) << 1) | ((x & 0x11111111) << 3) | ||||
|     const IR::U32 second_lsr = ir.LogicalShiftRight(ir.And(corrected, ir.Imm32(0x88888888)), ir.Imm8(3)); | ||||
|     const IR::U32 third_lsr = ir.LogicalShiftRight(ir.And(corrected, ir.Imm32(0x44444444)), ir.Imm8(1)); | ||||
|     const IR::U32 second_lsl = ir.LogicalShiftLeft(ir.And(corrected, ir.Imm32(0x22222222)), ir.Imm8(1)); | ||||
|     const IR::U32 third_lsl = ir.LogicalShiftLeft(ir.And(corrected, ir.Imm32(0x11111111)), ir.Imm8(3)); | ||||
|  | ||||
|     const IR::U32 result = ir.Or(ir.Or(ir.Or(second_lsr, third_lsr), second_lsl), third_lsl); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_REV(Reg n, Reg d, Reg m) { | ||||
|     if (m != n || d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto result = ir.ByteReverseWord(ir.GetRegister(m)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_REV16(Reg n, Reg d, Reg m) { | ||||
|     if (m != n || d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto lo = ir.And(ir.LogicalShiftRight(reg_m, ir.Imm8(8), ir.Imm1(0)).result, ir.Imm32(0x00FF00FF)); | ||||
|     const auto hi = ir.And(ir.LogicalShiftLeft(reg_m, ir.Imm8(8), ir.Imm1(0)).result, ir.Imm32(0xFF00FF00)); | ||||
|     const auto result = ir.Or(lo, hi); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_REVSH(Reg n, Reg d, Reg m) { | ||||
|     if (m != n || d == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto rev_half = ir.ByteReverseHalf(ir.LeastSignificantHalf(reg_m)); | ||||
|  | ||||
|     ir.SetRegister(d, ir.SignExtendHalfToWord(rev_half)); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SEL(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSelect(ir.GetGEFlags(), reg_m, reg_n); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| } // namespace Dynarmic::A32 | ||||
							
								
								
									
										312
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_multiply.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										312
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_multiply.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,312 @@ | ||||
| /* This file is part of the dynarmic project. | ||||
|  * Copyright (c) 2021 MerryMage | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #include "frontend/A32/translate/impl/translate_thumb.h" | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_MLA(Reg n, Reg a, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_a = ir.GetRegister(a); | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.Add(ir.Mul(reg_n, reg_m), reg_a); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_MLS(Reg n, Reg a, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_a = ir.GetRegister(a); | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.Sub(reg_a, ir.Mul(reg_n, reg_m)); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_MUL(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.Mul(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMLAD(Reg n, Reg a, Reg d, bool X, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 n32 = ir.GetRegister(n); | ||||
|     const IR::U32 m32 = ir.GetRegister(m); | ||||
|     const IR::U32 n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); | ||||
|     const IR::U32 n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|  | ||||
|     IR::U32 m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); | ||||
|     IR::U32 m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|     if (X) { | ||||
|         std::swap(m_lo, m_hi); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 product_lo = ir.Mul(n_lo, m_lo); | ||||
|     const IR::U32 product_hi = ir.Mul(n_hi, m_hi); | ||||
|     const IR::U32 addend = ir.GetRegister(a); | ||||
|  | ||||
|     auto result_overflow = ir.AddWithCarry(product_lo, product_hi, ir.Imm1(0)); | ||||
|     ir.OrQFlag(result_overflow.overflow); | ||||
|     result_overflow = ir.AddWithCarry(result_overflow.result, addend, ir.Imm1(0)); | ||||
|  | ||||
|     ir.SetRegister(d, result_overflow.result); | ||||
|     ir.OrQFlag(result_overflow.overflow); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMLSD(Reg n, Reg a, Reg d, bool X, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 n32 = ir.GetRegister(n); | ||||
|     const IR::U32 m32 = ir.GetRegister(m); | ||||
|     const IR::U32 n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); | ||||
|     const IR::U32 n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|  | ||||
|     IR::U32 m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); | ||||
|     IR::U32 m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|     if (X) { | ||||
|         std::swap(m_lo, m_hi); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 product_lo = ir.Mul(n_lo, m_lo); | ||||
|     const IR::U32 product_hi = ir.Mul(n_hi, m_hi); | ||||
|     const IR::U32 addend = ir.GetRegister(a); | ||||
|     const IR::U32 product = ir.Sub(product_lo, product_hi); | ||||
|     auto result_overflow = ir.AddWithCarry(product, addend, ir.Imm1(0)); | ||||
|  | ||||
|     ir.SetRegister(d, result_overflow.result); | ||||
|     ir.OrQFlag(result_overflow.overflow); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMLAXY(Reg n, Reg a, Reg d, bool N, bool M, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 n32 = ir.GetRegister(n); | ||||
|     const IR::U32 m32 = ir.GetRegister(m); | ||||
|     const IR::U32 n16 = N ? ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result | ||||
|                           : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); | ||||
|     const IR::U32 m16 = M ? ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result | ||||
|                           : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); | ||||
|     const IR::U32 product = ir.Mul(n16, m16); | ||||
|     const auto result_overflow = ir.AddWithCarry(product, ir.GetRegister(a), ir.Imm1(0)); | ||||
|  | ||||
|     ir.SetRegister(d, result_overflow.result); | ||||
|     ir.OrQFlag(result_overflow.overflow); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMMLA(Reg n, Reg a, Reg d, bool R, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); | ||||
|     const auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); | ||||
|     const auto a64 = ir.Pack2x32To1x64(ir.Imm32(0), ir.GetRegister(a)); | ||||
|     const auto temp = ir.Add(a64, ir.Mul(n64, m64)); | ||||
|     const auto result_carry = ir.MostSignificantWord(temp); | ||||
|     auto result = result_carry.result; | ||||
|     if (R) { | ||||
|         result = ir.AddWithCarry(result, ir.Imm32(0), result_carry.carry).result; | ||||
|     } | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMMLS(Reg n, Reg a, Reg d, bool R, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); | ||||
|     const auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); | ||||
|     const auto a64 = ir.Pack2x32To1x64(ir.Imm32(0), ir.GetRegister(a)); | ||||
|     const auto temp = ir.Sub(a64, ir.Mul(n64, m64)); | ||||
|     const auto result_carry = ir.MostSignificantWord(temp); | ||||
|     auto result = result_carry.result; | ||||
|     if (R) { | ||||
|         result = ir.AddWithCarry(result, ir.Imm32(0), result_carry.carry).result; | ||||
|     } | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMMUL(Reg n, Reg d, bool R, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n)); | ||||
|     const auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m)); | ||||
|     const auto product = ir.Mul(n64, m64); | ||||
|     const auto result_carry = ir.MostSignificantWord(product); | ||||
|     auto result = result_carry.result; | ||||
|     if (R) { | ||||
|         result = ir.AddWithCarry(result, ir.Imm32(0), result_carry.carry).result; | ||||
|     } | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMUAD(Reg n, Reg d, bool M, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 n32 = ir.GetRegister(n); | ||||
|     const IR::U32 m32 = ir.GetRegister(m); | ||||
|     const IR::U32 n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); | ||||
|     const IR::U32 n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|  | ||||
|     IR::U32 m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); | ||||
|     IR::U32 m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|     if (M) { | ||||
|         std::swap(m_lo, m_hi); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 product_lo = ir.Mul(n_lo, m_lo); | ||||
|     const IR::U32 product_hi = ir.Mul(n_hi, m_hi); | ||||
|     const auto result_overflow = ir.AddWithCarry(product_lo, product_hi, ir.Imm1(0)); | ||||
|  | ||||
|     ir.SetRegister(d, result_overflow.result); | ||||
|     ir.OrQFlag(result_overflow.overflow); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMUSD(Reg n, Reg d, bool M, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 n32 = ir.GetRegister(n); | ||||
|     const IR::U32 m32 = ir.GetRegister(m); | ||||
|     const IR::U32 n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); | ||||
|     const IR::U32 n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|  | ||||
|     IR::U32 m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); | ||||
|     IR::U32 m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|     if (M) { | ||||
|         std::swap(m_lo, m_hi); | ||||
|     } | ||||
|  | ||||
|     const IR::U32 product_lo = ir.Mul(n_lo, m_lo); | ||||
|     const IR::U32 product_hi = ir.Mul(n_hi, m_hi); | ||||
|     const IR::U32 result = ir.Sub(product_lo, product_hi); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMULXY(Reg n, Reg d, bool N, bool M, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto n32 = ir.GetRegister(n); | ||||
|     const auto m32 = ir.GetRegister(m); | ||||
|     const auto n16 = N ? ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result | ||||
|                        : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32)); | ||||
|     const auto m16 = M ? ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result | ||||
|                        : ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)); | ||||
|     const auto result = ir.Mul(n16, m16); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMLAWY(Reg n, Reg a, Reg d, bool M, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const IR::U64 n32 = ir.SignExtendWordToLong(ir.GetRegister(n)); | ||||
|     IR::U32 m32 = ir.GetRegister(m); | ||||
|     if (M) { | ||||
|         m32 = ir.LogicalShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|     } | ||||
|     const IR::U64 m16 = ir.SignExtendWordToLong(ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32))); | ||||
|     const auto product = ir.LeastSignificantWord(ir.LogicalShiftRight(ir.Mul(n32, m16), ir.Imm8(16))); | ||||
|     const auto result_overflow = ir.AddWithCarry(product, ir.GetRegister(a), ir.Imm1(0)); | ||||
|  | ||||
|     ir.SetRegister(d, result_overflow.result); | ||||
|     ir.OrQFlag(result_overflow.overflow); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SMULWY(Reg n, Reg d, bool M, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const IR::U64 n32 = ir.SignExtendWordToLong(ir.GetRegister(n)); | ||||
|     IR::U32 m32 = ir.GetRegister(m); | ||||
|     if (M) { | ||||
|         m32 = ir.LogicalShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result; | ||||
|     } | ||||
|     const IR::U64 m16 = ir.SignExtendWordToLong(ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32))); | ||||
|     const auto result = ir.LogicalShiftRight(ir.Mul(n32, m16), ir.Imm8(16)); | ||||
|  | ||||
|     ir.SetRegister(d, ir.LeastSignificantWord(result)); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_USAD8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedAbsDiffSumS8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_USADA8(Reg n, Reg a, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_a = ir.GetRegister(a); | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto tmp = ir.PackedAbsDiffSumS8(reg_n, reg_m); | ||||
|     const auto result = ir.AddWithCarry(reg_a, tmp, ir.Imm1(0)); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| } // namespace Dynarmic::A32 | ||||
							
								
								
									
										521
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_parallel.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										521
									
								
								externals/dynarmic/src/frontend/A32/translate/impl/thumb32_parallel.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,521 @@ | ||||
| /* This file is part of the dynarmic project. | ||||
|  * Copyright (c) 2016 MerryMage | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #include "frontend/A32/translate/impl/translate_thumb.h" | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
| static IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) { | ||||
|     return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result); | ||||
| } | ||||
|  | ||||
| static IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) { | ||||
|     return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result); | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SADD8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedAddS8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetGEFlags(result.ge); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SADD16(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedAddS16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetGEFlags(result.ge); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SASX(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedAddSubS16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetGEFlags(result.ge); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SSAX(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSubAddS16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetGEFlags(result.ge); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SSUB8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSubS8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetGEFlags(result.ge); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SSUB16(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSubS16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetGEFlags(result.ge); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UADD8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedAddU8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetGEFlags(result.ge); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UADD16(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedAddU16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetGEFlags(result.ge); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UASX(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedAddSubU16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetGEFlags(result.ge); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_USAX(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSubAddU16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetGEFlags(result.ge); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_USUB8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSubU8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetGEFlags(result.ge); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_USUB16(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSubU16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result.result); | ||||
|     ir.SetGEFlags(result.ge); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_QADD8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSaturatedAddS8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_QADD16(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSaturatedAddS16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_QASX(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto Rn = ir.GetRegister(n); | ||||
|     const auto Rm = ir.GetRegister(m); | ||||
|     const auto Rn_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rn)); | ||||
|     const auto Rn_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rn)); | ||||
|     const auto Rm_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rm)); | ||||
|     const auto Rm_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rm)); | ||||
|     const auto diff = ir.SignedSaturation(ir.Sub(Rn_lo, Rm_hi), 16).result; | ||||
|     const auto sum = ir.SignedSaturation(ir.Add(Rn_hi, Rm_lo), 16).result; | ||||
|     const auto result = Pack2x16To1x32(ir, diff, sum); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_QSAX(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto Rn = ir.GetRegister(n); | ||||
|     const auto Rm = ir.GetRegister(m); | ||||
|     const auto Rn_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rn)); | ||||
|     const auto Rn_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rn)); | ||||
|     const auto Rm_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rm)); | ||||
|     const auto Rm_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rm)); | ||||
|     const auto sum = ir.SignedSaturation(ir.Add(Rn_lo, Rm_hi), 16).result; | ||||
|     const auto diff = ir.SignedSaturation(ir.Sub(Rn_hi, Rm_lo), 16).result; | ||||
|     const auto result = Pack2x16To1x32(ir, sum, diff); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_QSUB8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSaturatedSubS8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_QSUB16(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSaturatedSubS16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UQADD8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSaturatedAddU8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UQADD16(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSaturatedAddU16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UQASX(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto Rn = ir.GetRegister(n); | ||||
|     const auto Rm = ir.GetRegister(m); | ||||
|     const auto Rn_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rn)); | ||||
|     const auto Rn_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rn)); | ||||
|     const auto Rm_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rm)); | ||||
|     const auto Rm_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rm)); | ||||
|     const auto diff = ir.UnsignedSaturation(ir.Sub(Rn_lo, Rm_hi), 16).result; | ||||
|     const auto sum = ir.UnsignedSaturation(ir.Add(Rn_hi, Rm_lo), 16).result; | ||||
|     const auto result = Pack2x16To1x32(ir, diff, sum); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UQSAX(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto Rn = ir.GetRegister(n); | ||||
|     const auto Rm = ir.GetRegister(m); | ||||
|     const auto Rn_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rn)); | ||||
|     const auto Rn_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rn)); | ||||
|     const auto Rm_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rm)); | ||||
|     const auto Rm_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rm)); | ||||
|     const auto sum = ir.UnsignedSaturation(ir.Add(Rn_lo, Rm_hi), 16).result; | ||||
|     const auto diff = ir.UnsignedSaturation(ir.Sub(Rn_hi, Rm_lo), 16).result; | ||||
|     const auto result = Pack2x16To1x32(ir, sum, diff); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UQSUB8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSaturatedSubU8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UQSUB16(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedSaturatedSubU16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SHADD8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedHalvingAddS8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SHADD16(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedHalvingAddS16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SHASX(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedHalvingAddSubS16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SHSAX(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedHalvingSubAddS16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SHSUB8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedHalvingSubS8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_SHSUB16(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedHalvingSubS16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UHADD8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedHalvingAddU8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UHADD16(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedHalvingAddU16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UHASX(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedHalvingAddSubU16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UHSAX(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedHalvingSubAddU16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UHSUB8(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedHalvingSubU8(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::thumb32_UHSUB16(Reg n, Reg d, Reg m) { | ||||
|     if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { | ||||
|         return UnpredictableInstruction(); | ||||
|     } | ||||
|  | ||||
|     const auto reg_m = ir.GetRegister(m); | ||||
|     const auto reg_n = ir.GetRegister(n); | ||||
|     const auto result = ir.PackedHalvingSubU16(reg_n, reg_m); | ||||
|  | ||||
|     ir.SetRegister(d, result); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| } // namespace Dynarmic::A32 | ||||
| @@ -10,6 +10,7 @@ | ||||
| #include "frontend/imm.h" | ||||
| #include "frontend/A32/ir_emitter.h" | ||||
| #include "frontend/A32/location_descriptor.h" | ||||
| #include "frontend/A32/translate/conditional_state.h" | ||||
| #include "frontend/A32/translate/translate.h" | ||||
| #include "frontend/A32/types.h" | ||||
|  | ||||
| @@ -17,21 +18,10 @@ namespace Dynarmic::A32 { | ||||
|  | ||||
| enum class Exception; | ||||
|  | ||||
| enum class ConditionalState { | ||||
|     /// We haven't met any conditional instructions yet. | ||||
|     None, | ||||
|     /// Current instruction is a conditional. This marks the end of this basic block. | ||||
|     Break, | ||||
|     /// This basic block is made up solely of conditional instructions. | ||||
|     Translating, | ||||
|     /// This basic block is made up of conditional instructions followed by unconditional instructions. | ||||
|     Trailing, | ||||
| }; | ||||
|  | ||||
| struct ArmTranslatorVisitor final { | ||||
|     using instruction_return_type = bool; | ||||
|  | ||||
|     explicit ArmTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor), options(options) { | ||||
|     explicit ArmTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor, options.arch_version), options(options) { | ||||
|         ASSERT_MSG(!descriptor.TFlag(), "The processor must be in Arm mode"); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| #include "frontend/imm.h" | ||||
| #include "frontend/A32/ir_emitter.h" | ||||
| #include "frontend/A32/location_descriptor.h" | ||||
| #include "frontend/A32/translate/conditional_state.h" | ||||
| #include "frontend/A32/translate/translate.h" | ||||
| #include "frontend/A32/types.h" | ||||
|  | ||||
| @@ -19,13 +20,48 @@ enum class Exception; | ||||
| struct ThumbTranslatorVisitor final { | ||||
|     using instruction_return_type = bool; | ||||
|  | ||||
|     explicit ThumbTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor), options(options) { | ||||
|     explicit ThumbTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor, options.arch_version), options(options) { | ||||
|         ASSERT_MSG(descriptor.TFlag(), "The processor must be in Thumb mode"); | ||||
|     } | ||||
|  | ||||
|     struct ImmAndCarry { | ||||
|         u32 imm32; | ||||
|         IR::U1 carry; | ||||
|     }; | ||||
|  | ||||
|     ImmAndCarry ThumbExpandImm_C(Imm<1> i, Imm<3> imm3, Imm<8> imm8, IR::U1 carry_in) { | ||||
|         const Imm<12> imm12 = concatenate(i, imm3, imm8); | ||||
|         if (imm12.Bits<10, 11>() == 0) { | ||||
|             const u32 imm32 = [&]{ | ||||
|                 const u32 imm8 = imm12.Bits<0, 7>(); | ||||
|                 switch (imm12.Bits<8, 9>()) { | ||||
|                 case 0b00: | ||||
|                     return imm8; | ||||
|                 case 0b01: | ||||
|                     return Common::Replicate(imm8, 16); | ||||
|                 case 0b10: | ||||
|                     return Common::Replicate(imm8 << 8, 16); | ||||
|                 case 0b11: | ||||
|                     return Common::Replicate(imm8, 8); | ||||
|                 } | ||||
|                 UNREACHABLE(); | ||||
|             }(); | ||||
|             return {imm32, carry_in}; | ||||
|         } | ||||
|         const u32 imm32 = Common::RotateRight<u32>((1 << 7) | imm12.Bits<0, 6>(), imm12.Bits<7, 11>()); | ||||
|         return {imm32, ir.Imm1(Common::Bit<31>(imm32))}; | ||||
|     } | ||||
|  | ||||
|     u32 ThumbExpandImm(Imm<1> i, Imm<3> imm3, Imm<8> imm8) { | ||||
|         return ThumbExpandImm_C(i, imm3, imm8, ir.Imm1(0)).imm32; | ||||
|     } | ||||
|  | ||||
|     A32::IREmitter ir; | ||||
|     ConditionalState cond_state = ConditionalState::None; | ||||
|     TranslationOptions options; | ||||
|  | ||||
|     bool ConditionPassed(bool is_thumb_16); | ||||
|  | ||||
|     bool InterpretThisInstruction(); | ||||
|     bool UnpredictableInstruction(); | ||||
|     bool UndefinedInstruction(); | ||||
| @@ -83,12 +119,13 @@ struct ThumbTranslatorVisitor final { | ||||
|     bool thumb16_ADD_sp_t1(Reg d, Imm<8> imm8); | ||||
|     bool thumb16_ADD_sp_t2(Imm<7> imm7); | ||||
|     bool thumb16_SUB_sp(Imm<7> imm7); | ||||
|     bool thumb16_NOP(); | ||||
|     bool thumb16_SEV(); | ||||
|     bool thumb16_SEVL(); | ||||
|     bool thumb16_WFE(); | ||||
|     bool thumb16_WFI(); | ||||
|     bool thumb16_YIELD(); | ||||
|     bool thumb16_NOP(); | ||||
|     bool thumb16_IT(Imm<8> imm8); | ||||
|     bool thumb16_SXTH(Reg m, Reg d); | ||||
|     bool thumb16_SXTB(Reg m, Reg d); | ||||
|     bool thumb16_UXTH(Reg m, Reg d); | ||||
| @@ -111,10 +148,115 @@ struct ThumbTranslatorVisitor final { | ||||
|     bool thumb16_B_t1(Cond cond, Imm<8> imm8); | ||||
|     bool thumb16_B_t2(Imm<11> imm11); | ||||
|  | ||||
|     // thumb32 | ||||
|     bool thumb32_BL_imm(Imm<11> hi, Imm<11> lo); | ||||
|     bool thumb32_BLX_imm(Imm<11> hi, Imm<11> lo); | ||||
|     // thumb32 data processing (modified immediate) instructions | ||||
|     bool thumb32_TST_imm(Imm<1> i, Reg n, Imm<3> imm3, Imm<8> imm8); | ||||
|     bool thumb32_AND_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Reg d, Imm<8> imm8); | ||||
|     bool thumb32_BIC_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Reg d, Imm<8> imm8); | ||||
|     bool thumb32_MOV_imm(Imm<1> i, bool S, Imm<3> imm3, Reg d, Imm<8> imm8); | ||||
|     bool thumb32_ORR_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Reg d, Imm<8> imm8); | ||||
|  | ||||
|     // thumb32 miscellaneous control instructions | ||||
|     bool thumb32_UDF(); | ||||
|  | ||||
|     // thumb32 branch instructions | ||||
|     bool thumb32_BL_imm(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> j2, Imm<11> lo); | ||||
|     bool thumb32_BLX_imm(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> j2, Imm<11> lo); | ||||
|  | ||||
|     // thumb32 data processing (register) instructions | ||||
|     bool thumb32_SXTB(Reg d, SignExtendRotation rotate, Reg m); | ||||
|     bool thumb32_SXTB16(Reg d, SignExtendRotation rotate, Reg m); | ||||
|     bool thumb32_SXTAB(Reg n, Reg d, SignExtendRotation rotate, Reg m); | ||||
|     bool thumb32_SXTAB16(Reg n, Reg d, SignExtendRotation rotate, Reg m); | ||||
|     bool thumb32_SXTH(Reg d, SignExtendRotation rotate, Reg m); | ||||
|     bool thumb32_SXTAH(Reg n, Reg d, SignExtendRotation rotate, Reg m); | ||||
|     bool thumb32_UXTB(Reg d, SignExtendRotation rotate, Reg m); | ||||
|     bool thumb32_UXTB16(Reg d, SignExtendRotation rotate, Reg m); | ||||
|     bool thumb32_UXTAB(Reg n, Reg d, SignExtendRotation rotate, Reg m); | ||||
|     bool thumb32_UXTAB16(Reg n, Reg d, SignExtendRotation rotate, Reg m); | ||||
|     bool thumb32_UXTH(Reg d, SignExtendRotation rotate, Reg m); | ||||
|     bool thumb32_UXTAH(Reg n, Reg d, SignExtendRotation rotate, Reg m); | ||||
|  | ||||
|     // thumb32 long multiply, long multiply accumulate, and divide instructions | ||||
|     bool thumb32_SDIV(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SMLAL(Reg n, Reg dLo, Reg dHi, Reg m); | ||||
|     bool thumb32_SMLALD(Reg n, Reg dLo, Reg dHi, bool M, Reg m); | ||||
|     bool thumb32_SMLALXY(Reg n, Reg dLo, Reg dHi, bool N, bool M, Reg m); | ||||
|     bool thumb32_SMLSLD(Reg n, Reg dLo, Reg dHi, bool M, Reg m); | ||||
|     bool thumb32_SMULL(Reg n, Reg dLo, Reg dHi, Reg m); | ||||
|     bool thumb32_UDIV(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UMAAL(Reg n, Reg dLo, Reg dHi, Reg m); | ||||
|     bool thumb32_UMLAL(Reg n, Reg dLo, Reg dHi, Reg m); | ||||
|     bool thumb32_UMULL(Reg n, Reg dLo, Reg dHi, Reg m); | ||||
|  | ||||
|     // thumb32 miscellaneous instructions | ||||
|     bool thumb32_CLZ(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_QADD(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_QDADD(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_QDSUB(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_QSUB(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_RBIT(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_REV(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_REV16(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_REVSH(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SEL(Reg n, Reg d, Reg m); | ||||
|  | ||||
|     // thumb32 multiply instructions | ||||
|     bool thumb32_MLA(Reg n, Reg a, Reg d, Reg m); | ||||
|     bool thumb32_MLS(Reg n, Reg a, Reg d, Reg m); | ||||
|     bool thumb32_MUL(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SMLAD(Reg n, Reg a, Reg d, bool X, Reg m); | ||||
|     bool thumb32_SMLAXY(Reg n, Reg a, Reg d, bool N, bool M, Reg m); | ||||
|     bool thumb32_SMLAWY(Reg n, Reg a, Reg d, bool M, Reg m); | ||||
|     bool thumb32_SMLSD(Reg n, Reg a, Reg d, bool X, Reg m); | ||||
|     bool thumb32_SMMLA(Reg n, Reg a, Reg d, bool R, Reg m); | ||||
|     bool thumb32_SMMLS(Reg n, Reg a, Reg d, bool R, Reg m); | ||||
|     bool thumb32_SMMUL(Reg n, Reg d, bool R, Reg m); | ||||
|     bool thumb32_SMUAD(Reg n, Reg d, bool M, Reg m); | ||||
|     bool thumb32_SMUSD(Reg n, Reg d, bool M, Reg m); | ||||
|     bool thumb32_SMULXY(Reg n, Reg d, bool N, bool M, Reg m); | ||||
|     bool thumb32_SMULWY(Reg n, Reg d, bool M, Reg m); | ||||
|     bool thumb32_USAD8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_USADA8(Reg n, Reg a, Reg d, Reg m); | ||||
|  | ||||
|     // thumb32 parallel add/sub instructions | ||||
|     bool thumb32_SADD8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SADD16(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SASX(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SSAX(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SSUB8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SSUB16(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UADD8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UADD16(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UASX(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_USAX(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_USUB8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_USUB16(Reg n, Reg d, Reg m); | ||||
|  | ||||
|     bool thumb32_QADD8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_QADD16(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_QASX(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_QSAX(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_QSUB8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_QSUB16(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UQADD8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UQADD16(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UQASX(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UQSAX(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UQSUB8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UQSUB16(Reg n, Reg d, Reg m); | ||||
|  | ||||
|     bool thumb32_SHADD8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SHADD16(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SHASX(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SHSAX(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SHSUB8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_SHSUB16(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UHADD8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UHADD16(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UHASX(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UHSAX(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UHSUB8(Reg n, Reg d, Reg m); | ||||
|     bool thumb32_UHSUB16(Reg n, Reg d, Reg m); | ||||
| }; | ||||
|  | ||||
| } // namespace Dynarmic::A32 | ||||
|   | ||||
| @@ -6,6 +6,8 @@ | ||||
|  | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| #include <dynarmic/A32/arch_version.h> | ||||
|  | ||||
| namespace Dynarmic::IR { | ||||
| class Block; | ||||
| } // namespace Dynarmic::IR | ||||
| @@ -17,6 +19,8 @@ class LocationDescriptor; | ||||
| using MemoryReadCodeFuncType = std::function<u32(u32 vaddr)>; | ||||
|  | ||||
| struct TranslationOptions { | ||||
|     ArchVersion arch_version; | ||||
|  | ||||
|     /// This changes what IR we emit when we translate an unpredictable instruction. | ||||
|     /// If this is false, the ExceptionRaised IR instruction is emitted. | ||||
|     /// If this is true, we define some behaviour for some instructions. | ||||
|   | ||||
| @@ -3,8 +3,6 @@ | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #include <algorithm> | ||||
|  | ||||
| #include <dynarmic/A32/config.h> | ||||
|  | ||||
| #include "common/assert.h" | ||||
| @@ -12,6 +10,7 @@ | ||||
| #include "frontend/A32/decoder/asimd.h" | ||||
| #include "frontend/A32/decoder/vfp.h" | ||||
| #include "frontend/A32/location_descriptor.h" | ||||
| #include "frontend/A32/translate/conditional_state.h" | ||||
| #include "frontend/A32/translate/impl/translate_arm.h" | ||||
| #include "frontend/A32/translate/translate.h" | ||||
| #include "frontend/A32/types.h" | ||||
| @@ -19,16 +18,6 @@ | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
|  | ||||
| static bool CondCanContinue(ConditionalState cond_state, const A32::IREmitter& ir) { | ||||
|     ASSERT_MSG(cond_state != ConditionalState::Break, "Should never happen."); | ||||
|  | ||||
|     if (cond_state == ConditionalState::None) | ||||
|         return true; | ||||
|  | ||||
|     // TODO: This is more conservative than necessary. | ||||
|     return std::all_of(ir.block.begin(), ir.block.end(), [](const IR::Inst& inst) { return !inst.WritesToCPSR(); }); | ||||
| } | ||||
|  | ||||
| IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) { | ||||
|     const bool single_step = descriptor.SingleStepping(); | ||||
|  | ||||
| @@ -102,53 +91,7 @@ bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descript | ||||
| } | ||||
|  | ||||
| bool ArmTranslatorVisitor::ConditionPassed(Cond cond) { | ||||
|     ASSERT_MSG(cond_state != ConditionalState::Break, | ||||
|                "This should never happen. We requested a break but that wasn't honored."); | ||||
|     if (cond == Cond::NV) { | ||||
|         // NV conditional is obsolete | ||||
|         ir.ExceptionRaised(Exception::UnpredictableInstruction); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     if (cond_state == ConditionalState::Translating) { | ||||
|         if (ir.block.ConditionFailedLocation() != ir.current_location || cond == Cond::AL) { | ||||
|             cond_state = ConditionalState::Trailing; | ||||
|         } else { | ||||
|             if (cond == ir.block.GetCondition()) { | ||||
|                 ir.block.SetConditionFailedLocation(ir.current_location.AdvancePC(4)); | ||||
|                 ir.block.ConditionFailedCycleCount()++; | ||||
|                 return true; | ||||
|             } | ||||
|  | ||||
|             // cond has changed, abort | ||||
|             cond_state = ConditionalState::Break; | ||||
|             ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location}); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (cond == Cond::AL) { | ||||
|         // Everything is fine with the world | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     // non-AL cond | ||||
|  | ||||
|     if (!ir.block.empty()) { | ||||
|         // We've already emitted instructions. Quit for now, we'll make a new block here later. | ||||
|         cond_state = ConditionalState::Break; | ||||
|         ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location}); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // We've not emitted instructions yet. | ||||
|     // We'll emit one instruction, and set the block-entry conditional appropriately. | ||||
|  | ||||
|     cond_state = ConditionalState::Translating; | ||||
|     ir.block.SetCondition(cond); | ||||
|     ir.block.SetConditionFailedLocation(ir.current_location.AdvancePC(4)); | ||||
|     ir.block.ConditionFailedCycleCount() = ir.block.CycleCount() + 1; | ||||
|     return true; | ||||
|     return IsConditionPassed(cond, cond_state, ir, 4); | ||||
| } | ||||
|  | ||||
| bool ArmTranslatorVisitor::InterpretThisInstruction() { | ||||
|   | ||||
| @@ -9,13 +9,14 @@ | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/bit_util.h" | ||||
| #include "frontend/imm.h" | ||||
| #include "frontend/A32/decoder/thumb16.h" | ||||
| #include "frontend/A32/decoder/thumb32.h" | ||||
| #include "frontend/A32/ir_emitter.h" | ||||
| #include "frontend/A32/location_descriptor.h" | ||||
| #include "frontend/A32/translate/conditional_state.h" | ||||
| #include "frontend/A32/translate/impl/translate_thumb.h" | ||||
| #include "frontend/A32/translate/translate.h" | ||||
| #include "frontend/imm.h" | ||||
|  | ||||
| namespace Dynarmic::A32 { | ||||
| namespace { | ||||
| @@ -25,7 +26,17 @@ enum class ThumbInstSize { | ||||
| }; | ||||
|  | ||||
| bool IsThumb16(u16 first_part) { | ||||
|     return (first_part & 0xF800) <= 0xE800; | ||||
|     return (first_part & 0xF800) < 0xE800; | ||||
| } | ||||
|  | ||||
| bool IsUnconditionalInstruction(bool is_thumb_16, u32 instruction) { | ||||
|     if (!is_thumb_16) | ||||
|         return false; | ||||
|     if ((instruction & 0xFF00) == 0b10111110'00000000) // BKPT | ||||
|         return true; | ||||
|     if ((instruction & 0xFFC0) == 0b10111010'10000000) // HLT | ||||
|         return true; | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| std::tuple<u32, ThumbInstSize> ReadThumbInstruction(u32 arm_pc, MemoryReadCodeFuncType memory_read_code) { | ||||
| @@ -64,30 +75,44 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType m | ||||
|     do { | ||||
|         const u32 arm_pc = visitor.ir.current_location.PC(); | ||||
|         const auto [thumb_instruction, inst_size] = ReadThumbInstruction(arm_pc, memory_read_code); | ||||
|         const bool is_thumb_16 = inst_size == ThumbInstSize::Thumb16; | ||||
|  | ||||
|         if (inst_size == ThumbInstSize::Thumb16) { | ||||
|             if (const auto decoder = DecodeThumb16<ThumbTranslatorVisitor>(static_cast<u16>(thumb_instruction))) { | ||||
|                 should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction)); | ||||
|         if (IsUnconditionalInstruction(is_thumb_16, thumb_instruction) || visitor.ConditionPassed(is_thumb_16)) { | ||||
|             if (is_thumb_16) { | ||||
|                 if (const auto decoder = DecodeThumb16<ThumbTranslatorVisitor>(static_cast<u16>(thumb_instruction))) { | ||||
|                     should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction)); | ||||
|                 } else { | ||||
|                     should_continue = visitor.thumb16_UDF(); | ||||
|                 } | ||||
|             } else { | ||||
|                 should_continue = visitor.thumb16_UDF(); | ||||
|             } | ||||
|         } else { | ||||
|             if (const auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction)) { | ||||
|                 should_continue = decoder->get().call(visitor, thumb_instruction); | ||||
|             } else { | ||||
|                 should_continue = visitor.thumb32_UDF(); | ||||
|                 if (const auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction)) { | ||||
|                     should_continue = decoder->get().call(visitor, thumb_instruction); | ||||
|                 } else { | ||||
|                     should_continue = visitor.thumb32_UDF(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         const s32 advance_pc = (inst_size == ThumbInstSize::Thumb16) ? 2 : 4; | ||||
|         visitor.ir.current_location = visitor.ir.current_location.AdvancePC(advance_pc); | ||||
|         block.CycleCount()++; | ||||
|     } while (should_continue && !single_step); | ||||
|         if (visitor.cond_state == ConditionalState::Break) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|     if (single_step && should_continue) { | ||||
|         visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location}); | ||||
|         visitor.ir.current_location = visitor.ir.current_location.AdvancePC(is_thumb_16 ? 2 : 4).AdvanceIT(); | ||||
|         block.CycleCount()++; | ||||
|     } while (should_continue && CondCanContinue(visitor.cond_state, visitor.ir) && !single_step); | ||||
|  | ||||
|     if (visitor.cond_state == ConditionalState::Translating || visitor.cond_state == ConditionalState::Trailing || single_step) { | ||||
|         if (should_continue) { | ||||
|             if (single_step) { | ||||
|                 visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location}); | ||||
|             } else { | ||||
|                 visitor.ir.SetTerm(IR::Term::LinkBlockFast{visitor.ir.current_location}); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ASSERT_MSG(block.HasTerminal(), "Terminal has not been set"); | ||||
|  | ||||
|     block.SetEndLocation(visitor.ir.current_location); | ||||
|  | ||||
|     return block; | ||||
| @@ -105,6 +130,7 @@ bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descri | ||||
|             should_continue = visitor.thumb16_UDF(); | ||||
|         } | ||||
|     } else { | ||||
|         thumb_instruction = Common::SwapHalves32(thumb_instruction); | ||||
|         if (const auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction)) { | ||||
|             should_continue = decoder->get().call(visitor, thumb_instruction); | ||||
|         } else { | ||||
| @@ -121,6 +147,11 @@ bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descri | ||||
|     return should_continue; | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::ConditionPassed(bool is_thumb_16) { | ||||
|     const Cond cond = ir.current_location.IT().Cond(); | ||||
|     return IsConditionPassed(cond, cond_state, ir, is_thumb_16 ? 2 : 4); | ||||
| } | ||||
|  | ||||
| bool ThumbTranslatorVisitor::InterpretThisInstruction() { | ||||
|     ir.SetTerm(IR::Term::Interpret(ir.current_location)); | ||||
|     return false; | ||||
|   | ||||
| @@ -108,6 +108,11 @@ INST(DC_CVAU,                "DC CVAU",                                   "11010 | ||||
| INST(DC_CVAP,                "DC CVAP",                                   "110101010000101101111100001ttttt") | ||||
| INST(DC_CIVAC,               "DC CIVAC",                                  "110101010000101101111110001ttttt") | ||||
|  | ||||
| // SYS: Instruction Cache | ||||
| INST(IC_IALLU,               "IC IALLU",                                  "11010101000010000111010100011111") | ||||
| INST(IC_IALLUIS,             "IC IALLUIS",                                "11010101000010000111000100011111") | ||||
| INST(IC_IVAU,                "IC IVAU",                                   "110101010000101101110101001ttttt") | ||||
|  | ||||
| // Unconditional branch (Register) | ||||
| INST(BLR,                    "BLR",                                       "1101011000111111000000nnnnn00000") | ||||
| INST(BR,                     "BR",                                        "1101011000011111000000nnnnn00000") | ||||
|   | ||||
| @@ -56,6 +56,10 @@ void IREmitter::DataCacheOperationRaised(DataCacheOperation op, const IR::U64& v | ||||
|     Inst(Opcode::A64DataCacheOperationRaised, Imm64(static_cast<u64>(op)), value); | ||||
| } | ||||
|  | ||||
| void IREmitter::InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value) { | ||||
|     Inst(Opcode::A64InstructionCacheOperationRaised, Imm64(static_cast<u64>(op)), value); | ||||
| } | ||||
|  | ||||
| void IREmitter::DataSynchronizationBarrier() { | ||||
|     Inst(Opcode::A64DataSynchronizationBarrier); | ||||
| } | ||||
|   | ||||
| @@ -42,6 +42,7 @@ public: | ||||
|     void CallSupervisor(u32 imm); | ||||
|     void ExceptionRaised(Exception exception); | ||||
|     void DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value); | ||||
|     void InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value); | ||||
|     void DataSynchronizationBarrier(); | ||||
|     void DataMemoryBarrier(); | ||||
|     void InstructionSynchronizationBarrier(); | ||||
|   | ||||
| @@ -174,6 +174,11 @@ struct TranslatorVisitor final { | ||||
|     bool DC_CVAP(Reg Rt); | ||||
|     bool DC_CIVAC(Reg Rt); | ||||
|  | ||||
|     // SYS: Instruction Cache | ||||
|     bool IC_IALLU(); | ||||
|     bool IC_IALLUIS(); | ||||
|     bool IC_IVAU(Reg Rt); | ||||
|  | ||||
|     // Unconditional branch (Register) | ||||
|     bool BR(Reg Rn); | ||||
|     bool BRA(bool Z, bool M, Reg Rn, Reg Rm); | ||||
|   | ||||
							
								
								
									
										25
									
								
								externals/dynarmic/src/frontend/A64/translate/impl/sys_ic.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										25
									
								
								externals/dynarmic/src/frontend/A64/translate/impl/sys_ic.cpp
									
									
									
									
										vendored
									
									
										Executable file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| /* This file is part of the dynarmic project. | ||||
|  * Copyright (c) 2018 MerryMage | ||||
|  * SPDX-License-Identifier: 0BSD | ||||
|  */ | ||||
|  | ||||
| #include "frontend/A64/translate/impl/impl.h" | ||||
|  | ||||
| namespace Dynarmic::A64 { | ||||
|  | ||||
| bool TranslatorVisitor::IC_IALLU() { | ||||
|     ir.InstructionCacheOperationRaised(InstructionCacheOperation::InvalidateAllToPoU, ir.Imm64(0)); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool TranslatorVisitor::IC_IALLUIS() { | ||||
|     ir.InstructionCacheOperationRaised(InstructionCacheOperation::InvalidateAllToPoUInnerSharable, ir.Imm64(0)); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| bool TranslatorVisitor::IC_IVAU(Reg Rt) { | ||||
|     ir.InstructionCacheOperationRaised(InstructionCacheOperation::InvalidateByVAToPoU, X(64, Rt)); | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| } // namespace Dynarmic::A64 | ||||
| @@ -8,23 +8,26 @@ | ||||
| namespace Dynarmic::A64 { | ||||
|  | ||||
| // Register encodings used by MRS and MSR. | ||||
| // Order of fields: op0, CRn, op1, op2, CRm. | ||||
| enum class SystemRegisterEncoding : u32 { | ||||
|     // Counter-timer Frequency register | ||||
|     CNTFRQ_EL0 = 0b11'011'1110'0000'000, | ||||
|     CNTFRQ_EL0 = 0b11'1110'011'000'0000, | ||||
|     // Counter-timer Physical Count register | ||||
|     CNTPCT_EL0 = 0b11'011'1110'0000'001, | ||||
|     CNTPCT_EL0 = 0b11'1110'011'001'0000, | ||||
|     // Cache Type Register | ||||
|     CTR_EL0 = 0b11'011'0000'0000'001, | ||||
|     CTR_EL0 = 0b11'0000'011'001'0000, | ||||
|     // Data Cache Zero ID register | ||||
|     DCZID_EL0 = 0b11'011'0000'0000'111, | ||||
|     DCZID_EL0 = 0b11'0000'011'111'0000, | ||||
|     // Floating-point Control Register | ||||
|     FPCR = 0b11'011'0100'0100'000, | ||||
|     FPCR = 0b11'0100'011'000'0100, | ||||
|     // Floating-point Status Register | ||||
|     FPSR = 0b11'011'0100'0100'001, | ||||
|     FPSR = 0b11'0100'011'001'0100, | ||||
|     // NZCV, Condition Flags | ||||
|     NZCV = 0b11'0100'011'000'0010, | ||||
|     // Read/Write Software Thread ID Register | ||||
|     TPIDR_EL0 = 0b11'011'1101'0000'010, | ||||
|     TPIDR_EL0 = 0b11'1101'011'010'0000, | ||||
|     // Read-Only Software Thread ID Register | ||||
|     TPIDRRO_EL0 = 0b11'011'1101'0000'011, | ||||
|     TPIDRRO_EL0 = 0b11'1101'011'011'0000, | ||||
| }; | ||||
|  | ||||
| bool TranslatorVisitor::HINT([[maybe_unused]] Imm<4> CRm, [[maybe_unused]] Imm<3> op2) { | ||||
| @@ -93,7 +96,7 @@ bool TranslatorVisitor::ISB(Imm<4> /*CRm*/) { | ||||
| } | ||||
|  | ||||
| bool TranslatorVisitor::MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { | ||||
|     const auto sys_reg = concatenate(Imm<1>{1}, o0, op1, CRn, CRm, op2).ZeroExtend<SystemRegisterEncoding>(); | ||||
|     const auto sys_reg = concatenate(Imm<1>{1}, o0, CRn, op1, op2, CRm).ZeroExtend<SystemRegisterEncoding>(); | ||||
|     switch (sys_reg) { | ||||
|     case SystemRegisterEncoding::FPCR: | ||||
|         ir.SetFPCR(X(32, Rt)); | ||||
| @@ -103,6 +106,9 @@ bool TranslatorVisitor::MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, I | ||||
|     case SystemRegisterEncoding::FPSR: | ||||
|         ir.SetFPSR(X(32, Rt)); | ||||
|         return true; | ||||
|     case SystemRegisterEncoding::NZCV: | ||||
|         ir.SetNZCVRaw(X(32, Rt)); | ||||
|         return true; | ||||
|     case SystemRegisterEncoding::TPIDR_EL0: | ||||
|         ir.SetTPIDR(X(64, Rt)); | ||||
|         return true; | ||||
| @@ -113,7 +119,7 @@ bool TranslatorVisitor::MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, I | ||||
| } | ||||
|  | ||||
| bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { | ||||
|     const auto sys_reg = concatenate(Imm<1>{1}, o0, op1, CRn, CRm, op2).ZeroExtend<SystemRegisterEncoding>(); | ||||
|     const auto sys_reg = concatenate(Imm<1>{1}, o0, CRn, op1, op2, CRm).ZeroExtend<SystemRegisterEncoding>(); | ||||
|     switch (sys_reg) { | ||||
|     case SystemRegisterEncoding::CNTFRQ_EL0: | ||||
|         X(32, Rt, ir.GetCNTFRQ()); | ||||
| @@ -139,6 +145,9 @@ bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3 | ||||
|     case SystemRegisterEncoding::FPSR: | ||||
|         X(32, Rt, ir.GetFPSR()); | ||||
|         return true; | ||||
|     case SystemRegisterEncoding::NZCV: | ||||
|         X(32, Rt, ir.GetNZCVRaw()); | ||||
|         return true; | ||||
|     case SystemRegisterEncoding::TPIDR_EL0: | ||||
|         X(64, Rt, ir.GetTPIDR()); | ||||
|         return true; | ||||
|   | ||||
| @@ -155,6 +155,7 @@ bool Inst::ReadsFromCPSR() const { | ||||
|     case Opcode::A32GetCFlag: | ||||
|     case Opcode::A32GetVFlag: | ||||
|     case Opcode::A32GetGEFlags: | ||||
|     case Opcode::A32UpdateUpperLocationDescriptor: | ||||
|     case Opcode::A64GetCFlag: | ||||
|     case Opcode::A64GetNZCVRaw: | ||||
|     case Opcode::ConditionalSelect32: | ||||
| @@ -179,6 +180,7 @@ bool Inst::WritesToCPSR() const { | ||||
|     case Opcode::A32OrQFlag: | ||||
|     case Opcode::A32SetGEFlags: | ||||
|     case Opcode::A32SetGEFlagsCompressed: | ||||
|     case Opcode::A32UpdateUpperLocationDescriptor: | ||||
|     case Opcode::A64SetNZCVRaw: | ||||
|     case Opcode::A64SetNZCV: | ||||
|         return true; | ||||
| @@ -518,18 +520,19 @@ bool Inst::IsSetCheckBitOperation() const { | ||||
| } | ||||
|  | ||||
| bool Inst::MayHaveSideEffects() const { | ||||
|     return op == Opcode::PushRSB                        || | ||||
|            op == Opcode::A64DataCacheOperationRaised    || | ||||
|            IsSetCheckBitOperation()                     || | ||||
|            IsBarrier()                                  || | ||||
|            CausesCPUException()                         || | ||||
|            WritesToCoreRegister()                       || | ||||
|            WritesToSystemRegister()                     || | ||||
|            WritesToCPSR()                               || | ||||
|            WritesToFPCR()                               || | ||||
|            WritesToFPSR()                               || | ||||
|            AltersExclusiveState()                       || | ||||
|            IsMemoryWrite()                              || | ||||
|     return op == Opcode::PushRSB                            || | ||||
|            op == Opcode::A64DataCacheOperationRaised        || | ||||
|            op == Opcode::A64InstructionCacheOperationRaised || | ||||
|            IsSetCheckBitOperation()                         || | ||||
|            IsBarrier()                                      || | ||||
|            CausesCPUException()                             || | ||||
|            WritesToCoreRegister()                           || | ||||
|            WritesToSystemRegister()                         || | ||||
|            WritesToCPSR()                                   || | ||||
|            WritesToFPCR()                                   || | ||||
|            WritesToFPSR()                                   || | ||||
|            AltersExclusiveState()                           || | ||||
|            IsMemoryWrite()                                  || | ||||
|            IsCoprocessorInstruction(); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -31,6 +31,7 @@ A32OPC(GetGEFlags,                                          U32, | ||||
| A32OPC(SetGEFlags,                                          Void,           U32                                                             ) | ||||
| A32OPC(SetGEFlagsCompressed,                                Void,           U32                                                             ) | ||||
| A32OPC(BXWritePC,                                           Void,           U32                                                             ) | ||||
| A32OPC(UpdateUpperLocationDescriptor,                       Void,                                                                           ) | ||||
| A32OPC(CallSupervisor,                                      Void,           U32                                                             ) | ||||
| A32OPC(ExceptionRaised,                                     Void,           U32,            U64                                             ) | ||||
| A32OPC(DataSynchronizationBarrier,                          Void,                                                                           ) | ||||
| @@ -68,6 +69,7 @@ A64OPC(SetPC,                                               Void,           U64 | ||||
| A64OPC(CallSupervisor,                                      Void,           U32                                                             ) | ||||
| A64OPC(ExceptionRaised,                                     Void,           U64,            U64                                             ) | ||||
| A64OPC(DataCacheOperationRaised,                            Void,           U64,            U64                                             ) | ||||
| A64OPC(InstructionCacheOperationRaised,                     Void,           U64,            U64                                             ) | ||||
| A64OPC(DataSynchronizationBarrier,                          Void,                                                                           ) | ||||
| A64OPC(DataMemoryBarrier,                                   Void,                                                                           ) | ||||
| A64OPC(InstructionSynchronizationBarrier,                   Void,                                                                           ) | ||||
|   | ||||
| @@ -138,13 +138,13 @@ void FoldByteReverse(IR::Inst& inst, Op op) { | ||||
|     } | ||||
|  | ||||
|     if (op == Op::ByteReverseWord) { | ||||
|         const u32 result = Common::Swap32(static_cast<u32>(operand.GetImmediateAsU64())); | ||||
|         const u32 result = Common::SwapBytes32(static_cast<u32>(operand.GetImmediateAsU64())); | ||||
|         inst.ReplaceUsesWith(IR::Value{result}); | ||||
|     } else if (op == Op::ByteReverseHalf) { | ||||
|         const u16 result = Common::Swap16(static_cast<u16>(operand.GetImmediateAsU64())); | ||||
|         const u16 result = Common::SwapBytes16(static_cast<u16>(operand.GetImmediateAsU64())); | ||||
|         inst.ReplaceUsesWith(IR::Value{result}); | ||||
|     } else { | ||||
|         const u64 result = Common::Swap64(operand.GetImmediateAsU64()); | ||||
|         const u64 result = Common::SwapBytes64(operand.GetImmediateAsU64()); | ||||
|         inst.ReplaceUsesWith(IR::Value{result}); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										232
									
								
								externals/dynarmic/tests/A32/fuzz_arm.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										232
									
								
								externals/dynarmic/tests/A32/fuzz_arm.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -16,8 +16,9 @@ | ||||
| #include "common/common_types.h" | ||||
| #include "common/fp/fpcr.h" | ||||
| #include "common/fp/fpsr.h" | ||||
| #include "common/llvm_disassemble.h" | ||||
| #include "common/scope_exit.h" | ||||
| #include "frontend/A32/disassembler/disassembler.h" | ||||
| #include "frontend/A32/ITState.h" | ||||
| #include "frontend/A32/location_descriptor.h" | ||||
| #include "frontend/A32/translate/translate.h" | ||||
| #include "frontend/A32/types.h" | ||||
| @@ -36,8 +37,8 @@ | ||||
| namespace { | ||||
| using namespace Dynarmic; | ||||
|  | ||||
| bool ShouldTestInst(u32 instruction, u32 pc, bool is_last_inst) { | ||||
|     const A32::LocationDescriptor location{pc, {}, {}}; | ||||
| bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A32::ITState it_state = {}) { | ||||
|     const A32::LocationDescriptor location = A32::LocationDescriptor{pc, {}, {}}.SetTFlag(is_thumb).SetIT(it_state); | ||||
|     IR::Block block{location}; | ||||
|     const bool should_continue = A32::TranslateSingleInstruction(block, location, instruction); | ||||
|  | ||||
| @@ -74,7 +75,7 @@ bool ShouldTestInst(u32 instruction, u32 pc, bool is_last_inst) { | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| u32 GenRandomInst(u32 pc, bool is_last_inst) { | ||||
| u32 GenRandomArmInst(u32 pc, bool is_last_inst) { | ||||
|     static const struct InstructionGeneratorInfo { | ||||
|         std::vector<InstructionGenerator> generators; | ||||
|         std::vector<InstructionGenerator> invalid; | ||||
| @@ -139,13 +140,59 @@ u32 GenRandomInst(u32 pc, bool is_last_inst) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (ShouldTestInst(inst, pc, is_last_inst)) { | ||||
|         if (ShouldTestInst(inst, pc, false, is_last_inst)) { | ||||
|             return inst; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv& testenv) { | ||||
| std::vector<u16> GenRandomThumbInst(u32 pc, bool is_last_inst, A32::ITState it_state = {}) { | ||||
|     static const struct InstructionGeneratorInfo { | ||||
|         std::vector<InstructionGenerator> generators; | ||||
|         std::vector<InstructionGenerator> invalid; | ||||
|     } instructions = []{ | ||||
|         const std::vector<std::tuple<std::string, const char*>> list { | ||||
| #define INST(fn, name, bitstring) {#fn, bitstring}, | ||||
| #include "frontend/A32/decoder/thumb16.inc" | ||||
| #include "frontend/A32/decoder/thumb32.inc" | ||||
| #undef INST | ||||
|         }; | ||||
|  | ||||
|         std::vector<InstructionGenerator> generators; | ||||
|         std::vector<InstructionGenerator> invalid; | ||||
|  | ||||
|         // List of instructions not to test | ||||
|         static constexpr std::array do_not_test { | ||||
|             "thumb16_BKPT", | ||||
|             "thumb16_IT", | ||||
|             "thumb16_SETEND", | ||||
|         }; | ||||
|  | ||||
|         for (const auto& [fn, bitstring] : list) { | ||||
|             if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) { | ||||
|                 invalid.emplace_back(InstructionGenerator{bitstring}); | ||||
|                 continue; | ||||
|             } | ||||
|             generators.emplace_back(InstructionGenerator{bitstring}); | ||||
|         } | ||||
|         return InstructionGeneratorInfo{generators, invalid}; | ||||
|     }(); | ||||
|  | ||||
|     while (true) { | ||||
|         const size_t index = RandInt<size_t>(0, instructions.generators.size() - 1); | ||||
|         const u32 inst = instructions.generators[index].Generate(); | ||||
|         const bool is_four_bytes = (inst >> 16) != 0; | ||||
|  | ||||
|         if (ShouldTestInst(is_four_bytes ? Common::SwapHalves32(inst) : inst, pc, true, is_last_inst, it_state)) { | ||||
|             if (is_four_bytes) | ||||
|                 return { static_cast<u16>(inst >> 16), static_cast<u16>(inst) }; | ||||
|             return { static_cast<u16>(inst) }; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| template <typename TestEnv> | ||||
| Dynarmic::A32::UserConfig GetUserConfig(TestEnv& testenv) { | ||||
|     Dynarmic::A32::UserConfig user_config; | ||||
|     user_config.optimizations &= ~OptimizationFlag::FastDispatch; | ||||
|     user_config.callbacks = &testenv; | ||||
| @@ -153,22 +200,30 @@ Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv& testenv) { | ||||
|     return user_config; | ||||
| } | ||||
|  | ||||
| static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn<ArmTestEnv>& uni, | ||||
|                             ArmTestEnv& jit_env, ArmTestEnv& uni_env, | ||||
|                             const A32Unicorn<ArmTestEnv>::RegisterArray& regs, | ||||
|                             const A32Unicorn<ArmTestEnv>::ExtRegArray& vecs, | ||||
|                             const std::vector<u32>& instructions, const u32 cpsr, const u32 fpscr) { | ||||
| template <typename TestEnv> | ||||
| static void RunTestInstance(Dynarmic::A32::Jit& jit, | ||||
|                             A32Unicorn<TestEnv>& uni, | ||||
|                             TestEnv& jit_env, | ||||
|                             TestEnv& uni_env, | ||||
|                             const typename A32Unicorn<TestEnv>::RegisterArray& regs, | ||||
|                             const typename A32Unicorn<TestEnv>::ExtRegArray& vecs, | ||||
|                             const std::vector<typename TestEnv::InstructionType>& instructions, | ||||
|                             const u32 cpsr, | ||||
|                             const u32 fpscr, | ||||
|                             const size_t ticks_left) { | ||||
|     const u32 initial_pc = regs[15]; | ||||
|     const u32 num_words = initial_pc / sizeof(u32); | ||||
|     const u32 num_words = initial_pc / sizeof(typename TestEnv::InstructionType); | ||||
|     const u32 code_mem_size = num_words + static_cast<u32>(instructions.size()); | ||||
|  | ||||
|     jit_env.code_mem.resize(code_mem_size + 1); | ||||
|     uni_env.code_mem.resize(code_mem_size + 1); | ||||
|     jit_env.code_mem.resize(code_mem_size); | ||||
|     uni_env.code_mem.resize(code_mem_size); | ||||
|     std::fill(jit_env.code_mem.begin(), jit_env.code_mem.end(), TestEnv::infinite_loop); | ||||
|     std::fill(uni_env.code_mem.begin(), uni_env.code_mem.end(), TestEnv::infinite_loop); | ||||
|  | ||||
|     std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + num_words); | ||||
|     std::copy(instructions.begin(), instructions.end(), uni_env.code_mem.begin() + num_words); | ||||
|     jit_env.code_mem.back() = 0xEAFFFFFE; // B . | ||||
|     uni_env.code_mem.back() = 0xEAFFFFFE; // B . | ||||
|     jit_env.PadCodeMem(); | ||||
|     uni_env.PadCodeMem(); | ||||
|     jit_env.modified_memory.clear(); | ||||
|     uni_env.modified_memory.clear(); | ||||
|     jit_env.interrupts.clear(); | ||||
| @@ -186,18 +241,15 @@ static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn<ArmTestEnv>& uni | ||||
|     uni.SetCpsr(cpsr); | ||||
|     uni.ClearPageCache(); | ||||
|  | ||||
|     jit_env.ticks_left = instructions.size(); | ||||
|     jit_env.ticks_left = ticks_left; | ||||
|     jit.Run(); | ||||
|  | ||||
|     uni_env.ticks_left = instructions.size(); | ||||
|     uni_env.ticks_left = instructions.size(); // Unicorn counts thumb instructions weirdly. | ||||
|     uni.Run(); | ||||
|  | ||||
|     SCOPE_FAIL { | ||||
|         fmt::print("Instruction Listing:\n"); | ||||
|         for (u32 instruction : instructions) { | ||||
|             fmt::print("{:08x} {}\n", instruction, A32::DisassembleArm(instruction)); | ||||
|         } | ||||
|         fmt::print("\n"); | ||||
|         fmt::print("{}\n", Common::DisassembleAArch32(std::is_same_v<TestEnv, ThumbTestEnv>, initial_pc, (const u8*)instructions.data(), instructions.size() * sizeof(instructions[0]))); | ||||
|  | ||||
|         fmt::print("Initial register listing:\n"); | ||||
|         for (size_t i = 0; i < regs.size(); ++i) { | ||||
| @@ -279,7 +331,7 @@ static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn<ArmTestEnv>& uni | ||||
| } | ||||
| } // Anonymous namespace | ||||
|  | ||||
| TEST_CASE("A32: Single random instruction", "[arm]") { | ||||
| TEST_CASE("A32: Single random arm instruction", "[arm]") { | ||||
|     ArmTestEnv jit_env{}; | ||||
|     ArmTestEnv uni_env{}; | ||||
|  | ||||
| @@ -294,7 +346,7 @@ TEST_CASE("A32: Single random instruction", "[arm]") { | ||||
|         std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); }); | ||||
|         std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); | ||||
|  | ||||
|         instructions[0] = GenRandomInst(0, true); | ||||
|         instructions[0] = GenRandomArmInst(0, true); | ||||
|  | ||||
|         const u32 start_address = 100; | ||||
|         const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10; | ||||
| @@ -303,11 +355,11 @@ TEST_CASE("A32: Single random instruction", "[arm]") { | ||||
|         INFO("Instruction: 0x" << std::hex << instructions[0]); | ||||
|  | ||||
|         regs[15] = start_address; | ||||
|         RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr); | ||||
|         RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("A32: Small random block", "[arm]") { | ||||
| TEST_CASE("A32: Small random arm block", "[arm]") { | ||||
|     ArmTestEnv jit_env{}; | ||||
|     ArmTestEnv uni_env{}; | ||||
|  | ||||
| @@ -322,11 +374,11 @@ TEST_CASE("A32: Small random block", "[arm]") { | ||||
|         std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); }); | ||||
|         std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); | ||||
|  | ||||
|         instructions[0] = GenRandomInst(0, false); | ||||
|         instructions[1] = GenRandomInst(4, false); | ||||
|         instructions[2] = GenRandomInst(8, false); | ||||
|         instructions[3] = GenRandomInst(12, false); | ||||
|         instructions[4] = GenRandomInst(16, true); | ||||
|         instructions[0] = GenRandomArmInst(0, false); | ||||
|         instructions[1] = GenRandomArmInst(4, false); | ||||
|         instructions[2] = GenRandomArmInst(8, false); | ||||
|         instructions[3] = GenRandomArmInst(12, false); | ||||
|         instructions[4] = GenRandomArmInst(16, true); | ||||
|  | ||||
|         const u32 start_address = 100; | ||||
|         const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10; | ||||
| @@ -339,11 +391,11 @@ TEST_CASE("A32: Small random block", "[arm]") { | ||||
|         INFO("Instruction 5: 0x" << std::hex << instructions[4]); | ||||
|  | ||||
|         regs[15] = start_address; | ||||
|         RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr); | ||||
|         RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 5); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("A32: Large random block", "[arm]") { | ||||
| TEST_CASE("A32: Large random arm block", "[arm]") { | ||||
|     ArmTestEnv jit_env{}; | ||||
|     ArmTestEnv uni_env{}; | ||||
|  | ||||
| @@ -361,7 +413,7 @@ TEST_CASE("A32: Large random block", "[arm]") { | ||||
|         std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); | ||||
|  | ||||
|         for (size_t j = 0; j < instruction_count; ++j) { | ||||
|             instructions[j] = GenRandomInst(j * 4, j == instruction_count - 1); | ||||
|             instructions[j] = GenRandomArmInst(j * 4, j == instruction_count - 1); | ||||
|         } | ||||
|  | ||||
|         const u64 start_address = 100; | ||||
| @@ -369,6 +421,116 @@ TEST_CASE("A32: Large random block", "[arm]") { | ||||
|         const u32 fpcr = RandomFpcr(); | ||||
|  | ||||
|         regs[15] = start_address; | ||||
|         RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr); | ||||
|         RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 100); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("A32: Single random thumb instruction", "[thumb]") { | ||||
|     ThumbTestEnv jit_env{}; | ||||
|     ThumbTestEnv uni_env{}; | ||||
|  | ||||
|     Dynarmic::A32::Jit jit{GetUserConfig(jit_env)}; | ||||
|     A32Unicorn<ThumbTestEnv> uni{uni_env}; | ||||
|  | ||||
|     A32Unicorn<ThumbTestEnv>::RegisterArray regs; | ||||
|     A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg; | ||||
|     std::vector<u16> instructions; | ||||
|  | ||||
|     for (size_t iteration = 0; iteration < 100000; ++iteration) { | ||||
|         std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); }); | ||||
|         std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); | ||||
|  | ||||
|         instructions = GenRandomThumbInst(0, true); | ||||
|  | ||||
|         const u32 start_address = 100; | ||||
|         const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0; | ||||
|         const u32 fpcr = RandomFpcr(); | ||||
|  | ||||
|         INFO("Instruction: 0x" << std::hex << instructions[0]); | ||||
|  | ||||
|         regs[15] = start_address; | ||||
|         RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("A32: Small random thumb block", "[thumb]") { | ||||
|     ThumbTestEnv jit_env{}; | ||||
|     ThumbTestEnv uni_env{}; | ||||
|  | ||||
|     Dynarmic::A32::Jit jit{GetUserConfig(jit_env)}; | ||||
|     A32Unicorn<ThumbTestEnv> uni{uni_env}; | ||||
|  | ||||
|     A32Unicorn<ThumbTestEnv>::RegisterArray regs; | ||||
|     A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg; | ||||
|     std::vector<u16> instructions; | ||||
|  | ||||
|     for (size_t iteration = 0; iteration < 100000; ++iteration) { | ||||
|         std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); }); | ||||
|         std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); | ||||
|  | ||||
|         instructions.clear(); | ||||
|         for (size_t i = 0; i < 5; i++) { | ||||
|             const std::vector<u16> inst = GenRandomThumbInst(instructions.size() * 2, i == 4); | ||||
|             instructions.insert(instructions.end(), inst.begin(), inst.end()); | ||||
|         } | ||||
|  | ||||
|         const u32 start_address = 100; | ||||
|         const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0; | ||||
|         const u32 fpcr = RandomFpcr(); | ||||
|  | ||||
|         regs[15] = start_address; | ||||
|         RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 5); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("A32: Test thumb IT instruction", "[thumb]") { | ||||
|     ThumbTestEnv jit_env{}; | ||||
|     ThumbTestEnv uni_env{}; | ||||
|  | ||||
|     Dynarmic::A32::Jit jit{GetUserConfig(jit_env)}; | ||||
|     A32Unicorn<ThumbTestEnv> uni{uni_env}; | ||||
|  | ||||
|     A32Unicorn<ThumbTestEnv>::RegisterArray regs; | ||||
|     A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg; | ||||
|     std::vector<u16> instructions; | ||||
|  | ||||
|     for (size_t iteration = 0; iteration < 100000; ++iteration) { | ||||
|         std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); }); | ||||
|         std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); }); | ||||
|  | ||||
|         const size_t pre_instructions = RandInt<size_t>(0, 3); | ||||
|         const size_t post_instructions = RandInt<size_t>(5, 8); | ||||
|  | ||||
|         instructions.clear(); | ||||
|  | ||||
|         for (size_t i = 0; i < pre_instructions; i++) { | ||||
|             const std::vector<u16> inst = GenRandomThumbInst(instructions.size() * 2, false); | ||||
|             instructions.insert(instructions.end(), inst.begin(), inst.end()); | ||||
|         } | ||||
|  | ||||
|         // Emit IT instruction | ||||
|         A32::ITState it_state = [&]{ | ||||
|             while (true) { | ||||
|                 const u16 imm8 = RandInt<u16>(0, 0xFF); | ||||
|                 if (Common::Bits<0, 3>(imm8) == 0b0000 || Common::Bits<4, 7>(imm8) == 0b1111 || (Common::Bits<4, 7>(imm8) == 0b1110 && Common::BitCount(Common::Bits<0, 3>(imm8)) != 1)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 instructions.push_back(0b1011111100000000 | imm8); | ||||
|                 return A32::ITState{static_cast<u8>(imm8)}; | ||||
|             } | ||||
|         }(); | ||||
|  | ||||
|         for (size_t i = 0; i < post_instructions; i++) { | ||||
|             const std::vector<u16> inst = GenRandomThumbInst(instructions.size() * 2, i == post_instructions - 1, it_state); | ||||
|             instructions.insert(instructions.end(), inst.begin(), inst.end()); | ||||
|             it_state = it_state.Advance(); | ||||
|         } | ||||
|  | ||||
|         const u32 start_address = 100; | ||||
|         const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0; | ||||
|         const u32 fpcr = RandomFpcr(); | ||||
|  | ||||
|         regs[15] = start_address; | ||||
|         RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, pre_instructions + 1 + post_instructions); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										249
									
								
								externals/dynarmic/tests/A32/fuzz_thumb.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										249
									
								
								externals/dynarmic/tests/A32/fuzz_thumb.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -9,6 +9,7 @@ | ||||
| #include <cstdio> | ||||
| #include <cstring> | ||||
| #include <functional> | ||||
| #include <string_view> | ||||
| #include <tuple> | ||||
|  | ||||
| #include <catch.hpp> | ||||
| @@ -41,11 +42,13 @@ using WriteRecords = std::map<u32, u8>; | ||||
|  | ||||
| struct ThumbInstGen final { | ||||
| public: | ||||
|     ThumbInstGen(const char* format, std::function<bool(u16)> is_valid = [](u16){ return true; }) : is_valid(is_valid) { | ||||
|         REQUIRE(strlen(format) == 16); | ||||
|     ThumbInstGen(std::string_view format, std::function<bool(u32)> is_valid = [](u32){ return true; }) : is_valid(is_valid) { | ||||
|         REQUIRE((format.size() == 16 || format.size() == 32)); | ||||
|  | ||||
|         for (int i = 0; i < 16; i++) { | ||||
|             const u16 bit = 1 << (15 - i); | ||||
|         const auto bit_size = format.size(); | ||||
|  | ||||
|         for (size_t i = 0; i < bit_size; i++) { | ||||
|             const u32 bit = 1U << (bit_size - 1 - i); | ||||
|             switch (format[i]) { | ||||
|             case '0': | ||||
|                 mask |= bit; | ||||
| @@ -60,11 +63,25 @@ public: | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     u16 Generate() const { | ||||
|         u16 inst; | ||||
|  | ||||
|     u16 Generate16() const { | ||||
|         u32 inst; | ||||
|  | ||||
|         do { | ||||
|             const u16 random = RandInt<u16>(0, 0xFFFF); | ||||
|             const auto random = RandInt<u16>(0, 0xFFFF); | ||||
|             inst = bits | (random & ~mask); | ||||
|         } while (!is_valid(inst)); | ||||
|  | ||||
|         ASSERT((inst & mask) == bits); | ||||
|  | ||||
|         return static_cast<u16>(inst); | ||||
|     } | ||||
|  | ||||
|     u32 Generate32() const { | ||||
|         u32 inst; | ||||
|  | ||||
|         do { | ||||
|             const auto random = RandInt<u32>(0, 0xFFFFFFFF); | ||||
|             inst = bits | (random & ~mask); | ||||
|         } while (!is_valid(inst)); | ||||
|  | ||||
| @@ -72,10 +89,11 @@ public: | ||||
|  | ||||
|         return inst; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     u16 bits = 0; | ||||
|     u16 mask = 0; | ||||
|     std::function<bool(u16)> is_valid; | ||||
|     u32 bits = 0; | ||||
|     u32 mask = 0; | ||||
|     std::function<bool(u32)> is_valid; | ||||
| }; | ||||
|  | ||||
| static bool DoesBehaviorMatch(const A32Unicorn<ThumbTestEnv>& uni, const A32::Jit& jit, | ||||
| @@ -179,7 +197,7 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<Th | ||||
|     } | ||||
| } | ||||
|  | ||||
| void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) { | ||||
| void FuzzJitThumb16(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) { | ||||
|     ThumbTestEnv test_env; | ||||
|  | ||||
|     // Prepare memory. | ||||
| @@ -201,7 +219,37 @@ void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_e | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") { | ||||
| void FuzzJitThumb32(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u32()> instruction_generator) { | ||||
|     ThumbTestEnv test_env; | ||||
|  | ||||
|     // Prepare memory. | ||||
|     // A Thumb-32 instruction is 32-bits so we multiply our count | ||||
|     test_env.code_mem.resize(instruction_count * 2 + 1); | ||||
|     test_env.code_mem.back() = 0xE7FE; // b +#0 | ||||
|  | ||||
|     // Prepare test subjects | ||||
|     A32Unicorn uni{test_env}; | ||||
|     A32::Jit jit{GetUserConfig(&test_env)}; | ||||
|  | ||||
|     for (size_t run_number = 0; run_number < run_count; run_number++) { | ||||
|         ThumbTestEnv::RegisterArray initial_regs; | ||||
|         std::generate_n(initial_regs.begin(), initial_regs.size() - 1, []{ return RandInt<u32>(0, 0xFFFFFFFF); }); | ||||
|         initial_regs[15] = 0; | ||||
|  | ||||
|         for (size_t i = 0; i < instruction_count; i++) { | ||||
|             const auto instruction = instruction_generator(); | ||||
|             const auto first_halfword = static_cast<u16>(Common::Bits<0, 15>(instruction)); | ||||
|             const auto second_halfword = static_cast<u16>(Common::Bits<16, 31>(instruction)); | ||||
|  | ||||
|             test_env.code_mem[i * 2 + 0] = second_halfword; | ||||
|             test_env.code_mem[i * 2 + 1] = first_halfword; | ||||
|         } | ||||
|  | ||||
|         RunInstance(run_number, test_env, uni, jit, initial_regs, instruction_count, instructions_to_execute_count); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb][Thumb16]") { | ||||
|     const std::array instructions = { | ||||
|         ThumbInstGen("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5> | ||||
|         ThumbInstGen("00001xxxxxxxxxxx"), // LSR <Rd>, <Rm>, #<imm5> | ||||
| @@ -212,9 +260,9 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") { | ||||
|         ThumbInstGen("010000ooooxxxxxx"), // Data Processing | ||||
|         ThumbInstGen("010001000hxxxxxx"), // ADD (high registers) | ||||
|         ThumbInstGen("0100010101xxxxxx",  // CMP (high registers) | ||||
|                      [](u16 inst){ return Common::Bits<3, 5>(inst) != 0b111; }), // R15 is UNPREDICTABLE | ||||
|                      [](u32 inst){ return Common::Bits<3, 5>(inst) != 0b111; }), // R15 is UNPREDICTABLE | ||||
|         ThumbInstGen("0100010110xxxxxx",  // CMP (high registers) | ||||
|                      [](u16 inst){ return Common::Bits<0, 2>(inst) != 0b111; }), // R15 is UNPREDICTABLE | ||||
|                      [](u32 inst){ return Common::Bits<0, 2>(inst) != 0b111; }), // R15 is UNPREDICTABLE | ||||
|         ThumbInstGen("010001100hxxxxxx"), // MOV (high registers) | ||||
|         ThumbInstGen("10110000oxxxxxxx"), // Adjust stack pointer | ||||
|         ThumbInstGen("10110010ooxxxxxx"), // SXT/UXT | ||||
| @@ -227,11 +275,11 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") { | ||||
|         ThumbInstGen("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset] | ||||
|         ThumbInstGen("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #] | ||||
|         ThumbInstGen("1011010xxxxxxxxx",  // PUSH | ||||
|                      [](u16 inst){ return Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE | ||||
|                      [](u32 inst){ return Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE | ||||
|         ThumbInstGen("10111100xxxxxxxx",  // POP (P = 0) | ||||
|                      [](u16 inst){ return Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE | ||||
|                      [](u32 inst){ return Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE | ||||
|         ThumbInstGen("1100xxxxxxxxxxxx", // STMIA/LDMIA | ||||
|                      [](u16 inst) { | ||||
|                      [](u32 inst) { | ||||
|                          // Ensure that the architecturally undefined case of | ||||
|                          // the base register being within the list isn't hit. | ||||
|                          const u32 rn = Common::Bits<8, 10>(inst); | ||||
| @@ -247,29 +295,29 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") { | ||||
|     }; | ||||
|  | ||||
|     const auto instruction_select = [&]() -> u16 { | ||||
|         size_t inst_index = RandInt<size_t>(0, instructions.size() - 1); | ||||
|         const auto inst_index = RandInt<size_t>(0, instructions.size() - 1); | ||||
|  | ||||
|         return instructions[inst_index].Generate(); | ||||
|         return instructions[inst_index].Generate16(); | ||||
|     }; | ||||
|  | ||||
|     SECTION("single instructions") { | ||||
|         FuzzJitThumb(1, 2, 10000, instruction_select); | ||||
|         FuzzJitThumb16(1, 2, 10000, instruction_select); | ||||
|     } | ||||
|  | ||||
|     SECTION("short blocks") { | ||||
|         FuzzJitThumb(5, 6, 3000, instruction_select); | ||||
|         FuzzJitThumb16(5, 6, 3000, instruction_select); | ||||
|     } | ||||
|  | ||||
|     // TODO: Test longer blocks when Unicorn can consistently | ||||
|     //       run these without going into an infinite loop. | ||||
| #if 0 | ||||
|     SECTION("long blocks") { | ||||
|         FuzzJitThumb(1024, 1025, 1000, instruction_select); | ||||
|         FuzzJitThumb16(1024, 1025, 1000, instruction_select); | ||||
|     } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") { | ||||
| TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb][Thumb16]") { | ||||
|     const std::array instructions = { | ||||
|         // TODO: We currently can't test BX/BLX as we have | ||||
|         //       no way of preventing the unpredictable | ||||
| @@ -278,7 +326,7 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") { | ||||
|         //       must not be address<1:0> == '10'. | ||||
| #if 0 | ||||
|         ThumbInstGen("01000111xmmmm000",  // BLX/BX | ||||
|                      [](u16 inst){ | ||||
|                      [](u32 inst){ | ||||
|                          const u32 Rm = Common::Bits<3, 6>(inst); | ||||
|                          return Rm != 15; | ||||
|                      }), | ||||
| @@ -288,7 +336,7 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") { | ||||
|         ThumbInstGen("01000100h0xxxxxx"), // ADD (high registers) | ||||
|         ThumbInstGen("01000110h0xxxxxx"), // MOV (high registers) | ||||
|         ThumbInstGen("1101ccccxxxxxxxx",  // B<cond> | ||||
|                      [](u16 inst){ | ||||
|                      [](u32 inst){ | ||||
|                          const u32 c = Common::Bits<9, 12>(inst); | ||||
|                          return c < 0b1110; // Don't want SWI or undefined instructions. | ||||
|                      }), | ||||
| @@ -304,15 +352,158 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") { | ||||
|     }; | ||||
|  | ||||
|     const auto instruction_select = [&]() -> u16 { | ||||
|         size_t inst_index = RandInt<size_t>(0, instructions.size() - 1); | ||||
|         const auto inst_index = RandInt<size_t>(0, instructions.size() - 1); | ||||
|  | ||||
|         return instructions[inst_index].Generate(); | ||||
|         return instructions[inst_index].Generate16(); | ||||
|     }; | ||||
|  | ||||
|     FuzzJitThumb(1, 1, 10000, instruction_select); | ||||
|     FuzzJitThumb16(1, 1, 10000, instruction_select); | ||||
| } | ||||
|  | ||||
| TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") { | ||||
| TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") { | ||||
|     const auto three_reg_not_r15 = [](u32 inst) { | ||||
|         const auto d = Common::Bits<8, 11>(inst); | ||||
|         const auto m = Common::Bits<0, 3>(inst); | ||||
|         const auto n = Common::Bits<16, 19>(inst); | ||||
|         return d != 15 && m != 15 && n != 15; | ||||
|     }; | ||||
|  | ||||
|     const std::array instructions = { | ||||
|         ThumbInstGen("111110101011nnnn1111dddd1000mmmm", // CLZ | ||||
|                      [](u32 inst) { | ||||
|                          const auto d = Common::Bits<8, 11>(inst); | ||||
|                          const auto m = Common::Bits<0, 3>(inst); | ||||
|                          const auto n = Common::Bits<16, 19>(inst); | ||||
|                          return m == n && d != 15 && m != 15; | ||||
|                      }), | ||||
|         ThumbInstGen("111110101000nnnn1111dddd1000mmmm", // QADD | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101000nnnn1111dddd0001mmmm", // QADD8 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101001nnnn1111dddd0001mmmm", // QADD16 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101010nnnn1111dddd0001mmmm", // QASX | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101000nnnn1111dddd1001mmmm", // QDADD | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101000nnnn1111dddd1011mmmm", // QDSUB | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101110nnnn1111dddd0001mmmm", // QSAX | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101000nnnn1111dddd1010mmmm", // QSUB | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101100nnnn1111dddd0001mmmm", // QSUB8 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101101nnnn1111dddd0001mmmm", // QSUB16 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101001nnnn1111dddd1010mmmm", // RBIT | ||||
|                      [](u32 inst) { | ||||
|                          const auto d = Common::Bits<8, 11>(inst); | ||||
|                          const auto m = Common::Bits<0, 3>(inst); | ||||
|                          const auto n = Common::Bits<16, 19>(inst); | ||||
|                          return m == n && d != 15 && m != 15; | ||||
|                      }), | ||||
|         ThumbInstGen("111110101001nnnn1111dddd1000mmmm", // REV | ||||
|                      [](u32 inst) { | ||||
|                          const auto d = Common::Bits<8, 11>(inst); | ||||
|                          const auto m = Common::Bits<0, 3>(inst); | ||||
|                          const auto n = Common::Bits<16, 19>(inst); | ||||
|                          return m == n && d != 15 && m != 15; | ||||
|                      }), | ||||
|         ThumbInstGen("111110101001nnnn1111dddd1001mmmm", // REV16 | ||||
|                      [](u32 inst) { | ||||
|                          const auto d = Common::Bits<8, 11>(inst); | ||||
|                          const auto m = Common::Bits<0, 3>(inst); | ||||
|                          const auto n = Common::Bits<16, 19>(inst); | ||||
|                          return m == n && d != 15 && m != 15; | ||||
|                      }), | ||||
|         ThumbInstGen("111110101001nnnn1111dddd1011mmmm", // REVSH | ||||
|                      [](u32 inst) { | ||||
|                          const auto d = Common::Bits<8, 11>(inst); | ||||
|                          const auto m = Common::Bits<0, 3>(inst); | ||||
|                          const auto n = Common::Bits<16, 19>(inst); | ||||
|                          return m == n && d != 15 && m != 15; | ||||
|                      }), | ||||
|         ThumbInstGen("111110101000nnnn1111dddd0000mmmm", // SADD8 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101001nnnn1111dddd0000mmmm", // SADD16 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101010nnnn1111dddd0000mmmm", // SASX | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101010nnnn1111dddd1000mmmm", // SEL | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101000nnnn1111dddd0010mmmm", // SHADD8 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101001nnnn1111dddd0010mmmm", // SHADD16 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101010nnnn1111dddd0010mmmm", // SHASX | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101110nnnn1111dddd0010mmmm", // SHSAX | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101100nnnn1111dddd0010mmmm", // SHSUB8 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101101nnnn1111dddd0010mmmm", // SHSUB16 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101110nnnn1111dddd0000mmmm", // SSAX | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101100nnnn1111dddd0000mmmm", // SSUB8 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101101nnnn1111dddd0000mmmm", // SSUB16 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101000nnnn1111dddd0100mmmm", // UADD8 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101001nnnn1111dddd0100mmmm", // UADD16 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101010nnnn1111dddd0100mmmm", // UASX | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101000nnnn1111dddd0110mmmm", // UHADD8 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101001nnnn1111dddd0110mmmm", // UHADD16 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101010nnnn1111dddd0110mmmm", // UHASX | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101110nnnn1111dddd0110mmmm", // UHSAX | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101100nnnn1111dddd0110mmmm", // UHSUB8 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101101nnnn1111dddd0110mmmm", // UHSUB16 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101000nnnn1111dddd0101mmmm", // UQADD8 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101001nnnn1111dddd0101mmmm", // UQADD16 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101010nnnn1111dddd0101mmmm", // UQASX | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101110nnnn1111dddd0101mmmm", // UQSAX | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101100nnnn1111dddd0101mmmm", // UQSUB8 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101101nnnn1111dddd0101mmmm", // UQSUB16 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101110nnnn1111dddd0100mmmm", // USAX | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101100nnnn1111dddd0100mmmm", // USUB8 | ||||
|                      three_reg_not_r15), | ||||
|         ThumbInstGen("111110101101nnnn1111dddd0100mmmm", // USUB16 | ||||
|                      three_reg_not_r15), | ||||
|     }; | ||||
|  | ||||
|     const auto instruction_select = [&]() -> u32 { | ||||
|         const auto inst_index = RandInt<size_t>(0, instructions.size() - 1); | ||||
|  | ||||
|         return instructions[inst_index].Generate32(); | ||||
|     }; | ||||
|  | ||||
|     SECTION("single instructions") { | ||||
|         FuzzJitThumb32(1, 2, 10000, instruction_select); | ||||
|     } | ||||
|  | ||||
|     SECTION("short blocks") { | ||||
|         FuzzJitThumb32(5, 6, 3000, instruction_select); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thumb16]") { | ||||
|     ThumbTestEnv test_env; | ||||
|  | ||||
|     // Prepare test subjects | ||||
|   | ||||
							
								
								
									
										32
									
								
								externals/dynarmic/tests/A32/testenv.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								externals/dynarmic/tests/A32/testenv.h
									
									
									
									
										vendored
									
									
								
							| @@ -16,31 +16,49 @@ | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| template <typename InstructionType_, u32 infinite_loop> | ||||
| template <typename InstructionType_, u32 infinite_loop_u32> | ||||
| class A32TestEnv final : public Dynarmic::A32::UserCallbacks { | ||||
| public: | ||||
|     using InstructionType = InstructionType_; | ||||
|     using RegisterArray = std::array<u32, 16>; | ||||
|     using ExtRegsArray = std::array<u32, 64>; | ||||
|  | ||||
| #ifdef _MSC_VER | ||||
| #pragma warning(push) | ||||
| #pragma warning(disable:4309) // C4309: 'static_cast': truncation of constant value | ||||
| #endif | ||||
|     static constexpr InstructionType infinite_loop = static_cast<InstructionType>(infinite_loop_u32); | ||||
| #ifdef _MSC_VER | ||||
| #pragma warning(pop) | ||||
| #endif | ||||
|  | ||||
|     u64 ticks_left = 0; | ||||
|     bool code_mem_modified_by_guest = false; | ||||
|     std::vector<InstructionType> code_mem; | ||||
|     std::map<u32, u8> modified_memory; | ||||
|     std::vector<std::string> interrupts; | ||||
|  | ||||
|     void PadCodeMem() { | ||||
|         do { | ||||
|             code_mem.push_back(infinite_loop); | ||||
|         } while (code_mem.size() % 2 != 0); | ||||
|     } | ||||
|  | ||||
|     bool IsInCodeMem(u32 vaddr) const { | ||||
|         return vaddr < sizeof(InstructionType) * code_mem.size(); | ||||
|     } | ||||
|  | ||||
|     std::uint32_t MemoryReadCode(u32 vaddr) override { | ||||
|         const size_t index = vaddr / sizeof(InstructionType); | ||||
|         if (index < code_mem.size()) { | ||||
|         if (IsInCodeMem(vaddr)) { | ||||
|             u32 value; | ||||
|             std::memcpy(&value, &code_mem[index], sizeof(u32)); | ||||
|             std::memcpy(&value, &code_mem[vaddr / sizeof(InstructionType)], sizeof(u32)); | ||||
|             return value; | ||||
|         } | ||||
|         return infinite_loop; // B . | ||||
|         return infinite_loop_u32; // B . | ||||
|     } | ||||
|  | ||||
|     std::uint8_t MemoryRead8(u32 vaddr) override { | ||||
|         if (vaddr < sizeof(InstructionType) * code_mem.size()) { | ||||
|         if (IsInCodeMem(vaddr)) { | ||||
|             return reinterpret_cast<u8*>(code_mem.data())[vaddr]; | ||||
|         } | ||||
|         if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) { | ||||
| @@ -81,7 +99,7 @@ public: | ||||
|  | ||||
|     void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC({})", swi); } | ||||
|  | ||||
|     void ExceptionRaised(u32 pc, Dynarmic::A32::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised({:08x})", pc); } | ||||
|     void ExceptionRaised(u32 pc, Dynarmic::A32::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised({:08x}) code = {:08x}", pc, MemoryReadCode(pc)); } | ||||
|  | ||||
|     void AddTicks(std::uint64_t ticks) override { | ||||
|         if (ticks > ticks_left) { | ||||
|   | ||||
							
								
								
									
										28
									
								
								externals/dynarmic/tests/A64/a64.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								externals/dynarmic/tests/A64/a64.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -634,3 +634,31 @@ TEST_CASE("A64: Optimization failure when folding ADD", "[a64]") { | ||||
|     REQUIRE(jit.GetPstate() == 0x20000000); | ||||
|     REQUIRE(jit.GetVector(30) == Vector{0xf7f6f5f4, 0}); | ||||
| } | ||||
|  | ||||
| TEST_CASE("A64: Cache Maintenance Instructions", "[a64]") { | ||||
|     class CacheMaintenanceTestEnv final : public A64TestEnv { | ||||
|         void InstructionCacheOperationRaised(A64::InstructionCacheOperation op, VAddr value) override { | ||||
|             REQUIRE(op == A64::InstructionCacheOperation::InvalidateByVAToPoU); | ||||
|             REQUIRE(value == 0xcafed00d); | ||||
|         } | ||||
|         void DataCacheOperationRaised(A64::DataCacheOperation op, VAddr value) override { | ||||
|             REQUIRE(op == A64::DataCacheOperation::InvalidateByVAToPoC); | ||||
|             REQUIRE(value == 0xcafebabe); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     CacheMaintenanceTestEnv env; | ||||
|     A64::UserConfig conf{&env}; | ||||
|     conf.hook_data_cache_operations = true; | ||||
|     A64::Jit jit{conf}; | ||||
|  | ||||
|     jit.SetRegister(0, 0xcafed00d); | ||||
|     jit.SetRegister(1, 0xcafebabe); | ||||
|  | ||||
|     env.code_mem.emplace_back(0xd50b7520); // ic ivau, x0 | ||||
|     env.code_mem.emplace_back(0xd5087621); // dc ivac, x1 | ||||
|     env.code_mem.emplace_back(0x14000000); // B . | ||||
|  | ||||
|     env.ticks_left = 3; | ||||
|     jit.Run(); | ||||
| } | ||||
|   | ||||
							
								
								
									
										2
									
								
								externals/dynarmic/tests/A64/testenv.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								externals/dynarmic/tests/A64/testenv.h
									
									
									
									
										vendored
									
									
								
							| @@ -15,7 +15,7 @@ | ||||
|  | ||||
| using Vector = Dynarmic::A64::Vector; | ||||
|  | ||||
| class A64TestEnv final : public Dynarmic::A64::UserCallbacks { | ||||
| class A64TestEnv : public Dynarmic::A64::UserCallbacks { | ||||
| public: | ||||
|     u64 ticks_left = 0; | ||||
|  | ||||
|   | ||||
							
								
								
									
										12
									
								
								externals/dynarmic/tests/fuzz_util.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								externals/dynarmic/tests/fuzz_util.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -35,10 +35,16 @@ u32 RandomFpcr() { | ||||
| } | ||||
|  | ||||
| InstructionGenerator::InstructionGenerator(const char* format){ | ||||
|     ASSERT(std::strlen(format) == 32); | ||||
|     const size_t format_len = std::strlen(format); | ||||
|     ASSERT(format_len == 16 || format_len == 32); | ||||
|  | ||||
|     for (int i = 0; i < 32; i++) { | ||||
|         const u32 bit = 1u << (31 - i); | ||||
|     if (format_len == 16) { | ||||
|         // Begin with 16 zeros | ||||
|         mask |= 0xFFFF0000; | ||||
|     } | ||||
|  | ||||
|     for (size_t i = 0; i < format_len; i++) { | ||||
|         const u32 bit = 1u << (format_len - i - 1); | ||||
|         switch (format[i]) { | ||||
|         case '0': | ||||
|             mask |= bit; | ||||
|   | ||||
							
								
								
									
										2
									
								
								externals/dynarmic/tests/print_info.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								externals/dynarmic/tests/print_info.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -54,7 +54,7 @@ const char* GetNameOfA64Instruction(u32 instruction) { | ||||
| } | ||||
|  | ||||
| void PrintA32Instruction(u32 instruction) { | ||||
|     fmt::print("{:08x} {}\n", instruction, Common::DisassembleAArch32(instruction)); | ||||
|     fmt::print("{:08x} {}\n", instruction, Common::DisassembleAArch32(false, 0, (u8*)&instruction, sizeof(instruction))); | ||||
|     fmt::print("Name: {}\n", GetNameOfA32Instruction(instruction)); | ||||
|  | ||||
|     const A32::LocationDescriptor location{0, {}, {}}; | ||||
|   | ||||
| @@ -44,6 +44,9 @@ void A32Unicorn<TestEnvironment>::Run() { | ||||
|     constexpr u64 pc_mask = std::is_same_v<TestEnvironment, ArmTestEnv> ? 0 : 1; | ||||
|     while (testenv.ticks_left > 0) { | ||||
|         const u32 pc = GetPC() | pc_mask; | ||||
|         if (!testenv.IsInCodeMem(pc)) { | ||||
|             return; | ||||
|         } | ||||
|         if (auto cerr_ = uc_emu_start(uc, pc, END_ADDRESS, 0, 1)) { | ||||
|             ASSERT_MSG(false, "uc_emu_start failed @ {:08x} (code = {:08x}) with error {} ({})", pc, testenv.MemoryReadCode(pc), cerr_, uc_strerror(cerr_)); | ||||
|         } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user