early-access version 1475

This commit is contained in:
pineappleEA 2021-02-19 01:42:57 +01:00
parent 762f890987
commit d8bef01911
56 changed files with 3213 additions and 736 deletions

View file

@ -1,7 +1,7 @@
yuzu emulator early access
=============
This is the source code for early-access 1474.
This is the source code for early-access 1475.
## Legal Notice

View file

@ -97,6 +97,12 @@ else()
list(APPEND DYNARMIC_CXX_FLAGS
-Wfatal-errors)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang")
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
# And this in turns limits the size of a std::array.
list(APPEND DYNARMIC_CXX_FLAGS -fbracket-depth=1024)
endif()
endif()
# Arch detection

View file

@ -199,7 +199,7 @@ Legal
dynarmic is under a 0BSD license. See LICENSE.txt for more details.
dynarmic uses several other libraries, whose licenes are included below:
dynarmic uses several other libraries, whose licenses are included below:
### catch

View 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

View file

@ -10,6 +10,7 @@
#include <cstdint>
#include <memory>
#include <dynarmic/A32/arch_version.h>
#include <dynarmic/optimization_flags.h>
namespace Dynarmic {
@ -105,6 +106,10 @@ struct UserConfig {
size_t processor_id = 0;
ExclusiveMonitor* global_monitor = nullptr;
/// Select the architecture version to use.
/// There are minor behavioural differences between versions.
ArchVersion arch_version = ArchVersion::v8;
/// This selects other optimizations than can't otherwise be disabled by setting other
/// configuration options. This includes:
/// - IR optimizations

View file

@ -68,6 +68,15 @@ enum class DataCacheOperation {
ZeroByVA,
};
enum class InstructionCacheOperation {
/// IC IVAU
InvalidateByVAToPoU,
/// IC IALLU
InvalidateAllToPoU,
/// IC IALLUIS
InvalidateAllToPoUInnerSharable
};
struct UserCallbacks {
virtual ~UserCallbacks() = default;
@ -110,6 +119,7 @@ struct UserCallbacks {
virtual void ExceptionRaised(VAddr pc, Exception exception) = 0;
virtual void DataCacheOperationRaised(DataCacheOperation /*op*/, VAddr /*value*/) {}
virtual void InstructionCacheOperationRaised(InstructionCacheOperation /*op*/, VAddr /*value*/) {}
virtual void InstructionSynchronizationBarrierRaised() {}
// Timing-related callbacks

View file

@ -1,5 +1,6 @@
add_library(dynarmic
../include/dynarmic/A32/a32.h
../include/dynarmic/A32/arch_version.h
../include/dynarmic/A32/config.h
../include/dynarmic/A32/coprocessor.h
../include/dynarmic/A32/coprocessor_util.h
@ -124,6 +125,8 @@ if ("A32" IN_LIST DYNARMIC_FRONTENDS)
frontend/A32/location_descriptor.cpp
frontend/A32/location_descriptor.h
frontend/A32/PSR.h
frontend/A32/translate/conditional_state.cpp
frontend/A32/translate/conditional_state.h
frontend/A32/translate/impl/asimd_load_store_structures.cpp
frontend/A32/translate/impl/asimd_misc.cpp
frontend/A32/translate/impl/asimd_one_reg_modified_immediate.cpp
@ -150,7 +153,14 @@ if ("A32" IN_LIST DYNARMIC_FRONTENDS)
frontend/A32/translate/impl/status_register_access.cpp
frontend/A32/translate/impl/synchronization.cpp
frontend/A32/translate/impl/thumb16.cpp
frontend/A32/translate/impl/thumb32.cpp
frontend/A32/translate/impl/thumb32_branch.cpp
frontend/A32/translate/impl/thumb32_control.cpp
frontend/A32/translate/impl/thumb32_data_processing_register.cpp
frontend/A32/translate/impl/thumb32_data_processing_modified_immediate.cpp
frontend/A32/translate/impl/thumb32_long_multiply.cpp
frontend/A32/translate/impl/thumb32_misc.cpp
frontend/A32/translate/impl/thumb32_multiply.cpp
frontend/A32/translate/impl/thumb32_parallel.cpp
frontend/A32/translate/impl/translate_arm.h
frontend/A32/translate/impl/translate_thumb.h
frontend/A32/translate/impl/vfp.cpp
@ -226,6 +236,7 @@ if ("A64" IN_LIST DYNARMIC_FRONTENDS)
frontend/A64/translate/impl/simd_two_register_misc.cpp
frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp
frontend/A64/translate/impl/sys_dc.cpp
frontend/A64/translate/impl/sys_ic.cpp
frontend/A64/translate/impl/system.cpp
frontend/A64/translate/impl/system_flag_format.cpp
frontend/A64/translate/impl/system_flag_manipulation.cpp

View file

@ -67,6 +67,10 @@ A32::LocationDescriptor A32EmitContext::Location() const {
return A32::LocationDescriptor{block.Location()};
}
A32::LocationDescriptor A32EmitContext::EndLocation() const {
return A32::LocationDescriptor{block.EndLocation()};
}
bool A32EmitContext::IsSingleStep() const {
return Location().SingleStepping();
}
@ -732,7 +736,7 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto& arg = args[0];
const u32 upper_without_t = (ctx.Location().SetSingleStepping(false).UniqueHash() >> 32) & 0xFFFFFFFE;
const u32 upper_without_t = (ctx.EndLocation().SetSingleStepping(false).UniqueHash() >> 32) & 0xFFFFFFFE;
// Pseudocode:
// if (new_pc & 1) {
@ -766,6 +770,15 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) {
}
}
void A32EmitX64::EmitA32UpdateUpperLocationDescriptor(A32EmitContext& ctx, IR::Inst*) {
for (auto& inst : ctx.block) {
if (inst.GetOpcode() == IR::Opcode::A32BXWritePC) {
return;
}
}
EmitSetUpperLocationDescriptor(ctx.EndLocation(), ctx.Location());
}
void A32EmitX64::EmitA32CallSupervisor(A32EmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.HostCall(nullptr);

View file

@ -29,6 +29,7 @@ struct A32EmitContext final : public EmitContext {
A32EmitContext(const A32::UserConfig& conf, RegAlloc& reg_alloc, IR::Block& block);
A32::LocationDescriptor Location() const;
A32::LocationDescriptor EndLocation() const;
bool IsSingleStep() const;
FP::FPCR FPCR(bool fpcr_controlled = true) const override;

View file

@ -175,7 +175,7 @@ private:
PerformCacheInvalidation();
}
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, [this](u32 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); }, {conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
IR::Block ir_block = A32::Translate(A32::LocationDescriptor{descriptor}, [this](u32 vaddr) { return conf.callbacks->MemoryReadCode(vaddr); }, {conf.arch_version, conf.define_unpredictable_behaviour, conf.hook_hint_instructions});
if (conf.HasOptimization(OptimizationFlag::GetSetElimination)) {
Optimization::A32GetSetElimination(ir_block);
Optimization::DeadCodeElimination(ir_block);

View file

@ -647,10 +647,16 @@ void A64EmitX64::EmitA64ExceptionRaised(A64EmitContext& ctx, IR::Inst* inst) {
void A64EmitX64::EmitA64DataCacheOperationRaised(A64EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ctx.reg_alloc.HostCall(nullptr, args[0], args[1]);
ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
Devirtualize<&A64::UserCallbacks::DataCacheOperationRaised>(conf.callbacks).EmitCall(code);
}
void A64EmitX64::EmitA64InstructionCacheOperationRaised(A64EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
Devirtualize<&A64::UserCallbacks::InstructionCacheOperationRaised>(conf.callbacks).EmitCall(code);
}
void A64EmitX64::EmitA64DataSynchronizationBarrier(A64EmitContext&, IR::Inst*) {
code.mfence();
}

View file

@ -218,18 +218,23 @@ constexpr T RotateRight(T value, size_t amount) {
return static_cast<T>((x >> amount) | (x << (BitSize<T>() - amount)));
}
constexpr u16 Swap16(u16 value) {
constexpr u32 SwapHalves32(u32 value) {
return ((value & 0xFFFF0000U) >> 16) |
((value & 0x0000FFFFU) << 16);
}
constexpr u16 SwapBytes16(u16 value) {
return static_cast<u16>(u32{value} >> 8 | u32{value} << 8);
}
constexpr u32 Swap32(u32 value) {
constexpr u32 SwapBytes32(u32 value) {
return ((value & 0xFF000000U) >> 24) |
((value & 0x00FF0000U) >> 8) |
((value & 0x0000FF00U) << 8) |
((value & 0x000000FFU) << 24);
}
constexpr u64 Swap64(u64 value) {
constexpr u64 SwapBytes64(u64 value) {
return ((value & 0xFF00000000000000ULL) >> 56) |
((value & 0x00FF000000000000ULL) >> 40) |
((value & 0x0000FF0000000000ULL) >> 24) |

View file

@ -54,21 +54,39 @@ std::string DisassembleX64(const void* begin, const void* end) {
return result;
}
std::string DisassembleAArch32([[maybe_unused]] u32 instruction, [[maybe_unused]] u64 pc) {
std::string DisassembleAArch32([[maybe_unused]] bool is_thumb, [[maybe_unused]] u32 pc, [[maybe_unused]] const u8* instructions, [[maybe_unused]] size_t length) {
std::string result;
#ifdef DYNARMIC_USE_LLVM
LLVMInitializeARMTargetInfo();
LLVMInitializeARMTargetMC();
LLVMInitializeARMDisassembler();
LLVMDisasmContextRef llvm_ctx = LLVMCreateDisasm("armv8-arm", nullptr, 0, nullptr, nullptr);
LLVMDisasmContextRef llvm_ctx = LLVMCreateDisasm(is_thumb ? "thumbv8-arm" : "armv8-arm", nullptr, 0, nullptr, nullptr);
LLVMSetDisasmOptions(llvm_ctx, LLVMDisassembler_Option_AsmPrinterVariant);
char buffer[80];
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, (u8*)&instruction, sizeof(instruction), pc, buffer, sizeof(buffer));
result = inst_size > 0 ? buffer : "<invalid instruction>";
char buffer[1024];
while (length) {
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, const_cast<u8*>(instructions), length, pc, buffer, sizeof(buffer));
result += fmt::format("{:08x} ", pc);
for (size_t i = 0; i < 4; i++) {
if (i < inst_size) {
result += fmt::format("{:02x}", instructions[inst_size - i - 1]);
} else {
result += " ";
}
}
result += inst_size > 0 ? buffer : "<invalid instruction>";
result += '\n';
if (inst_size == 0) inst_size = is_thumb ? 2 : 4;
if (length <= inst_size) break;
pc += inst_size;
instructions += inst_size;
length -= inst_size;
}
LLVMDisasmDispose(llvm_ctx);
#else
result += fmt::format("(disassembly disabled)\n");

View file

@ -12,7 +12,7 @@
namespace Dynarmic::Common {
std::string DisassembleX64(const void* pos, const void* end);
std::string DisassembleAArch32(u32 instruction, u64 pc = 0);
std::string DisassembleAArch32(bool is_thumb, u32 pc, const u8* instructions, size_t length);
std::string DisassembleAArch64(u32 instruction, u64 pc = 0);
} // namespace Dynarmic::Common

View file

@ -22,34 +22,25 @@ public:
}
IR::Cond Cond() const {
if (value == 0b00000000) {
return IR::Cond::AL;
}
return static_cast<IR::Cond>(Common::Bits<4, 7>(value));
}
void Cond(IR::Cond cond) {
value = Common::ModifyBits<4, 7>(value, static_cast<u8>(cond));
}
u8 Mask() const {
return Common::Bits<0, 3>(value);
}
void Mask(u8 mask) {
value = Common::ModifyBits<0, 3>(value, mask);
}
bool IsInITBlock() const {
return Mask() != 0b0000;
return Common::Bits<0, 3>(value) != 0b0000;
}
bool IsLastInITBlock() const {
return Mask() == 0b1000;
return Common::Bits<0, 3>(value) == 0b1000;
}
ITState Advance() const {
ITState result{*this};
result.Mask(result.Mask() << 1);
if (result.Mask() == 0) {
return ITState{0};
if (Common::Bits<0, 2>(value) == 0b000) {
return ITState{0b00000000};
}
return result;
return ITState{Common::ModifyBits<0, 4>(value, static_cast<u8>(Common::Bits<0, 4>(value) << 1))};
}
u8 Value() const {
@ -57,7 +48,7 @@ public:
}
private:
u8 value;
u8 value = 0;
};
inline bool operator==(ITState lhs, ITState rhs) {

View file

@ -23,104 +23,8 @@ template<typename V>
std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16 instruction) {
static const std::vector<Thumb16Matcher<V>> table = {
#define INST(fn, name, bitstring) Decoder::detail::detail<Thumb16Matcher<V>>::GetMatcher(fn, name, bitstring)
// Shift (immediate), add, subtract, move and compare instructions
INST(&V::thumb16_LSL_imm, "LSL (imm)", "00000vvvvvmmmddd"),
INST(&V::thumb16_LSR_imm, "LSR (imm)", "00001vvvvvmmmddd"),
INST(&V::thumb16_ASR_imm, "ASR (imm)", "00010vvvvvmmmddd"),
INST(&V::thumb16_ADD_reg_t1, "ADD (reg, T1)", "0001100mmmnnnddd"),
INST(&V::thumb16_SUB_reg, "SUB (reg)", "0001101mmmnnnddd"),
INST(&V::thumb16_ADD_imm_t1, "ADD (imm, T1)", "0001110vvvnnnddd"),
INST(&V::thumb16_SUB_imm_t1, "SUB (imm, T1)", "0001111vvvnnnddd"),
INST(&V::thumb16_MOV_imm, "MOV (imm)", "00100dddvvvvvvvv"),
INST(&V::thumb16_CMP_imm, "CMP (imm)", "00101nnnvvvvvvvv"),
INST(&V::thumb16_ADD_imm_t2, "ADD (imm, T2)", "00110dddvvvvvvvv"),
INST(&V::thumb16_SUB_imm_t2, "SUB (imm, T2)", "00111dddvvvvvvvv"),
// Data-processing instructions
INST(&V::thumb16_AND_reg, "AND (reg)", "0100000000mmmddd"),
INST(&V::thumb16_EOR_reg, "EOR (reg)", "0100000001mmmddd"),
INST(&V::thumb16_LSL_reg, "LSL (reg)", "0100000010mmmddd"),
INST(&V::thumb16_LSR_reg, "LSR (reg)", "0100000011mmmddd"),
INST(&V::thumb16_ASR_reg, "ASR (reg)", "0100000100mmmddd"),
INST(&V::thumb16_ADC_reg, "ADC (reg)", "0100000101mmmddd"),
INST(&V::thumb16_SBC_reg, "SBC (reg)", "0100000110mmmddd"),
INST(&V::thumb16_ROR_reg, "ROR (reg)", "0100000111sssddd"),
INST(&V::thumb16_TST_reg, "TST (reg)", "0100001000mmmnnn"),
INST(&V::thumb16_RSB_imm, "RSB (imm)", "0100001001nnnddd"),
INST(&V::thumb16_CMP_reg_t1, "CMP (reg, T1)", "0100001010mmmnnn"),
INST(&V::thumb16_CMN_reg, "CMN (reg)", "0100001011mmmnnn"),
INST(&V::thumb16_ORR_reg, "ORR (reg)", "0100001100mmmddd"),
INST(&V::thumb16_MUL_reg, "MUL (reg)", "0100001101nnnddd"),
INST(&V::thumb16_BIC_reg, "BIC (reg)", "0100001110mmmddd"),
INST(&V::thumb16_MVN_reg, "MVN (reg)", "0100001111mmmddd"),
// Special data instructions
INST(&V::thumb16_ADD_reg_t2, "ADD (reg, T2)", "01000100Dmmmmddd"), // v4T, Low regs: v6T2
INST(&V::thumb16_CMP_reg_t2, "CMP (reg, T2)", "01000101Nmmmmnnn"), // v4T
INST(&V::thumb16_MOV_reg, "MOV (reg)", "01000110Dmmmmddd"), // v4T, Low regs: v6
// Store/Load single data item instructions
INST(&V::thumb16_LDR_literal, "LDR (literal)", "01001tttvvvvvvvv"),
INST(&V::thumb16_STR_reg, "STR (reg)", "0101000mmmnnnttt"),
INST(&V::thumb16_STRH_reg, "STRH (reg)", "0101001mmmnnnttt"),
INST(&V::thumb16_STRB_reg, "STRB (reg)", "0101010mmmnnnttt"),
INST(&V::thumb16_LDRSB_reg, "LDRSB (reg)", "0101011mmmnnnttt"),
INST(&V::thumb16_LDR_reg, "LDR (reg)", "0101100mmmnnnttt"),
INST(&V::thumb16_LDRH_reg, "LDRH (reg)", "0101101mmmnnnttt"),
INST(&V::thumb16_LDRB_reg, "LDRB (reg)", "0101110mmmnnnttt"),
INST(&V::thumb16_LDRSH_reg, "LDRSH (reg)", "0101111mmmnnnttt"),
INST(&V::thumb16_STR_imm_t1, "STR (imm, T1)", "01100vvvvvnnnttt"),
INST(&V::thumb16_LDR_imm_t1, "LDR (imm, T1)", "01101vvvvvnnnttt"),
INST(&V::thumb16_STRB_imm, "STRB (imm)", "01110vvvvvnnnttt"),
INST(&V::thumb16_LDRB_imm, "LDRB (imm)", "01111vvvvvnnnttt"),
INST(&V::thumb16_STRH_imm, "STRH (imm)", "10000vvvvvnnnttt"),
INST(&V::thumb16_LDRH_imm, "LDRH (imm)", "10001vvvvvnnnttt"),
INST(&V::thumb16_STR_imm_t2, "STR (imm, T2)", "10010tttvvvvvvvv"),
INST(&V::thumb16_LDR_imm_t2, "LDR (imm, T2)", "10011tttvvvvvvvv"),
// Generate relative address instructions
INST(&V::thumb16_ADR, "ADR", "10100dddvvvvvvvv"),
INST(&V::thumb16_ADD_sp_t1, "ADD (SP plus imm, T1)", "10101dddvvvvvvvv"),
INST(&V::thumb16_ADD_sp_t2, "ADD (SP plus imm, T2)", "101100000vvvvvvv"), // v4T
INST(&V::thumb16_SUB_sp, "SUB (SP minus imm)", "101100001vvvvvvv"), // v4T
// Hint instructions
INST(&V::thumb16_NOP, "NOP", "1011111100000000"), // v6T2
INST(&V::thumb16_SEV, "SEV", "1011111101000000"), // v7
INST(&V::thumb16_SEVL, "SEVL", "1011111101010000"), // v8
INST(&V::thumb16_WFE, "WFE", "1011111100100000"), // v7
INST(&V::thumb16_WFI, "WFI", "1011111100110000"), // v7
INST(&V::thumb16_YIELD, "YIELD", "1011111100010000"), // v7
// Miscellaneous 16-bit instructions
INST(&V::thumb16_SXTH, "SXTH", "1011001000mmmddd"), // v6
INST(&V::thumb16_SXTB, "SXTB", "1011001001mmmddd"), // v6
INST(&V::thumb16_UXTH, "UXTH", "1011001010mmmddd"), // v6
INST(&V::thumb16_UXTB, "UXTB", "1011001011mmmddd"), // v6
INST(&V::thumb16_PUSH, "PUSH", "1011010Mxxxxxxxx"), // v4T
INST(&V::thumb16_POP, "POP", "1011110Pxxxxxxxx"), // v4T
INST(&V::thumb16_SETEND, "SETEND", "101101100101x000"), // v6
INST(&V::thumb16_CPS, "CPS", "10110110011m0aif"), // v6
INST(&V::thumb16_REV, "REV", "1011101000mmmddd"), // v6
INST(&V::thumb16_REV16, "REV16", "1011101001mmmddd"), // v6
INST(&V::thumb16_REVSH, "REVSH", "1011101011mmmddd"), // v6
INST(&V::thumb16_BKPT, "BKPT", "10111110xxxxxxxx"), // v5
// Store/Load multiple registers
INST(&V::thumb16_STMIA, "STMIA", "11000nnnxxxxxxxx"),
INST(&V::thumb16_LDMIA, "LDMIA", "11001nnnxxxxxxxx"),
// Branch instructions
INST(&V::thumb16_BX, "BX", "010001110mmmm000"), // v4T
INST(&V::thumb16_BLX_reg, "BLX (reg)", "010001111mmmm000"), // v5T
INST(&V::thumb16_CBZ_CBNZ, "CBZ/CBNZ", "1011o0i1iiiiinnn"), // v6T2
INST(&V::thumb16_UDF, "UDF", "11011110--------"),
INST(&V::thumb16_SVC, "SVC", "11011111xxxxxxxx"),
INST(&V::thumb16_B_t1, "B (T1)", "1101ccccvvvvvvvv"),
INST(&V::thumb16_B_t2, "B (T2)", "11100vvvvvvvvvvv"),
#define INST(fn, name, bitstring) Decoder::detail::detail<Thumb16Matcher<V>>::GetMatcher(&V::fn, name, bitstring),
#include "thumb16.inc"
#undef INST
};

View 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")

View file

@ -22,325 +22,8 @@ template<typename V>
std::optional<std::reference_wrapper<const Thumb32Matcher<V>>> DecodeThumb32(u32 instruction) {
static const std::vector<Thumb32Matcher<V>> table = {
#define INST(fn, name, bitstring) Decoder::detail::detail<Thumb32Matcher<V>>::GetMatcher(fn, name, bitstring)
// Load/Store Multiple
//INST(&V::thumb32_SRS_1, "SRS", "1110100000-0--------------------"),
//INST(&V::thumb32_RFE_2, "RFE", "1110100000-1--------------------"),
//INST(&V::thumb32_STMIA, "STMIA/STMEA", "1110100010-0--------------------"),
//INST(&V::thumb32_POP, "POP", "1110100010111101----------------"),
//INST(&V::thumb32_LDMIA, "LDMIA/LDMFD", "1110100010-1--------------------"),
//INST(&V::thumb32_PUSH, "PUSH", "1110100100101101----------------"),
//INST(&V::thumb32_STMDB, "STMDB/STMFD", "1110100100-0--------------------"),
//INST(&V::thumb32_LDMDB, "LDMDB/LDMEA", "1110100100-1--------------------"),
//INST(&V::thumb32_SRS_1, "SRS", "1110100110-0--------------------"),
//INST(&V::thumb32_RFE_2, "RFE", "1110100110-1--------------------"),
// Load/Store Dual, Load/Store Exclusive, Table Branch
//INST(&V::thumb32_STREX, "STREX", "111010000100--------------------"),
//INST(&V::thumb32_LDREX, "LDREX", "111010000101--------------------"),
//INST(&V::thumb32_STRD_imm_1, "STRD (imm)", "11101000-110--------------------"),
//INST(&V::thumb32_STRD_imm_2, "STRD (imm)", "11101001-1-0--------------------"),
//INST(&V::thumb32_LDRD_imm_1, "LDRD (lit)", "11101000-1111111----------------"),
//INST(&V::thumb32_LDRD_imm_2, "LDRD (lit)", "11101001-1-11111----------------"),
//INST(&V::thumb32_LDRD_imm_1, "LDRD (imm)", "11101000-111--------------------"),
//INST(&V::thumb32_LDRD_imm_2, "LDRD (imm)", "11101001-1-1--------------------"),
//INST(&V::thumb32_STREXB, "STREXB", "111010001100------------0100----"),
//INST(&V::thumb32_STREXH, "STREXH", "111010001100------------0101----"),
//INST(&V::thumb32_STREXD, "STREXD", "111010001100------------0111----"),
//INST(&V::thumb32_TBB, "TBB", "111010001101------------0000----"),
//INST(&V::thumb32_TBH, "TBH", "111010001101------------0001----"),
//INST(&V::thumb32_LDREXB, "LDREXB", "111010001101------------0100----"),
//INST(&V::thumb32_LDREXH, "LDREXH", "111010001101------------0101----"),
//INST(&V::thumb32_LDREXD, "LDREXD", "111010001101------------0111----"),
// Data Processing (Shifted Register)
//INST(&V::thumb32_TST_reg, "TST (reg)", "111010100001--------1111--------"),
//INST(&V::thumb32_AND_reg, "AND (reg)", "11101010000---------------------"),
//INST(&V::thumb32_BIC_reg, "BIC (reg)", "11101010001---------------------"),
//INST(&V::thumb32_MOV_reg, "MOV (reg)", "11101010010-1111-000----0000----"),
//INST(&V::thumb32_LSL_imm, "LSL (imm)", "11101010010-1111----------00----"),
//INST(&V::thumb32_LSR_imm, "LSR (imm)", "11101010010-1111----------01----"),
//INST(&V::thumb32_ASR_imm, "ASR (imm)", "11101010010-1111----------10----"),
//INST(&V::thumb32_RRX, "RRX", "11101010010-1111-000----0011----"),
//INST(&V::thumb32_ROR_imm, "ROR (imm)", "11101010010-1111----------11----"),
//INST(&V::thumb32_ORR_reg, "ORR (reg)", "11101010010---------------------"),
//INST(&V::thumb32_MVN_reg, "MVN (reg)", "11101010011-1111----------------"),
//INST(&V::thumb32_ORN_reg, "ORN (reg)", "11101010011---------------------"),
//INST(&V::thumb32_TEQ_reg, "TEQ (reg)", "111010101001--------1111--------"),
//INST(&V::thumb32_EOR_reg, "EOR (reg)", "11101010100---------------------"),
//INST(&V::thumb32_PKH, "PKH", "11101010110---------------------"),
//INST(&V::thumb32_CMN_reg, "CMN (reg)", "111010110001--------1111--------"),
//INST(&V::thumb32_ADD_reg, "ADD (reg)", "11101011000---------------------"),
//INST(&V::thumb32_ADC_reg, "ADC (reg)", "11101011010---------------------"),
//INST(&V::thumb32_SBC_reg, "SBC (reg)", "11101011011---------------------"),
//INST(&V::thumb32_CMP_reg, "CMP (reg)", "111010111011--------1111--------"),
//INST(&V::thumb32_SUB_reg, "SUB (reg)", "11101011101---------------------"),
//INST(&V::thumb32_RSB_reg, "RSB (reg)", "11101011110---------------------"),
// Data Processing (Modified Immediate)
//INST(&V::thumb32_TST_imm, "TST (imm)", "11110-000001----0---1111--------"),
//INST(&V::thumb32_AND_imm, "AND (imm)", "11110-00000-----0---------------"),
//INST(&V::thumb32_BIC_imm, "BIC (imm)", "11110-00001-----0---------------"),
//INST(&V::thumb32_MOV_imm, "MOV (imm)", "11110000010-11110---------------"),
//INST(&V::thumb32_ORR_imm, "ORR (imm)", "11110-00010-----0---------------"),
//INST(&V::thumb32_MVN_imm, "MVN (imm)", "11110000011-11110---------------"),
//INST(&V::thumb32_ORN_imm, "ORN (imm)", "11110-00011-----0---------------"),
//INST(&V::thumb32_TEQ_imm, "TEQ (imm)", "11110-001001----0---1111--------"),
//INST(&V::thumb32_EOR_imm, "EOR (imm)", "11110-00100-----0---------------"),
//INST(&V::thumb32_CMN_imm, "CMN (imm)", "11110-010001----0---1111--------"),
//INST(&V::thumb32_ADD_imm_1, "ADD (imm)", "11110-01000-----0---------------"),
//INST(&V::thumb32_ADC_imm, "ADC (imm)", "11110-01010-----0---------------"),
//INST(&V::thumb32_SBC_imm, "SBC (imm)", "11110-01011-----0---------------"),
//INST(&V::thumb32_CMP_imm, "CMP (imm)", "11110-011011----0---1111--------"),
//INST(&V::thumb32_SUB_imm_1, "SUB (imm)", "11110-01101-----0---------------"),
//INST(&V::thumb32_RSB_imm, "RSB (imm)", "11110-01110-----0---------------"),
// Data Processing (Plain Binary Immediate)
//INST(&V::thumb32_ADR, "ADR", "11110-10000011110---------------"),
//INST(&V::thumb32_ADD_imm_2, "ADD (imm)", "11110-100000----0---------------"),
//INST(&V::thumb32_MOVW_imm, "MOVW (imm)", "11110-100100----0---------------"),
//INST(&V::thumb32_ADR, "ADR", "11110-10101011110---------------"),
//INST(&V::thumb32_SUB_imm_2, "SUB (imm)", "11110-101010----0---------------"),
//INST(&V::thumb32_MOVT, "MOVT", "11110-101100----0---------------"),
//INST(&V::thumb32_SSAT, "SSAT", "11110-110000----0---------------"),
//INST(&V::thumb32_SSAT16, "SSAT16", "11110-110010----0000----00------"),
//INST(&V::thumb32_SSAT, "SSAT", "11110-110010----0---------------"),
//INST(&V::thumb32_SBFX, "SBFX", "11110-110100----0---------------"),
//INST(&V::thumb32_BFC, "BFC", "11110-11011011110---------------"),
//INST(&V::thumb32_BFI, "BFI", "11110-110110----0---------------"),
//INST(&V::thumb32_USAT, "USAT", "11110-111000----0---------------"),
//INST(&V::thumb32_USAT16, "USAT16", "11110-111010----0000----00------"),
//INST(&V::thumb32_USAT, "USAT", "11110-111010----0---------------"),
//INST(&V::thumb32_UBFX, "UBFX", "11110-111100----0---------------"),
// Branches and Miscellaneous Control
//INST(&V::thumb32_MSR_banked, "MSR (banked)", "11110011100-----10-0------1-----"),
//INST(&V::thumb32_MSR_reg_1, "MSR (reg)", "111100111001----10-0------0-----"),
//INST(&V::thumb32_MSR_reg_2, "MSR (reg)", "111100111000----10-0--01--0-----"),
//INST(&V::thumb32_MSR_reg_3, "MSR (reg)", "111100111000----10-0--1---0-----"),
//INST(&V::thumb32_MSR_reg_4, "MSR (reg)", "111100111000----10-0--00--0-----"),
//INST(&V::thumb32_NOP, "NOP", "111100111010----10-0-00000000000"),
//INST(&V::thumb32_YIELD, "YIELD", "111100111010----10-0-00000000001"),
//INST(&V::thumb32_WFE, "WFE", "111100111010----10-0-00000000010"),
//INST(&V::thumb32_WFI, "WFI", "111100111010----10-0-00000000011"),
//INST(&V::thumb32_SEV, "SEV", "111100111010----10-0-00000000100"),
//INST(&V::thumb32_SEVL, "SEVL", "111100111010----10-0-00000000101"),
//INST(&V::thumb32_DBG, "DBG", "111100111010----10-0-0001111----"),
//INST(&V::thumb32_CPS, "CPS", "111100111010----10-0------------"),
//INST(&V::thumb32_ENTERX, "ENTERX", "111100111011----10-0----0001----"),
//INST(&V::thumb32_LEAVEX, "LEAVEX", "111100111011----10-0----0000----"),
//INST(&V::thumb32_CLREX, "CLREX", "111100111011----10-0----0010----"),
//INST(&V::thumb32_DSB, "DSB", "111100111011----10-0----0100----"),
//INST(&V::thumb32_DMB, "DMB", "111100111011----10-0----0101----"),
//INST(&V::thumb32_ISB, "ISB", "111100111011----10-0----0110----"),
//INST(&V::thumb32_BXJ, "BXJ", "111100111100----1000111100000000"),
//INST(&V::thumb32_ERET, "ERET", "11110011110111101000111100000000"),
//INST(&V::thumb32_SUBS_pc_lr, "SUBS PC, LR", "111100111101111010001111--------"),
//INST(&V::thumb32_MRS_banked, "MRS (banked)", "11110011111-----10-0------1-----"),
//INST(&V::thumb32_MRS_reg_1, "MRS (reg)", "111100111111----10-0------0-----"),
//INST(&V::thumb32_MRS_reg_2, "MRS (reg)", "111100111110----10-0------0-----"),
//INST(&V::thumb32_HVC, "HVC", "111101111110----1000------------"),
//INST(&V::thumb32_SMC, "SMC", "111101111111----1000000000000000"),
//INST(&V::thumb32_UDF, "UDF", "111101111111----1010------------"),
//INST(&V::thumb32_BL, "BL", "11110-----------11-1------------"),
//INST(&V::thumb32_BLX, "BLX", "11110-----------11-0------------"),
//INST(&V::thumb32_B, "B", "11110-----------10-1------------"),
//INST(&V::thumb32_B_cond, "B (cond)", "11110-----------10-0------------"),
// Store Single Data Item
//INST(&V::thumb32_STRB_imm_1, "STRB (imm)", "111110000000--------1--1--------"),
//INST(&V::thumb32_STRB_imm_2, "STRB (imm)", "111110000000--------1100--------"),
//INST(&V::thumb32_STRB_imm_3, "STRB (imm)", "111110001000--------------------"),
//INST(&V::thumb32_STRBT, "STRBT", "111110000000--------1110--------"),
//INST(&V::thumb32_STRB, "STRB (reg)", "111110000000--------000000------"),
//INST(&V::thumb32_STRH_imm_1, "STRH (imm)", "111110000010--------1--1--------"),
//INST(&V::thumb32_STRH_imm_2, "STRH (imm)", "111110000010--------1100--------"),
//INST(&V::thumb32_STRH_imm_3, "STRH (imm)", "111110001010--------------------"),
//INST(&V::thumb32_STRHT, "STRHT", "111110000010--------1110--------"),
//INST(&V::thumb32_STRH, "STRH (reg)", "111110000010--------000000------"),
//INST(&V::thumb32_STR_imm_1, "STR (imm)", "111110000100--------1--1--------"),
//INST(&V::thumb32_STR_imm_2, "STR (imm)", "111110000100--------1100--------"),
//INST(&V::thumb32_STR_imm_3, "STR (imm)", "111110001100--------------------"),
//INST(&V::thumb32_STRT, "STRT", "111110000100--------1110--------"),
//INST(&V::thumb32_STR_reg, "STR (reg)", "111110000100--------000000------"),
// Load Byte and Memory Hints
//INST(&V::thumb32_PLD_lit, "PLD (lit)", "11111000-00111111111------------"),
//INST(&V::thumb32_PLD_reg, "PLD (reg)", "111110000001----1111000000------"),
//INST(&V::thumb32_PLD_imm8, "PLD (imm8)", "1111100000-1----11111100--------"),
//INST(&V::thumb32_PLD_imm12, "PLD (imm12)", "111110001001----1111------------"),
//INST(&V::thumb32_PLI_lit, "PLI (lit)", "11111001-00111111111------------"),
//INST(&V::thumb32_PLI_reg, "PLI (reg)", "111110010001----1111000000------"),
//INST(&V::thumb32_PLI_imm8, "PLI (imm8)", "111110010001----11111100--------"),
//INST(&V::thumb32_PLI_imm12, "PLI (imm12)", "111110011001----1111------------"),
//INST(&V::thumb32_LDRB_lit, "LDRB (lit)", "11111000-0011111----------------"),
//INST(&V::thumb32_LDRB_reg, "LDRB (reg)", "111110000001--------000000------"),
//INST(&V::thumb32_LDRBT, "LDRBT", "111110000001--------1110--------"),
//INST(&V::thumb32_LDRB_imm8, "LDRB (imm8)", "111110000001--------1-----------"),
//INST(&V::thumb32_LDRB_imm12, "LDRB (imm12)", "111110001001--------------------"),
//INST(&V::thumb32_LDRSB_lit, "LDRSB (lit)", "11111001-0011111----------------"),
//INST(&V::thumb32_LDRSB_reg, "LDRSB (reg)", "111110010001--------000000------"),
//INST(&V::thumb32_LDRSBT, "LDRSBT", "111110010001--------1110--------"),
//INST(&V::thumb32_LDRSB_imm8, "LDRSB (imm8)", "111110010001--------1-----------"),
//INST(&V::thumb32_LDRSB_imm12, "LDRSB (imm12)", "111110011001--------------------"),
// Load Halfword and Memory Hints
//INST(&V::thumb32_LDRH_lit, "LDRH (lit)", "11111000-0111111----------------"),
//INST(&V::thumb32_LDRH_reg, "LDRH (reg)", "111110000011--------000000------"),
//INST(&V::thumb32_LDRHT, "LDRHT", "111110000011--------1110--------"),
//INST(&V::thumb32_LDRH_imm8, "LDRH (imm8)", "111110000011--------1-----------"),
//INST(&V::thumb32_LDRH_imm12, "LDRH (imm12)", "111110001011--------------------"),
//INST(&V::thumb32_LDRSH_lit, "LDRSH (lit)", "11111001-0111111----------------"),
//INST(&V::thumb32_LDRSH_reg, "LDRSH (reg)", "111110010011--------000000------"),
//INST(&V::thumb32_LDRSHT, "LDRSHT", "111110010011--------1110--------"),
//INST(&V::thumb32_LDRSH_imm8, "LDRSH (imm8)", "111110010011--------1-----------"),
//INST(&V::thumb32_LDRSH_imm12, "LDRSH (imm12)", "111110011011--------------------"),
//INST(&V::thumb32_NOP, "NOP", "111110010011----1111000000------"),
//INST(&V::thumb32_NOP, "NOP", "111110010011----11111100--------"),
//INST(&V::thumb32_NOP, "NOP", "11111001-01111111111------------"),
//INST(&V::thumb32_NOP, "NOP", "111110011011----1111------------"),
// Load Word
//INST(&V::thumb32_LDR_lit, "LDR (lit)", "11111000-1011111----------------"),
//INST(&V::thumb32_LDRT, "LDRT", "111110000101--------1110--------"),
//INST(&V::thumb32_LDR_reg, "LDR (reg)", "111110000101--------000000------"),
//INST(&V::thumb32_LDR_imm8, "LDR (imm8)", "111110000101--------1-----------"),
//INST(&V::thumb32_LDR_imm12, "LDR (imm12)", "111110001101--------------------"),
// Undefined
//INST(&V::thumb32_UDF, "UDF", "1111100--111--------------------"),
// Data Processing (register)
//INST(&V::thumb32_LSL_reg, "LSL (reg)", "11111010000-----1111----0000----"),
//INST(&V::thumb32_LSR_reg, "LSR (reg)", "11111010001-----1111----0000----"),
//INST(&V::thumb32_ASR_reg, "ASR (reg)", "11111010010-----1111----0000----"),
//INST(&V::thumb32_ROR_reg, "ROR (reg)", "11111010011-----1111----0000----"),
//INST(&V::thumb32_SXTH, "SXTH", "11111010000011111111----1-------"),
//INST(&V::thumb32_SXTAH, "SXTAH", "111110100000----1111----1-------"),
//INST(&V::thumb32_UXTH, "UXTH", "11111010000111111111----1-------"),
//INST(&V::thumb32_UXTAH, "UXTAH", "111110100001----1111----1-------"),
//INST(&V::thumb32_SXTB16, "SXTB16", "11111010001011111111----1-------"),
//INST(&V::thumb32_SXTAB16, "SXTAB16", "111110100010----1111----1-------"),
//INST(&V::thumb32_UXTB16, "UXTB16", "11111010001111111111----1-------"),
//INST(&V::thumb32_UXTAB16, "UXTAB16", "111110100011----1111----1-------"),
//INST(&V::thumb32_SXTB, "SXTB", "11111010010011111111----1-------"),
//INST(&V::thumb32_SXTAB, "SXTAB", "111110100100----1111----1-------"),
//INST(&V::thumb32_UXTB, "UXTB", "11111010010111111111----1-------"),
//INST(&V::thumb32_UXTAB, "UXTAB", "111110100101----1111----1-------"),
// Parallel Addition and Subtraction (signed)
//INST(&V::thumb32_SADD16, "SADD16", "111110101001----1111----0000----"),
//INST(&V::thumb32_SASX, "SASX", "111110101010----1111----0000----"),
//INST(&V::thumb32_SSAX, "SSAX", "111110101110----1111----0000----"),
//INST(&V::thumb32_SSUB16, "SSUB16", "111110101101----1111----0000----"),
//INST(&V::thumb32_SADD8, "SADD8", "111110101000----1111----0000----"),
//INST(&V::thumb32_SSUB8, "SSUB8", "111110101100----1111----0000----"),
//INST(&V::thumb32_QADD16, "QADD16", "111110101001----1111----0001----"),
//INST(&V::thumb32_QASX, "QASX", "111110101010----1111----0001----"),
//INST(&V::thumb32_QSAX, "QSAX", "111110101110----1111----0001----"),
//INST(&V::thumb32_QSUB16, "QSUB16", "111110101101----1111----0001----"),
//INST(&V::thumb32_QADD8, "QADD8", "111110101000----1111----0001----"),
//INST(&V::thumb32_QSUB8, "QSUB8", "111110101100----1111----0001----"),
//INST(&V::thumb32_SHADD16, "SHADD16", "111110101001----1111----0010----"),
//INST(&V::thumb32_SHASX, "SHASX", "111110101010----1111----0010----"),
//INST(&V::thumb32_SHSAX, "SHSAX", "111110101110----1111----0010----"),
//INST(&V::thumb32_SHSUB16, "SHSUB16", "111110101101----1111----0010----"),
//INST(&V::thumb32_SHADD8, "SHADD8", "111110101000----1111----0010----"),
//INST(&V::thumb32_SHSUB8, "SHSUB8", "111110101100----1111----0010----"),
// Parallel Addition and Subtraction (unsigned)
//INST(&V::thumb32_UADD16, "UADD16", "111110101001----1111----0100----"),
//INST(&V::thumb32_UASX, "UASX", "111110101010----1111----0100----"),
//INST(&V::thumb32_USAX, "USAX", "111110101110----1111----0100----"),
//INST(&V::thumb32_USUB16, "USUB16", "111110101101----1111----0100----"),
//INST(&V::thumb32_UADD8, "UADD8", "111110101000----1111----0100----"),
//INST(&V::thumb32_USUB8, "USUB8", "111110101100----1111----0100----"),
//INST(&V::thumb32_UQADD16, "UQADD16", "111110101001----1111----0101----"),
//INST(&V::thumb32_UQASX, "UQASX", "111110101010----1111----0101----"),
//INST(&V::thumb32_UQSAX, "UQSAX", "111110101110----1111----0101----"),
//INST(&V::thumb32_UQSUB16, "UQSUB16", "111110101101----1111----0101----"),
//INST(&V::thumb32_UQADD8, "UQADD8", "111110101000----1111----0101----"),
//INST(&V::thumb32_UQSUB8, "UQSUB8", "111110101100----1111----0101----"),
//INST(&V::thumb32_UHADD16, "UHADD16", "111110101001----1111----0110----"),
//INST(&V::thumb32_UHASX, "UHASX", "111110101010----1111----0110----"),
//INST(&V::thumb32_UHSAX, "UHSAX", "111110101110----1111----0110----"),
//INST(&V::thumb32_UHSUB16, "UHSUB16", "111110101101----1111----0110----"),
//INST(&V::thumb32_UHADD8, "UHADD8", "111110101000----1111----0110----"),
//INST(&V::thumb32_UHSUB8, "UHSUB8", "111110101100----1111----0110----"),
// Miscellaneous Operations
//INST(&V::thumb32_QADD, "QADD", "111110101000----1111----1000----"),
//INST(&V::thumb32_QDADD, "QDADD", "111110101000----1111----1001----"),
//INST(&V::thumb32_QSUB, "QSUB", "111110101000----1111----1010----"),
//INST(&V::thumb32_QDSUB, "QDSUB", "111110101000----1111----1011----"),
//INST(&V::thumb32_REV, "REV", "111110101001----1111----1000----"),
//INST(&V::thumb32_REV16, "REV16", "111110101001----1111----1001----"),
//INST(&V::thumb32_RBIT, "RBIT", "111110101001----1111----1010----"),
//INST(&V::thumb32_REVSH, "REVSH", "111110101001----1111----1011----"),
//INST(&V::thumb32_SEL, "SEL", "111110101010----1111----1000----"),
//INST(&V::thumb32_CLZ, "CLZ", "111110101011----1111----1000----"),
// Multiply, Multiply Accumulate, and Absolute Difference
//INST(&V::thumb32_MUL, "MUL", "111110110000----1111----0000----"),
//INST(&V::thumb32_MLA, "MLA", "111110110000------------0000----"),
//INST(&V::thumb32_MLS, "MLS", "111110110000------------0001----"),
//INST(&V::thumb32_SMULXY, "SMULXY", "111110110001----1111----00------"),
//INST(&V::thumb32_SMLAXY, "SMLAXY", "111110110001------------00------"),
//INST(&V::thumb32_SMUAD, "SMUAD", "111110110010----1111----000-----"),
//INST(&V::thumb32_SMLAD, "SMLAD", "111110110010------------000-----"),
//INST(&V::thumb32_SMULWY, "SMULWY", "111110110011----1111----000-----"),
//INST(&V::thumb32_SMLAWY, "SMLAWY", "111110110011------------000-----"),
//INST(&V::thumb32_SMUSD, "SMUSD", "111110110100----1111----000-----"),
//INST(&V::thumb32_SMLSD, "SMLSD", "111110110100------------000-----"),
//INST(&V::thumb32_SMMUL, "SMMUL", "111110110101----1111----000-----"),
//INST(&V::thumb32_SMMLA, "SMMLA", "111110110101------------000-----"),
//INST(&V::thumb32_SMMLS, "SMMLS", "111110110110------------000-----"),
//INST(&V::thumb32_USAD8, "USAD8", "111110110111----1111----0000----"),
//INST(&V::thumb32_USADA8, "USADA8", "111110110111------------0000----"),
// Long Multiply, Long Multiply Accumulate, and Divide
//INST(&V::thumb32_SMULL, "SMULL", "111110111000------------0000----"),
//INST(&V::thumb32_SDIV, "SDIV", "111110111001------------1111----"),
//INST(&V::thumb32_UMULL, "UMULL", "111110111010------------0000----"),
//INST(&V::thumb32_UDIV, "UDIV", "111110111011------------1111----"),
//INST(&V::thumb32_SMLAL, "SMLAL", "111110111100------------0000----"),
//INST(&V::thumb32_SMLALXY, "SMLALXY", "111110111100------------10------"),
//INST(&V::thumb32_SMLALD, "SMLALD", "111110111100------------110-----"),
//INST(&V::thumb32_SMLSLD, "SMLSLD", "111110111101------------110-----"),
//INST(&V::thumb32_UMLAL, "UMLAL", "111110111110------------0000----"),
//INST(&V::thumb32_UMAAL, "UMAAL", "111110111110------------0110----"),
// Coprocessor
//INST(&V::thumb32_MCRR2, "MCRR2", "111111000100--------------------"),
//INST(&V::thumb32_MCRR, "MCRR", "111011000100--------------------"),
//INST(&V::thumb32_STC2, "STC2", "1111110----0--------------------"),
//INST(&V::thumb32_STC, "STC", "1110110----0--------------------"),
//INST(&V::thumb32_MRRC2, "MRRC2", "111111000101--------------------"),
//INST(&V::thumb32_MRRC, "MRRC", "111011000101--------------------"),
//INST(&V::thumb32_LDC2_lit, "LDC2 (lit)", "1111110----11111----------------"),
//INST(&V::thumb32_LDC_lit, "LDC (lit)", "1110110----11111----------------"),
//INST(&V::thumb32_LDC2_imm, "LDC2 (imm)", "1111110----1--------------------"),
//INST(&V::thumb32_LDC_imm, "LDC (imm)", "1110110----1--------------------"),
//INST(&V::thumb32_CDP2, "CDP2", "11111110-------------------0----"),
//INST(&V::thumb32_CDP, "CDP", "11101110-------------------0----"),
//INST(&V::thumb32_MCR2, "MCR2", "11111110---0---------------1----"),
//INST(&V::thumb32_MCR, "MCR", "11101110---0---------------1----"),
//INST(&V::thumb32_MRC2, "MRC2", "11111110---1---------------1----"),
//INST(&V::thumb32_MRC, "MRC", "11101110---1---------------1----"),
// Branch instructions
INST(&V::thumb32_BL_imm, "BL (imm)", "11110vvvvvvvvvvv11111vvvvvvvvvvv"), // v4T
INST(&V::thumb32_BLX_imm, "BLX (imm)", "11110vvvvvvvvvvv11101vvvvvvvvvvv"), // v5T
// Misc instructions
INST(&V::thumb32_UDF, "UDF", "111101111111----1010------------"), // v6T2
#define INST(fn, name, bitstring) Decoder::detail::detail<Thumb32Matcher<V>>::GetMatcher(&V::fn, name, bitstring),
#include "thumb32.inc"
#undef INST
};

View 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----")

View file

@ -269,6 +269,25 @@ public:
return "yield";
}
std::string thumb16_IT(Imm<8> imm8) {
const Cond firstcond = imm8.Bits<4, 7, Cond>();
const bool firstcond0 = imm8.Bit<4>();
const auto [x, y, z] = [&]{
if (imm8.Bits<0, 3>() == 0b1000) {
return std::make_tuple("", "", "");
}
if (imm8.Bits<0, 2>() == 0b100) {
return std::make_tuple(imm8.Bit<3>() == firstcond0 ? "t" : "e", "", "");
}
if (imm8.Bits<0, 1>() == 0b10) {
return std::make_tuple(imm8.Bit<3>() == firstcond0 ? "t" : "e", imm8.Bit<2>() == firstcond0 ? "t" : "e", "");
}
// Sanity note: Here imm8.Bit<0>() is guaranteed to be == 1. (imm8 can never be 0bxxxx0000)
return std::make_tuple(imm8.Bit<3>() == firstcond0 ? "t" : "e", imm8.Bit<2>() == firstcond0 ? "t" : "e", imm8.Bit<1>() == firstcond0 ? "t" : "e");
}();
return fmt::format("it{}{}{} {}", x, y, z, firstcond);
}
std::string thumb16_SXTH(Reg m, Reg d) {
return fmt::format("sxth {}, {}", d, m);
}

View file

@ -8,10 +8,32 @@
#include "frontend/A32/types.h"
#include "frontend/ir/opcodes.h"
#include <dynarmic/A32/arch_version.h>
namespace Dynarmic::A32 {
using Opcode = IR::Opcode;
size_t IREmitter::ArchVersion() const {
switch (arch_version) {
case ArchVersion::v3:
return 3;
case ArchVersion::v4:
case ArchVersion::v4T:
return 4;
case ArchVersion::v5TE:
return 5;
case ArchVersion::v6K:
case ArchVersion::v6T2:
return 6;
case ArchVersion::v7:
return 7;
case ArchVersion::v8:
return 8;
}
UNREACHABLE();
}
u32 IREmitter::PC() const {
const u32 offset = current_location.TFlag() ? 4 : 8;
return current_location.PC() + offset;
@ -68,12 +90,16 @@ void IREmitter::SetVector(ExtReg reg, const IR::U128& value) {
void IREmitter::ALUWritePC(const IR::U32& value) {
// This behaviour is ARM version-dependent.
// The below implementation is for ARMv6k
if (ArchVersion() >= 7 && !current_location.TFlag()) {
BXWritePC(value);
} else {
BranchWritePC(value);
}
}
void IREmitter::BranchWritePC(const IR::U32& value) {
if (!current_location.TFlag()) {
// Note that for ArchVersion() < 6, this is UNPREDICTABLE when value<1:0> != 0b00
const auto new_pc = And(value, Imm32(0xFFFFFFFC));
Inst(Opcode::A32SetRegister, IR::Value(A32::Reg::PC), new_pc);
} else {
@ -88,8 +114,15 @@ void IREmitter::BXWritePC(const IR::U32& value) {
void IREmitter::LoadWritePC(const IR::U32& value) {
// This behaviour is ARM version-dependent.
// The below implementation is for ARMv6k
if (ArchVersion() >= 5) {
BXWritePC(value);
} else {
BranchWritePC(value);
}
}
void IREmitter::UpdateUpperLocationDescriptor() {
Inst(Opcode::A32UpdateUpperLocationDescriptor);
}
void IREmitter::CallSupervisor(const IR::U32& value) {

View file

@ -14,6 +14,7 @@
namespace Dynarmic::A32 {
enum class ArchVersion;
enum class CoprocReg;
enum class Exception;
enum class ExtReg;
@ -26,10 +27,12 @@ enum class Reg;
*/
class IREmitter : public IR::IREmitter {
public:
explicit IREmitter(IR::Block& block, LocationDescriptor descriptor) : IR::IREmitter(block), current_location(descriptor) {}
IREmitter(IR::Block& block, LocationDescriptor descriptor, ArchVersion arch_version) : IR::IREmitter(block), current_location(descriptor), arch_version(arch_version) {}
LocationDescriptor current_location;
size_t ArchVersion() const;
u32 PC() const;
u32 AlignPC(size_t alignment) const;
@ -44,6 +47,7 @@ public:
void BranchWritePC(const IR::U32& value);
void BXWritePC(const IR::U32& value);
void LoadWritePC(const IR::U32& value);
void UpdateUpperLocationDescriptor();
void CallSupervisor(const IR::U32& value);
void ExceptionRaised(Exception exception);
@ -99,6 +103,9 @@ public:
IR::U64 CoprocGetTwoWords(size_t coproc_no, bool two, size_t opc, CoprocReg CRm);
void CoprocLoadWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option);
void CoprocStoreWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option);
private:
enum ArchVersion arch_version;
};
} // namespace Dynarmic::A32

View file

@ -88,13 +88,17 @@ public:
return LocationDescriptor(arm_pc, cpsr, A32::FPSCR{new_fpscr & FPSCR_MODE_MASK}, single_stepping);
}
LocationDescriptor AdvanceIT() const {
LocationDescriptor SetIT(ITState new_it) const {
PSR new_cpsr = cpsr;
new_cpsr.IT(new_cpsr.IT().Advance());
new_cpsr.IT(new_it);
return LocationDescriptor(arm_pc, new_cpsr, fpscr, single_stepping);
}
LocationDescriptor AdvanceIT() const {
return SetIT(IT().Advance());
}
LocationDescriptor SetSingleStepping(bool new_single_stepping) const {
return LocationDescriptor(arm_pc, cpsr, fpscr, new_single_stepping);
}

View 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

View 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

View file

@ -12,13 +12,19 @@ namespace Dynarmic::A32 {
// LSLS <Rd>, <Rm>, #<imm5>
bool ThumbTranslatorVisitor::thumb16_LSL_imm(Imm<5> imm5, Reg m, Reg d) {
const u8 shift_n = imm5.ZeroExtend<u8>();
if (shift_n == 0 && ir.current_location.IT().IsInITBlock()) {
return UnpredictableInstruction();
}
const auto cpsr_c = ir.GetCFlag();
const auto result = ir.LogicalShiftLeft(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
return true;
}
@ -29,9 +35,11 @@ bool ThumbTranslatorVisitor::thumb16_LSR_imm(Imm<5> imm5, Reg m, Reg d) {
const auto result = ir.LogicalShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
return true;
}
@ -42,9 +50,11 @@ bool ThumbTranslatorVisitor::thumb16_ASR_imm(Imm<5> imm5, Reg m, Reg d) {
const auto result = ir.ArithmeticShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
return true;
}
@ -52,11 +62,14 @@ bool ThumbTranslatorVisitor::thumb16_ASR_imm(Imm<5> imm5, Reg m, Reg d) {
// Note that it is not possible to encode Rd == R15.
bool ThumbTranslatorVisitor::thumb16_ADD_reg_t1(Reg m, Reg n, Reg d) {
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0));
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
return true;
}
@ -64,11 +77,14 @@ bool ThumbTranslatorVisitor::thumb16_ADD_reg_t1(Reg m, Reg n, Reg d) {
// Note that it is not possible to encode Rd == R15.
bool ThumbTranslatorVisitor::thumb16_SUB_reg(Reg m, Reg n, Reg d) {
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(1));
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
return true;
}
@ -79,10 +95,12 @@ bool ThumbTranslatorVisitor::thumb16_ADD_imm_t1(Imm<3> imm3, Reg n, Reg d) {
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(0));
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
return true;
}
@ -93,10 +111,12 @@ bool ThumbTranslatorVisitor::thumb16_SUB_imm_t1(Imm<3> imm3, Reg n, Reg d) {
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1));
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
return true;
}
@ -107,8 +127,10 @@ bool ThumbTranslatorVisitor::thumb16_MOV_imm(Reg d, Imm<8> imm8) {
const auto result = ir.Imm32(imm32);
ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
@ -133,10 +155,12 @@ bool ThumbTranslatorVisitor::thumb16_ADD_imm_t2(Reg d_n, Imm<8> imm8) {
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(0));
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
return true;
}
@ -149,10 +173,12 @@ bool ThumbTranslatorVisitor::thumb16_SUB_imm_t2(Reg d_n, Imm<8> imm8) {
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1));
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
return true;
}
@ -164,8 +190,10 @@ bool ThumbTranslatorVisitor::thumb16_AND_reg(Reg m, Reg d_n) {
const auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
@ -177,8 +205,10 @@ bool ThumbTranslatorVisitor::thumb16_EOR_reg(Reg m, Reg d_n) {
const auto result = ir.Eor(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
@ -191,9 +221,11 @@ bool ThumbTranslatorVisitor::thumb16_LSL_reg(Reg m, Reg d_n) {
const auto result_carry = ir.LogicalShiftLeft(ir.GetRegister(n), shift_n, apsr_c);
ir.SetRegister(d, result_carry.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result_carry.result));
ir.SetZFlag(ir.IsZero(result_carry.result));
ir.SetCFlag(result_carry.carry);
}
return true;
}
@ -206,9 +238,11 @@ bool ThumbTranslatorVisitor::thumb16_LSR_reg(Reg m, Reg d_n) {
const auto result = ir.LogicalShiftRight(ir.GetRegister(n), shift_n, cpsr_c);
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
return true;
}
@ -221,9 +255,11 @@ bool ThumbTranslatorVisitor::thumb16_ASR_reg(Reg m, Reg d_n) {
const auto result = ir.ArithmeticShiftRight(ir.GetRegister(n), shift_n, cpsr_c);
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
return true;
}
@ -236,10 +272,12 @@ bool ThumbTranslatorVisitor::thumb16_ADC_reg(Reg m, Reg d_n) {
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), aspr_c);
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
return true;
}
@ -252,10 +290,12 @@ bool ThumbTranslatorVisitor::thumb16_SBC_reg(Reg m, Reg d_n) {
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), aspr_c);
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
return true;
}
@ -268,9 +308,11 @@ bool ThumbTranslatorVisitor::thumb16_ROR_reg(Reg m, Reg d_n) {
const auto result = ir.RotateRight(ir.GetRegister(n), shift_n, cpsr_c);
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
}
return true;
}
@ -287,10 +329,12 @@ bool ThumbTranslatorVisitor::thumb16_TST_reg(Reg m, Reg n) {
bool ThumbTranslatorVisitor::thumb16_RSB_imm(Reg n, Reg d) {
const auto result = ir.SubWithCarry(ir.Imm32(0), ir.GetRegister(n), ir.Imm1(1));
ir.SetRegister(d, result.result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
return true;
}
@ -322,8 +366,10 @@ bool ThumbTranslatorVisitor::thumb16_ORR_reg(Reg m, Reg d_n) {
const auto result = ir.Or(ir.GetRegister(m), ir.GetRegister(n));
ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
@ -335,8 +381,10 @@ bool ThumbTranslatorVisitor::thumb16_MUL_reg(Reg n, Reg d_m) {
const auto result = ir.Mul(ir.GetRegister(m), ir.GetRegister(n));
ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
@ -348,8 +396,10 @@ bool ThumbTranslatorVisitor::thumb16_BIC_reg(Reg m, Reg d_n) {
const auto result = ir.And(ir.GetRegister(n), ir.Not(ir.GetRegister(m)));
ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
@ -357,9 +407,12 @@ bool ThumbTranslatorVisitor::thumb16_BIC_reg(Reg m, Reg d_n) {
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_MVN_reg(Reg m, Reg d) {
const auto result = ir.Not(ir.GetRegister(m));
ir.SetRegister(d, result);
if (!ir.current_location.IT().IsInITBlock()) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
@ -367,13 +420,17 @@ bool ThumbTranslatorVisitor::thumb16_MVN_reg(Reg m, Reg d) {
bool ThumbTranslatorVisitor::thumb16_ADD_reg_t2(bool d_n_hi, Reg m, Reg d_n_lo) {
const Reg d_n = d_n_hi ? (d_n_lo + 8) : d_n_lo;
const Reg n = d_n;
const Reg d = d_n;
if (n == Reg::PC && m == Reg::PC) {
return UnpredictableInstruction();
}
if (d == Reg::PC && ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) {
return UnpredictableInstruction();
}
const Reg d = d_n;
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0));
if (d == Reg::PC) {
ir.UpdateUpperLocationDescriptor();
ir.ALUWritePC(result.result);
// Return to dispatch as we can't predict what PC is going to be. Stop compilation.
ir.SetTerm(IR::Term::FastDispatchHint{});
@ -405,9 +462,14 @@ bool ThumbTranslatorVisitor::thumb16_CMP_reg_t2(bool n_hi, Reg m, Reg n_lo) {
// MOV <Rd>, <Rm>
bool ThumbTranslatorVisitor::thumb16_MOV_reg(bool d_hi, Reg m, Reg d_lo) {
const Reg d = d_hi ? (d_lo + 8) : d_lo;
if (d == Reg::PC && ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) {
return UnpredictableInstruction();
}
const auto result = ir.GetRegister(m);
if (d == Reg::PC) {
ir.UpdateUpperLocationDescriptor();
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
@ -635,11 +697,6 @@ bool ThumbTranslatorVisitor::thumb16_SUB_sp(Imm<7> imm7) {
return true;
}
// NOP<c>
bool ThumbTranslatorVisitor::thumb16_NOP() {
return true;
}
// SEV<c>
bool ThumbTranslatorVisitor::thumb16_SEV() {
if (!options.hook_hint_instructions) {
@ -680,6 +737,26 @@ bool ThumbTranslatorVisitor::thumb16_YIELD() {
return RaiseException(Exception::Yield);
}
// NOP<c>
bool ThumbTranslatorVisitor::thumb16_NOP() {
return true;
}
// IT{<x>{<y>{<z>}}} <cond>
bool ThumbTranslatorVisitor::thumb16_IT(Imm<8> imm8) {
ASSERT_MSG((imm8.Bits<0, 3>() != 0b0000), "Decode Error");
if (imm8.Bits<4, 7>() == 0b1111 || (imm8.Bits<4, 7>() == 0b1110 && Common::BitCount(imm8.Bits<0, 3>()) != 1)) {
return UnpredictableInstruction();
}
if (ir.current_location.IT().IsInITBlock()) {
return UnpredictableInstruction();
}
const auto next_location = ir.current_location.AdvancePC(2).SetIT(ITState{imm8.ZeroExtend<u8>()});
ir.SetTerm(IR::Term::LinkBlockFast{next_location});
return false;
}
// SXTH <Rd>, <Rm>
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_SXTH(Reg m, Reg d) {
@ -761,6 +838,7 @@ bool ThumbTranslatorVisitor::thumb16_POP(bool P, RegList reg_list) {
if (Common::Bit<15>(reg_list)) {
// TODO(optimization): Possible location for an RSB pop.
const auto data = ir.ReadMemory32(address);
ir.UpdateUpperLocationDescriptor();
ir.LoadWritePC(data);
address = ir.Add(address, ir.Imm32(4));
ir.SetRegister(Reg::SP, address);
@ -774,6 +852,10 @@ bool ThumbTranslatorVisitor::thumb16_POP(bool P, RegList reg_list) {
// SETEND <endianness>
bool ThumbTranslatorVisitor::thumb16_SETEND(bool E) {
if (ir.current_location.IT().IsInITBlock()) {
return UnpredictableInstruction();
}
if (E == ir.current_location.EFlag()) {
return true;
}
@ -822,6 +904,8 @@ bool ThumbTranslatorVisitor::thumb16_REVSH(Reg m, Reg d) {
// BKPT #<imm8>
bool ThumbTranslatorVisitor::thumb16_BKPT([[maybe_unused]] Imm<8> imm8) {
ir.ExceptionRaised(Exception::Breakpoint);
ir.UpdateUpperLocationDescriptor();
ir.LoadWritePC(ir.Imm32(ir.current_location.PC()));
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
@ -873,15 +957,18 @@ bool ThumbTranslatorVisitor::thumb16_LDMIA(Reg n, RegList reg_list) {
// CB{N}Z <Rn>, <label>
bool ThumbTranslatorVisitor::thumb16_CBZ_CBNZ(bool nonzero, Imm<1> i, Imm<5> imm5, Reg n) {
if (ir.current_location.IT().IsInITBlock()) {
return UnpredictableInstruction();
}
const u32 imm = concatenate(i, imm5, Imm<1>{0}).ZeroExtend();
const IR::U32 rn = ir.GetRegister(n);
ir.SetCheckBit(ir.IsZero(rn));
const auto [cond_pass, cond_fail] = [this, imm, nonzero] {
const u32 target = ir.PC() + imm;
const auto skip = IR::Term::LinkBlock{ir.current_location.AdvancePC(2)};
const auto branch = IR::Term::LinkBlock{ir.current_location.AdvancePC(target)};
const auto branch = IR::Term::LinkBlock{ir.current_location.AdvancePC(imm + 4)};
if (nonzero) {
return std::make_pair(skip, branch);
@ -900,6 +987,11 @@ bool ThumbTranslatorVisitor::thumb16_UDF() {
// BX <Rm>
bool ThumbTranslatorVisitor::thumb16_BX(Reg m) {
if (ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) {
return UnpredictableInstruction();
}
ir.UpdateUpperLocationDescriptor();
ir.BXWritePC(ir.GetRegister(m));
if (m == Reg::R14)
ir.SetTerm(IR::Term::PopRSBHint{});
@ -910,7 +1002,12 @@ bool ThumbTranslatorVisitor::thumb16_BX(Reg m) {
// BLX <Rm>
bool ThumbTranslatorVisitor::thumb16_BLX_reg(Reg m) {
if (ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) {
return UnpredictableInstruction();
}
ir.PushRSB(ir.current_location.AdvancePC(2));
ir.UpdateUpperLocationDescriptor();
ir.BXWritePC(ir.GetRegister(m));
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 2) | 1));
ir.SetTerm(IR::Term::FastDispatchHint{});
@ -920,6 +1017,7 @@ bool ThumbTranslatorVisitor::thumb16_BLX_reg(Reg m) {
// SVC #<imm8>
bool ThumbTranslatorVisitor::thumb16_SVC(Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend();
ir.UpdateUpperLocationDescriptor();
ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 2));
ir.PushRSB(ir.current_location.AdvancePC(2));
ir.CallSupervisor(ir.Imm32(imm32));
@ -929,13 +1027,17 @@ bool ThumbTranslatorVisitor::thumb16_SVC(Imm<8> imm8) {
// B<cond> <label>
bool ThumbTranslatorVisitor::thumb16_B_t1(Cond cond, Imm<8> imm8) {
if (ir.current_location.IT().IsInITBlock()) {
return UnpredictableInstruction();
}
if (cond == Cond::AL) {
return thumb16_UDF();
}
const s32 imm32 = static_cast<s32>((imm8.SignExtend<u32>() << 1) + 4);
const auto then_location = ir.current_location.AdvancePC(imm32);
const auto else_location = ir.current_location.AdvancePC(2);
const auto then_location = ir.current_location.AdvancePC(imm32).AdvanceIT();
const auto else_location = ir.current_location.AdvancePC(2).AdvanceIT();
ir.SetTerm(IR::Term::If{cond, IR::Term::LinkBlock{then_location}, IR::Term::LinkBlock{else_location}});
return false;
@ -943,8 +1045,12 @@ bool ThumbTranslatorVisitor::thumb16_B_t1(Cond cond, Imm<8> imm8) {
// B <label>
bool ThumbTranslatorVisitor::thumb16_B_t2(Imm<11> imm11) {
if (ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) {
return UnpredictableInstruction();
}
const s32 imm32 = static_cast<s32>((imm11.SignExtend<u32>() << 1) + 4);
const auto next_location = ir.current_location.AdvancePC(imm32);
const auto next_location = ir.current_location.AdvancePC(imm32).AdvanceIT();
ir.SetTerm(IR::Term::LinkBlock{next_location});
return false;

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View file

@ -10,6 +10,7 @@
#include "frontend/imm.h"
#include "frontend/A32/ir_emitter.h"
#include "frontend/A32/location_descriptor.h"
#include "frontend/A32/translate/conditional_state.h"
#include "frontend/A32/translate/translate.h"
#include "frontend/A32/types.h"
@ -17,21 +18,10 @@ namespace Dynarmic::A32 {
enum class Exception;
enum class ConditionalState {
/// We haven't met any conditional instructions yet.
None,
/// Current instruction is a conditional. This marks the end of this basic block.
Break,
/// This basic block is made up solely of conditional instructions.
Translating,
/// This basic block is made up of conditional instructions followed by unconditional instructions.
Trailing,
};
struct ArmTranslatorVisitor final {
using instruction_return_type = bool;
explicit ArmTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor), options(options) {
explicit ArmTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor, options.arch_version), options(options) {
ASSERT_MSG(!descriptor.TFlag(), "The processor must be in Arm mode");
}

View file

@ -9,6 +9,7 @@
#include "frontend/imm.h"
#include "frontend/A32/ir_emitter.h"
#include "frontend/A32/location_descriptor.h"
#include "frontend/A32/translate/conditional_state.h"
#include "frontend/A32/translate/translate.h"
#include "frontend/A32/types.h"
@ -19,13 +20,48 @@ enum class Exception;
struct ThumbTranslatorVisitor final {
using instruction_return_type = bool;
explicit ThumbTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor), options(options) {
explicit ThumbTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor, options.arch_version), options(options) {
ASSERT_MSG(descriptor.TFlag(), "The processor must be in Thumb mode");
}
struct ImmAndCarry {
u32 imm32;
IR::U1 carry;
};
ImmAndCarry ThumbExpandImm_C(Imm<1> i, Imm<3> imm3, Imm<8> imm8, IR::U1 carry_in) {
const Imm<12> imm12 = concatenate(i, imm3, imm8);
if (imm12.Bits<10, 11>() == 0) {
const u32 imm32 = [&]{
const u32 imm8 = imm12.Bits<0, 7>();
switch (imm12.Bits<8, 9>()) {
case 0b00:
return imm8;
case 0b01:
return Common::Replicate(imm8, 16);
case 0b10:
return Common::Replicate(imm8 << 8, 16);
case 0b11:
return Common::Replicate(imm8, 8);
}
UNREACHABLE();
}();
return {imm32, carry_in};
}
const u32 imm32 = Common::RotateRight<u32>((1 << 7) | imm12.Bits<0, 6>(), imm12.Bits<7, 11>());
return {imm32, ir.Imm1(Common::Bit<31>(imm32))};
}
u32 ThumbExpandImm(Imm<1> i, Imm<3> imm3, Imm<8> imm8) {
return ThumbExpandImm_C(i, imm3, imm8, ir.Imm1(0)).imm32;
}
A32::IREmitter ir;
ConditionalState cond_state = ConditionalState::None;
TranslationOptions options;
bool ConditionPassed(bool is_thumb_16);
bool InterpretThisInstruction();
bool UnpredictableInstruction();
bool UndefinedInstruction();
@ -83,12 +119,13 @@ struct ThumbTranslatorVisitor final {
bool thumb16_ADD_sp_t1(Reg d, Imm<8> imm8);
bool thumb16_ADD_sp_t2(Imm<7> imm7);
bool thumb16_SUB_sp(Imm<7> imm7);
bool thumb16_NOP();
bool thumb16_SEV();
bool thumb16_SEVL();
bool thumb16_WFE();
bool thumb16_WFI();
bool thumb16_YIELD();
bool thumb16_NOP();
bool thumb16_IT(Imm<8> imm8);
bool thumb16_SXTH(Reg m, Reg d);
bool thumb16_SXTB(Reg m, Reg d);
bool thumb16_UXTH(Reg m, Reg d);
@ -111,10 +148,115 @@ struct ThumbTranslatorVisitor final {
bool thumb16_B_t1(Cond cond, Imm<8> imm8);
bool thumb16_B_t2(Imm<11> imm11);
// thumb32
bool thumb32_BL_imm(Imm<11> hi, Imm<11> lo);
bool thumb32_BLX_imm(Imm<11> hi, Imm<11> lo);
// thumb32 data processing (modified immediate) instructions
bool thumb32_TST_imm(Imm<1> i, Reg n, Imm<3> imm3, Imm<8> imm8);
bool thumb32_AND_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Reg d, Imm<8> imm8);
bool thumb32_BIC_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Reg d, Imm<8> imm8);
bool thumb32_MOV_imm(Imm<1> i, bool S, Imm<3> imm3, Reg d, Imm<8> imm8);
bool thumb32_ORR_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Reg d, Imm<8> imm8);
// thumb32 miscellaneous control instructions
bool thumb32_UDF();
// thumb32 branch instructions
bool thumb32_BL_imm(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> j2, Imm<11> lo);
bool thumb32_BLX_imm(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> j2, Imm<11> lo);
// thumb32 data processing (register) instructions
bool thumb32_SXTB(Reg d, SignExtendRotation rotate, Reg m);
bool thumb32_SXTB16(Reg d, SignExtendRotation rotate, Reg m);
bool thumb32_SXTAB(Reg n, Reg d, SignExtendRotation rotate, Reg m);
bool thumb32_SXTAB16(Reg n, Reg d, SignExtendRotation rotate, Reg m);
bool thumb32_SXTH(Reg d, SignExtendRotation rotate, Reg m);
bool thumb32_SXTAH(Reg n, Reg d, SignExtendRotation rotate, Reg m);
bool thumb32_UXTB(Reg d, SignExtendRotation rotate, Reg m);
bool thumb32_UXTB16(Reg d, SignExtendRotation rotate, Reg m);
bool thumb32_UXTAB(Reg n, Reg d, SignExtendRotation rotate, Reg m);
bool thumb32_UXTAB16(Reg n, Reg d, SignExtendRotation rotate, Reg m);
bool thumb32_UXTH(Reg d, SignExtendRotation rotate, Reg m);
bool thumb32_UXTAH(Reg n, Reg d, SignExtendRotation rotate, Reg m);
// thumb32 long multiply, long multiply accumulate, and divide instructions
bool thumb32_SDIV(Reg n, Reg d, Reg m);
bool thumb32_SMLAL(Reg n, Reg dLo, Reg dHi, Reg m);
bool thumb32_SMLALD(Reg n, Reg dLo, Reg dHi, bool M, Reg m);
bool thumb32_SMLALXY(Reg n, Reg dLo, Reg dHi, bool N, bool M, Reg m);
bool thumb32_SMLSLD(Reg n, Reg dLo, Reg dHi, bool M, Reg m);
bool thumb32_SMULL(Reg n, Reg dLo, Reg dHi, Reg m);
bool thumb32_UDIV(Reg n, Reg d, Reg m);
bool thumb32_UMAAL(Reg n, Reg dLo, Reg dHi, Reg m);
bool thumb32_UMLAL(Reg n, Reg dLo, Reg dHi, Reg m);
bool thumb32_UMULL(Reg n, Reg dLo, Reg dHi, Reg m);
// thumb32 miscellaneous instructions
bool thumb32_CLZ(Reg n, Reg d, Reg m);
bool thumb32_QADD(Reg n, Reg d, Reg m);
bool thumb32_QDADD(Reg n, Reg d, Reg m);
bool thumb32_QDSUB(Reg n, Reg d, Reg m);
bool thumb32_QSUB(Reg n, Reg d, Reg m);
bool thumb32_RBIT(Reg n, Reg d, Reg m);
bool thumb32_REV(Reg n, Reg d, Reg m);
bool thumb32_REV16(Reg n, Reg d, Reg m);
bool thumb32_REVSH(Reg n, Reg d, Reg m);
bool thumb32_SEL(Reg n, Reg d, Reg m);
// thumb32 multiply instructions
bool thumb32_MLA(Reg n, Reg a, Reg d, Reg m);
bool thumb32_MLS(Reg n, Reg a, Reg d, Reg m);
bool thumb32_MUL(Reg n, Reg d, Reg m);
bool thumb32_SMLAD(Reg n, Reg a, Reg d, bool X, Reg m);
bool thumb32_SMLAXY(Reg n, Reg a, Reg d, bool N, bool M, Reg m);
bool thumb32_SMLAWY(Reg n, Reg a, Reg d, bool M, Reg m);
bool thumb32_SMLSD(Reg n, Reg a, Reg d, bool X, Reg m);
bool thumb32_SMMLA(Reg n, Reg a, Reg d, bool R, Reg m);
bool thumb32_SMMLS(Reg n, Reg a, Reg d, bool R, Reg m);
bool thumb32_SMMUL(Reg n, Reg d, bool R, Reg m);
bool thumb32_SMUAD(Reg n, Reg d, bool M, Reg m);
bool thumb32_SMUSD(Reg n, Reg d, bool M, Reg m);
bool thumb32_SMULXY(Reg n, Reg d, bool N, bool M, Reg m);
bool thumb32_SMULWY(Reg n, Reg d, bool M, Reg m);
bool thumb32_USAD8(Reg n, Reg d, Reg m);
bool thumb32_USADA8(Reg n, Reg a, Reg d, Reg m);
// thumb32 parallel add/sub instructions
bool thumb32_SADD8(Reg n, Reg d, Reg m);
bool thumb32_SADD16(Reg n, Reg d, Reg m);
bool thumb32_SASX(Reg n, Reg d, Reg m);
bool thumb32_SSAX(Reg n, Reg d, Reg m);
bool thumb32_SSUB8(Reg n, Reg d, Reg m);
bool thumb32_SSUB16(Reg n, Reg d, Reg m);
bool thumb32_UADD8(Reg n, Reg d, Reg m);
bool thumb32_UADD16(Reg n, Reg d, Reg m);
bool thumb32_UASX(Reg n, Reg d, Reg m);
bool thumb32_USAX(Reg n, Reg d, Reg m);
bool thumb32_USUB8(Reg n, Reg d, Reg m);
bool thumb32_USUB16(Reg n, Reg d, Reg m);
bool thumb32_QADD8(Reg n, Reg d, Reg m);
bool thumb32_QADD16(Reg n, Reg d, Reg m);
bool thumb32_QASX(Reg n, Reg d, Reg m);
bool thumb32_QSAX(Reg n, Reg d, Reg m);
bool thumb32_QSUB8(Reg n, Reg d, Reg m);
bool thumb32_QSUB16(Reg n, Reg d, Reg m);
bool thumb32_UQADD8(Reg n, Reg d, Reg m);
bool thumb32_UQADD16(Reg n, Reg d, Reg m);
bool thumb32_UQASX(Reg n, Reg d, Reg m);
bool thumb32_UQSAX(Reg n, Reg d, Reg m);
bool thumb32_UQSUB8(Reg n, Reg d, Reg m);
bool thumb32_UQSUB16(Reg n, Reg d, Reg m);
bool thumb32_SHADD8(Reg n, Reg d, Reg m);
bool thumb32_SHADD16(Reg n, Reg d, Reg m);
bool thumb32_SHASX(Reg n, Reg d, Reg m);
bool thumb32_SHSAX(Reg n, Reg d, Reg m);
bool thumb32_SHSUB8(Reg n, Reg d, Reg m);
bool thumb32_SHSUB16(Reg n, Reg d, Reg m);
bool thumb32_UHADD8(Reg n, Reg d, Reg m);
bool thumb32_UHADD16(Reg n, Reg d, Reg m);
bool thumb32_UHASX(Reg n, Reg d, Reg m);
bool thumb32_UHSAX(Reg n, Reg d, Reg m);
bool thumb32_UHSUB8(Reg n, Reg d, Reg m);
bool thumb32_UHSUB16(Reg n, Reg d, Reg m);
};
} // namespace Dynarmic::A32

View file

@ -6,6 +6,8 @@
#include "common/common_types.h"
#include <dynarmic/A32/arch_version.h>
namespace Dynarmic::IR {
class Block;
} // namespace Dynarmic::IR
@ -17,6 +19,8 @@ class LocationDescriptor;
using MemoryReadCodeFuncType = std::function<u32(u32 vaddr)>;
struct TranslationOptions {
ArchVersion arch_version;
/// This changes what IR we emit when we translate an unpredictable instruction.
/// If this is false, the ExceptionRaised IR instruction is emitted.
/// If this is true, we define some behaviour for some instructions.

View file

@ -3,8 +3,6 @@
* SPDX-License-Identifier: 0BSD
*/
#include <algorithm>
#include <dynarmic/A32/config.h>
#include "common/assert.h"
@ -12,6 +10,7 @@
#include "frontend/A32/decoder/asimd.h"
#include "frontend/A32/decoder/vfp.h"
#include "frontend/A32/location_descriptor.h"
#include "frontend/A32/translate/conditional_state.h"
#include "frontend/A32/translate/impl/translate_arm.h"
#include "frontend/A32/translate/translate.h"
#include "frontend/A32/types.h"
@ -19,16 +18,6 @@
namespace Dynarmic::A32 {
static bool CondCanContinue(ConditionalState cond_state, const A32::IREmitter& ir) {
ASSERT_MSG(cond_state != ConditionalState::Break, "Should never happen.");
if (cond_state == ConditionalState::None)
return true;
// TODO: This is more conservative than necessary.
return std::all_of(ir.block.begin(), ir.block.end(), [](const IR::Inst& inst) { return !inst.WritesToCPSR(); });
}
IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) {
const bool single_step = descriptor.SingleStepping();
@ -102,53 +91,7 @@ bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descript
}
bool ArmTranslatorVisitor::ConditionPassed(Cond cond) {
ASSERT_MSG(cond_state != ConditionalState::Break,
"This should never happen. We requested a break but that wasn't honored.");
if (cond == Cond::NV) {
// NV conditional is obsolete
ir.ExceptionRaised(Exception::UnpredictableInstruction);
return false;
}
if (cond_state == ConditionalState::Translating) {
if (ir.block.ConditionFailedLocation() != ir.current_location || cond == Cond::AL) {
cond_state = ConditionalState::Trailing;
} else {
if (cond == ir.block.GetCondition()) {
ir.block.SetConditionFailedLocation(ir.current_location.AdvancePC(4));
ir.block.ConditionFailedCycleCount()++;
return true;
}
// cond has changed, abort
cond_state = ConditionalState::Break;
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location});
return false;
}
}
if (cond == Cond::AL) {
// Everything is fine with the world
return true;
}
// non-AL cond
if (!ir.block.empty()) {
// We've already emitted instructions. Quit for now, we'll make a new block here later.
cond_state = ConditionalState::Break;
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location});
return false;
}
// We've not emitted instructions yet.
// We'll emit one instruction, and set the block-entry conditional appropriately.
cond_state = ConditionalState::Translating;
ir.block.SetCondition(cond);
ir.block.SetConditionFailedLocation(ir.current_location.AdvancePC(4));
ir.block.ConditionFailedCycleCount() = ir.block.CycleCount() + 1;
return true;
return IsConditionPassed(cond, cond_state, ir, 4);
}
bool ArmTranslatorVisitor::InterpretThisInstruction() {

View file

@ -9,13 +9,14 @@
#include "common/assert.h"
#include "common/bit_util.h"
#include "frontend/imm.h"
#include "frontend/A32/decoder/thumb16.h"
#include "frontend/A32/decoder/thumb32.h"
#include "frontend/A32/ir_emitter.h"
#include "frontend/A32/location_descriptor.h"
#include "frontend/A32/translate/conditional_state.h"
#include "frontend/A32/translate/impl/translate_thumb.h"
#include "frontend/A32/translate/translate.h"
#include "frontend/imm.h"
namespace Dynarmic::A32 {
namespace {
@ -25,7 +26,17 @@ enum class ThumbInstSize {
};
bool IsThumb16(u16 first_part) {
return (first_part & 0xF800) <= 0xE800;
return (first_part & 0xF800) < 0xE800;
}
bool IsUnconditionalInstruction(bool is_thumb_16, u32 instruction) {
if (!is_thumb_16)
return false;
if ((instruction & 0xFF00) == 0b10111110'00000000) // BKPT
return true;
if ((instruction & 0xFFC0) == 0b10111010'10000000) // HLT
return true;
return false;
}
std::tuple<u32, ThumbInstSize> ReadThumbInstruction(u32 arm_pc, MemoryReadCodeFuncType memory_read_code) {
@ -64,8 +75,10 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType m
do {
const u32 arm_pc = visitor.ir.current_location.PC();
const auto [thumb_instruction, inst_size] = ReadThumbInstruction(arm_pc, memory_read_code);
const bool is_thumb_16 = inst_size == ThumbInstSize::Thumb16;
if (inst_size == ThumbInstSize::Thumb16) {
if (IsUnconditionalInstruction(is_thumb_16, thumb_instruction) || visitor.ConditionPassed(is_thumb_16)) {
if (is_thumb_16) {
if (const auto decoder = DecodeThumb16<ThumbTranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
} else {
@ -78,16 +91,28 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType m
should_continue = visitor.thumb32_UDF();
}
}
const s32 advance_pc = (inst_size == ThumbInstSize::Thumb16) ? 2 : 4;
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(advance_pc);
block.CycleCount()++;
} while (should_continue && !single_step);
if (single_step && should_continue) {
visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location});
}
if (visitor.cond_state == ConditionalState::Break) {
break;
}
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(is_thumb_16 ? 2 : 4).AdvanceIT();
block.CycleCount()++;
} while (should_continue && CondCanContinue(visitor.cond_state, visitor.ir) && !single_step);
if (visitor.cond_state == ConditionalState::Translating || visitor.cond_state == ConditionalState::Trailing || single_step) {
if (should_continue) {
if (single_step) {
visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location});
} else {
visitor.ir.SetTerm(IR::Term::LinkBlockFast{visitor.ir.current_location});
}
}
}
ASSERT_MSG(block.HasTerminal(), "Terminal has not been set");
block.SetEndLocation(visitor.ir.current_location);
return block;
@ -105,6 +130,7 @@ bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descri
should_continue = visitor.thumb16_UDF();
}
} else {
thumb_instruction = Common::SwapHalves32(thumb_instruction);
if (const auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction)) {
should_continue = decoder->get().call(visitor, thumb_instruction);
} else {
@ -121,6 +147,11 @@ bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descri
return should_continue;
}
bool ThumbTranslatorVisitor::ConditionPassed(bool is_thumb_16) {
const Cond cond = ir.current_location.IT().Cond();
return IsConditionPassed(cond, cond_state, ir, is_thumb_16 ? 2 : 4);
}
bool ThumbTranslatorVisitor::InterpretThisInstruction() {
ir.SetTerm(IR::Term::Interpret(ir.current_location));
return false;

View file

@ -108,6 +108,11 @@ INST(DC_CVAU, "DC CVAU", "11010
INST(DC_CVAP, "DC CVAP", "110101010000101101111100001ttttt")
INST(DC_CIVAC, "DC CIVAC", "110101010000101101111110001ttttt")
// SYS: Instruction Cache
INST(IC_IALLU, "IC IALLU", "11010101000010000111010100011111")
INST(IC_IALLUIS, "IC IALLUIS", "11010101000010000111000100011111")
INST(IC_IVAU, "IC IVAU", "110101010000101101110101001ttttt")
// Unconditional branch (Register)
INST(BLR, "BLR", "1101011000111111000000nnnnn00000")
INST(BR, "BR", "1101011000011111000000nnnnn00000")

View file

@ -56,6 +56,10 @@ void IREmitter::DataCacheOperationRaised(DataCacheOperation op, const IR::U64& v
Inst(Opcode::A64DataCacheOperationRaised, Imm64(static_cast<u64>(op)), value);
}
void IREmitter::InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value) {
Inst(Opcode::A64InstructionCacheOperationRaised, Imm64(static_cast<u64>(op)), value);
}
void IREmitter::DataSynchronizationBarrier() {
Inst(Opcode::A64DataSynchronizationBarrier);
}

View file

@ -42,6 +42,7 @@ public:
void CallSupervisor(u32 imm);
void ExceptionRaised(Exception exception);
void DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value);
void InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value);
void DataSynchronizationBarrier();
void DataMemoryBarrier();
void InstructionSynchronizationBarrier();

View file

@ -174,6 +174,11 @@ struct TranslatorVisitor final {
bool DC_CVAP(Reg Rt);
bool DC_CIVAC(Reg Rt);
// SYS: Instruction Cache
bool IC_IALLU();
bool IC_IALLUIS();
bool IC_IVAU(Reg Rt);
// Unconditional branch (Register)
bool BR(Reg Rn);
bool BRA(bool Z, bool M, Reg Rn, Reg Rm);

View 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

View file

@ -8,23 +8,26 @@
namespace Dynarmic::A64 {
// Register encodings used by MRS and MSR.
// Order of fields: op0, CRn, op1, op2, CRm.
enum class SystemRegisterEncoding : u32 {
// Counter-timer Frequency register
CNTFRQ_EL0 = 0b11'011'1110'0000'000,
CNTFRQ_EL0 = 0b11'1110'011'000'0000,
// Counter-timer Physical Count register
CNTPCT_EL0 = 0b11'011'1110'0000'001,
CNTPCT_EL0 = 0b11'1110'011'001'0000,
// Cache Type Register
CTR_EL0 = 0b11'011'0000'0000'001,
CTR_EL0 = 0b11'0000'011'001'0000,
// Data Cache Zero ID register
DCZID_EL0 = 0b11'011'0000'0000'111,
DCZID_EL0 = 0b11'0000'011'111'0000,
// Floating-point Control Register
FPCR = 0b11'011'0100'0100'000,
FPCR = 0b11'0100'011'000'0100,
// Floating-point Status Register
FPSR = 0b11'011'0100'0100'001,
FPSR = 0b11'0100'011'001'0100,
// NZCV, Condition Flags
NZCV = 0b11'0100'011'000'0010,
// Read/Write Software Thread ID Register
TPIDR_EL0 = 0b11'011'1101'0000'010,
TPIDR_EL0 = 0b11'1101'011'010'0000,
// Read-Only Software Thread ID Register
TPIDRRO_EL0 = 0b11'011'1101'0000'011,
TPIDRRO_EL0 = 0b11'1101'011'011'0000,
};
bool TranslatorVisitor::HINT([[maybe_unused]] Imm<4> CRm, [[maybe_unused]] Imm<3> op2) {
@ -93,7 +96,7 @@ bool TranslatorVisitor::ISB(Imm<4> /*CRm*/) {
}
bool TranslatorVisitor::MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) {
const auto sys_reg = concatenate(Imm<1>{1}, o0, op1, CRn, CRm, op2).ZeroExtend<SystemRegisterEncoding>();
const auto sys_reg = concatenate(Imm<1>{1}, o0, CRn, op1, op2, CRm).ZeroExtend<SystemRegisterEncoding>();
switch (sys_reg) {
case SystemRegisterEncoding::FPCR:
ir.SetFPCR(X(32, Rt));
@ -103,6 +106,9 @@ bool TranslatorVisitor::MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, I
case SystemRegisterEncoding::FPSR:
ir.SetFPSR(X(32, Rt));
return true;
case SystemRegisterEncoding::NZCV:
ir.SetNZCVRaw(X(32, Rt));
return true;
case SystemRegisterEncoding::TPIDR_EL0:
ir.SetTPIDR(X(64, Rt));
return true;
@ -113,7 +119,7 @@ bool TranslatorVisitor::MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, I
}
bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) {
const auto sys_reg = concatenate(Imm<1>{1}, o0, op1, CRn, CRm, op2).ZeroExtend<SystemRegisterEncoding>();
const auto sys_reg = concatenate(Imm<1>{1}, o0, CRn, op1, op2, CRm).ZeroExtend<SystemRegisterEncoding>();
switch (sys_reg) {
case SystemRegisterEncoding::CNTFRQ_EL0:
X(32, Rt, ir.GetCNTFRQ());
@ -139,6 +145,9 @@ bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3
case SystemRegisterEncoding::FPSR:
X(32, Rt, ir.GetFPSR());
return true;
case SystemRegisterEncoding::NZCV:
X(32, Rt, ir.GetNZCVRaw());
return true;
case SystemRegisterEncoding::TPIDR_EL0:
X(64, Rt, ir.GetTPIDR());
return true;

View file

@ -155,6 +155,7 @@ bool Inst::ReadsFromCPSR() const {
case Opcode::A32GetCFlag:
case Opcode::A32GetVFlag:
case Opcode::A32GetGEFlags:
case Opcode::A32UpdateUpperLocationDescriptor:
case Opcode::A64GetCFlag:
case Opcode::A64GetNZCVRaw:
case Opcode::ConditionalSelect32:
@ -179,6 +180,7 @@ bool Inst::WritesToCPSR() const {
case Opcode::A32OrQFlag:
case Opcode::A32SetGEFlags:
case Opcode::A32SetGEFlagsCompressed:
case Opcode::A32UpdateUpperLocationDescriptor:
case Opcode::A64SetNZCVRaw:
case Opcode::A64SetNZCV:
return true;
@ -520,6 +522,7 @@ bool Inst::IsSetCheckBitOperation() const {
bool Inst::MayHaveSideEffects() const {
return op == Opcode::PushRSB ||
op == Opcode::A64DataCacheOperationRaised ||
op == Opcode::A64InstructionCacheOperationRaised ||
IsSetCheckBitOperation() ||
IsBarrier() ||
CausesCPUException() ||

View file

@ -31,6 +31,7 @@ A32OPC(GetGEFlags, U32,
A32OPC(SetGEFlags, Void, U32 )
A32OPC(SetGEFlagsCompressed, Void, U32 )
A32OPC(BXWritePC, Void, U32 )
A32OPC(UpdateUpperLocationDescriptor, Void, )
A32OPC(CallSupervisor, Void, U32 )
A32OPC(ExceptionRaised, Void, U32, U64 )
A32OPC(DataSynchronizationBarrier, Void, )
@ -68,6 +69,7 @@ A64OPC(SetPC, Void, U64
A64OPC(CallSupervisor, Void, U32 )
A64OPC(ExceptionRaised, Void, U64, U64 )
A64OPC(DataCacheOperationRaised, Void, U64, U64 )
A64OPC(InstructionCacheOperationRaised, Void, U64, U64 )
A64OPC(DataSynchronizationBarrier, Void, )
A64OPC(DataMemoryBarrier, Void, )
A64OPC(InstructionSynchronizationBarrier, Void, )

View file

@ -138,13 +138,13 @@ void FoldByteReverse(IR::Inst& inst, Op op) {
}
if (op == Op::ByteReverseWord) {
const u32 result = Common::Swap32(static_cast<u32>(operand.GetImmediateAsU64()));
const u32 result = Common::SwapBytes32(static_cast<u32>(operand.GetImmediateAsU64()));
inst.ReplaceUsesWith(IR::Value{result});
} else if (op == Op::ByteReverseHalf) {
const u16 result = Common::Swap16(static_cast<u16>(operand.GetImmediateAsU64()));
const u16 result = Common::SwapBytes16(static_cast<u16>(operand.GetImmediateAsU64()));
inst.ReplaceUsesWith(IR::Value{result});
} else {
const u64 result = Common::Swap64(operand.GetImmediateAsU64());
const u64 result = Common::SwapBytes64(operand.GetImmediateAsU64());
inst.ReplaceUsesWith(IR::Value{result});
}
}

View file

@ -16,8 +16,9 @@
#include "common/common_types.h"
#include "common/fp/fpcr.h"
#include "common/fp/fpsr.h"
#include "common/llvm_disassemble.h"
#include "common/scope_exit.h"
#include "frontend/A32/disassembler/disassembler.h"
#include "frontend/A32/ITState.h"
#include "frontend/A32/location_descriptor.h"
#include "frontend/A32/translate/translate.h"
#include "frontend/A32/types.h"
@ -36,8 +37,8 @@
namespace {
using namespace Dynarmic;
bool ShouldTestInst(u32 instruction, u32 pc, bool is_last_inst) {
const A32::LocationDescriptor location{pc, {}, {}};
bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A32::ITState it_state = {}) {
const A32::LocationDescriptor location = A32::LocationDescriptor{pc, {}, {}}.SetTFlag(is_thumb).SetIT(it_state);
IR::Block block{location};
const bool should_continue = A32::TranslateSingleInstruction(block, location, instruction);
@ -74,7 +75,7 @@ bool ShouldTestInst(u32 instruction, u32 pc, bool is_last_inst) {
return true;
}
u32 GenRandomInst(u32 pc, bool is_last_inst) {
u32 GenRandomArmInst(u32 pc, bool is_last_inst) {
static const struct InstructionGeneratorInfo {
std::vector<InstructionGenerator> generators;
std::vector<InstructionGenerator> invalid;
@ -139,13 +140,59 @@ u32 GenRandomInst(u32 pc, bool is_last_inst) {
continue;
}
if (ShouldTestInst(inst, pc, is_last_inst)) {
if (ShouldTestInst(inst, pc, false, is_last_inst)) {
return inst;
}
}
}
Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv& testenv) {
std::vector<u16> GenRandomThumbInst(u32 pc, bool is_last_inst, A32::ITState it_state = {}) {
static const struct InstructionGeneratorInfo {
std::vector<InstructionGenerator> generators;
std::vector<InstructionGenerator> invalid;
} instructions = []{
const std::vector<std::tuple<std::string, const char*>> list {
#define INST(fn, name, bitstring) {#fn, bitstring},
#include "frontend/A32/decoder/thumb16.inc"
#include "frontend/A32/decoder/thumb32.inc"
#undef INST
};
std::vector<InstructionGenerator> generators;
std::vector<InstructionGenerator> invalid;
// List of instructions not to test
static constexpr std::array do_not_test {
"thumb16_BKPT",
"thumb16_IT",
"thumb16_SETEND",
};
for (const auto& [fn, bitstring] : list) {
if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) {
invalid.emplace_back(InstructionGenerator{bitstring});
continue;
}
generators.emplace_back(InstructionGenerator{bitstring});
}
return InstructionGeneratorInfo{generators, invalid};
}();
while (true) {
const size_t index = RandInt<size_t>(0, instructions.generators.size() - 1);
const u32 inst = instructions.generators[index].Generate();
const bool is_four_bytes = (inst >> 16) != 0;
if (ShouldTestInst(is_four_bytes ? Common::SwapHalves32(inst) : inst, pc, true, is_last_inst, it_state)) {
if (is_four_bytes)
return { static_cast<u16>(inst >> 16), static_cast<u16>(inst) };
return { static_cast<u16>(inst) };
}
}
}
template <typename TestEnv>
Dynarmic::A32::UserConfig GetUserConfig(TestEnv& testenv) {
Dynarmic::A32::UserConfig user_config;
user_config.optimizations &= ~OptimizationFlag::FastDispatch;
user_config.callbacks = &testenv;
@ -153,22 +200,30 @@ Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv& testenv) {
return user_config;
}
static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn<ArmTestEnv>& uni,
ArmTestEnv& jit_env, ArmTestEnv& uni_env,
const A32Unicorn<ArmTestEnv>::RegisterArray& regs,
const A32Unicorn<ArmTestEnv>::ExtRegArray& vecs,
const std::vector<u32>& instructions, const u32 cpsr, const u32 fpscr) {
template <typename TestEnv>
static void RunTestInstance(Dynarmic::A32::Jit& jit,
A32Unicorn<TestEnv>& uni,
TestEnv& jit_env,
TestEnv& uni_env,
const typename A32Unicorn<TestEnv>::RegisterArray& regs,
const typename A32Unicorn<TestEnv>::ExtRegArray& vecs,
const std::vector<typename TestEnv::InstructionType>& instructions,
const u32 cpsr,
const u32 fpscr,
const size_t ticks_left) {
const u32 initial_pc = regs[15];
const u32 num_words = initial_pc / sizeof(u32);
const u32 num_words = initial_pc / sizeof(typename TestEnv::InstructionType);
const u32 code_mem_size = num_words + static_cast<u32>(instructions.size());
jit_env.code_mem.resize(code_mem_size + 1);
uni_env.code_mem.resize(code_mem_size + 1);
jit_env.code_mem.resize(code_mem_size);
uni_env.code_mem.resize(code_mem_size);
std::fill(jit_env.code_mem.begin(), jit_env.code_mem.end(), TestEnv::infinite_loop);
std::fill(uni_env.code_mem.begin(), uni_env.code_mem.end(), TestEnv::infinite_loop);
std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + num_words);
std::copy(instructions.begin(), instructions.end(), uni_env.code_mem.begin() + num_words);
jit_env.code_mem.back() = 0xEAFFFFFE; // B .
uni_env.code_mem.back() = 0xEAFFFFFE; // B .
jit_env.PadCodeMem();
uni_env.PadCodeMem();
jit_env.modified_memory.clear();
uni_env.modified_memory.clear();
jit_env.interrupts.clear();
@ -186,18 +241,15 @@ static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn<ArmTestEnv>& uni
uni.SetCpsr(cpsr);
uni.ClearPageCache();
jit_env.ticks_left = instructions.size();
jit_env.ticks_left = ticks_left;
jit.Run();
uni_env.ticks_left = instructions.size();
uni_env.ticks_left = instructions.size(); // Unicorn counts thumb instructions weirdly.
uni.Run();
SCOPE_FAIL {
fmt::print("Instruction Listing:\n");
for (u32 instruction : instructions) {
fmt::print("{:08x} {}\n", instruction, A32::DisassembleArm(instruction));
}
fmt::print("\n");
fmt::print("{}\n", Common::DisassembleAArch32(std::is_same_v<TestEnv, ThumbTestEnv>, initial_pc, (const u8*)instructions.data(), instructions.size() * sizeof(instructions[0])));
fmt::print("Initial register listing:\n");
for (size_t i = 0; i < regs.size(); ++i) {
@ -279,7 +331,7 @@ static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn<ArmTestEnv>& uni
}
} // Anonymous namespace
TEST_CASE("A32: Single random instruction", "[arm]") {
TEST_CASE("A32: Single random arm instruction", "[arm]") {
ArmTestEnv jit_env{};
ArmTestEnv uni_env{};
@ -294,7 +346,7 @@ TEST_CASE("A32: Single random instruction", "[arm]") {
std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
instructions[0] = GenRandomInst(0, true);
instructions[0] = GenRandomArmInst(0, true);
const u32 start_address = 100;
const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10;
@ -303,11 +355,11 @@ TEST_CASE("A32: Single random instruction", "[arm]") {
INFO("Instruction: 0x" << std::hex << instructions[0]);
regs[15] = start_address;
RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr);
RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 1);
}
}
TEST_CASE("A32: Small random block", "[arm]") {
TEST_CASE("A32: Small random arm block", "[arm]") {
ArmTestEnv jit_env{};
ArmTestEnv uni_env{};
@ -322,11 +374,11 @@ TEST_CASE("A32: Small random block", "[arm]") {
std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
instructions[0] = GenRandomInst(0, false);
instructions[1] = GenRandomInst(4, false);
instructions[2] = GenRandomInst(8, false);
instructions[3] = GenRandomInst(12, false);
instructions[4] = GenRandomInst(16, true);
instructions[0] = GenRandomArmInst(0, false);
instructions[1] = GenRandomArmInst(4, false);
instructions[2] = GenRandomArmInst(8, false);
instructions[3] = GenRandomArmInst(12, false);
instructions[4] = GenRandomArmInst(16, true);
const u32 start_address = 100;
const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10;
@ -339,11 +391,11 @@ TEST_CASE("A32: Small random block", "[arm]") {
INFO("Instruction 5: 0x" << std::hex << instructions[4]);
regs[15] = start_address;
RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr);
RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 5);
}
}
TEST_CASE("A32: Large random block", "[arm]") {
TEST_CASE("A32: Large random arm block", "[arm]") {
ArmTestEnv jit_env{};
ArmTestEnv uni_env{};
@ -361,7 +413,7 @@ TEST_CASE("A32: Large random block", "[arm]") {
std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
for (size_t j = 0; j < instruction_count; ++j) {
instructions[j] = GenRandomInst(j * 4, j == instruction_count - 1);
instructions[j] = GenRandomArmInst(j * 4, j == instruction_count - 1);
}
const u64 start_address = 100;
@ -369,6 +421,116 @@ TEST_CASE("A32: Large random block", "[arm]") {
const u32 fpcr = RandomFpcr();
regs[15] = start_address;
RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr);
RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 100);
}
}
TEST_CASE("A32: Single random thumb instruction", "[thumb]") {
ThumbTestEnv jit_env{};
ThumbTestEnv uni_env{};
Dynarmic::A32::Jit jit{GetUserConfig(jit_env)};
A32Unicorn<ThumbTestEnv> uni{uni_env};
A32Unicorn<ThumbTestEnv>::RegisterArray regs;
A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg;
std::vector<u16> instructions;
for (size_t iteration = 0; iteration < 100000; ++iteration) {
std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
instructions = GenRandomThumbInst(0, true);
const u32 start_address = 100;
const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0;
const u32 fpcr = RandomFpcr();
INFO("Instruction: 0x" << std::hex << instructions[0]);
regs[15] = start_address;
RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 1);
}
}
TEST_CASE("A32: Small random thumb block", "[thumb]") {
ThumbTestEnv jit_env{};
ThumbTestEnv uni_env{};
Dynarmic::A32::Jit jit{GetUserConfig(jit_env)};
A32Unicorn<ThumbTestEnv> uni{uni_env};
A32Unicorn<ThumbTestEnv>::RegisterArray regs;
A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg;
std::vector<u16> instructions;
for (size_t iteration = 0; iteration < 100000; ++iteration) {
std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
instructions.clear();
for (size_t i = 0; i < 5; i++) {
const std::vector<u16> inst = GenRandomThumbInst(instructions.size() * 2, i == 4);
instructions.insert(instructions.end(), inst.begin(), inst.end());
}
const u32 start_address = 100;
const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0;
const u32 fpcr = RandomFpcr();
regs[15] = start_address;
RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 5);
}
}
TEST_CASE("A32: Test thumb IT instruction", "[thumb]") {
ThumbTestEnv jit_env{};
ThumbTestEnv uni_env{};
Dynarmic::A32::Jit jit{GetUserConfig(jit_env)};
A32Unicorn<ThumbTestEnv> uni{uni_env};
A32Unicorn<ThumbTestEnv>::RegisterArray regs;
A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg;
std::vector<u16> instructions;
for (size_t iteration = 0; iteration < 100000; ++iteration) {
std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
const size_t pre_instructions = RandInt<size_t>(0, 3);
const size_t post_instructions = RandInt<size_t>(5, 8);
instructions.clear();
for (size_t i = 0; i < pre_instructions; i++) {
const std::vector<u16> inst = GenRandomThumbInst(instructions.size() * 2, false);
instructions.insert(instructions.end(), inst.begin(), inst.end());
}
// Emit IT instruction
A32::ITState it_state = [&]{
while (true) {
const u16 imm8 = RandInt<u16>(0, 0xFF);
if (Common::Bits<0, 3>(imm8) == 0b0000 || Common::Bits<4, 7>(imm8) == 0b1111 || (Common::Bits<4, 7>(imm8) == 0b1110 && Common::BitCount(Common::Bits<0, 3>(imm8)) != 1)) {
continue;
}
instructions.push_back(0b1011111100000000 | imm8);
return A32::ITState{static_cast<u8>(imm8)};
}
}();
for (size_t i = 0; i < post_instructions; i++) {
const std::vector<u16> inst = GenRandomThumbInst(instructions.size() * 2, i == post_instructions - 1, it_state);
instructions.insert(instructions.end(), inst.begin(), inst.end());
it_state = it_state.Advance();
}
const u32 start_address = 100;
const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0;
const u32 fpcr = RandomFpcr();
regs[15] = start_address;
RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, pre_instructions + 1 + post_instructions);
}
}

View file

@ -9,6 +9,7 @@
#include <cstdio>
#include <cstring>
#include <functional>
#include <string_view>
#include <tuple>
#include <catch.hpp>
@ -41,11 +42,13 @@ using WriteRecords = std::map<u32, u8>;
struct ThumbInstGen final {
public:
ThumbInstGen(const char* format, std::function<bool(u16)> is_valid = [](u16){ return true; }) : is_valid(is_valid) {
REQUIRE(strlen(format) == 16);
ThumbInstGen(std::string_view format, std::function<bool(u32)> is_valid = [](u32){ return true; }) : is_valid(is_valid) {
REQUIRE((format.size() == 16 || format.size() == 32));
for (int i = 0; i < 16; i++) {
const u16 bit = 1 << (15 - i);
const auto bit_size = format.size();
for (size_t i = 0; i < bit_size; i++) {
const u32 bit = 1U << (bit_size - 1 - i);
switch (format[i]) {
case '0':
mask |= bit;
@ -60,11 +63,25 @@ public:
}
}
}
u16 Generate() const {
u16 inst;
u16 Generate16() const {
u32 inst;
do {
const u16 random = RandInt<u16>(0, 0xFFFF);
const auto random = RandInt<u16>(0, 0xFFFF);
inst = bits | (random & ~mask);
} while (!is_valid(inst));
ASSERT((inst & mask) == bits);
return static_cast<u16>(inst);
}
u32 Generate32() const {
u32 inst;
do {
const auto random = RandInt<u32>(0, 0xFFFFFFFF);
inst = bits | (random & ~mask);
} while (!is_valid(inst));
@ -72,10 +89,11 @@ public:
return inst;
}
private:
u16 bits = 0;
u16 mask = 0;
std::function<bool(u16)> is_valid;
u32 bits = 0;
u32 mask = 0;
std::function<bool(u32)> is_valid;
};
static bool DoesBehaviorMatch(const A32Unicorn<ThumbTestEnv>& uni, const A32::Jit& jit,
@ -179,7 +197,7 @@ static void RunInstance(size_t run_number, ThumbTestEnv& test_env, A32Unicorn<Th
}
}
void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) {
void FuzzJitThumb16(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) {
ThumbTestEnv test_env;
// Prepare memory.
@ -201,7 +219,37 @@ void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_e
}
}
TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
void FuzzJitThumb32(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u32()> instruction_generator) {
ThumbTestEnv test_env;
// Prepare memory.
// A Thumb-32 instruction is 32-bits so we multiply our count
test_env.code_mem.resize(instruction_count * 2 + 1);
test_env.code_mem.back() = 0xE7FE; // b +#0
// Prepare test subjects
A32Unicorn uni{test_env};
A32::Jit jit{GetUserConfig(&test_env)};
for (size_t run_number = 0; run_number < run_count; run_number++) {
ThumbTestEnv::RegisterArray initial_regs;
std::generate_n(initial_regs.begin(), initial_regs.size() - 1, []{ return RandInt<u32>(0, 0xFFFFFFFF); });
initial_regs[15] = 0;
for (size_t i = 0; i < instruction_count; i++) {
const auto instruction = instruction_generator();
const auto first_halfword = static_cast<u16>(Common::Bits<0, 15>(instruction));
const auto second_halfword = static_cast<u16>(Common::Bits<16, 31>(instruction));
test_env.code_mem[i * 2 + 0] = second_halfword;
test_env.code_mem[i * 2 + 1] = first_halfword;
}
RunInstance(run_number, test_env, uni, jit, initial_regs, instruction_count, instructions_to_execute_count);
}
}
TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb][Thumb16]") {
const std::array instructions = {
ThumbInstGen("00000xxxxxxxxxxx"), // LSL <Rd>, <Rm>, #<imm5>
ThumbInstGen("00001xxxxxxxxxxx"), // LSR <Rd>, <Rm>, #<imm5>
@ -212,9 +260,9 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
ThumbInstGen("010000ooooxxxxxx"), // Data Processing
ThumbInstGen("010001000hxxxxxx"), // ADD (high registers)
ThumbInstGen("0100010101xxxxxx", // CMP (high registers)
[](u16 inst){ return Common::Bits<3, 5>(inst) != 0b111; }), // R15 is UNPREDICTABLE
[](u32 inst){ return Common::Bits<3, 5>(inst) != 0b111; }), // R15 is UNPREDICTABLE
ThumbInstGen("0100010110xxxxxx", // CMP (high registers)
[](u16 inst){ return Common::Bits<0, 2>(inst) != 0b111; }), // R15 is UNPREDICTABLE
[](u32 inst){ return Common::Bits<0, 2>(inst) != 0b111; }), // R15 is UNPREDICTABLE
ThumbInstGen("010001100hxxxxxx"), // MOV (high registers)
ThumbInstGen("10110000oxxxxxxx"), // Adjust stack pointer
ThumbInstGen("10110010ooxxxxxx"), // SXT/UXT
@ -227,11 +275,11 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
ThumbInstGen("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset]
ThumbInstGen("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #]
ThumbInstGen("1011010xxxxxxxxx", // PUSH
[](u16 inst){ return Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
[](u32 inst){ return Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
ThumbInstGen("10111100xxxxxxxx", // POP (P = 0)
[](u16 inst){ return Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
[](u32 inst){ return Common::Bits<0, 7>(inst) != 0; }), // Empty reg_list is UNPREDICTABLE
ThumbInstGen("1100xxxxxxxxxxxx", // STMIA/LDMIA
[](u16 inst) {
[](u32 inst) {
// Ensure that the architecturally undefined case of
// the base register being within the list isn't hit.
const u32 rn = Common::Bits<8, 10>(inst);
@ -247,29 +295,29 @@ TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") {
};
const auto instruction_select = [&]() -> u16 {
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
const auto inst_index = RandInt<size_t>(0, instructions.size() - 1);
return instructions[inst_index].Generate();
return instructions[inst_index].Generate16();
};
SECTION("single instructions") {
FuzzJitThumb(1, 2, 10000, instruction_select);
FuzzJitThumb16(1, 2, 10000, instruction_select);
}
SECTION("short blocks") {
FuzzJitThumb(5, 6, 3000, instruction_select);
FuzzJitThumb16(5, 6, 3000, instruction_select);
}
// TODO: Test longer blocks when Unicorn can consistently
// run these without going into an infinite loop.
#if 0
SECTION("long blocks") {
FuzzJitThumb(1024, 1025, 1000, instruction_select);
FuzzJitThumb16(1024, 1025, 1000, instruction_select);
}
#endif
}
TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb][Thumb16]") {
const std::array instructions = {
// TODO: We currently can't test BX/BLX as we have
// no way of preventing the unpredictable
@ -278,7 +326,7 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
// must not be address<1:0> == '10'.
#if 0
ThumbInstGen("01000111xmmmm000", // BLX/BX
[](u16 inst){
[](u32 inst){
const u32 Rm = Common::Bits<3, 6>(inst);
return Rm != 15;
}),
@ -288,7 +336,7 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
ThumbInstGen("01000100h0xxxxxx"), // ADD (high registers)
ThumbInstGen("01000110h0xxxxxx"), // MOV (high registers)
ThumbInstGen("1101ccccxxxxxxxx", // B<cond>
[](u16 inst){
[](u32 inst){
const u32 c = Common::Bits<9, 12>(inst);
return c < 0b1110; // Don't want SWI or undefined instructions.
}),
@ -304,15 +352,158 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
};
const auto instruction_select = [&]() -> u16 {
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
const auto inst_index = RandInt<size_t>(0, instructions.size() - 1);
return instructions[inst_index].Generate();
return instructions[inst_index].Generate16();
};
FuzzJitThumb(1, 1, 10000, instruction_select);
FuzzJitThumb16(1, 1, 10000, instruction_select);
}
TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") {
TEST_CASE("Fuzz Thumb32 instructions set", "[JitX64][Thumb][Thumb32]") {
const auto three_reg_not_r15 = [](u32 inst) {
const auto d = Common::Bits<8, 11>(inst);
const auto m = Common::Bits<0, 3>(inst);
const auto n = Common::Bits<16, 19>(inst);
return d != 15 && m != 15 && n != 15;
};
const std::array instructions = {
ThumbInstGen("111110101011nnnn1111dddd1000mmmm", // CLZ
[](u32 inst) {
const auto d = Common::Bits<8, 11>(inst);
const auto m = Common::Bits<0, 3>(inst);
const auto n = Common::Bits<16, 19>(inst);
return m == n && d != 15 && m != 15;
}),
ThumbInstGen("111110101000nnnn1111dddd1000mmmm", // QADD
three_reg_not_r15),
ThumbInstGen("111110101000nnnn1111dddd0001mmmm", // QADD8
three_reg_not_r15),
ThumbInstGen("111110101001nnnn1111dddd0001mmmm", // QADD16
three_reg_not_r15),
ThumbInstGen("111110101010nnnn1111dddd0001mmmm", // QASX
three_reg_not_r15),
ThumbInstGen("111110101000nnnn1111dddd1001mmmm", // QDADD
three_reg_not_r15),
ThumbInstGen("111110101000nnnn1111dddd1011mmmm", // QDSUB
three_reg_not_r15),
ThumbInstGen("111110101110nnnn1111dddd0001mmmm", // QSAX
three_reg_not_r15),
ThumbInstGen("111110101000nnnn1111dddd1010mmmm", // QSUB
three_reg_not_r15),
ThumbInstGen("111110101100nnnn1111dddd0001mmmm", // QSUB8
three_reg_not_r15),
ThumbInstGen("111110101101nnnn1111dddd0001mmmm", // QSUB16
three_reg_not_r15),
ThumbInstGen("111110101001nnnn1111dddd1010mmmm", // RBIT
[](u32 inst) {
const auto d = Common::Bits<8, 11>(inst);
const auto m = Common::Bits<0, 3>(inst);
const auto n = Common::Bits<16, 19>(inst);
return m == n && d != 15 && m != 15;
}),
ThumbInstGen("111110101001nnnn1111dddd1000mmmm", // REV
[](u32 inst) {
const auto d = Common::Bits<8, 11>(inst);
const auto m = Common::Bits<0, 3>(inst);
const auto n = Common::Bits<16, 19>(inst);
return m == n && d != 15 && m != 15;
}),
ThumbInstGen("111110101001nnnn1111dddd1001mmmm", // REV16
[](u32 inst) {
const auto d = Common::Bits<8, 11>(inst);
const auto m = Common::Bits<0, 3>(inst);
const auto n = Common::Bits<16, 19>(inst);
return m == n && d != 15 && m != 15;
}),
ThumbInstGen("111110101001nnnn1111dddd1011mmmm", // REVSH
[](u32 inst) {
const auto d = Common::Bits<8, 11>(inst);
const auto m = Common::Bits<0, 3>(inst);
const auto n = Common::Bits<16, 19>(inst);
return m == n && d != 15 && m != 15;
}),
ThumbInstGen("111110101000nnnn1111dddd0000mmmm", // SADD8
three_reg_not_r15),
ThumbInstGen("111110101001nnnn1111dddd0000mmmm", // SADD16
three_reg_not_r15),
ThumbInstGen("111110101010nnnn1111dddd0000mmmm", // SASX
three_reg_not_r15),
ThumbInstGen("111110101010nnnn1111dddd1000mmmm", // SEL
three_reg_not_r15),
ThumbInstGen("111110101000nnnn1111dddd0010mmmm", // SHADD8
three_reg_not_r15),
ThumbInstGen("111110101001nnnn1111dddd0010mmmm", // SHADD16
three_reg_not_r15),
ThumbInstGen("111110101010nnnn1111dddd0010mmmm", // SHASX
three_reg_not_r15),
ThumbInstGen("111110101110nnnn1111dddd0010mmmm", // SHSAX
three_reg_not_r15),
ThumbInstGen("111110101100nnnn1111dddd0010mmmm", // SHSUB8
three_reg_not_r15),
ThumbInstGen("111110101101nnnn1111dddd0010mmmm", // SHSUB16
three_reg_not_r15),
ThumbInstGen("111110101110nnnn1111dddd0000mmmm", // SSAX
three_reg_not_r15),
ThumbInstGen("111110101100nnnn1111dddd0000mmmm", // SSUB8
three_reg_not_r15),
ThumbInstGen("111110101101nnnn1111dddd0000mmmm", // SSUB16
three_reg_not_r15),
ThumbInstGen("111110101000nnnn1111dddd0100mmmm", // UADD8
three_reg_not_r15),
ThumbInstGen("111110101001nnnn1111dddd0100mmmm", // UADD16
three_reg_not_r15),
ThumbInstGen("111110101010nnnn1111dddd0100mmmm", // UASX
three_reg_not_r15),
ThumbInstGen("111110101000nnnn1111dddd0110mmmm", // UHADD8
three_reg_not_r15),
ThumbInstGen("111110101001nnnn1111dddd0110mmmm", // UHADD16
three_reg_not_r15),
ThumbInstGen("111110101010nnnn1111dddd0110mmmm", // UHASX
three_reg_not_r15),
ThumbInstGen("111110101110nnnn1111dddd0110mmmm", // UHSAX
three_reg_not_r15),
ThumbInstGen("111110101100nnnn1111dddd0110mmmm", // UHSUB8
three_reg_not_r15),
ThumbInstGen("111110101101nnnn1111dddd0110mmmm", // UHSUB16
three_reg_not_r15),
ThumbInstGen("111110101000nnnn1111dddd0101mmmm", // UQADD8
three_reg_not_r15),
ThumbInstGen("111110101001nnnn1111dddd0101mmmm", // UQADD16
three_reg_not_r15),
ThumbInstGen("111110101010nnnn1111dddd0101mmmm", // UQASX
three_reg_not_r15),
ThumbInstGen("111110101110nnnn1111dddd0101mmmm", // UQSAX
three_reg_not_r15),
ThumbInstGen("111110101100nnnn1111dddd0101mmmm", // UQSUB8
three_reg_not_r15),
ThumbInstGen("111110101101nnnn1111dddd0101mmmm", // UQSUB16
three_reg_not_r15),
ThumbInstGen("111110101110nnnn1111dddd0100mmmm", // USAX
three_reg_not_r15),
ThumbInstGen("111110101100nnnn1111dddd0100mmmm", // USUB8
three_reg_not_r15),
ThumbInstGen("111110101101nnnn1111dddd0100mmmm", // USUB16
three_reg_not_r15),
};
const auto instruction_select = [&]() -> u32 {
const auto inst_index = RandInt<size_t>(0, instructions.size() - 1);
return instructions[inst_index].Generate32();
};
SECTION("single instructions") {
FuzzJitThumb32(1, 2, 10000, instruction_select);
}
SECTION("short blocks") {
FuzzJitThumb32(5, 6, 3000, instruction_select);
}
}
TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb][Thumb16]") {
ThumbTestEnv test_env;
// Prepare test subjects

View file

@ -16,31 +16,49 @@
#include "common/assert.h"
#include "common/common_types.h"
template <typename InstructionType_, u32 infinite_loop>
template <typename InstructionType_, u32 infinite_loop_u32>
class A32TestEnv final : public Dynarmic::A32::UserCallbacks {
public:
using InstructionType = InstructionType_;
using RegisterArray = std::array<u32, 16>;
using ExtRegsArray = std::array<u32, 64>;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4309) // C4309: 'static_cast': truncation of constant value
#endif
static constexpr InstructionType infinite_loop = static_cast<InstructionType>(infinite_loop_u32);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
u64 ticks_left = 0;
bool code_mem_modified_by_guest = false;
std::vector<InstructionType> code_mem;
std::map<u32, u8> modified_memory;
std::vector<std::string> interrupts;
void PadCodeMem() {
do {
code_mem.push_back(infinite_loop);
} while (code_mem.size() % 2 != 0);
}
bool IsInCodeMem(u32 vaddr) const {
return vaddr < sizeof(InstructionType) * code_mem.size();
}
std::uint32_t MemoryReadCode(u32 vaddr) override {
const size_t index = vaddr / sizeof(InstructionType);
if (index < code_mem.size()) {
if (IsInCodeMem(vaddr)) {
u32 value;
std::memcpy(&value, &code_mem[index], sizeof(u32));
std::memcpy(&value, &code_mem[vaddr / sizeof(InstructionType)], sizeof(u32));
return value;
}
return infinite_loop; // B .
return infinite_loop_u32; // B .
}
std::uint8_t MemoryRead8(u32 vaddr) override {
if (vaddr < sizeof(InstructionType) * code_mem.size()) {
if (IsInCodeMem(vaddr)) {
return reinterpret_cast<u8*>(code_mem.data())[vaddr];
}
if (auto iter = modified_memory.find(vaddr); iter != modified_memory.end()) {
@ -81,7 +99,7 @@ public:
void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC({})", swi); }
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised({:08x})", pc); }
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised({:08x}) code = {:08x}", pc, MemoryReadCode(pc)); }
void AddTicks(std::uint64_t ticks) override {
if (ticks > ticks_left) {

View file

@ -634,3 +634,31 @@ TEST_CASE("A64: Optimization failure when folding ADD", "[a64]") {
REQUIRE(jit.GetPstate() == 0x20000000);
REQUIRE(jit.GetVector(30) == Vector{0xf7f6f5f4, 0});
}
TEST_CASE("A64: Cache Maintenance Instructions", "[a64]") {
class CacheMaintenanceTestEnv final : public A64TestEnv {
void InstructionCacheOperationRaised(A64::InstructionCacheOperation op, VAddr value) override {
REQUIRE(op == A64::InstructionCacheOperation::InvalidateByVAToPoU);
REQUIRE(value == 0xcafed00d);
}
void DataCacheOperationRaised(A64::DataCacheOperation op, VAddr value) override {
REQUIRE(op == A64::DataCacheOperation::InvalidateByVAToPoC);
REQUIRE(value == 0xcafebabe);
}
};
CacheMaintenanceTestEnv env;
A64::UserConfig conf{&env};
conf.hook_data_cache_operations = true;
A64::Jit jit{conf};
jit.SetRegister(0, 0xcafed00d);
jit.SetRegister(1, 0xcafebabe);
env.code_mem.emplace_back(0xd50b7520); // ic ivau, x0
env.code_mem.emplace_back(0xd5087621); // dc ivac, x1
env.code_mem.emplace_back(0x14000000); // B .
env.ticks_left = 3;
jit.Run();
}

View file

@ -15,7 +15,7 @@
using Vector = Dynarmic::A64::Vector;
class A64TestEnv final : public Dynarmic::A64::UserCallbacks {
class A64TestEnv : public Dynarmic::A64::UserCallbacks {
public:
u64 ticks_left = 0;

View file

@ -35,10 +35,16 @@ u32 RandomFpcr() {
}
InstructionGenerator::InstructionGenerator(const char* format){
ASSERT(std::strlen(format) == 32);
const size_t format_len = std::strlen(format);
ASSERT(format_len == 16 || format_len == 32);
for (int i = 0; i < 32; i++) {
const u32 bit = 1u << (31 - i);
if (format_len == 16) {
// Begin with 16 zeros
mask |= 0xFFFF0000;
}
for (size_t i = 0; i < format_len; i++) {
const u32 bit = 1u << (format_len - i - 1);
switch (format[i]) {
case '0':
mask |= bit;

View file

@ -54,7 +54,7 @@ const char* GetNameOfA64Instruction(u32 instruction) {
}
void PrintA32Instruction(u32 instruction) {
fmt::print("{:08x} {}\n", instruction, Common::DisassembleAArch32(instruction));
fmt::print("{:08x} {}\n", instruction, Common::DisassembleAArch32(false, 0, (u8*)&instruction, sizeof(instruction)));
fmt::print("Name: {}\n", GetNameOfA32Instruction(instruction));
const A32::LocationDescriptor location{0, {}, {}};

View file

@ -44,6 +44,9 @@ void A32Unicorn<TestEnvironment>::Run() {
constexpr u64 pc_mask = std::is_same_v<TestEnvironment, ArmTestEnv> ? 0 : 1;
while (testenv.ticks_left > 0) {
const u32 pc = GetPC() | pc_mask;
if (!testenv.IsInCodeMem(pc)) {
return;
}
if (auto cerr_ = uc_emu_start(uc, pc, END_ADDRESS, 0, 1)) {
ASSERT_MSG(false, "uc_emu_start failed @ {:08x} (code = {:08x}) with error {} ({})", pc, testenv.MemoryReadCode(pc), cerr_, uc_strerror(cerr_));
}