early-access version 1475
This commit is contained in:
parent
762f890987
commit
d8bef01911
56 changed files with 3213 additions and 736 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 1474.
|
This is the source code for early-access 1475.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
6
externals/dynarmic/CMakeLists.txt
vendored
6
externals/dynarmic/CMakeLists.txt
vendored
|
@ -97,6 +97,12 @@ else()
|
||||||
list(APPEND DYNARMIC_CXX_FLAGS
|
list(APPEND DYNARMIC_CXX_FLAGS
|
||||||
-Wfatal-errors)
|
-Wfatal-errors)
|
||||||
endif()
|
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()
|
endif()
|
||||||
|
|
||||||
# Arch detection
|
# 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 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
|
### 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 <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <dynarmic/A32/arch_version.h>
|
||||||
#include <dynarmic/optimization_flags.h>
|
#include <dynarmic/optimization_flags.h>
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
@ -105,6 +106,10 @@ struct UserConfig {
|
||||||
size_t processor_id = 0;
|
size_t processor_id = 0;
|
||||||
ExclusiveMonitor* global_monitor = nullptr;
|
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
|
/// This selects other optimizations than can't otherwise be disabled by setting other
|
||||||
/// configuration options. This includes:
|
/// configuration options. This includes:
|
||||||
/// - IR optimizations
|
/// - 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,
|
ZeroByVA,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class InstructionCacheOperation {
|
||||||
|
/// IC IVAU
|
||||||
|
InvalidateByVAToPoU,
|
||||||
|
/// IC IALLU
|
||||||
|
InvalidateAllToPoU,
|
||||||
|
/// IC IALLUIS
|
||||||
|
InvalidateAllToPoUInnerSharable
|
||||||
|
};
|
||||||
|
|
||||||
struct UserCallbacks {
|
struct UserCallbacks {
|
||||||
virtual ~UserCallbacks() = default;
|
virtual ~UserCallbacks() = default;
|
||||||
|
|
||||||
|
@ -110,6 +119,7 @@ struct UserCallbacks {
|
||||||
|
|
||||||
virtual void ExceptionRaised(VAddr pc, Exception exception) = 0;
|
virtual void ExceptionRaised(VAddr pc, Exception exception) = 0;
|
||||||
virtual void DataCacheOperationRaised(DataCacheOperation /*op*/, VAddr /*value*/) {}
|
virtual void DataCacheOperationRaised(DataCacheOperation /*op*/, VAddr /*value*/) {}
|
||||||
|
virtual void InstructionCacheOperationRaised(InstructionCacheOperation /*op*/, VAddr /*value*/) {}
|
||||||
virtual void InstructionSynchronizationBarrierRaised() {}
|
virtual void InstructionSynchronizationBarrierRaised() {}
|
||||||
|
|
||||||
// Timing-related callbacks
|
// Timing-related callbacks
|
||||||
|
|
13
externals/dynarmic/src/CMakeLists.txt
vendored
13
externals/dynarmic/src/CMakeLists.txt
vendored
|
@ -1,5 +1,6 @@
|
||||||
add_library(dynarmic
|
add_library(dynarmic
|
||||||
../include/dynarmic/A32/a32.h
|
../include/dynarmic/A32/a32.h
|
||||||
|
../include/dynarmic/A32/arch_version.h
|
||||||
../include/dynarmic/A32/config.h
|
../include/dynarmic/A32/config.h
|
||||||
../include/dynarmic/A32/coprocessor.h
|
../include/dynarmic/A32/coprocessor.h
|
||||||
../include/dynarmic/A32/coprocessor_util.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.cpp
|
||||||
frontend/A32/location_descriptor.h
|
frontend/A32/location_descriptor.h
|
||||||
frontend/A32/PSR.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_load_store_structures.cpp
|
||||||
frontend/A32/translate/impl/asimd_misc.cpp
|
frontend/A32/translate/impl/asimd_misc.cpp
|
||||||
frontend/A32/translate/impl/asimd_one_reg_modified_immediate.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/status_register_access.cpp
|
||||||
frontend/A32/translate/impl/synchronization.cpp
|
frontend/A32/translate/impl/synchronization.cpp
|
||||||
frontend/A32/translate/impl/thumb16.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_arm.h
|
||||||
frontend/A32/translate/impl/translate_thumb.h
|
frontend/A32/translate/impl/translate_thumb.h
|
||||||
frontend/A32/translate/impl/vfp.cpp
|
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_two_register_misc.cpp
|
||||||
frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp
|
frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp
|
||||||
frontend/A64/translate/impl/sys_dc.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.cpp
|
||||||
frontend/A64/translate/impl/system_flag_format.cpp
|
frontend/A64/translate/impl/system_flag_format.cpp
|
||||||
frontend/A64/translate/impl/system_flag_manipulation.cpp
|
frontend/A64/translate/impl/system_flag_manipulation.cpp
|
||||||
|
|
|
@ -67,6 +67,10 @@ A32::LocationDescriptor A32EmitContext::Location() const {
|
||||||
return A32::LocationDescriptor{block.Location()};
|
return A32::LocationDescriptor{block.Location()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
A32::LocationDescriptor A32EmitContext::EndLocation() const {
|
||||||
|
return A32::LocationDescriptor{block.EndLocation()};
|
||||||
|
}
|
||||||
|
|
||||||
bool A32EmitContext::IsSingleStep() const {
|
bool A32EmitContext::IsSingleStep() const {
|
||||||
return Location().SingleStepping();
|
return Location().SingleStepping();
|
||||||
}
|
}
|
||||||
|
@ -732,7 +736,7 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
auto& arg = args[0];
|
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:
|
// Pseudocode:
|
||||||
// if (new_pc & 1) {
|
// 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) {
|
void A32EmitX64::EmitA32CallSupervisor(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
ctx.reg_alloc.HostCall(nullptr);
|
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);
|
A32EmitContext(const A32::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block);
|
||||||
|
|
||||||
A32::LocationDescriptor Location() const;
|
A32::LocationDescriptor Location() const;
|
||||||
|
A32::LocationDescriptor EndLocation() const;
|
||||||
bool IsSingleStep() const;
|
bool IsSingleStep() const;
|
||||||
FP::FPCR FPCR(bool fpcr_controlled = true) const override;
|
FP::FPCR FPCR(bool fpcr_controlled = true) const override;
|
||||||
|
|
||||||
|
|
|
@ -175,7 +175,7 @@ private:
|
||||||
PerformCacheInvalidation();
|
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)) {
|
if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) {
|
||||||
Optimization::A32GetSetElimination(ir_block);
|
Optimization::A32GetSetElimination(ir_block);
|
||||||
Optimization::DeadCodeElimination(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) {
|
void A64EmitX64::EmitA64DataCacheOperationRaised(A64EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(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);
|
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*) {
|
void A64EmitX64::EmitA64DataSynchronizationBarrier(A64EmitContext&, IR::Inst*) {
|
||||||
code.mfence();
|
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)));
|
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);
|
return static_cast<u16>(u32{value} >> 8 | u32{value} << 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr u32 Swap32(u32 value) {
|
constexpr u32 SwapBytes32(u32 value) {
|
||||||
return ((value & 0xFF000000U) >> 24) |
|
return ((value & 0xFF000000U) >> 24) |
|
||||||
((value & 0x00FF0000U) >> 8) |
|
((value & 0x00FF0000U) >> 8) |
|
||||||
((value & 0x0000FF00U) << 8) |
|
((value & 0x0000FF00U) << 8) |
|
||||||
((value & 0x000000FFU) << 24);
|
((value & 0x000000FFU) << 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr u64 Swap64(u64 value) {
|
constexpr u64 SwapBytes64(u64 value) {
|
||||||
return ((value & 0xFF00000000000000ULL) >> 56) |
|
return ((value & 0xFF00000000000000ULL) >> 56) |
|
||||||
((value & 0x00FF000000000000ULL) >> 40) |
|
((value & 0x00FF000000000000ULL) >> 40) |
|
||||||
((value & 0x0000FF0000000000ULL) >> 24) |
|
((value & 0x0000FF0000000000ULL) >> 24) |
|
||||||
|
|
|
@ -54,20 +54,38 @@ std::string DisassembleX64(const void* begin, const void* end) {
|
||||||
return result;
|
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;
|
std::string result;
|
||||||
|
|
||||||
#ifdef DYNARMIC_USE_LLVM
|
#ifdef DYNARMIC_USE_LLVM
|
||||||
LLVMInitializeARMTargetInfo();
|
LLVMInitializeARMTargetInfo();
|
||||||
LLVMInitializeARMTargetMC();
|
LLVMInitializeARMTargetMC();
|
||||||
LLVMInitializeARMDisassembler();
|
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);
|
LLVMSetDisasmOptions(llvm_ctx, LLVMDisassembler_Option_AsmPrinterVariant);
|
||||||
|
|
||||||
char buffer[80];
|
char buffer[1024];
|
||||||
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, (u8*)&instruction, sizeof(instruction), pc, buffer, sizeof(buffer));
|
while (length) {
|
||||||
result = inst_size > 0 ? buffer : "<invalid instruction>";
|
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, const_cast<u8*>(instructions), length, pc, buffer, sizeof(buffer));
|
||||||
result += '\n';
|
|
||||||
|
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);
|
LLVMDisasmDispose(llvm_ctx);
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
namespace Dynarmic::Common {
|
namespace Dynarmic::Common {
|
||||||
|
|
||||||
std::string DisassembleX64(const void* pos, const void* end);
|
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);
|
std::string DisassembleAArch64(u32 instruction, u64 pc = 0);
|
||||||
|
|
||||||
} // namespace Dynarmic::Common
|
} // 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 {
|
IR::Cond Cond() const {
|
||||||
|
if (value == 0b00000000) {
|
||||||
|
return IR::Cond::AL;
|
||||||
|
}
|
||||||
return static_cast<IR::Cond>(Common::Bits<4, 7>(value));
|
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 {
|
bool IsInITBlock() const {
|
||||||
return Mask() != 0b0000;
|
return Common::Bits<0, 3>(value) != 0b0000;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsLastInITBlock() const {
|
bool IsLastInITBlock() const {
|
||||||
return Mask() == 0b1000;
|
return Common::Bits<0, 3>(value) == 0b1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
ITState Advance() const {
|
ITState Advance() const {
|
||||||
ITState result{*this};
|
if (Common::Bits<0, 2>(value) == 0b000) {
|
||||||
result.Mask(result.Mask() << 1);
|
return ITState{0b00000000};
|
||||||
if (result.Mask() == 0) {
|
|
||||||
return ITState{0};
|
|
||||||
}
|
}
|
||||||
return result;
|
return ITState{Common::ModifyBits<0, 4>(value, static_cast<u8>(Common::Bits<0, 4>(value) << 1))};
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 Value() const {
|
u8 Value() const {
|
||||||
|
@ -57,7 +48,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u8 value;
|
u8 value = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool operator==(ITState lhs, ITState rhs) {
|
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) {
|
std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16 instruction) {
|
||||||
static const std::vector<Thumb16Matcher<V>> table = {
|
static const std::vector<Thumb16Matcher<V>> table = {
|
||||||
|
|
||||||
#define INST(fn, name, bitstring) Decoder::detail::detail<Thumb16Matcher<V>>::GetMatcher(fn, name, bitstring)
|
#define INST(fn, name, bitstring) Decoder::detail::detail<Thumb16Matcher<V>>::GetMatcher(&V::fn, name, bitstring),
|
||||||
|
#include "thumb16.inc"
|
||||||
// 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"),
|
|
||||||
|
|
||||||
#undef INST
|
#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) {
|
std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32 instruction) {
|
||||||
static const std::vector<Thumb32Matcher<V>> table = {
|
static const std::vector<Thumb32Matcher<V>> table = {
|
||||||
|
|
||||||
#define INST(fn, name, bitstring) Decoder::detail::detail<Thumb32Matcher<V>>::GetMatcher(fn, name, bitstring)
|
#define INST(fn, name, bitstring) Decoder::detail::detail<Thumb32Matcher<V>>::GetMatcher(&V::fn, name, bitstring),
|
||||||
|
#include "thumb32.inc"
|
||||||
// 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
|
|
||||||
|
|
||||||
#undef INST
|
#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";
|
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) {
|
std::string thumb16_SXTH(Reg m, Reg d) {
|
||||||
return fmt::format("sxth {}, {}", d, m);
|
return fmt::format("sxth {}, {}", d, m);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,32 @@
|
||||||
#include "frontend/A32/types.h"
|
#include "frontend/A32/types.h"
|
||||||
#include "frontend/ir/opcodes.h"
|
#include "frontend/ir/opcodes.h"
|
||||||
|
|
||||||
|
#include <dynarmic/A32/arch_version.h>
|
||||||
|
|
||||||
namespace Dynarmic::A32 {
|
namespace Dynarmic::A32 {
|
||||||
|
|
||||||
using Opcode = IR::Opcode;
|
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 {
|
u32 IREmitter::PC() const {
|
||||||
const u32 offset = current_location.TFlag() ? 4 : 8;
|
const u32 offset = current_location.TFlag() ? 4 : 8;
|
||||||
return current_location.PC() + offset;
|
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) {
|
void IREmitter::ALUWritePC(const IR::U32& value) {
|
||||||
// This behaviour is ARM version-dependent.
|
// This behaviour is ARM version-dependent.
|
||||||
// The below implementation is for ARMv6k
|
if (ArchVersion() >= 7 && !current_location.TFlag()) {
|
||||||
BranchWritePC(value);
|
BXWritePC(value);
|
||||||
|
} else {
|
||||||
|
BranchWritePC(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::BranchWritePC(const IR::U32& value) {
|
void IREmitter::BranchWritePC(const IR::U32& value) {
|
||||||
if (!current_location.TFlag()) {
|
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));
|
const auto new_pc = And(value, Imm32(0xFFFFFFFC));
|
||||||
Inst(Opcode::A32SetRegister, IR::Value(A32::Reg::PC), new_pc);
|
Inst(Opcode::A32SetRegister, IR::Value(A32::Reg::PC), new_pc);
|
||||||
} else {
|
} else {
|
||||||
|
@ -88,8 +114,15 @@ void IREmitter::BXWritePC(const IR::U32& value) {
|
||||||
|
|
||||||
void IREmitter::LoadWritePC(const IR::U32& value) {
|
void IREmitter::LoadWritePC(const IR::U32& value) {
|
||||||
// This behaviour is ARM version-dependent.
|
// This behaviour is ARM version-dependent.
|
||||||
// The below implementation is for ARMv6k
|
if (ArchVersion() >= 5) {
|
||||||
BXWritePC(value);
|
BXWritePC(value);
|
||||||
|
} else {
|
||||||
|
BranchWritePC(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IREmitter::UpdateUpperLocationDescriptor() {
|
||||||
|
Inst(Opcode::A32UpdateUpperLocationDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::CallSupervisor(const IR::U32& value) {
|
void IREmitter::CallSupervisor(const IR::U32& value) {
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
namespace Dynarmic::A32 {
|
namespace Dynarmic::A32 {
|
||||||
|
|
||||||
|
enum class ArchVersion;
|
||||||
enum class CoprocReg;
|
enum class CoprocReg;
|
||||||
enum class Exception;
|
enum class Exception;
|
||||||
enum class ExtReg;
|
enum class ExtReg;
|
||||||
|
@ -26,10 +27,12 @@ enum class Reg;
|
||||||
*/
|
*/
|
||||||
class IREmitter : public IR::IREmitter {
|
class IREmitter : public IR::IREmitter {
|
||||||
public:
|
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;
|
LocationDescriptor current_location;
|
||||||
|
|
||||||
|
size_t ArchVersion() const;
|
||||||
|
|
||||||
u32 PC() const;
|
u32 PC() const;
|
||||||
u32 AlignPC(size_t alignment) const;
|
u32 AlignPC(size_t alignment) const;
|
||||||
|
|
||||||
|
@ -44,6 +47,7 @@ public:
|
||||||
void BranchWritePC(const IR::U32& value);
|
void BranchWritePC(const IR::U32& value);
|
||||||
void BXWritePC(const IR::U32& value);
|
void BXWritePC(const IR::U32& value);
|
||||||
void LoadWritePC(const IR::U32& value);
|
void LoadWritePC(const IR::U32& value);
|
||||||
|
void UpdateUpperLocationDescriptor();
|
||||||
|
|
||||||
void CallSupervisor(const IR::U32& value);
|
void CallSupervisor(const IR::U32& value);
|
||||||
void ExceptionRaised(Exception exception);
|
void ExceptionRaised(Exception exception);
|
||||||
|
@ -99,6 +103,9 @@ public:
|
||||||
IR::U64 CoprocGetTwoWords(size_t coproc_no, bool two, size_t opc, CoprocReg CRm);
|
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 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);
|
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
|
} // namespace Dynarmic::A32
|
||||||
|
|
|
@ -88,13 +88,17 @@ public:
|
||||||
return LocationDescriptor(arm_pc, cpsr, A32::FPSCR{new_fpscr & FPSCR_MODE_MASK}, single_stepping);
|
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;
|
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);
|
return LocationDescriptor(arm_pc, new_cpsr, fpscr, single_stepping);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LocationDescriptor AdvanceIT() const {
|
||||||
|
return SetIT(IT().Advance());
|
||||||
|
}
|
||||||
|
|
||||||
LocationDescriptor SetSingleStepping(bool new_single_stepping) const {
|
LocationDescriptor SetSingleStepping(bool new_single_stepping) const {
|
||||||
return LocationDescriptor(arm_pc, cpsr, fpscr, new_single_stepping);
|
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>
|
// LSLS <Rd>, <Rm>, #<imm5>
|
||||||
bool ThumbTranslatorVisitor::thumb16_LSL_imm(Imm<5> imm5, Reg m, Reg d) {
|
bool ThumbTranslatorVisitor::thumb16_LSL_imm(Imm<5> imm5, Reg m, Reg d) {
|
||||||
const u8 shift_n = imm5.ZeroExtend<u8>();
|
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 cpsr_c = ir.GetCFlag();
|
||||||
const auto result = ir.LogicalShiftLeft(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
|
const auto result = ir.LogicalShiftLeft(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
|
ir.SetCFlag(result.carry);
|
||||||
|
}
|
||||||
return true;
|
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);
|
const auto result = ir.LogicalShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
|
ir.SetCFlag(result.carry);
|
||||||
|
}
|
||||||
return true;
|
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);
|
const auto result = ir.ArithmeticShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
|
ir.SetCFlag(result.carry);
|
||||||
|
}
|
||||||
return true;
|
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.
|
// Note that it is not possible to encode Rd == R15.
|
||||||
bool ThumbTranslatorVisitor::thumb16_ADD_reg_t1(Reg m, Reg n, Reg d) {
|
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));
|
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0));
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetVFlag(result.overflow);
|
ir.SetCFlag(result.carry);
|
||||||
|
ir.SetVFlag(result.overflow);
|
||||||
|
}
|
||||||
return true;
|
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.
|
// Note that it is not possible to encode Rd == R15.
|
||||||
bool ThumbTranslatorVisitor::thumb16_SUB_reg(Reg m, Reg n, Reg d) {
|
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));
|
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(1));
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetVFlag(result.overflow);
|
ir.SetCFlag(result.carry);
|
||||||
|
ir.SetVFlag(result.overflow);
|
||||||
|
}
|
||||||
return true;
|
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));
|
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(0));
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetVFlag(result.overflow);
|
ir.SetCFlag(result.carry);
|
||||||
|
ir.SetVFlag(result.overflow);
|
||||||
|
}
|
||||||
return true;
|
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));
|
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1));
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetVFlag(result.overflow);
|
ir.SetCFlag(result.carry);
|
||||||
|
ir.SetVFlag(result.overflow);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,8 +127,10 @@ bool ThumbTranslatorVisitor::thumb16_MOV_imm(Reg d, Imm<8> imm8) {
|
||||||
const auto result = ir.Imm32(imm32);
|
const auto result = ir.Imm32(imm32);
|
||||||
|
|
||||||
ir.SetRegister(d, result);
|
ir.SetRegister(d, result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result));
|
ir.SetNFlag(ir.MostSignificantBit(result));
|
||||||
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
|
}
|
||||||
return true;
|
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));
|
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(0));
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetVFlag(result.overflow);
|
ir.SetCFlag(result.carry);
|
||||||
|
ir.SetVFlag(result.overflow);
|
||||||
|
}
|
||||||
return true;
|
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));
|
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1));
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetVFlag(result.overflow);
|
ir.SetCFlag(result.carry);
|
||||||
|
ir.SetVFlag(result.overflow);
|
||||||
|
}
|
||||||
return true;
|
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));
|
const auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m));
|
||||||
|
|
||||||
ir.SetRegister(d, result);
|
ir.SetRegister(d, result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result));
|
ir.SetNFlag(ir.MostSignificantBit(result));
|
||||||
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
|
}
|
||||||
return true;
|
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));
|
const auto result = ir.Eor(ir.GetRegister(n), ir.GetRegister(m));
|
||||||
|
|
||||||
ir.SetRegister(d, result);
|
ir.SetRegister(d, result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result));
|
ir.SetNFlag(ir.MostSignificantBit(result));
|
||||||
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
|
}
|
||||||
return true;
|
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);
|
const auto result_carry = ir.LogicalShiftLeft(ir.GetRegister(n), shift_n, apsr_c);
|
||||||
|
|
||||||
ir.SetRegister(d, result_carry.result);
|
ir.SetRegister(d, result_carry.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result_carry.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result_carry.result));
|
ir.SetNFlag(ir.MostSignificantBit(result_carry.result));
|
||||||
ir.SetCFlag(result_carry.carry);
|
ir.SetZFlag(ir.IsZero(result_carry.result));
|
||||||
|
ir.SetCFlag(result_carry.carry);
|
||||||
|
}
|
||||||
return true;
|
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);
|
const auto result = ir.LogicalShiftRight(ir.GetRegister(n), shift_n, cpsr_c);
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
|
ir.SetCFlag(result.carry);
|
||||||
|
}
|
||||||
return true;
|
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);
|
const auto result = ir.ArithmeticShiftRight(ir.GetRegister(n), shift_n, cpsr_c);
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
|
ir.SetCFlag(result.carry);
|
||||||
|
}
|
||||||
return true;
|
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);
|
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), aspr_c);
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetVFlag(result.overflow);
|
ir.SetCFlag(result.carry);
|
||||||
|
ir.SetVFlag(result.overflow);
|
||||||
|
}
|
||||||
return true;
|
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);
|
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), aspr_c);
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetVFlag(result.overflow);
|
ir.SetCFlag(result.carry);
|
||||||
|
ir.SetVFlag(result.overflow);
|
||||||
|
}
|
||||||
return true;
|
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);
|
const auto result = ir.RotateRight(ir.GetRegister(n), shift_n, cpsr_c);
|
||||||
|
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
|
ir.SetCFlag(result.carry);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,10 +329,12 @@ bool ThumbTranslatorVisitor::thumb16_TST_reg(Reg m, Reg n) {
|
||||||
bool ThumbTranslatorVisitor::thumb16_RSB_imm(Reg n, Reg d) {
|
bool ThumbTranslatorVisitor::thumb16_RSB_imm(Reg n, Reg d) {
|
||||||
const auto result = ir.SubWithCarry(ir.Imm32(0), ir.GetRegister(n), ir.Imm1(1));
|
const auto result = ir.SubWithCarry(ir.Imm32(0), ir.GetRegister(n), ir.Imm1(1));
|
||||||
ir.SetRegister(d, result.result);
|
ir.SetRegister(d, result.result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result.result));
|
ir.SetNFlag(ir.MostSignificantBit(result.result));
|
||||||
ir.SetCFlag(result.carry);
|
ir.SetZFlag(ir.IsZero(result.result));
|
||||||
ir.SetVFlag(result.overflow);
|
ir.SetCFlag(result.carry);
|
||||||
|
ir.SetVFlag(result.overflow);
|
||||||
|
}
|
||||||
return true;
|
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));
|
const auto result = ir.Or(ir.GetRegister(m), ir.GetRegister(n));
|
||||||
|
|
||||||
ir.SetRegister(d, result);
|
ir.SetRegister(d, result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result));
|
ir.SetNFlag(ir.MostSignificantBit(result));
|
||||||
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
|
}
|
||||||
return true;
|
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));
|
const auto result = ir.Mul(ir.GetRegister(m), ir.GetRegister(n));
|
||||||
|
|
||||||
ir.SetRegister(d, result);
|
ir.SetRegister(d, result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result));
|
ir.SetNFlag(ir.MostSignificantBit(result));
|
||||||
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
|
}
|
||||||
return true;
|
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)));
|
const auto result = ir.And(ir.GetRegister(n), ir.Not(ir.GetRegister(m)));
|
||||||
|
|
||||||
ir.SetRegister(d, result);
|
ir.SetRegister(d, result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result));
|
ir.SetNFlag(ir.MostSignificantBit(result));
|
||||||
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,9 +407,12 @@ bool ThumbTranslatorVisitor::thumb16_BIC_reg(Reg m, Reg d_n) {
|
||||||
// Rd cannot encode R15.
|
// Rd cannot encode R15.
|
||||||
bool ThumbTranslatorVisitor::thumb16_MVN_reg(Reg m, Reg d) {
|
bool ThumbTranslatorVisitor::thumb16_MVN_reg(Reg m, Reg d) {
|
||||||
const auto result = ir.Not(ir.GetRegister(m));
|
const auto result = ir.Not(ir.GetRegister(m));
|
||||||
|
|
||||||
ir.SetRegister(d, result);
|
ir.SetRegister(d, result);
|
||||||
ir.SetNFlag(ir.MostSignificantBit(result));
|
if (!ir.current_location.IT().IsInITBlock()) {
|
||||||
ir.SetZFlag(ir.IsZero(result));
|
ir.SetNFlag(ir.MostSignificantBit(result));
|
||||||
|
ir.SetZFlag(ir.IsZero(result));
|
||||||
|
}
|
||||||
return true;
|
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) {
|
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 d_n = d_n_hi ? (d_n_lo + 8) : d_n_lo;
|
||||||
const Reg n = d_n;
|
const Reg n = d_n;
|
||||||
|
const Reg d = d_n;
|
||||||
if (n == Reg::PC && m == Reg::PC) {
|
if (n == Reg::PC && m == Reg::PC) {
|
||||||
return UnpredictableInstruction();
|
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));
|
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0));
|
||||||
if (d == Reg::PC) {
|
if (d == Reg::PC) {
|
||||||
|
ir.UpdateUpperLocationDescriptor();
|
||||||
ir.ALUWritePC(result.result);
|
ir.ALUWritePC(result.result);
|
||||||
// Return to dispatch as we can't predict what PC is going to be. Stop compilation.
|
// Return to dispatch as we can't predict what PC is going to be. Stop compilation.
|
||||||
ir.SetTerm(IR::Term::FastDispatchHint{});
|
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>
|
// MOV <Rd>, <Rm>
|
||||||
bool ThumbTranslatorVisitor::thumb16_MOV_reg(bool d_hi, Reg m, Reg d_lo) {
|
bool ThumbTranslatorVisitor::thumb16_MOV_reg(bool d_hi, Reg m, Reg d_lo) {
|
||||||
const Reg d = d_hi ? (d_lo + 8) : 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);
|
const auto result = ir.GetRegister(m);
|
||||||
|
|
||||||
if (d == Reg::PC) {
|
if (d == Reg::PC) {
|
||||||
|
ir.UpdateUpperLocationDescriptor();
|
||||||
ir.ALUWritePC(result);
|
ir.ALUWritePC(result);
|
||||||
ir.SetTerm(IR::Term::FastDispatchHint{});
|
ir.SetTerm(IR::Term::FastDispatchHint{});
|
||||||
return false;
|
return false;
|
||||||
|
@ -635,11 +697,6 @@ bool ThumbTranslatorVisitor::thumb16_SUB_sp(Imm<7> imm7) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOP<c>
|
|
||||||
bool ThumbTranslatorVisitor::thumb16_NOP() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SEV<c>
|
// SEV<c>
|
||||||
bool ThumbTranslatorVisitor::thumb16_SEV() {
|
bool ThumbTranslatorVisitor::thumb16_SEV() {
|
||||||
if (!options.hook_hint_instructions) {
|
if (!options.hook_hint_instructions) {
|
||||||
|
@ -680,6 +737,26 @@ bool ThumbTranslatorVisitor::thumb16_YIELD() {
|
||||||
return RaiseException(Exception::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>
|
// SXTH <Rd>, <Rm>
|
||||||
// Rd cannot encode R15.
|
// Rd cannot encode R15.
|
||||||
bool ThumbTranslatorVisitor::thumb16_SXTH(Reg m, Reg d) {
|
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)) {
|
if (Common::Bit<15>(reg_list)) {
|
||||||
// TODO(optimization): Possible location for an RSB pop.
|
// TODO(optimization): Possible location for an RSB pop.
|
||||||
const auto data = ir.ReadMemory32(address);
|
const auto data = ir.ReadMemory32(address);
|
||||||
|
ir.UpdateUpperLocationDescriptor();
|
||||||
ir.LoadWritePC(data);
|
ir.LoadWritePC(data);
|
||||||
address = ir.Add(address, ir.Imm32(4));
|
address = ir.Add(address, ir.Imm32(4));
|
||||||
ir.SetRegister(Reg::SP, address);
|
ir.SetRegister(Reg::SP, address);
|
||||||
|
@ -774,6 +852,10 @@ bool ThumbTranslatorVisitor::thumb16_POP(bool P, RegList reg_list) {
|
||||||
|
|
||||||
// SETEND <endianness>
|
// SETEND <endianness>
|
||||||
bool ThumbTranslatorVisitor::thumb16_SETEND(bool E) {
|
bool ThumbTranslatorVisitor::thumb16_SETEND(bool E) {
|
||||||
|
if (ir.current_location.IT().IsInITBlock()) {
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
if (E == ir.current_location.EFlag()) {
|
if (E == ir.current_location.EFlag()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -822,6 +904,8 @@ bool ThumbTranslatorVisitor::thumb16_REVSH(Reg m, Reg d) {
|
||||||
// BKPT #<imm8>
|
// BKPT #<imm8>
|
||||||
bool ThumbTranslatorVisitor::thumb16_BKPT([[maybe_unused]] Imm<8> imm8) {
|
bool ThumbTranslatorVisitor::thumb16_BKPT([[maybe_unused]] Imm<8> imm8) {
|
||||||
ir.ExceptionRaised(Exception::Breakpoint);
|
ir.ExceptionRaised(Exception::Breakpoint);
|
||||||
|
ir.UpdateUpperLocationDescriptor();
|
||||||
|
ir.LoadWritePC(ir.Imm32(ir.current_location.PC()));
|
||||||
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
|
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -873,15 +957,18 @@ bool ThumbTranslatorVisitor::thumb16_LDMIA(Reg n, RegList reg_list) {
|
||||||
|
|
||||||
// CB{N}Z <Rn>, <label>
|
// CB{N}Z <Rn>, <label>
|
||||||
bool ThumbTranslatorVisitor::thumb16_CBZ_CBNZ(bool nonzero, Imm<1> i, Imm<5> imm5, Reg n) {
|
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 u32 imm = concatenate(i, imm5, Imm<1>{0}).ZeroExtend();
|
||||||
const IR::U32 rn = ir.GetRegister(n);
|
const IR::U32 rn = ir.GetRegister(n);
|
||||||
|
|
||||||
ir.SetCheckBit(ir.IsZero(rn));
|
ir.SetCheckBit(ir.IsZero(rn));
|
||||||
|
|
||||||
const auto [cond_pass, cond_fail] = [this, imm, nonzero] {
|
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 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) {
|
if (nonzero) {
|
||||||
return std::make_pair(skip, branch);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -900,6 +987,11 @@ bool ThumbTranslatorVisitor::thumb16_UDF() {
|
||||||
|
|
||||||
// BX <Rm>
|
// BX <Rm>
|
||||||
bool ThumbTranslatorVisitor::thumb16_BX(Reg m) {
|
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));
|
ir.BXWritePC(ir.GetRegister(m));
|
||||||
if (m == Reg::R14)
|
if (m == Reg::R14)
|
||||||
ir.SetTerm(IR::Term::PopRSBHint{});
|
ir.SetTerm(IR::Term::PopRSBHint{});
|
||||||
|
@ -910,7 +1002,12 @@ bool ThumbTranslatorVisitor::thumb16_BX(Reg m) {
|
||||||
|
|
||||||
// BLX <Rm>
|
// BLX <Rm>
|
||||||
bool ThumbTranslatorVisitor::thumb16_BLX_reg(Reg m) {
|
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.PushRSB(ir.current_location.AdvancePC(2));
|
||||||
|
ir.UpdateUpperLocationDescriptor();
|
||||||
ir.BXWritePC(ir.GetRegister(m));
|
ir.BXWritePC(ir.GetRegister(m));
|
||||||
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 2) | 1));
|
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 2) | 1));
|
||||||
ir.SetTerm(IR::Term::FastDispatchHint{});
|
ir.SetTerm(IR::Term::FastDispatchHint{});
|
||||||
|
@ -920,6 +1017,7 @@ bool ThumbTranslatorVisitor::thumb16_BLX_reg(Reg m) {
|
||||||
// SVC #<imm8>
|
// SVC #<imm8>
|
||||||
bool ThumbTranslatorVisitor::thumb16_SVC(Imm<8> imm8) {
|
bool ThumbTranslatorVisitor::thumb16_SVC(Imm<8> imm8) {
|
||||||
const u32 imm32 = imm8.ZeroExtend();
|
const u32 imm32 = imm8.ZeroExtend();
|
||||||
|
ir.UpdateUpperLocationDescriptor();
|
||||||
ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 2));
|
ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 2));
|
||||||
ir.PushRSB(ir.current_location.AdvancePC(2));
|
ir.PushRSB(ir.current_location.AdvancePC(2));
|
||||||
ir.CallSupervisor(ir.Imm32(imm32));
|
ir.CallSupervisor(ir.Imm32(imm32));
|
||||||
|
@ -929,13 +1027,17 @@ bool ThumbTranslatorVisitor::thumb16_SVC(Imm<8> imm8) {
|
||||||
|
|
||||||
// B<cond> <label>
|
// B<cond> <label>
|
||||||
bool ThumbTranslatorVisitor::thumb16_B_t1(Cond cond, Imm<8> imm8) {
|
bool ThumbTranslatorVisitor::thumb16_B_t1(Cond cond, Imm<8> imm8) {
|
||||||
|
if (ir.current_location.IT().IsInITBlock()) {
|
||||||
|
return UnpredictableInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
if (cond == Cond::AL) {
|
if (cond == Cond::AL) {
|
||||||
return thumb16_UDF();
|
return thumb16_UDF();
|
||||||
}
|
}
|
||||||
|
|
||||||
const s32 imm32 = static_cast<s32>((imm8.SignExtend<u32>() << 1) + 4);
|
const s32 imm32 = static_cast<s32>((imm8.SignExtend<u32>() << 1) + 4);
|
||||||
const auto then_location = ir.current_location.AdvancePC(imm32);
|
const auto then_location = ir.current_location.AdvancePC(imm32).AdvanceIT();
|
||||||
const auto else_location = ir.current_location.AdvancePC(2);
|
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}});
|
ir.SetTerm(IR::Term::If{cond, IR::Term::LinkBlock{then_location}, IR::Term::LinkBlock{else_location}});
|
||||||
return false;
|
return false;
|
||||||
|
@ -943,8 +1045,12 @@ bool ThumbTranslatorVisitor::thumb16_B_t1(Cond cond, Imm<8> imm8) {
|
||||||
|
|
||||||
// B <label>
|
// B <label>
|
||||||
bool ThumbTranslatorVisitor::thumb16_B_t2(Imm<11> imm11) {
|
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 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});
|
ir.SetTerm(IR::Term::LinkBlock{next_location});
|
||||||
return false;
|
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/imm.h"
|
||||||
#include "frontend/A32/ir_emitter.h"
|
#include "frontend/A32/ir_emitter.h"
|
||||||
#include "frontend/A32/location_descriptor.h"
|
#include "frontend/A32/location_descriptor.h"
|
||||||
|
#include "frontend/A32/translate/conditional_state.h"
|
||||||
#include "frontend/A32/translate/translate.h"
|
#include "frontend/A32/translate/translate.h"
|
||||||
#include "frontend/A32/types.h"
|
#include "frontend/A32/types.h"
|
||||||
|
|
||||||
|
@ -17,21 +18,10 @@ namespace Dynarmic::A32 {
|
||||||
|
|
||||||
enum class Exception;
|
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 {
|
struct ArmTranslatorVisitor final {
|
||||||
using instruction_return_type = bool;
|
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");
|
ASSERT_MSG(!descriptor.TFlag(), "The processor must be in Arm mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "frontend/imm.h"
|
#include "frontend/imm.h"
|
||||||
#include "frontend/A32/ir_emitter.h"
|
#include "frontend/A32/ir_emitter.h"
|
||||||
#include "frontend/A32/location_descriptor.h"
|
#include "frontend/A32/location_descriptor.h"
|
||||||
|
#include "frontend/A32/translate/conditional_state.h"
|
||||||
#include "frontend/A32/translate/translate.h"
|
#include "frontend/A32/translate/translate.h"
|
||||||
#include "frontend/A32/types.h"
|
#include "frontend/A32/types.h"
|
||||||
|
|
||||||
|
@ -19,13 +20,48 @@ enum class Exception;
|
||||||
struct ThumbTranslatorVisitor final {
|
struct ThumbTranslatorVisitor final {
|
||||||
using instruction_return_type = bool;
|
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");
|
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;
|
A32::IREmitter ir;
|
||||||
|
ConditionalState cond_state = ConditionalState::None;
|
||||||
TranslationOptions options;
|
TranslationOptions options;
|
||||||
|
|
||||||
|
bool ConditionPassed(bool is_thumb_16);
|
||||||
|
|
||||||
bool InterpretThisInstruction();
|
bool InterpretThisInstruction();
|
||||||
bool UnpredictableInstruction();
|
bool UnpredictableInstruction();
|
||||||
bool UndefinedInstruction();
|
bool UndefinedInstruction();
|
||||||
|
@ -83,12 +119,13 @@ struct ThumbTranslatorVisitor final {
|
||||||
bool thumb16_ADD_sp_t1(Reg d, Imm<8> imm8);
|
bool thumb16_ADD_sp_t1(Reg d, Imm<8> imm8);
|
||||||
bool thumb16_ADD_sp_t2(Imm<7> imm7);
|
bool thumb16_ADD_sp_t2(Imm<7> imm7);
|
||||||
bool thumb16_SUB_sp(Imm<7> imm7);
|
bool thumb16_SUB_sp(Imm<7> imm7);
|
||||||
bool thumb16_NOP();
|
|
||||||
bool thumb16_SEV();
|
bool thumb16_SEV();
|
||||||
bool thumb16_SEVL();
|
bool thumb16_SEVL();
|
||||||
bool thumb16_WFE();
|
bool thumb16_WFE();
|
||||||
bool thumb16_WFI();
|
bool thumb16_WFI();
|
||||||
bool thumb16_YIELD();
|
bool thumb16_YIELD();
|
||||||
|
bool thumb16_NOP();
|
||||||
|
bool thumb16_IT(Imm<8> imm8);
|
||||||
bool thumb16_SXTH(Reg m, Reg d);
|
bool thumb16_SXTH(Reg m, Reg d);
|
||||||
bool thumb16_SXTB(Reg m, Reg d);
|
bool thumb16_SXTB(Reg m, Reg d);
|
||||||
bool thumb16_UXTH(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_t1(Cond cond, Imm<8> imm8);
|
||||||
bool thumb16_B_t2(Imm<11> imm11);
|
bool thumb16_B_t2(Imm<11> imm11);
|
||||||
|
|
||||||
// thumb32
|
// thumb32 data processing (modified immediate) instructions
|
||||||
bool thumb32_BL_imm(Imm<11> hi, Imm<11> lo);
|
bool thumb32_TST_imm(Imm<1> i, Reg n, Imm<3> imm3, Imm<8> imm8);
|
||||||
bool thumb32_BLX_imm(Imm<11> hi, Imm<11> lo);
|
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();
|
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
|
} // namespace Dynarmic::A32
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
#include <dynarmic/A32/arch_version.h>
|
||||||
|
|
||||||
namespace Dynarmic::IR {
|
namespace Dynarmic::IR {
|
||||||
class Block;
|
class Block;
|
||||||
} // namespace Dynarmic::IR
|
} // namespace Dynarmic::IR
|
||||||
|
@ -17,6 +19,8 @@ class LocationDescriptor;
|
||||||
using MemoryReadCodeFuncType = std::function<u32(u32 vaddr)>;
|
using MemoryReadCodeFuncType = std::function<u32(u32 vaddr)>;
|
||||||
|
|
||||||
struct TranslationOptions {
|
struct TranslationOptions {
|
||||||
|
ArchVersion arch_version;
|
||||||
|
|
||||||
/// This changes what IR we emit when we translate an unpredictable instruction.
|
/// 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 false, the ExceptionRaised IR instruction is emitted.
|
||||||
/// If this is true, we define some behaviour for some instructions.
|
/// If this is true, we define some behaviour for some instructions.
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
* SPDX-License-Identifier: 0BSD
|
* SPDX-License-Identifier: 0BSD
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include <dynarmic/A32/config.h>
|
#include <dynarmic/A32/config.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
@ -12,6 +10,7 @@
|
||||||
#include "frontend/A32/decoder/asimd.h"
|
#include "frontend/A32/decoder/asimd.h"
|
||||||
#include "frontend/A32/decoder/vfp.h"
|
#include "frontend/A32/decoder/vfp.h"
|
||||||
#include "frontend/A32/location_descriptor.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/impl/translate_arm.h"
|
||||||
#include "frontend/A32/translate/translate.h"
|
#include "frontend/A32/translate/translate.h"
|
||||||
#include "frontend/A32/types.h"
|
#include "frontend/A32/types.h"
|
||||||
|
@ -19,16 +18,6 @@
|
||||||
|
|
||||||
namespace Dynarmic::A32 {
|
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) {
|
IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) {
|
||||||
const bool single_step = descriptor.SingleStepping();
|
const bool single_step = descriptor.SingleStepping();
|
||||||
|
|
||||||
|
@ -102,53 +91,7 @@ bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descript
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArmTranslatorVisitor::ConditionPassed(Cond cond) {
|
bool ArmTranslatorVisitor::ConditionPassed(Cond cond) {
|
||||||
ASSERT_MSG(cond_state != ConditionalState::Break,
|
return IsConditionPassed(cond, cond_state, ir, 4);
|
||||||
"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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArmTranslatorVisitor::InterpretThisInstruction() {
|
bool ArmTranslatorVisitor::InterpretThisInstruction() {
|
||||||
|
|
|
@ -9,13 +9,14 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/bit_util.h"
|
#include "common/bit_util.h"
|
||||||
#include "frontend/imm.h"
|
|
||||||
#include "frontend/A32/decoder/thumb16.h"
|
#include "frontend/A32/decoder/thumb16.h"
|
||||||
#include "frontend/A32/decoder/thumb32.h"
|
#include "frontend/A32/decoder/thumb32.h"
|
||||||
#include "frontend/A32/ir_emitter.h"
|
#include "frontend/A32/ir_emitter.h"
|
||||||
#include "frontend/A32/location_descriptor.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/impl/translate_thumb.h"
|
||||||
#include "frontend/A32/translate/translate.h"
|
#include "frontend/A32/translate/translate.h"
|
||||||
|
#include "frontend/imm.h"
|
||||||
|
|
||||||
namespace Dynarmic::A32 {
|
namespace Dynarmic::A32 {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -25,7 +26,17 @@ enum class ThumbInstSize {
|
||||||
};
|
};
|
||||||
|
|
||||||
bool IsThumb16(u16 first_part) {
|
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) {
|
std::tuple<u32, ThumbInstSize> ReadThumbInstruction(u32 arm_pc, MemoryReadCodeFuncType memory_read_code) {
|
||||||
|
@ -64,30 +75,44 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType m
|
||||||
do {
|
do {
|
||||||
const u32 arm_pc = visitor.ir.current_location.PC();
|
const u32 arm_pc = visitor.ir.current_location.PC();
|
||||||
const auto [thumb_instruction, inst_size] = ReadThumbInstruction(arm_pc, memory_read_code);
|
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 (IsUnconditionalInstruction(is_thumb_16, thumb_instruction) || visitor.ConditionPassed(is_thumb_16)) {
|
||||||
if (const auto decoder = DecodeThumb16<ThumbTranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
|
if (is_thumb_16) {
|
||||||
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
|
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 {
|
} else {
|
||||||
should_continue = visitor.thumb16_UDF();
|
if (const auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction)) {
|
||||||
}
|
should_continue = decoder->get().call(visitor, thumb_instruction);
|
||||||
} else {
|
} else {
|
||||||
if (const auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction)) {
|
should_continue = visitor.thumb32_UDF();
|
||||||
should_continue = decoder->get().call(visitor, thumb_instruction);
|
}
|
||||||
} else {
|
|
||||||
should_continue = visitor.thumb32_UDF();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const s32 advance_pc = (inst_size == ThumbInstSize::Thumb16) ? 2 : 4;
|
if (visitor.cond_state == ConditionalState::Break) {
|
||||||
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(advance_pc);
|
break;
|
||||||
block.CycleCount()++;
|
}
|
||||||
} while (should_continue && !single_step);
|
|
||||||
|
|
||||||
if (single_step && should_continue) {
|
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(is_thumb_16 ? 2 : 4).AdvanceIT();
|
||||||
visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location});
|
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);
|
block.SetEndLocation(visitor.ir.current_location);
|
||||||
|
|
||||||
return block;
|
return block;
|
||||||
|
@ -105,6 +130,7 @@ bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descri
|
||||||
should_continue = visitor.thumb16_UDF();
|
should_continue = visitor.thumb16_UDF();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
thumb_instruction = Common::SwapHalves32(thumb_instruction);
|
||||||
if (const auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction)) {
|
if (const auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction)) {
|
||||||
should_continue = decoder->get().call(visitor, thumb_instruction);
|
should_continue = decoder->get().call(visitor, thumb_instruction);
|
||||||
} else {
|
} else {
|
||||||
|
@ -121,6 +147,11 @@ bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descri
|
||||||
return should_continue;
|
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() {
|
bool ThumbTranslatorVisitor::InterpretThisInstruction() {
|
||||||
ir.SetTerm(IR::Term::Interpret(ir.current_location));
|
ir.SetTerm(IR::Term::Interpret(ir.current_location));
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -108,6 +108,11 @@ INST(DC_CVAU, "DC CVAU", "11010
|
||||||
INST(DC_CVAP, "DC CVAP", "110101010000101101111100001ttttt")
|
INST(DC_CVAP, "DC CVAP", "110101010000101101111100001ttttt")
|
||||||
INST(DC_CIVAC, "DC CIVAC", "110101010000101101111110001ttttt")
|
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)
|
// Unconditional branch (Register)
|
||||||
INST(BLR, "BLR", "1101011000111111000000nnnnn00000")
|
INST(BLR, "BLR", "1101011000111111000000nnnnn00000")
|
||||||
INST(BR, "BR", "1101011000011111000000nnnnn00000")
|
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);
|
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() {
|
void IREmitter::DataSynchronizationBarrier() {
|
||||||
Inst(Opcode::A64DataSynchronizationBarrier);
|
Inst(Opcode::A64DataSynchronizationBarrier);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ public:
|
||||||
void CallSupervisor(u32 imm);
|
void CallSupervisor(u32 imm);
|
||||||
void ExceptionRaised(Exception exception);
|
void ExceptionRaised(Exception exception);
|
||||||
void DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value);
|
void DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value);
|
||||||
|
void InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value);
|
||||||
void DataSynchronizationBarrier();
|
void DataSynchronizationBarrier();
|
||||||
void DataMemoryBarrier();
|
void DataMemoryBarrier();
|
||||||
void InstructionSynchronizationBarrier();
|
void InstructionSynchronizationBarrier();
|
||||||
|
|
|
@ -174,6 +174,11 @@ struct TranslatorVisitor final {
|
||||||
bool DC_CVAP(Reg Rt);
|
bool DC_CVAP(Reg Rt);
|
||||||
bool DC_CIVAC(Reg Rt);
|
bool DC_CIVAC(Reg Rt);
|
||||||
|
|
||||||
|
// SYS: Instruction Cache
|
||||||
|
bool IC_IALLU();
|
||||||
|
bool IC_IALLUIS();
|
||||||
|
bool IC_IVAU(Reg Rt);
|
||||||
|
|
||||||
// Unconditional branch (Register)
|
// Unconditional branch (Register)
|
||||||
bool BR(Reg Rn);
|
bool BR(Reg Rn);
|
||||||
bool BRA(bool Z, bool M, Reg Rn, Reg Rm);
|
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 {
|
namespace Dynarmic::A64 {
|
||||||
|
|
||||||
// Register encodings used by MRS and MSR.
|
// Register encodings used by MRS and MSR.
|
||||||
|
// Order of fields: op0, CRn, op1, op2, CRm.
|
||||||
enum class SystemRegisterEncoding : u32 {
|
enum class SystemRegisterEncoding : u32 {
|
||||||
// Counter-timer Frequency register
|
// Counter-timer Frequency register
|
||||||
CNTFRQ_EL0 = 0b11'011'1110'0000'000,
|
CNTFRQ_EL0 = 0b11'1110'011'000'0000,
|
||||||
// Counter-timer Physical Count register
|
// Counter-timer Physical Count register
|
||||||
CNTPCT_EL0 = 0b11'011'1110'0000'001,
|
CNTPCT_EL0 = 0b11'1110'011'001'0000,
|
||||||
// Cache Type Register
|
// Cache Type Register
|
||||||
CTR_EL0 = 0b11'011'0000'0000'001,
|
CTR_EL0 = 0b11'0000'011'001'0000,
|
||||||
// Data Cache Zero ID register
|
// Data Cache Zero ID register
|
||||||
DCZID_EL0 = 0b11'011'0000'0000'111,
|
DCZID_EL0 = 0b11'0000'011'111'0000,
|
||||||
// Floating-point Control Register
|
// Floating-point Control Register
|
||||||
FPCR = 0b11'011'0100'0100'000,
|
FPCR = 0b11'0100'011'000'0100,
|
||||||
// Floating-point Status Register
|
// 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
|
// 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
|
// 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) {
|
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) {
|
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) {
|
switch (sys_reg) {
|
||||||
case SystemRegisterEncoding::FPCR:
|
case SystemRegisterEncoding::FPCR:
|
||||||
ir.SetFPCR(X(32, Rt));
|
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:
|
case SystemRegisterEncoding::FPSR:
|
||||||
ir.SetFPSR(X(32, Rt));
|
ir.SetFPSR(X(32, Rt));
|
||||||
return true;
|
return true;
|
||||||
|
case SystemRegisterEncoding::NZCV:
|
||||||
|
ir.SetNZCVRaw(X(32, Rt));
|
||||||
|
return true;
|
||||||
case SystemRegisterEncoding::TPIDR_EL0:
|
case SystemRegisterEncoding::TPIDR_EL0:
|
||||||
ir.SetTPIDR(X(64, Rt));
|
ir.SetTPIDR(X(64, Rt));
|
||||||
return true;
|
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) {
|
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) {
|
switch (sys_reg) {
|
||||||
case SystemRegisterEncoding::CNTFRQ_EL0:
|
case SystemRegisterEncoding::CNTFRQ_EL0:
|
||||||
X(32, Rt, ir.GetCNTFRQ());
|
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:
|
case SystemRegisterEncoding::FPSR:
|
||||||
X(32, Rt, ir.GetFPSR());
|
X(32, Rt, ir.GetFPSR());
|
||||||
return true;
|
return true;
|
||||||
|
case SystemRegisterEncoding::NZCV:
|
||||||
|
X(32, Rt, ir.GetNZCVRaw());
|
||||||
|
return true;
|
||||||
case SystemRegisterEncoding::TPIDR_EL0:
|
case SystemRegisterEncoding::TPIDR_EL0:
|
||||||
X(64, Rt, ir.GetTPIDR());
|
X(64, Rt, ir.GetTPIDR());
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -155,6 +155,7 @@ bool Inst::ReadsFromCPSR() const {
|
||||||
case Opcode::A32GetCFlag:
|
case Opcode::A32GetCFlag:
|
||||||
case Opcode::A32GetVFlag:
|
case Opcode::A32GetVFlag:
|
||||||
case Opcode::A32GetGEFlags:
|
case Opcode::A32GetGEFlags:
|
||||||
|
case Opcode::A32UpdateUpperLocationDescriptor:
|
||||||
case Opcode::A64GetCFlag:
|
case Opcode::A64GetCFlag:
|
||||||
case Opcode::A64GetNZCVRaw:
|
case Opcode::A64GetNZCVRaw:
|
||||||
case Opcode::ConditionalSelect32:
|
case Opcode::ConditionalSelect32:
|
||||||
|
@ -179,6 +180,7 @@ bool Inst::WritesToCPSR() const {
|
||||||
case Opcode::A32OrQFlag:
|
case Opcode::A32OrQFlag:
|
||||||
case Opcode::A32SetGEFlags:
|
case Opcode::A32SetGEFlags:
|
||||||
case Opcode::A32SetGEFlagsCompressed:
|
case Opcode::A32SetGEFlagsCompressed:
|
||||||
|
case Opcode::A32UpdateUpperLocationDescriptor:
|
||||||
case Opcode::A64SetNZCVRaw:
|
case Opcode::A64SetNZCVRaw:
|
||||||
case Opcode::A64SetNZCV:
|
case Opcode::A64SetNZCV:
|
||||||
return true;
|
return true;
|
||||||
|
@ -518,18 +520,19 @@ bool Inst::IsSetCheckBitOperation() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Inst::MayHaveSideEffects() const {
|
bool Inst::MayHaveSideEffects() const {
|
||||||
return op == Opcode::PushRSB ||
|
return op == Opcode::PushRSB ||
|
||||||
op == Opcode::A64DataCacheOperationRaised ||
|
op == Opcode::A64DataCacheOperationRaised ||
|
||||||
IsSetCheckBitOperation() ||
|
op == Opcode::A64InstructionCacheOperationRaised ||
|
||||||
IsBarrier() ||
|
IsSetCheckBitOperation() ||
|
||||||
CausesCPUException() ||
|
IsBarrier() ||
|
||||||
WritesToCoreRegister() ||
|
CausesCPUException() ||
|
||||||
WritesToSystemRegister() ||
|
WritesToCoreRegister() ||
|
||||||
WritesToCPSR() ||
|
WritesToSystemRegister() ||
|
||||||
WritesToFPCR() ||
|
WritesToCPSR() ||
|
||||||
WritesToFPSR() ||
|
WritesToFPCR() ||
|
||||||
AltersExclusiveState() ||
|
WritesToFPSR() ||
|
||||||
IsMemoryWrite() ||
|
AltersExclusiveState() ||
|
||||||
|
IsMemoryWrite() ||
|
||||||
IsCoprocessorInstruction();
|
IsCoprocessorInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ A32OPC(GetGEFlags, U32,
|
||||||
A32OPC(SetGEFlags, Void, U32 )
|
A32OPC(SetGEFlags, Void, U32 )
|
||||||
A32OPC(SetGEFlagsCompressed, Void, U32 )
|
A32OPC(SetGEFlagsCompressed, Void, U32 )
|
||||||
A32OPC(BXWritePC, Void, U32 )
|
A32OPC(BXWritePC, Void, U32 )
|
||||||
|
A32OPC(UpdateUpperLocationDescriptor, Void, )
|
||||||
A32OPC(CallSupervisor, Void, U32 )
|
A32OPC(CallSupervisor, Void, U32 )
|
||||||
A32OPC(ExceptionRaised, Void, U32, U64 )
|
A32OPC(ExceptionRaised, Void, U32, U64 )
|
||||||
A32OPC(DataSynchronizationBarrier, Void, )
|
A32OPC(DataSynchronizationBarrier, Void, )
|
||||||
|
@ -68,6 +69,7 @@ A64OPC(SetPC, Void, U64
|
||||||
A64OPC(CallSupervisor, Void, U32 )
|
A64OPC(CallSupervisor, Void, U32 )
|
||||||
A64OPC(ExceptionRaised, Void, U64, U64 )
|
A64OPC(ExceptionRaised, Void, U64, U64 )
|
||||||
A64OPC(DataCacheOperationRaised, Void, U64, U64 )
|
A64OPC(DataCacheOperationRaised, Void, U64, U64 )
|
||||||
|
A64OPC(InstructionCacheOperationRaised, Void, U64, U64 )
|
||||||
A64OPC(DataSynchronizationBarrier, Void, )
|
A64OPC(DataSynchronizationBarrier, Void, )
|
||||||
A64OPC(DataMemoryBarrier, Void, )
|
A64OPC(DataMemoryBarrier, Void, )
|
||||||
A64OPC(InstructionSynchronizationBarrier, Void, )
|
A64OPC(InstructionSynchronizationBarrier, Void, )
|
||||||
|
|
|
@ -138,13 +138,13 @@ void FoldByteReverse(IR::Inst& inst, Op op) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op == Op::ByteReverseWord) {
|
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});
|
inst.ReplaceUsesWith(IR::Value{result});
|
||||||
} else if (op == Op::ByteReverseHalf) {
|
} 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});
|
inst.ReplaceUsesWith(IR::Value{result});
|
||||||
} else {
|
} else {
|
||||||
const u64 result = Common::Swap64(operand.GetImmediateAsU64());
|
const u64 result = Common::SwapBytes64(operand.GetImmediateAsU64());
|
||||||
inst.ReplaceUsesWith(IR::Value{result});
|
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/common_types.h"
|
||||||
#include "common/fp/fpcr.h"
|
#include "common/fp/fpcr.h"
|
||||||
#include "common/fp/fpsr.h"
|
#include "common/fp/fpsr.h"
|
||||||
|
#include "common/llvm_disassemble.h"
|
||||||
#include "common/scope_exit.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/location_descriptor.h"
|
||||||
#include "frontend/A32/translate/translate.h"
|
#include "frontend/A32/translate/translate.h"
|
||||||
#include "frontend/A32/types.h"
|
#include "frontend/A32/types.h"
|
||||||
|
@ -36,8 +37,8 @@
|
||||||
namespace {
|
namespace {
|
||||||
using namespace Dynarmic;
|
using namespace Dynarmic;
|
||||||
|
|
||||||
bool ShouldTestInst(u32 instruction, u32 pc, bool is_last_inst) {
|
bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A32::ITState it_state = {}) {
|
||||||
const A32::LocationDescriptor location{pc, {}, {}};
|
const A32::LocationDescriptor location = A32::LocationDescriptor{pc, {}, {}}.SetTFlag(is_thumb).SetIT(it_state);
|
||||||
IR::Block block{location};
|
IR::Block block{location};
|
||||||
const bool should_continue = A32::TranslateSingleInstruction(block, location, instruction);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GenRandomInst(u32 pc, bool is_last_inst) {
|
u32 GenRandomArmInst(u32 pc, bool is_last_inst) {
|
||||||
static const struct InstructionGeneratorInfo {
|
static const struct InstructionGeneratorInfo {
|
||||||
std::vector<InstructionGenerator> generators;
|
std::vector<InstructionGenerator> generators;
|
||||||
std::vector<InstructionGenerator> invalid;
|
std::vector<InstructionGenerator> invalid;
|
||||||
|
@ -139,13 +140,59 @@ u32 GenRandomInst(u32 pc, bool is_last_inst) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShouldTestInst(inst, pc, is_last_inst)) {
|
if (ShouldTestInst(inst, pc, false, is_last_inst)) {
|
||||||
return 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;
|
Dynarmic::A32::UserConfig user_config;
|
||||||
user_config.optimizations &= ~OptimizationFlag::FastDispatch;
|
user_config.optimizations &= ~OptimizationFlag::FastDispatch;
|
||||||
user_config.callbacks = &testenv;
|
user_config.callbacks = &testenv;
|
||||||
|
@ -153,22 +200,30 @@ Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv& testenv) {
|
||||||
return user_config;
|
return user_config;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn<ArmTestEnv>& uni,
|
template <typename TestEnv>
|
||||||
ArmTestEnv& jit_env, ArmTestEnv& uni_env,
|
static void RunTestInstance(Dynarmic::A32::Jit& jit,
|
||||||
const A32Unicorn<ArmTestEnv>::RegisterArray& regs,
|
A32Unicorn<TestEnv>& uni,
|
||||||
const A32Unicorn<ArmTestEnv>::ExtRegArray& vecs,
|
TestEnv& jit_env,
|
||||||
const std::vector<u32>& instructions, const u32 cpsr, const u32 fpscr) {
|
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 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());
|
const u32 code_mem_size = num_words + static_cast<u32>(instructions.size());
|
||||||
|
|
||||||
jit_env.code_mem.resize(code_mem_size + 1);
|
jit_env.code_mem.resize(code_mem_size);
|
||||||
uni_env.code_mem.resize(code_mem_size + 1);
|
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(), jit_env.code_mem.begin() + num_words);
|
||||||
std::copy(instructions.begin(), instructions.end(), uni_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 .
|
jit_env.PadCodeMem();
|
||||||
uni_env.code_mem.back() = 0xEAFFFFFE; // B .
|
uni_env.PadCodeMem();
|
||||||
jit_env.modified_memory.clear();
|
jit_env.modified_memory.clear();
|
||||||
uni_env.modified_memory.clear();
|
uni_env.modified_memory.clear();
|
||||||
jit_env.interrupts.clear();
|
jit_env.interrupts.clear();
|
||||||
|
@ -186,18 +241,15 @@ static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn<ArmTestEnv>& uni
|
||||||
uni.SetCpsr(cpsr);
|
uni.SetCpsr(cpsr);
|
||||||
uni.ClearPageCache();
|
uni.ClearPageCache();
|
||||||
|
|
||||||
jit_env.ticks_left = instructions.size();
|
jit_env.ticks_left = ticks_left;
|
||||||
jit.Run();
|
jit.Run();
|
||||||
|
|
||||||
uni_env.ticks_left = instructions.size();
|
uni_env.ticks_left = instructions.size(); // Unicorn counts thumb instructions weirdly.
|
||||||
uni.Run();
|
uni.Run();
|
||||||
|
|
||||||
SCOPE_FAIL {
|
SCOPE_FAIL {
|
||||||
fmt::print("Instruction Listing:\n");
|
fmt::print("Instruction Listing:\n");
|
||||||
for (u32 instruction : instructions) {
|
fmt::print("{}\n", Common::DisassembleAArch32(std::is_same_v<TestEnv, ThumbTestEnv>, initial_pc, (const u8*)instructions.data(), instructions.size() * sizeof(instructions[0])));
|
||||||
fmt::print("{:08x} {}\n", instruction, A32::DisassembleArm(instruction));
|
|
||||||
}
|
|
||||||
fmt::print("\n");
|
|
||||||
|
|
||||||
fmt::print("Initial register listing:\n");
|
fmt::print("Initial register listing:\n");
|
||||||
for (size_t i = 0; i < regs.size(); ++i) {
|
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
|
} // Anonymous namespace
|
||||||
|
|
||||||
TEST_CASE("A32: Single random instruction", "[arm]") {
|
TEST_CASE("A32: Single random arm instruction", "[arm]") {
|
||||||
ArmTestEnv jit_env{};
|
ArmTestEnv jit_env{};
|
||||||
ArmTestEnv uni_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(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
|
||||||
std::generate(ext_reg.begin(), ext_reg.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 start_address = 100;
|
||||||
const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10;
|
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]);
|
INFO("Instruction: 0x" << std::hex << instructions[0]);
|
||||||
|
|
||||||
regs[15] = start_address;
|
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 jit_env{};
|
||||||
ArmTestEnv uni_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(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
|
||||||
std::generate(ext_reg.begin(), ext_reg.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[0] = GenRandomArmInst(0, false);
|
||||||
instructions[1] = GenRandomInst(4, false);
|
instructions[1] = GenRandomArmInst(4, false);
|
||||||
instructions[2] = GenRandomInst(8, false);
|
instructions[2] = GenRandomArmInst(8, false);
|
||||||
instructions[3] = GenRandomInst(12, false);
|
instructions[3] = GenRandomArmInst(12, false);
|
||||||
instructions[4] = GenRandomInst(16, true);
|
instructions[4] = GenRandomArmInst(16, true);
|
||||||
|
|
||||||
const u32 start_address = 100;
|
const u32 start_address = 100;
|
||||||
const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10;
|
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]);
|
INFO("Instruction 5: 0x" << std::hex << instructions[4]);
|
||||||
|
|
||||||
regs[15] = start_address;
|
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 jit_env{};
|
||||||
ArmTestEnv uni_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)); });
|
std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
|
||||||
|
|
||||||
for (size_t j = 0; j < instruction_count; ++j) {
|
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;
|
const u64 start_address = 100;
|
||||||
|
@ -369,6 +421,116 @@ TEST_CASE("A32: Large random block", "[arm]") {
|
||||||
const u32 fpcr = RandomFpcr();
|
const u32 fpcr = RandomFpcr();
|
||||||
|
|
||||||
regs[15] = start_address;
|
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 <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <string_view>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
@ -41,11 +42,13 @@ using WriteRecords = std::map<u32, u8>;
|
||||||
|
|
||||||
struct ThumbInstGen final {
|
struct ThumbInstGen final {
|
||||||
public:
|
public:
|
||||||
ThumbInstGen(const char* format, std::function<bool(u16)> is_valid = [](u16){ return true; }) : is_valid(is_valid) {
|
ThumbInstGen(std::string_view format, std::function<bool(u32)> is_valid = [](u32){ return true; }) : is_valid(is_valid) {
|
||||||
REQUIRE(strlen(format) == 16);
|
REQUIRE((format.size() == 16 || format.size() == 32));
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++) {
|
const auto bit_size = format.size();
|
||||||
const u16 bit = 1 << (15 - i);
|
|
||||||
|
for (size_t i = 0; i < bit_size; i++) {
|
||||||
|
const u32 bit = 1U << (bit_size - 1 - i);
|
||||||
switch (format[i]) {
|
switch (format[i]) {
|
||||||
case '0':
|
case '0':
|
||||||
mask |= bit;
|
mask |= bit;
|
||||||
|
@ -60,11 +63,25 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u16 Generate() const {
|
|
||||||
u16 inst;
|
u16 Generate16() const {
|
||||||
|
u32 inst;
|
||||||
|
|
||||||
do {
|
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);
|
inst = bits | (random & ~mask);
|
||||||
} while (!is_valid(inst));
|
} while (!is_valid(inst));
|
||||||
|
|
||||||
|
@ -72,10 +89,11 @@ public:
|
||||||
|
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
u16 bits = 0;
|
u32 bits = 0;
|
||||||
u16 mask = 0;
|
u32 mask = 0;
|
||||||
std::function<bool(u16)> is_valid;
|
std::function<bool(u32)> is_valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool DoesBehaviorMatch(const A32Unicorn<ThumbTestEnv>& uni, const A32::Jit& jit,
|
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;
|
ThumbTestEnv test_env;
|
||||||
|
|
||||||
// Prepare memory.
|
// 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 = {
|
const std::array instructions = {
|
||||||
ThumbInstGen("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
|
ThumbInstGen("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
|
||||||
ThumbInstGen("00001xxxxxxxxxxx"), // LSR <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("010000ooooxxxxxx"), // Data Processing
|
||||||
ThumbInstGen("010001000hxxxxxx"), // ADD (high registers)
|
ThumbInstGen("010001000hxxxxxx"), // ADD (high registers)
|
||||||
ThumbInstGen("0100010101xxxxxx", // CMP (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)
|
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("010001100hxxxxxx"), // MOV (high registers)
|
||||||
ThumbInstGen("10110000oxxxxxxx"), // Adjust stack pointer
|
ThumbInstGen("10110000oxxxxxxx"), // Adjust stack pointer
|
||||||
ThumbInstGen("10110010ooxxxxxx"), // SXT/UXT
|
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("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset]
|
||||||
ThumbInstGen("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #]
|
ThumbInstGen("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #]
|
||||||
ThumbInstGen("1011010xxxxxxxxx", // PUSH
|
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)
|
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
|
ThumbInstGen("1100xxxxxxxxxxxx", // STMIA/LDMIA
|
||||||
[](u16 inst) {
|
[](u32 inst) {
|
||||||
// Ensure that the architecturally undefined case of
|
// Ensure that the architecturally undefined case of
|
||||||
// the base register being within the list isn't hit.
|
// the base register being within the list isn't hit.
|
||||||
const u32 rn = Common::Bits<8, 10>(inst);
|
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 {
|
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") {
|
SECTION("single instructions") {
|
||||||
FuzzJitThumb(1, 2, 10000, instruction_select);
|
FuzzJitThumb16(1, 2, 10000, instruction_select);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("short blocks") {
|
SECTION("short blocks") {
|
||||||
FuzzJitThumb(5, 6, 3000, instruction_select);
|
FuzzJitThumb16(5, 6, 3000, instruction_select);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Test longer blocks when Unicorn can consistently
|
// TODO: Test longer blocks when Unicorn can consistently
|
||||||
// run these without going into an infinite loop.
|
// run these without going into an infinite loop.
|
||||||
#if 0
|
#if 0
|
||||||
SECTION("long blocks") {
|
SECTION("long blocks") {
|
||||||
FuzzJitThumb(1024, 1025, 1000, instruction_select);
|
FuzzJitThumb16(1024, 1025, 1000, instruction_select);
|
||||||
}
|
}
|
||||||
#endif
|
#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 = {
|
const std::array instructions = {
|
||||||
// TODO: We currently can't test BX/BLX as we have
|
// TODO: We currently can't test BX/BLX as we have
|
||||||
// no way of preventing the unpredictable
|
// 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'.
|
// must not be address<1:0> == '10'.
|
||||||
#if 0
|
#if 0
|
||||||
ThumbInstGen("01000111xmmmm000", // BLX/BX
|
ThumbInstGen("01000111xmmmm000", // BLX/BX
|
||||||
[](u16 inst){
|
[](u32 inst){
|
||||||
const u32 Rm = Common::Bits<3, 6>(inst);
|
const u32 Rm = Common::Bits<3, 6>(inst);
|
||||||
return Rm != 15;
|
return Rm != 15;
|
||||||
}),
|
}),
|
||||||
|
@ -288,7 +336,7 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
|
||||||
ThumbInstGen("01000100h0xxxxxx"), // ADD (high registers)
|
ThumbInstGen("01000100h0xxxxxx"), // ADD (high registers)
|
||||||
ThumbInstGen("01000110h0xxxxxx"), // MOV (high registers)
|
ThumbInstGen("01000110h0xxxxxx"), // MOV (high registers)
|
||||||
ThumbInstGen("1101ccccxxxxxxxx", // B<cond>
|
ThumbInstGen("1101ccccxxxxxxxx", // B<cond>
|
||||||
[](u16 inst){
|
[](u32 inst){
|
||||||
const u32 c = Common::Bits<9, 12>(inst);
|
const u32 c = Common::Bits<9, 12>(inst);
|
||||||
return c < 0b1110; // Don't want SWI or undefined instructions.
|
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 {
|
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;
|
ThumbTestEnv test_env;
|
||||||
|
|
||||||
// Prepare test subjects
|
// 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/assert.h"
|
||||||
#include "common/common_types.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 {
|
class A32TestEnv final : public Dynarmic::A32::UserCallbacks {
|
||||||
public:
|
public:
|
||||||
using InstructionType = InstructionType_;
|
using InstructionType = InstructionType_;
|
||||||
using RegisterArray = std::array<u32, 16>;
|
using RegisterArray = std::array<u32, 16>;
|
||||||
using ExtRegsArray = std::array<u32, 64>;
|
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;
|
u64 ticks_left = 0;
|
||||||
bool code_mem_modified_by_guest = false;
|
bool code_mem_modified_by_guest = false;
|
||||||
std::vector<InstructionType> code_mem;
|
std::vector<InstructionType> code_mem;
|
||||||
std::map<u32, u8> modified_memory;
|
std::map<u32, u8> modified_memory;
|
||||||
std::vector<std::string> interrupts;
|
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 {
|
std::uint32_t MemoryReadCode(u32 vaddr) override {
|
||||||
const size_t index = vaddr / sizeof(InstructionType);
|
if (IsInCodeMem(vaddr)) {
|
||||||
if (index < code_mem.size()) {
|
|
||||||
u32 value;
|
u32 value;
|
||||||
std::memcpy(&value, &code_mem[index], sizeof(u32));
|
std::memcpy(&value, &code_mem[vaddr / sizeof(InstructionType)], sizeof(u32));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
return infinite_loop; // B .
|
return infinite_loop_u32; // B .
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint8_t MemoryRead8(u32 vaddr) override {
|
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];
|
return reinterpret_cast<u8*>(code_mem.data())[vaddr];
|
||||||
}
|
}
|
||||||
if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) {
|
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 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 {
|
void AddTicks(std::uint64_t ticks) override {
|
||||||
if (ticks > ticks_left) {
|
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.GetPstate() == 0x20000000);
|
||||||
REQUIRE(jit.GetVector(30) == Vector{0xf7f6f5f4, 0});
|
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;
|
using Vector = Dynarmic::A64::Vector;
|
||||||
|
|
||||||
class A64TestEnv final : public Dynarmic::A64::UserCallbacks {
|
class A64TestEnv : public Dynarmic::A64::UserCallbacks {
|
||||||
public:
|
public:
|
||||||
u64 ticks_left = 0;
|
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){
|
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++) {
|
if (format_len == 16) {
|
||||||
const u32 bit = 1u << (31 - i);
|
// 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]) {
|
switch (format[i]) {
|
||||||
case '0':
|
case '0':
|
||||||
mask |= bit;
|
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) {
|
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));
|
fmt::print("Name: {}\n", GetNameOfA32Instruction(instruction));
|
||||||
|
|
||||||
const A32::LocationDescriptor location{0, {}, {}};
|
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;
|
constexpr u64 pc_mask = std::is_same_v<TestEnvironment, ArmTestEnv> ? 0 : 1;
|
||||||
while (testenv.ticks_left > 0) {
|
while (testenv.ticks_left > 0) {
|
||||||
const u32 pc = GetPC() | pc_mask;
|
const u32 pc = GetPC() | pc_mask;
|
||||||
|
if (!testenv.IsInCodeMem(pc)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (auto cerr_ = uc_emu_start(uc, pc, END_ADDRESS, 0, 1)) {
|
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_));
|
ASSERT_MSG(false, "uc_emu_start failed @ {:08x} (code = {:08x}) with error {} ({})", pc, testenv.MemoryReadCode(pc), cerr_, uc_strerror(cerr_));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue