diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp')
-rw-r--r-- | contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp | 14473 |
1 files changed, 14473 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp new file mode 100644 index 000000000000..147c00e51b40 --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp @@ -0,0 +1,14473 @@ +//===-- EmulateInstructionARM.cpp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include <cstdlib> +#include <optional> + +#include "EmulateInstructionARM.h" +#include "EmulationStateARM.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/PosixApi.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueDictionary.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/Stream.h" + +#include "Plugins/Process/Utility/ARMDefines.h" +#include "Plugins/Process/Utility/ARMUtils.h" +#include "Utility/ARM_DWARF_Registers.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/MathExtras.h" + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionARM, InstructionARM) + +// Convenient macro definitions. +#define APSR_C Bit32(m_opcode_cpsr, CPSR_C_POS) +#define APSR_V Bit32(m_opcode_cpsr, CPSR_V_POS) + +#define AlignPC(pc_val) (pc_val & 0xFFFFFFFC) + +// +// ITSession implementation +// + +static std::optional<RegisterInfo> GetARMDWARFRegisterInfo(unsigned reg_num) { + RegisterInfo reg_info; + ::memset(®_info, 0, sizeof(RegisterInfo)); + ::memset(reg_info.kinds, LLDB_INVALID_REGNUM, sizeof(reg_info.kinds)); + + if (reg_num >= dwarf_q0 && reg_num <= dwarf_q15) { + reg_info.byte_size = 16; + reg_info.format = eFormatVectorOfUInt8; + reg_info.encoding = eEncodingVector; + } + + if (reg_num >= dwarf_d0 && reg_num <= dwarf_d31) { + reg_info.byte_size = 8; + reg_info.format = eFormatFloat; + reg_info.encoding = eEncodingIEEE754; + } else if (reg_num >= dwarf_s0 && reg_num <= dwarf_s31) { + reg_info.byte_size = 4; + reg_info.format = eFormatFloat; + reg_info.encoding = eEncodingIEEE754; + } else if (reg_num >= dwarf_f0 && reg_num <= dwarf_f7) { + reg_info.byte_size = 12; + reg_info.format = eFormatFloat; + reg_info.encoding = eEncodingIEEE754; + } else { + reg_info.byte_size = 4; + reg_info.format = eFormatHex; + reg_info.encoding = eEncodingUint; + } + + reg_info.kinds[eRegisterKindDWARF] = reg_num; + + switch (reg_num) { + case dwarf_r0: + reg_info.name = "r0"; + break; + case dwarf_r1: + reg_info.name = "r1"; + break; + case dwarf_r2: + reg_info.name = "r2"; + break; + case dwarf_r3: + reg_info.name = "r3"; + break; + case dwarf_r4: + reg_info.name = "r4"; + break; + case dwarf_r5: + reg_info.name = "r5"; + break; + case dwarf_r6: + reg_info.name = "r6"; + break; + case dwarf_r7: + reg_info.name = "r7"; + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FP; + break; + case dwarf_r8: + reg_info.name = "r8"; + break; + case dwarf_r9: + reg_info.name = "r9"; + break; + case dwarf_r10: + reg_info.name = "r10"; + break; + case dwarf_r11: + reg_info.name = "r11"; + break; + case dwarf_r12: + reg_info.name = "r12"; + break; + case dwarf_sp: + reg_info.name = "sp"; + reg_info.alt_name = "r13"; + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_SP; + break; + case dwarf_lr: + reg_info.name = "lr"; + reg_info.alt_name = "r14"; + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_RA; + break; + case dwarf_pc: + reg_info.name = "pc"; + reg_info.alt_name = "r15"; + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + break; + case dwarf_cpsr: + reg_info.name = "cpsr"; + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_FLAGS; + break; + + case dwarf_s0: + reg_info.name = "s0"; + break; + case dwarf_s1: + reg_info.name = "s1"; + break; + case dwarf_s2: + reg_info.name = "s2"; + break; + case dwarf_s3: + reg_info.name = "s3"; + break; + case dwarf_s4: + reg_info.name = "s4"; + break; + case dwarf_s5: + reg_info.name = "s5"; + break; + case dwarf_s6: + reg_info.name = "s6"; + break; + case dwarf_s7: + reg_info.name = "s7"; + break; + case dwarf_s8: + reg_info.name = "s8"; + break; + case dwarf_s9: + reg_info.name = "s9"; + break; + case dwarf_s10: + reg_info.name = "s10"; + break; + case dwarf_s11: + reg_info.name = "s11"; + break; + case dwarf_s12: + reg_info.name = "s12"; + break; + case dwarf_s13: + reg_info.name = "s13"; + break; + case dwarf_s14: + reg_info.name = "s14"; + break; + case dwarf_s15: + reg_info.name = "s15"; + break; + case dwarf_s16: + reg_info.name = "s16"; + break; + case dwarf_s17: + reg_info.name = "s17"; + break; + case dwarf_s18: + reg_info.name = "s18"; + break; + case dwarf_s19: + reg_info.name = "s19"; + break; + case dwarf_s20: + reg_info.name = "s20"; + break; + case dwarf_s21: + reg_info.name = "s21"; + break; + case dwarf_s22: + reg_info.name = "s22"; + break; + case dwarf_s23: + reg_info.name = "s23"; + break; + case dwarf_s24: + reg_info.name = "s24"; + break; + case dwarf_s25: + reg_info.name = "s25"; + break; + case dwarf_s26: + reg_info.name = "s26"; + break; + case dwarf_s27: + reg_info.name = "s27"; + break; + case dwarf_s28: + reg_info.name = "s28"; + break; + case dwarf_s29: + reg_info.name = "s29"; + break; + case dwarf_s30: + reg_info.name = "s30"; + break; + case dwarf_s31: + reg_info.name = "s31"; + break; + + // FPA Registers 0-7 + case dwarf_f0: + reg_info.name = "f0"; + break; + case dwarf_f1: + reg_info.name = "f1"; + break; + case dwarf_f2: + reg_info.name = "f2"; + break; + case dwarf_f3: + reg_info.name = "f3"; + break; + case dwarf_f4: + reg_info.name = "f4"; + break; + case dwarf_f5: + reg_info.name = "f5"; + break; + case dwarf_f6: + reg_info.name = "f6"; + break; + case dwarf_f7: + reg_info.name = "f7"; + break; + + // Intel wireless MMX general purpose registers 0 - 7 XScale accumulator + // register 0 - 7 (they do overlap with wCGR0 - wCGR7) + case dwarf_wCGR0: + reg_info.name = "wCGR0/ACC0"; + break; + case dwarf_wCGR1: + reg_info.name = "wCGR1/ACC1"; + break; + case dwarf_wCGR2: + reg_info.name = "wCGR2/ACC2"; + break; + case dwarf_wCGR3: + reg_info.name = "wCGR3/ACC3"; + break; + case dwarf_wCGR4: + reg_info.name = "wCGR4/ACC4"; + break; + case dwarf_wCGR5: + reg_info.name = "wCGR5/ACC5"; + break; + case dwarf_wCGR6: + reg_info.name = "wCGR6/ACC6"; + break; + case dwarf_wCGR7: + reg_info.name = "wCGR7/ACC7"; + break; + + // Intel wireless MMX data registers 0 - 15 + case dwarf_wR0: + reg_info.name = "wR0"; + break; + case dwarf_wR1: + reg_info.name = "wR1"; + break; + case dwarf_wR2: + reg_info.name = "wR2"; + break; + case dwarf_wR3: + reg_info.name = "wR3"; + break; + case dwarf_wR4: + reg_info.name = "wR4"; + break; + case dwarf_wR5: + reg_info.name = "wR5"; + break; + case dwarf_wR6: + reg_info.name = "wR6"; + break; + case dwarf_wR7: + reg_info.name = "wR7"; + break; + case dwarf_wR8: + reg_info.name = "wR8"; + break; + case dwarf_wR9: + reg_info.name = "wR9"; + break; + case dwarf_wR10: + reg_info.name = "wR10"; + break; + case dwarf_wR11: + reg_info.name = "wR11"; + break; + case dwarf_wR12: + reg_info.name = "wR12"; + break; + case dwarf_wR13: + reg_info.name = "wR13"; + break; + case dwarf_wR14: + reg_info.name = "wR14"; + break; + case dwarf_wR15: + reg_info.name = "wR15"; + break; + + case dwarf_spsr: + reg_info.name = "spsr"; + break; + case dwarf_spsr_fiq: + reg_info.name = "spsr_fiq"; + break; + case dwarf_spsr_irq: + reg_info.name = "spsr_irq"; + break; + case dwarf_spsr_abt: + reg_info.name = "spsr_abt"; + break; + case dwarf_spsr_und: + reg_info.name = "spsr_und"; + break; + case dwarf_spsr_svc: + reg_info.name = "spsr_svc"; + break; + + case dwarf_r8_usr: + reg_info.name = "r8_usr"; + break; + case dwarf_r9_usr: + reg_info.name = "r9_usr"; + break; + case dwarf_r10_usr: + reg_info.name = "r10_usr"; + break; + case dwarf_r11_usr: + reg_info.name = "r11_usr"; + break; + case dwarf_r12_usr: + reg_info.name = "r12_usr"; + break; + case dwarf_r13_usr: + reg_info.name = "r13_usr"; + break; + case dwarf_r14_usr: + reg_info.name = "r14_usr"; + break; + case dwarf_r8_fiq: + reg_info.name = "r8_fiq"; + break; + case dwarf_r9_fiq: + reg_info.name = "r9_fiq"; + break; + case dwarf_r10_fiq: + reg_info.name = "r10_fiq"; + break; + case dwarf_r11_fiq: + reg_info.name = "r11_fiq"; + break; + case dwarf_r12_fiq: + reg_info.name = "r12_fiq"; + break; + case dwarf_r13_fiq: + reg_info.name = "r13_fiq"; + break; + case dwarf_r14_fiq: + reg_info.name = "r14_fiq"; + break; + case dwarf_r13_irq: + reg_info.name = "r13_irq"; + break; + case dwarf_r14_irq: + reg_info.name = "r14_irq"; + break; + case dwarf_r13_abt: + reg_info.name = "r13_abt"; + break; + case dwarf_r14_abt: + reg_info.name = "r14_abt"; + break; + case dwarf_r13_und: + reg_info.name = "r13_und"; + break; + case dwarf_r14_und: + reg_info.name = "r14_und"; + break; + case dwarf_r13_svc: + reg_info.name = "r13_svc"; + break; + case dwarf_r14_svc: + reg_info.name = "r14_svc"; + break; + + // Intel wireless MMX control register in co-processor 0 - 7 + case dwarf_wC0: + reg_info.name = "wC0"; + break; + case dwarf_wC1: + reg_info.name = "wC1"; + break; + case dwarf_wC2: + reg_info.name = "wC2"; + break; + case dwarf_wC3: + reg_info.name = "wC3"; + break; + case dwarf_wC4: + reg_info.name = "wC4"; + break; + case dwarf_wC5: + reg_info.name = "wC5"; + break; + case dwarf_wC6: + reg_info.name = "wC6"; + break; + case dwarf_wC7: + reg_info.name = "wC7"; + break; + + // VFP-v3/Neon + case dwarf_d0: + reg_info.name = "d0"; + break; + case dwarf_d1: + reg_info.name = "d1"; + break; + case dwarf_d2: + reg_info.name = "d2"; + break; + case dwarf_d3: + reg_info.name = "d3"; + break; + case dwarf_d4: + reg_info.name = "d4"; + break; + case dwarf_d5: + reg_info.name = "d5"; + break; + case dwarf_d6: + reg_info.name = "d6"; + break; + case dwarf_d7: + reg_info.name = "d7"; + break; + case dwarf_d8: + reg_info.name = "d8"; + break; + case dwarf_d9: + reg_info.name = "d9"; + break; + case dwarf_d10: + reg_info.name = "d10"; + break; + case dwarf_d11: + reg_info.name = "d11"; + break; + case dwarf_d12: + reg_info.name = "d12"; + break; + case dwarf_d13: + reg_info.name = "d13"; + break; + case dwarf_d14: + reg_info.name = "d14"; + break; + case dwarf_d15: + reg_info.name = "d15"; + break; + case dwarf_d16: + reg_info.name = "d16"; + break; + case dwarf_d17: + reg_info.name = "d17"; + break; + case dwarf_d18: + reg_info.name = "d18"; + break; + case dwarf_d19: + reg_info.name = "d19"; + break; + case dwarf_d20: + reg_info.name = "d20"; + break; + case dwarf_d21: + reg_info.name = "d21"; + break; + case dwarf_d22: + reg_info.name = "d22"; + break; + case dwarf_d23: + reg_info.name = "d23"; + break; + case dwarf_d24: + reg_info.name = "d24"; + break; + case dwarf_d25: + reg_info.name = "d25"; + break; + case dwarf_d26: + reg_info.name = "d26"; + break; + case dwarf_d27: + reg_info.name = "d27"; + break; + case dwarf_d28: + reg_info.name = "d28"; + break; + case dwarf_d29: + reg_info.name = "d29"; + break; + case dwarf_d30: + reg_info.name = "d30"; + break; + case dwarf_d31: + reg_info.name = "d31"; + break; + + // NEON 128-bit vector registers (overlays the d registers) + case dwarf_q0: + reg_info.name = "q0"; + break; + case dwarf_q1: + reg_info.name = "q1"; + break; + case dwarf_q2: + reg_info.name = "q2"; + break; + case dwarf_q3: + reg_info.name = "q3"; + break; + case dwarf_q4: + reg_info.name = "q4"; + break; + case dwarf_q5: + reg_info.name = "q5"; + break; + case dwarf_q6: + reg_info.name = "q6"; + break; + case dwarf_q7: + reg_info.name = "q7"; + break; + case dwarf_q8: + reg_info.name = "q8"; + break; + case dwarf_q9: + reg_info.name = "q9"; + break; + case dwarf_q10: + reg_info.name = "q10"; + break; + case dwarf_q11: + reg_info.name = "q11"; + break; + case dwarf_q12: + reg_info.name = "q12"; + break; + case dwarf_q13: + reg_info.name = "q13"; + break; + case dwarf_q14: + reg_info.name = "q14"; + break; + case dwarf_q15: + reg_info.name = "q15"; + break; + + default: + return {}; + } + return reg_info; +} + +// A8.6.50 +// Valid return values are {1, 2, 3, 4}, with 0 signifying an error condition. +static uint32_t CountITSize(uint32_t ITMask) { + // First count the trailing zeros of the IT mask. + uint32_t TZ = llvm::countr_zero(ITMask); + if (TZ > 3) { + return 0; + } + return (4 - TZ); +} + +// Init ITState. Note that at least one bit is always 1 in mask. +bool ITSession::InitIT(uint32_t bits7_0) { + ITCounter = CountITSize(Bits32(bits7_0, 3, 0)); + if (ITCounter == 0) + return false; + + // A8.6.50 IT + unsigned short FirstCond = Bits32(bits7_0, 7, 4); + if (FirstCond == 0xF) { + return false; + } + if (FirstCond == 0xE && ITCounter != 1) { + return false; + } + + ITState = bits7_0; + return true; +} + +// Update ITState if necessary. +void ITSession::ITAdvance() { + // assert(ITCounter); + --ITCounter; + if (ITCounter == 0) + ITState = 0; + else { + unsigned short NewITState4_0 = Bits32(ITState, 4, 0) << 1; + SetBits32(ITState, 4, 0, NewITState4_0); + } +} + +// Return true if we're inside an IT Block. +bool ITSession::InITBlock() { return ITCounter != 0; } + +// Return true if we're the last instruction inside an IT Block. +bool ITSession::LastInITBlock() { return ITCounter == 1; } + +// Get condition bits for the current thumb instruction. +uint32_t ITSession::GetCond() { + if (InITBlock()) + return Bits32(ITState, 7, 4); + else + return COND_AL; +} + +// ARM constants used during decoding +#define REG_RD 0 +#define LDM_REGLIST 1 +#define SP_REG 13 +#define LR_REG 14 +#define PC_REG 15 +#define PC_REGLIST_BIT 0x8000 + +#define ARMv4 (1u << 0) +#define ARMv4T (1u << 1) +#define ARMv5T (1u << 2) +#define ARMv5TE (1u << 3) +#define ARMv5TEJ (1u << 4) +#define ARMv6 (1u << 5) +#define ARMv6K (1u << 6) +#define ARMv6T2 (1u << 7) +#define ARMv7 (1u << 8) +#define ARMv7S (1u << 9) +#define ARMv8 (1u << 10) +#define ARMvAll (0xffffffffu) + +#define ARMV4T_ABOVE \ + (ARMv4T | ARMv5T | ARMv5TE | ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | \ + ARMv7S | ARMv8) +#define ARMV5_ABOVE \ + (ARMv5T | ARMv5TE | ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | \ + ARMv8) +#define ARMV5TE_ABOVE \ + (ARMv5TE | ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | ARMv8) +#define ARMV5J_ABOVE \ + (ARMv5TEJ | ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | ARMv8) +#define ARMV6_ABOVE (ARMv6 | ARMv6K | ARMv6T2 | ARMv7 | ARMv7S | ARMv8) +#define ARMV6T2_ABOVE (ARMv6T2 | ARMv7 | ARMv7S | ARMv8) +#define ARMV7_ABOVE (ARMv7 | ARMv7S | ARMv8) + +#define No_VFP 0 +#define VFPv1 (1u << 1) +#define VFPv2 (1u << 2) +#define VFPv3 (1u << 3) +#define AdvancedSIMD (1u << 4) + +#define VFPv1_ABOVE (VFPv1 | VFPv2 | VFPv3 | AdvancedSIMD) +#define VFPv2_ABOVE (VFPv2 | VFPv3 | AdvancedSIMD) +#define VFPv2v3 (VFPv2 | VFPv3) + +// +// EmulateInstructionARM implementation +// + +void EmulateInstructionARM::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance); +} + +void EmulateInstructionARM::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +llvm::StringRef EmulateInstructionARM::GetPluginDescriptionStatic() { + return "Emulate instructions for the ARM architecture."; +} + +EmulateInstruction * +EmulateInstructionARM::CreateInstance(const ArchSpec &arch, + InstructionType inst_type) { + if (EmulateInstructionARM::SupportsEmulatingInstructionsOfTypeStatic( + inst_type)) { + if (arch.GetTriple().getArch() == llvm::Triple::arm) { + std::unique_ptr<EmulateInstructionARM> emulate_insn_up( + new EmulateInstructionARM(arch)); + + if (emulate_insn_up) + return emulate_insn_up.release(); + } else if (arch.GetTriple().getArch() == llvm::Triple::thumb) { + std::unique_ptr<EmulateInstructionARM> emulate_insn_up( + new EmulateInstructionARM(arch)); + + if (emulate_insn_up) + return emulate_insn_up.release(); + } + } + + return nullptr; +} + +bool EmulateInstructionARM::SetTargetTriple(const ArchSpec &arch) { + if (arch.GetTriple().getArch() == llvm::Triple::arm) + return true; + else if (arch.GetTriple().getArch() == llvm::Triple::thumb) + return true; + + return false; +} + +// Write "bits (32) UNKNOWN" to memory address "address". Helper function for +// many ARM instructions. +bool EmulateInstructionARM::WriteBits32UnknownToMemory(addr_t address) { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextWriteMemoryRandomBits; + context.SetNoArgs(); + + uint32_t random_data = rand(); + const uint32_t addr_byte_size = GetAddressByteSize(); + + return MemAWrite(context, address, random_data, addr_byte_size); +} + +// Write "bits (32) UNKNOWN" to register n. Helper function for many ARM +// instructions. +bool EmulateInstructionARM::WriteBits32Unknown(int n) { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextWriteRegisterRandomBits; + context.SetNoArgs(); + + bool success; + uint32_t data = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, data)) + return false; + + return true; +} + +std::optional<RegisterInfo> +EmulateInstructionARM::GetRegisterInfo(lldb::RegisterKind reg_kind, + uint32_t reg_num) { + if (reg_kind == eRegisterKindGeneric) { + switch (reg_num) { + case LLDB_REGNUM_GENERIC_PC: + reg_kind = eRegisterKindDWARF; + reg_num = dwarf_pc; + break; + case LLDB_REGNUM_GENERIC_SP: + reg_kind = eRegisterKindDWARF; + reg_num = dwarf_sp; + break; + case LLDB_REGNUM_GENERIC_FP: + reg_kind = eRegisterKindDWARF; + reg_num = dwarf_r7; + break; + case LLDB_REGNUM_GENERIC_RA: + reg_kind = eRegisterKindDWARF; + reg_num = dwarf_lr; + break; + case LLDB_REGNUM_GENERIC_FLAGS: + reg_kind = eRegisterKindDWARF; + reg_num = dwarf_cpsr; + break; + default: + return {}; + } + } + + if (reg_kind == eRegisterKindDWARF) + return GetARMDWARFRegisterInfo(reg_num); + return {}; +} + +uint32_t EmulateInstructionARM::GetFramePointerRegisterNumber() const { + if (m_arch.GetTriple().isAndroid()) + return LLDB_INVALID_REGNUM; // Don't use frame pointer on android + bool is_apple = false; + if (m_arch.GetTriple().getVendor() == llvm::Triple::Apple) + is_apple = true; + switch (m_arch.GetTriple().getOS()) { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + case llvm::Triple::TvOS: + case llvm::Triple::WatchOS: + case llvm::Triple::XROS: + case llvm::Triple::BridgeOS: + is_apple = true; + break; + default: + break; + } + + /* On Apple iOS et al, the frame pointer register is always r7. + * Typically on other ARM systems, thumb code uses r7; arm code uses r11. + * Windows on ARM, which is in thumb mode, uses r11 though. + */ + + uint32_t fp_regnum = 11; + + if (is_apple) + fp_regnum = 7; + + if (m_opcode_mode == eModeThumb && !m_arch.GetTriple().isOSWindows()) + fp_regnum = 7; + + return fp_regnum; +} + +uint32_t EmulateInstructionARM::GetFramePointerDWARFRegisterNumber() const { + bool is_apple = false; + if (m_arch.GetTriple().getVendor() == llvm::Triple::Apple) + is_apple = true; + switch (m_arch.GetTriple().getOS()) { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + is_apple = true; + break; + default: + break; + } + + /* On Apple iOS et al, the frame pointer register is always r7. + * Typically on other ARM systems, thumb code uses r7; arm code uses r11. + * Windows on ARM, which is in thumb mode, uses r11 though. + */ + + uint32_t fp_regnum = dwarf_r11; + + if (is_apple) + fp_regnum = dwarf_r7; + + if (m_opcode_mode == eModeThumb && !m_arch.GetTriple().isOSWindows()) + fp_regnum = dwarf_r7; + + return fp_regnum; +} + +// Push Multiple Registers stores multiple registers to the stack, storing to +// consecutive memory locations ending just below the address in SP, and +// updates +// SP to point to the start of the stored data. +bool EmulateInstructionARM::EmulatePUSH(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + NullCheckIfThumbEE(13); + address = SP - 4*BitCount(registers); + + for (i = 0 to 14) + { + if (registers<i> == '1') + { + if i == 13 && i != LowestSetBit(registers) // Only possible for encoding A1 + MemA[address,4] = bits(32) UNKNOWN; + else + MemA[address,4] = R[i]; + address = address + 4; + } + } + + if (registers<15> == '1') // Only possible for encoding A1 or A2 + MemA[address,4] = PCStoreValue(); + + SP = SP - 4*BitCount(registers); + } +#endif + + bool success = false; + if (ConditionPassed(opcode)) { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg(SP_REG, &success); + if (!success) + return false; + uint32_t registers = 0; + uint32_t Rt; // the source register + switch (encoding) { + case eEncodingT1: + registers = Bits32(opcode, 7, 0); + // The M bit represents LR. + if (Bit32(opcode, 8)) + registers |= (1u << 14); + // if BitCount(registers) < 1 then UNPREDICTABLE; + if (BitCount(registers) < 1) + return false; + break; + case eEncodingT2: + // Ignore bits 15 & 13. + registers = Bits32(opcode, 15, 0) & ~0xa000; + // if BitCount(registers) < 2 then UNPREDICTABLE; + if (BitCount(registers) < 2) + return false; + break; + case eEncodingT3: + Rt = Bits32(opcode, 15, 12); + // if BadReg(t) then UNPREDICTABLE; + if (BadReg(Rt)) + return false; + registers = (1u << Rt); + break; + case eEncodingA1: + registers = Bits32(opcode, 15, 0); + // Instead of return false, let's handle the following case as well, + // which amounts to pushing one reg onto the full descending stacks. + // if BitCount(register_list) < 2 then SEE STMDB / STMFD; + break; + case eEncodingA2: + Rt = Bits32(opcode, 15, 12); + // if t == 13 then UNPREDICTABLE; + if (Rt == dwarf_sp) + return false; + registers = (1u << Rt); + break; + default: + return false; + } + addr_t sp_offset = addr_byte_size * BitCount(registers); + addr_t addr = sp - sp_offset; + uint32_t i; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextPushRegisterOnStack; + std::optional<RegisterInfo> sp_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); + for (i = 0; i < 15; ++i) { + if (BitIsSet(registers, i)) { + std::optional<RegisterInfo> reg_info = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i); + context.SetRegisterToRegisterPlusOffset(*reg_info, *sp_reg, addr - sp); + uint32_t reg_value = ReadCoreReg(i, &success); + if (!success) + return false; + if (!MemAWrite(context, addr, reg_value, addr_byte_size)) + return false; + addr += addr_byte_size; + } + } + + if (BitIsSet(registers, 15)) { + std::optional<RegisterInfo> reg_info = + GetRegisterInfo(eRegisterKindDWARF, dwarf_pc); + context.SetRegisterToRegisterPlusOffset(*reg_info, *sp_reg, addr - sp); + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + if (!MemAWrite(context, addr, pc, addr_byte_size)) + return false; + } + + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned(-sp_offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_SP, sp - sp_offset)) + return false; + } + return true; +} + +// Pop Multiple Registers loads multiple registers from the stack, loading from +// consecutive memory locations staring at the address in SP, and updates +// SP to point just above the loaded data. +bool EmulateInstructionARM::EmulatePOP(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); NullCheckIfThumbEE(13); + address = SP; + for i = 0 to 14 + if registers<i> == '1' then + R[i] = if UnalignedAllowed then MemU[address,4] else MemA[address,4]; address = address + 4; + if registers<15> == '1' then + if UnalignedAllowed then + LoadWritePC(MemU[address,4]); + else + LoadWritePC(MemA[address,4]); + if registers<13> == '0' then SP = SP + 4*BitCount(registers); + if registers<13> == '1' then SP = bits(32) UNKNOWN; + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg(SP_REG, &success); + if (!success) + return false; + uint32_t registers = 0; + uint32_t Rt; // the destination register + switch (encoding) { + case eEncodingT1: + registers = Bits32(opcode, 7, 0); + // The P bit represents PC. + if (Bit32(opcode, 8)) + registers |= (1u << 15); + // if BitCount(registers) < 1 then UNPREDICTABLE; + if (BitCount(registers) < 1) + return false; + break; + case eEncodingT2: + // Ignore bit 13. + registers = Bits32(opcode, 15, 0) & ~0x2000; + // if BitCount(registers) < 2 || (P == '1' && M == '1') then + // UNPREDICTABLE; + if (BitCount(registers) < 2 || (Bit32(opcode, 15) && Bit32(opcode, 14))) + return false; + // if registers<15> == '1' && InITBlock() && !LastInITBlock() then + // UNPREDICTABLE; + if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingT3: + Rt = Bits32(opcode, 15, 12); + // if t == 13 || (t == 15 && InITBlock() && !LastInITBlock()) then + // UNPREDICTABLE; + if (Rt == 13) + return false; + if (Rt == 15 && InITBlock() && !LastInITBlock()) + return false; + registers = (1u << Rt); + break; + case eEncodingA1: + registers = Bits32(opcode, 15, 0); + // Instead of return false, let's handle the following case as well, + // which amounts to popping one reg from the full descending stacks. + // if BitCount(register_list) < 2 then SEE LDM / LDMIA / LDMFD; + + // if registers<13> == '1' && ArchVersion() >= 7 then UNPREDICTABLE; + if (BitIsSet(opcode, 13) && ArchVersion() >= ARMv7) + return false; + break; + case eEncodingA2: + Rt = Bits32(opcode, 15, 12); + // if t == 13 then UNPREDICTABLE; + if (Rt == dwarf_sp) + return false; + registers = (1u << Rt); + break; + default: + return false; + } + addr_t sp_offset = addr_byte_size * BitCount(registers); + addr_t addr = sp; + uint32_t i, data; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextPopRegisterOffStack; + + std::optional<RegisterInfo> sp_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); + + for (i = 0; i < 15; ++i) { + if (BitIsSet(registers, i)) { + context.SetAddress(addr); + data = MemARead(context, addr, 4, 0, &success); + if (!success) + return false; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, + data)) + return false; + addr += addr_byte_size; + } + } + + if (BitIsSet(registers, 15)) { + context.SetRegisterPlusOffset(*sp_reg, addr - sp); + data = MemARead(context, addr, 4, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + // addr += addr_byte_size; + } + + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned(sp_offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_SP, sp + sp_offset)) + return false; + } + return true; +} + +// Set r7 or ip to point to saved value residing within the stack. +// ADD (SP plus immediate) +bool EmulateInstructionARM::EmulateADDRdSPImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, imm32, '0'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + const addr_t sp = ReadCoreReg(SP_REG, &success); + if (!success) + return false; + uint32_t Rd; // the destination register + uint32_t imm32; + switch (encoding) { + case eEncodingT1: + Rd = 7; + imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32) + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + addr_t sp_offset = imm32; + addr_t addr = sp + sp_offset; // a pointer to the stack area + + EmulateInstruction::Context context; + if (Rd == GetFramePointerRegisterNumber()) + context.type = eContextSetFramePointer; + else + context.type = EmulateInstruction::eContextRegisterPlusOffset; + std::optional<RegisterInfo> sp_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); + context.SetRegisterPlusOffset(*sp_reg, sp_offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rd, + addr)) + return false; + } + return true; +} + +// Set r7 or ip to the current stack pointer. +// MOV (register) +bool EmulateInstructionARM::EmulateMOVRdSP(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + result = R[m]; + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + // APSR.C unchanged + // APSR.V unchanged + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + const addr_t sp = ReadCoreReg(SP_REG, &success); + if (!success) + return false; + uint32_t Rd; // the destination register + switch (encoding) { + case eEncodingT1: + Rd = 7; + break; + case eEncodingA1: + Rd = 12; + break; + default: + return false; + } + + EmulateInstruction::Context context; + if (Rd == GetFramePointerRegisterNumber()) + context.type = EmulateInstruction::eContextSetFramePointer; + else + context.type = EmulateInstruction::eContextRegisterPlusOffset; + std::optional<RegisterInfo> sp_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); + context.SetRegisterPlusOffset(*sp_reg, 0); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rd, sp)) + return false; + } + return true; +} + +// Move from high register (r8-r15) to low register (r0-r7). +// MOV (register) +bool EmulateInstructionARM::EmulateMOVLowHigh(const uint32_t opcode, + const ARMEncoding encoding) { + return EmulateMOVRdRm(opcode, encoding); +} + +// Move from register to register. +// MOV (register) +bool EmulateInstructionARM::EmulateMOVRdRm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + result = R[m]; + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + // APSR.C unchanged + // APSR.V unchanged + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rm; // the source register + uint32_t Rd; // the destination register + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 6, 3); + setflags = false; + if (Rd == 15 && InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingT2: + Rd = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = true; + if (InITBlock()) + return false; + break; + case eEncodingT3: + Rd = Bits32(opcode, 11, 8); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + // if setflags && (BadReg(d) || BadReg(m)) then UNPREDICTABLE; + if (setflags && (BadReg(Rd) || BadReg(Rm))) + return false; + // if !setflags && (d == 15 || m == 15 || (d == 13 && m == 13)) then + // UNPREDICTABLE; + if (!setflags && (Rd == 15 || Rm == 15 || (Rd == 13 && Rm == 13))) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + uint32_t result = ReadCoreReg(Rm, &success); + if (!success) + return false; + + // The context specifies that Rm is to be moved into Rd. + EmulateInstruction::Context context; + if (Rd == 13) + context.type = EmulateInstruction::eContextAdjustStackPointer; + else if (Rd == GetFramePointerRegisterNumber() && Rm == 13) + context.type = EmulateInstruction::eContextSetFramePointer; + else + context.type = EmulateInstruction::eContextRegisterPlusOffset; + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm); + context.SetRegisterPlusOffset(*dwarf_reg, 0); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags)) + return false; + } + return true; +} + +// Move (immediate) writes an immediate value to the destination register. It +// can optionally update the condition flags based on the value. +// MOV (immediate) +bool EmulateInstructionARM::EmulateMOVRdImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + result = imm32; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged + } +#endif + + if (ConditionPassed(opcode)) { + uint32_t Rd; // the destination register + uint32_t imm32; // the immediate value to be written to Rd + uint32_t carry = + 0; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C. + // for setflags == false, this value is a don't care initialized to + // 0 to silence the static analyzer + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 10, 8); + setflags = !InITBlock(); + imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32) + carry = APSR_C; + + break; + + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); + if (BadReg(Rd)) + return false; + + break; + + case eEncodingT3: { + // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:i:imm3:imm8, + // 32); + Rd = Bits32(opcode, 11, 8); + setflags = false; + uint32_t imm4 = Bits32(opcode, 19, 16); + uint32_t imm3 = Bits32(opcode, 14, 12); + uint32_t i = Bit32(opcode, 26); + uint32_t imm8 = Bits32(opcode, 7, 0); + imm32 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; + + // if BadReg(d) then UNPREDICTABLE; + if (BadReg(Rd)) + return false; + } break; + + case eEncodingA1: + // d = UInt(Rd); setflags = (S == '1'); (imm32, carry) = + // ARMExpandImm_C(imm12, APSR.C); + Rd = Bits32(opcode, 15, 12); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if ((Rd == 15) && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + + break; + + case eEncodingA2: { + // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:imm12, 32); + Rd = Bits32(opcode, 15, 12); + setflags = false; + uint32_t imm4 = Bits32(opcode, 19, 16); + uint32_t imm12 = Bits32(opcode, 11, 0); + imm32 = (imm4 << 12) | imm12; + + // if d == 15 then UNPREDICTABLE; + if (Rd == 15) + return false; + } break; + + default: + return false; + } + uint32_t result = imm32; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// MUL multiplies two register values. The least significant 32 bits of the +// result are written to the destination +// register. These 32 bits do not depend on whether the source register values +// are considered to be signed values or unsigned values. +// +// Optionally, it can update the condition flags based on the result. In the +// Thumb instruction set, this option is limited to only a few forms of the +// instruction. +bool EmulateInstructionARM::EmulateMUL(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final results + operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final results + result = operand1 * operand2; + R[d] = result<31:0>; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + if ArchVersion() == 4 then + APSR.C = bit UNKNOWN; + // else APSR.C unchanged + // APSR.V always unchanged +#endif + + if (ConditionPassed(opcode)) { + uint32_t d; + uint32_t n; + uint32_t m; + bool setflags; + + // EncodingSpecificOperations(); + switch (encoding) { + case eEncodingT1: + // d = UInt(Rdm); n = UInt(Rn); m = UInt(Rdm); setflags = !InITBlock(); + d = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + m = Bits32(opcode, 2, 0); + setflags = !InITBlock(); + + // if ArchVersion() < 6 && d == n then UNPREDICTABLE; + if ((ArchVersion() < ARMv6) && (d == n)) + return false; + + break; + + case eEncodingT2: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = FALSE; + d = Bits32(opcode, 11, 8); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + setflags = false; + + // if BadReg(d) || BadReg(n) || BadReg(m) then UNPREDICTABLE; + if (BadReg(d) || BadReg(n) || BadReg(m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == '1'); + d = Bits32(opcode, 19, 16); + n = Bits32(opcode, 3, 0); + m = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + + // if d == 15 || n == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (n == 15) || (m == 15)) + return false; + + // if ArchVersion() < 6 && d == n then UNPREDICTABLE; + if ((ArchVersion() < ARMv6) && (d == n)) + return false; + + break; + + default: + return false; + } + + bool success = false; + + // operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final + // results + uint64_t operand1 = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + // operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final + // results + uint64_t operand2 = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // result = operand1 * operand2; + uint64_t result = operand1 * operand2; + + // R[d] = result<31:0>; + std::optional<RegisterInfo> op1_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> op2_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + context.SetRegisterRegisterOperands(*op1_reg, *op2_reg); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, + (0x0000ffff & result))) + return false; + + // if setflags then + if (setflags) { + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + m_new_inst_cpsr = m_opcode_cpsr; + SetBit32(m_new_inst_cpsr, CPSR_N_POS, Bit32(result, 31)); + SetBit32(m_new_inst_cpsr, CPSR_Z_POS, result == 0 ? 1 : 0); + if (m_new_inst_cpsr != m_opcode_cpsr) { + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr)) + return false; + } + + // if ArchVersion() == 4 then + // APSR.C = bit UNKNOWN; + } + } + return true; +} + +// Bitwise NOT (immediate) writes the bitwise inverse of an immediate value to +// the destination register. It can optionally update the condition flags based +// on the value. +bool EmulateInstructionARM::EmulateMVNImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + result = NOT(imm32); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged + } +#endif + + if (ConditionPassed(opcode)) { + uint32_t Rd; // the destination register + uint32_t imm32; // the output after ThumbExpandImm_C or ARMExpandImm_C + uint32_t carry; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm_C(opcode, APSR_C, carry); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + uint32_t result = ~imm32; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise NOT (register) writes the bitwise inverse of a register value to the +// destination register. It can optionally update the condition flags based on +// the result. +bool EmulateInstructionARM::EmulateMVNReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = NOT(shifted); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged + } +#endif + + if (ConditionPassed(opcode)) { + uint32_t Rm; // the source register + uint32_t Rd; // the destination register + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; // the carry bit after the shift operation + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + if (InITBlock()) + return false; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE; + if (BadReg(Rd) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + bool success = false; + uint32_t value = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = + Shift_C(value, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = ~shifted; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// PC relative immediate load into register, possibly followed by ADD (SP plus +// register). +// LDR (literal) +bool EmulateInstructionARM::EmulateLDRRtPCRelative(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + data = MemU[address,4]; + if t == 15 then + if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + elsif UnalignedSupport() || address<1:0> = '00' then + R[t] = data; + else // Can only apply before ARMv7 + if CurrentInstrSet() == InstrSet_ARM then + R[t] = ROR(data, 8*UInt(address<1:0>)); + else + R[t] = bits(32) UNKNOWN; + } +#endif + + if (ConditionPassed(opcode)) { + bool success = false; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + // PC relative immediate load context + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + std::optional<RegisterInfo> pc_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_pc); + context.SetRegisterPlusOffset(*pc_reg, 0); + + uint32_t Rt; // the destination register + uint32_t imm32; // immediate offset from the PC + bool add; // +imm32 or -imm32? + addr_t base; // the base address + addr_t address; // the PC relative address + uint32_t data; // the literal data value from the PC relative load + switch (encoding) { + case eEncodingT1: + Rt = Bits32(opcode, 10, 8); + imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32); + add = true; + break; + case eEncodingT2: + Rt = Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 11, 0) << 2; // imm32 = ZeroExtend(imm12, 32); + add = BitIsSet(opcode, 23); + if (Rt == 15 && InITBlock() && !LastInITBlock()) + return false; + break; + default: + return false; + } + + base = Align(pc, 4); + if (add) + address = base + imm32; + else + address = base - imm32; + + context.SetRegisterPlusOffset(*pc_reg, address - base); + data = MemURead(context, address, 4, 0, &success); + if (!success) + return false; + + if (Rt == 15) { + if (Bits32(address, 1, 0) == 0) { + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } else + return false; + } else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) { + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rt, + data)) + return false; + } else // We don't handle ARM for now. + return false; + } + return true; +} + +// An add operation to adjust the SP. +// ADD (SP plus immediate) +bool EmulateInstructionARM::EmulateADDSPImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, imm32, '0'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + const addr_t sp = ReadCoreReg(SP_REG, &success); + if (!success) + return false; + uint32_t imm32; // the immediate operand + uint32_t d; + bool setflags; + switch (encoding) { + case eEncodingT1: + // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm8:'00', 32); + d = Bits32(opcode, 10, 8); + imm32 = (Bits32(opcode, 7, 0) << 2); + setflags = false; + break; + + case eEncodingT2: + // d = 13; setflags = FALSE; imm32 = ZeroExtend(imm7:'00', 32); + d = 13; + imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32) + setflags = false; + break; + + case eEncodingT3: + // d = UInt(Rd); setflags = (S == "1"); imm32 = + // ThumbExpandImm(i:imm3:imm8); + d = Bits32(opcode, 11, 8); + imm32 = ThumbExpandImm(opcode); + setflags = Bit32(opcode, 20); + + // if Rd == "1111" && S == "1" then SEE CMN (immediate); + if (d == 15 && setflags == 1) + return false; // CMN (immediate) not yet supported + + // if d == 15 && S == "0" then UNPREDICTABLE; + if (d == 15 && setflags == 0) + return false; + break; + + case eEncodingT4: { + // if Rn == '1111' then SEE ADR; + // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(i:imm3:imm8, 32); + d = Bits32(opcode, 11, 8); + setflags = false; + uint32_t i = Bit32(opcode, 26); + uint32_t imm3 = Bits32(opcode, 14, 12); + uint32_t imm8 = Bits32(opcode, 7, 0); + imm32 = (i << 11) | (imm3 << 8) | imm8; + + // if d == 15 then UNPREDICTABLE; + if (d == 15) + return false; + } break; + + default: + return false; + } + // (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + AddWithCarryResult res = AddWithCarry(sp, imm32, 0); + + EmulateInstruction::Context context; + if (d == 13) + context.type = EmulateInstruction::eContextAdjustStackPointer; + else + context.type = EmulateInstruction::eContextRegisterPlusOffset; + + std::optional<RegisterInfo> sp_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); + context.SetRegisterPlusOffset(*sp_reg, res.result - sp); + + if (d == 15) { + if (!ALUWritePC(context, res.result)) + return false; + } else { + // R[d] = result; + // if setflags then + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + // APSR.C = carry; + // APSR.V = overflow; + if (!WriteCoreRegOptionalFlags(context, res.result, d, setflags, + res.carry_out, res.overflow)) + return false; + } + } + return true; +} + +// An add operation to adjust the SP. +// ADD (SP plus register) +bool EmulateInstructionARM::EmulateADDSPRm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(SP, shifted, '0'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + const addr_t sp = ReadCoreReg(SP_REG, &success); + if (!success) + return false; + uint32_t Rm; // the second operand + switch (encoding) { + case eEncodingT2: + Rm = Bits32(opcode, 6, 3); + break; + default: + return false; + } + int32_t reg_value = ReadCoreReg(Rm, &success); + if (!success) + return false; + + addr_t addr = (int32_t)sp + reg_value; // the adjusted stack pointer value + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + std::optional<RegisterInfo> sp_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); + std::optional<RegisterInfo> other_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm); + context.SetRegisterRegisterOperands(*sp_reg, *other_reg); + + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_SP, addr)) + return false; + } + return true; +} + +// Branch with Link and Exchange Instruction Sets (immediate) calls a +// subroutine at a PC-relative address, and changes instruction set from ARM to +// Thumb, or from Thumb to ARM. +// BLX (immediate) +bool EmulateInstructionARM::EmulateBLXImmediate(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + if CurrentInstrSet() == InstrSet_ARM then + LR = PC - 4; + else + LR = PC<31:1> : '1'; + if targetInstrSet == InstrSet_ARM then + targetAddress = Align(PC,4) + imm32; + else + targetAddress = PC + imm32; + SelectInstrSet(targetInstrSet); + BranchWritePC(targetAddress); + } +#endif + + bool success = true; + + if (ConditionPassed(opcode)) { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRelativeBranchImmediate; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + addr_t lr; // next instruction address + addr_t target; // target address + int32_t imm32; // PC-relative offset + switch (encoding) { + case eEncodingT1: { + lr = pc | 1u; // return address + uint32_t S = Bit32(opcode, 26); + uint32_t imm10 = Bits32(opcode, 25, 16); + uint32_t J1 = Bit32(opcode, 13); + uint32_t J2 = Bit32(opcode, 11); + uint32_t imm11 = Bits32(opcode, 10, 0); + uint32_t I1 = !(J1 ^ S); + uint32_t I2 = !(J2 ^ S); + uint32_t imm25 = + (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1); + imm32 = llvm::SignExtend32<25>(imm25); + target = pc + imm32; + SelectInstrSet(eModeThumb); + context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32); + if (InITBlock() && !LastInITBlock()) + return false; + break; + } + case eEncodingT2: { + lr = pc | 1u; // return address + uint32_t S = Bit32(opcode, 26); + uint32_t imm10H = Bits32(opcode, 25, 16); + uint32_t J1 = Bit32(opcode, 13); + uint32_t J2 = Bit32(opcode, 11); + uint32_t imm10L = Bits32(opcode, 10, 1); + uint32_t I1 = !(J1 ^ S); + uint32_t I2 = !(J2 ^ S); + uint32_t imm25 = + (S << 24) | (I1 << 23) | (I2 << 22) | (imm10H << 12) | (imm10L << 2); + imm32 = llvm::SignExtend32<25>(imm25); + target = Align(pc, 4) + imm32; + SelectInstrSet(eModeARM); + context.SetISAAndImmediateSigned(eModeARM, 4 + imm32); + if (InITBlock() && !LastInITBlock()) + return false; + break; + } + case eEncodingA1: + lr = pc - 4; // return address + imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2); + target = Align(pc, 4) + imm32; + SelectInstrSet(eModeARM); + context.SetISAAndImmediateSigned(eModeARM, 8 + imm32); + break; + case eEncodingA2: + lr = pc - 4; // return address + imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2 | + Bits32(opcode, 24, 24) << 1); + target = pc + imm32; + SelectInstrSet(eModeThumb); + context.SetISAAndImmediateSigned(eModeThumb, 8 + imm32); + break; + default: + return false; + } + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_RA, lr)) + return false; + if (!BranchWritePC(context, target)) + return false; + if (m_opcode_cpsr != m_new_inst_cpsr) + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr)) + return false; + } + return true; +} + +// Branch with Link and Exchange (register) calls a subroutine at an address +// and instruction set specified by a register. +// BLX (register) +bool EmulateInstructionARM::EmulateBLXRm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + target = R[m]; + if CurrentInstrSet() == InstrSet_ARM then + next_instr_addr = PC - 4; + LR = next_instr_addr; + else + next_instr_addr = PC - 2; + LR = next_instr_addr<31:1> : '1'; + BXWritePC(target); + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextAbsoluteBranchRegister; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + addr_t lr; // next instruction address + if (!success) + return false; + uint32_t Rm; // the register with the target address + switch (encoding) { + case eEncodingT1: + lr = (pc - 2) | 1u; // return address + Rm = Bits32(opcode, 6, 3); + // if m == 15 then UNPREDICTABLE; + if (Rm == 15) + return false; + if (InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingA1: + lr = pc - 4; // return address + Rm = Bits32(opcode, 3, 0); + // if m == 15 then UNPREDICTABLE; + if (Rm == 15) + return false; + break; + default: + return false; + } + addr_t target = ReadCoreReg(Rm, &success); + if (!success) + return false; + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm); + context.SetRegister(*dwarf_reg); + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_RA, lr)) + return false; + if (!BXWritePC(context, target)) + return false; + } + return true; +} + +// Branch and Exchange causes a branch to an address and instruction set +// specified by a register. +bool EmulateInstructionARM::EmulateBXRm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + BXWritePC(R[m]); + } +#endif + + if (ConditionPassed(opcode)) { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextAbsoluteBranchRegister; + uint32_t Rm; // the register with the target address + switch (encoding) { + case eEncodingT1: + Rm = Bits32(opcode, 6, 3); + if (InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingA1: + Rm = Bits32(opcode, 3, 0); + break; + default: + return false; + } + bool success = false; + addr_t target = ReadCoreReg(Rm, &success); + if (!success) + return false; + + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm); + context.SetRegister(*dwarf_reg); + if (!BXWritePC(context, target)) + return false; + } + return true; +} + +// Branch and Exchange Jazelle attempts to change to Jazelle state. If the +// attempt fails, it branches to an address and instruction set specified by a +// register as though it were a BX instruction. +// +// TODO: Emulate Jazelle architecture? +// We currently assume that switching to Jazelle state fails, thus +// treating BXJ as a BX operation. +bool EmulateInstructionARM::EmulateBXJRm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + if JMCR.JE == '0' || CurrentInstrSet() == InstrSet_ThumbEE then + BXWritePC(R[m]); + else + if JazelleAcceptsExecution() then + SwitchToJazelleExecution(); + else + SUBARCHITECTURE_DEFINED handler call; + } +#endif + + if (ConditionPassed(opcode)) { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextAbsoluteBranchRegister; + uint32_t Rm; // the register with the target address + switch (encoding) { + case eEncodingT1: + Rm = Bits32(opcode, 19, 16); + if (BadReg(Rm)) + return false; + if (InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingA1: + Rm = Bits32(opcode, 3, 0); + if (Rm == 15) + return false; + break; + default: + return false; + } + bool success = false; + addr_t target = ReadCoreReg(Rm, &success); + if (!success) + return false; + + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm); + context.SetRegister(*dwarf_reg); + if (!BXWritePC(context, target)) + return false; + } + return true; +} + +// Set r7 to point to some ip offset. +// SUB (immediate) +bool EmulateInstructionARM::EmulateSUBR7IPImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + if (ConditionPassed(opcode)) { + bool success = false; + const addr_t ip = ReadCoreReg(12, &success); + if (!success) + return false; + uint32_t imm32; + switch (encoding) { + case eEncodingA1: + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + addr_t ip_offset = imm32; + addr_t addr = ip - ip_offset; // the adjusted ip value + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r12); + context.SetRegisterPlusOffset(*dwarf_reg, -ip_offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r7, addr)) + return false; + } + return true; +} + +// Set ip to point to some stack offset. +// SUB (SP minus immediate) +bool EmulateInstructionARM::EmulateSUBIPSPImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + if (ConditionPassed(opcode)) { + bool success = false; + const addr_t sp = ReadCoreReg(SP_REG, &success); + if (!success) + return false; + uint32_t imm32; + switch (encoding) { + case eEncodingA1: + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + addr_t sp_offset = imm32; + addr_t addr = sp - sp_offset; // the adjusted stack pointer value + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); + context.SetRegisterPlusOffset(*dwarf_reg, -sp_offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r12, addr)) + return false; + } + return true; +} + +// This instruction subtracts an immediate value from the SP value, and writes +// the result to the destination register. +// +// If Rd == 13 => A sub operation to adjust the SP -- allocate space for local +// storage. +bool EmulateInstructionARM::EmulateSUBSPImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; + } +#endif + + bool success = false; + if (ConditionPassed(opcode)) { + const addr_t sp = ReadCoreReg(SP_REG, &success); + if (!success) + return false; + + uint32_t Rd; + bool setflags; + uint32_t imm32; + switch (encoding) { + case eEncodingT1: + Rd = 13; + setflags = false; + imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32) + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (Rd == 15 && setflags) + return EmulateCMPImm(opcode, eEncodingT2); + if (Rd == 15 && !setflags) + return false; + break; + case eEncodingT3: + Rd = Bits32(opcode, 11, 8); + setflags = false; + imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) + if (Rd == 15) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + AddWithCarryResult res = AddWithCarry(sp, ~imm32, 1); + + EmulateInstruction::Context context; + if (Rd == 13) { + uint64_t imm64 = imm32; // Need to expand it to 64 bits before attempting + // to negate it, or the wrong + // value gets passed down to context.SetImmediateSigned. + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned(-imm64); // the stack pointer offset + } else { + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + } + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow)) + return false; + } + return true; +} + +// A store operation to the stack that also updates the SP. +bool EmulateInstructionARM::EmulateSTRRtSP(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; + if wback then R[n] = offset_addr; + } +#endif + + bool success = false; + if (ConditionPassed(opcode)) { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg(SP_REG, &success); + if (!success) + return false; + uint32_t Rt; // the source register + uint32_t imm12; + uint32_t + Rn; // This function assumes Rn is the SP, but we should verify that. + + bool index; + bool add; + bool wback; + switch (encoding) { + case eEncodingA1: + Rt = Bits32(opcode, 15, 12); + imm12 = Bits32(opcode, 11, 0); + Rn = Bits32(opcode, 19, 16); + + if (Rn != 13) // 13 is the SP reg on ARM. Verify that Rn == SP. + return false; + + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); + + if (wback && ((Rn == 15) || (Rn == Rt))) + return false; + break; + default: + return false; + } + addr_t offset_addr; + if (add) + offset_addr = sp + imm12; + else + offset_addr = sp - imm12; + + addr_t addr; + if (index) + addr = offset_addr; + else + addr = sp; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextPushRegisterOnStack; + std::optional<RegisterInfo> sp_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rt); + + context.SetRegisterToRegisterPlusOffset(*dwarf_reg, *sp_reg, addr - sp); + if (Rt != 15) { + uint32_t reg_value = ReadCoreReg(Rt, &success); + if (!success) + return false; + if (!MemUWrite(context, addr, reg_value, addr_byte_size)) + return false; + } else { + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + if (!MemUWrite(context, addr, pc, addr_byte_size)) + return false; + } + + if (wback) { + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned(addr - sp); + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_SP, offset_addr)) + return false; + } + } + return true; +} + +// Vector Push stores multiple extension registers to the stack. It also +// updates SP to point to the start of the stored data. +bool EmulateInstructionARM::EmulateVPUSH(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(13); + address = SP - imm32; + SP = SP - imm32; + if single_regs then + for r = 0 to regs-1 + MemA[address,4] = S[d+r]; address = address+4; + else + for r = 0 to regs-1 + // Store as two word-aligned words in the correct order for + // current endianness. + MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; + MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; + address = address+8; + } +#endif + + bool success = false; + if (ConditionPassed(opcode)) { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg(SP_REG, &success); + if (!success) + return false; + bool single_regs; + uint32_t d; // UInt(D:Vd) or UInt(Vd:D) starting register + uint32_t imm32; // stack offset + uint32_t regs; // number of registers + switch (encoding) { + case eEncodingT1: + case eEncodingA1: + single_regs = false; + d = Bit32(opcode, 22) << 4 | Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 7, 0) * addr_byte_size; + // If UInt(imm8) is odd, see "FSTMX". + regs = Bits32(opcode, 7, 0) / 2; + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if (regs == 0 || regs > 16 || (d + regs) > 32) + return false; + break; + case eEncodingT2: + case eEncodingA2: + single_regs = true; + d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22); + imm32 = Bits32(opcode, 7, 0) * addr_byte_size; + regs = Bits32(opcode, 7, 0); + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if (regs == 0 || regs > 16 || (d + regs) > 32) + return false; + break; + default: + return false; + } + uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; + uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2; + addr_t sp_offset = imm32; + addr_t addr = sp - sp_offset; + uint32_t i; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextPushRegisterOnStack; + + std::optional<RegisterInfo> sp_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); + for (i = 0; i < regs; ++i) { + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, start_reg + d + i); + context.SetRegisterToRegisterPlusOffset(*dwarf_reg, *sp_reg, addr - sp); + // uint64_t to accommodate 64-bit registers. + uint64_t reg_value = ReadRegisterUnsigned(*dwarf_reg, 0, &success); + if (!success) + return false; + if (!MemAWrite(context, addr, reg_value, reg_byte_size)) + return false; + addr += reg_byte_size; + } + + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned(-sp_offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_SP, sp - sp_offset)) + return false; + } + return true; +} + +// Vector Pop loads multiple extension registers from the stack. It also +// updates SP to point just above the loaded data. +bool EmulateInstructionARM::EmulateVPOP(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(13); + address = SP; + SP = SP + imm32; + if single_regs then + for r = 0 to regs-1 + S[d+r] = MemA[address,4]; address = address+4; + else + for r = 0 to regs-1 + word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8; + // Combine the word-aligned words in the correct order for + // current endianness. + D[d+r] = if BigEndian() then word1:word2 else word2:word1; + } +#endif + + bool success = false; + if (ConditionPassed(opcode)) { + const uint32_t addr_byte_size = GetAddressByteSize(); + const addr_t sp = ReadCoreReg(SP_REG, &success); + if (!success) + return false; + bool single_regs; + uint32_t d; // UInt(D:Vd) or UInt(Vd:D) starting register + uint32_t imm32; // stack offset + uint32_t regs; // number of registers + switch (encoding) { + case eEncodingT1: + case eEncodingA1: + single_regs = false; + d = Bit32(opcode, 22) << 4 | Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 7, 0) * addr_byte_size; + // If UInt(imm8) is odd, see "FLDMX". + regs = Bits32(opcode, 7, 0) / 2; + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if (regs == 0 || regs > 16 || (d + regs) > 32) + return false; + break; + case eEncodingT2: + case eEncodingA2: + single_regs = true; + d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22); + imm32 = Bits32(opcode, 7, 0) * addr_byte_size; + regs = Bits32(opcode, 7, 0); + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if (regs == 0 || regs > 16 || (d + regs) > 32) + return false; + break; + default: + return false; + } + uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; + uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2; + addr_t sp_offset = imm32; + addr_t addr = sp; + uint32_t i; + uint64_t data; // uint64_t to accommodate 64-bit registers. + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextPopRegisterOffStack; + + for (i = 0; i < regs; ++i) { + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, start_reg + d + i); + context.SetAddress(addr); + data = MemARead(context, addr, reg_byte_size, 0, &success); + if (!success) + return false; + if (!WriteRegisterUnsigned(context, *dwarf_reg, data)) + return false; + addr += reg_byte_size; + } + + context.type = EmulateInstruction::eContextAdjustStackPointer; + context.SetImmediateSigned(sp_offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_SP, sp + sp_offset)) + return false; + } + return true; +} + +// SVC (previously SWI) +bool EmulateInstructionARM::EmulateSVC(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + CallSupervisor(); + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + const uint32_t pc = ReadCoreReg(PC_REG, &success); + addr_t lr; // next instruction address + if (!success) + return false; + uint32_t imm32; // the immediate constant + uint32_t mode; // ARM or Thumb mode + switch (encoding) { + case eEncodingT1: + lr = (pc + 2) | 1u; // return address + imm32 = Bits32(opcode, 7, 0); + mode = eModeThumb; + break; + case eEncodingA1: + lr = pc + 4; // return address + imm32 = Bits32(opcode, 23, 0); + mode = eModeARM; + break; + default: + return false; + } + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextSupervisorCall; + context.SetISAAndImmediate(mode, imm32); + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_RA, lr)) + return false; + } + return true; +} + +// If Then makes up to four following instructions (the IT block) conditional. +bool EmulateInstructionARM::EmulateIT(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + EncodingSpecificOperations(); + ITSTATE.IT<7:0> = firstcond:mask; +#endif + + m_it_session.InitIT(Bits32(opcode, 7, 0)); + return true; +} + +bool EmulateInstructionARM::EmulateNop(const uint32_t opcode, + const ARMEncoding encoding) { + // NOP, nothing to do... + return true; +} + +// Branch causes a branch to a target address. +bool EmulateInstructionARM::EmulateB(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); + BranchWritePC(PC + imm32); + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRelativeBranchImmediate; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + addr_t target; // target address + int32_t imm32; // PC-relative offset + switch (encoding) { + case eEncodingT1: + // The 'cond' field is handled in EmulateInstructionARM::CurrentCond(). + imm32 = llvm::SignExtend32<9>(Bits32(opcode, 7, 0) << 1); + target = pc + imm32; + context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32); + break; + case eEncodingT2: + imm32 = llvm::SignExtend32<12>(Bits32(opcode, 10, 0) << 1); + target = pc + imm32; + context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32); + break; + case eEncodingT3: + // The 'cond' field is handled in EmulateInstructionARM::CurrentCond(). + { + if (Bits32(opcode, 25, 23) == 7) + return false; // See Branches and miscellaneous control on page + // A6-235. + + uint32_t S = Bit32(opcode, 26); + uint32_t imm6 = Bits32(opcode, 21, 16); + uint32_t J1 = Bit32(opcode, 13); + uint32_t J2 = Bit32(opcode, 11); + uint32_t imm11 = Bits32(opcode, 10, 0); + uint32_t imm21 = + (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1); + imm32 = llvm::SignExtend32<21>(imm21); + target = pc + imm32; + context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32); + break; + } + case eEncodingT4: { + uint32_t S = Bit32(opcode, 26); + uint32_t imm10 = Bits32(opcode, 25, 16); + uint32_t J1 = Bit32(opcode, 13); + uint32_t J2 = Bit32(opcode, 11); + uint32_t imm11 = Bits32(opcode, 10, 0); + uint32_t I1 = !(J1 ^ S); + uint32_t I2 = !(J2 ^ S); + uint32_t imm25 = + (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1); + imm32 = llvm::SignExtend32<25>(imm25); + target = pc + imm32; + context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32); + break; + } + case eEncodingA1: + imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2); + target = pc + imm32; + context.SetISAAndImmediateSigned(eModeARM, 8 + imm32); + break; + default: + return false; + } + if (!BranchWritePC(context, target)) + return false; + } + return true; +} + +// Compare and Branch on Nonzero and Compare and Branch on Zero compare the +// value in a register with zero and conditionally branch forward a constant +// value. They do not affect the condition flags. CBNZ, CBZ +bool EmulateInstructionARM::EmulateCB(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + EncodingSpecificOperations(); + if nonzero ^ IsZero(R[n]) then + BranchWritePC(PC + imm32); +#endif + + bool success = false; + + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Bits32(opcode, 2, 0), &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRelativeBranchImmediate; + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + addr_t target; // target address + uint32_t imm32; // PC-relative offset to branch forward + bool nonzero; + switch (encoding) { + case eEncodingT1: + imm32 = Bit32(opcode, 9) << 6 | Bits32(opcode, 7, 3) << 1; + nonzero = BitIsSet(opcode, 11); + target = pc + imm32; + context.SetISAAndImmediateSigned(eModeThumb, 4 + imm32); + break; + default: + return false; + } + if (m_ignore_conditions || (nonzero ^ (reg_val == 0))) + if (!BranchWritePC(context, target)) + return false; + + return true; +} + +// Table Branch Byte causes a PC-relative forward branch using a table of +// single byte offsets. +// A base register provides a pointer to the table, and a second register +// supplies an index into the table. +// The branch length is twice the value of the byte returned from the table. +// +// Table Branch Halfword causes a PC-relative forward branch using a table of +// single halfword offsets. +// A base register provides a pointer to the table, and a second register +// supplies an index into the table. +// The branch length is twice the value of the halfword returned from the +// table. TBB, TBH +bool EmulateInstructionARM::EmulateTB(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + if is_tbh then + halfwords = UInt(MemU[R[n]+LSL(R[m],1), 2]); + else + halfwords = UInt(MemU[R[n]+R[m], 1]); + BranchWritePC(PC + 2*halfwords); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rn; // the base register which contains the address of the table of + // branch lengths + uint32_t Rm; // the index register which contains an integer pointing to a + // byte/halfword in the table + bool is_tbh; // true if table branch halfword + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + is_tbh = BitIsSet(opcode, 4); + if (Rn == 13 || BadReg(Rm)) + return false; + if (InITBlock() && !LastInITBlock()) + return false; + break; + default: + return false; + } + + // Read the address of the table from the operand register Rn. The PC can + // be used, in which case the table immediately follows this instruction. + uint32_t base = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // the table index + uint32_t index = ReadCoreReg(Rm, &success); + if (!success) + return false; + + // the offsetted table address + addr_t addr = base + (is_tbh ? index * 2 : index); + + // PC-relative offset to branch forward + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextTableBranchReadMemory; + uint32_t offset = MemURead(context, addr, is_tbh ? 2 : 1, 0, &success) * 2; + if (!success) + return false; + + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + // target address + addr_t target = pc + offset; + context.type = EmulateInstruction::eContextRelativeBranchImmediate; + context.SetISAAndImmediateSigned(eModeThumb, 4 + offset); + + if (!BranchWritePC(context, target)) + return false; + } + + return true; +} + +// This instruction adds an immediate value to a register value, and writes the +// result to the destination register. It can optionally update the condition +// flags based on the result. +bool EmulateInstructionARM::EmulateADDImmThumb(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t d; + uint32_t n; + bool setflags; + uint32_t imm32; + uint32_t carry_out; + + // EncodingSpecificOperations(); + switch (encoding) { + case eEncodingT1: + // d = UInt(Rd); n = UInt(Rn); setflags = !InITBlock(); imm32 = + // ZeroExtend(imm3, 32); + d = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + imm32 = Bits32(opcode, 8, 6); + + break; + + case eEncodingT2: + // d = UInt(Rdn); n = UInt(Rdn); setflags = !InITBlock(); imm32 = + // ZeroExtend(imm8, 32); + d = Bits32(opcode, 10, 8); + n = Bits32(opcode, 10, 8); + setflags = !InITBlock(); + imm32 = Bits32(opcode, 7, 0); + + break; + + case eEncodingT3: + // if Rd == '1111' && S == '1' then SEE CMN (immediate); + // d = UInt(Rd); n = UInt(Rn); setflags = (S == '1'); imm32 = + // ThumbExpandImm(i:imm3:imm8); + d = Bits32(opcode, 11, 8); + n = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C(opcode, APSR_C, carry_out); + + // if Rn == '1101' then SEE ADD (SP plus immediate); + if (n == 13) + return EmulateADDSPImm(opcode, eEncodingT3); + + // if BadReg(d) || n == 15 then UNPREDICTABLE; + if (BadReg(d) || (n == 15)) + return false; + + break; + + case eEncodingT4: { + // if Rn == '1111' then SEE ADR; + // d = UInt(Rd); n = UInt(Rn); setflags = FALSE; imm32 = + // ZeroExtend(i:imm3:imm8, 32); + d = Bits32(opcode, 11, 8); + n = Bits32(opcode, 19, 16); + setflags = false; + uint32_t i = Bit32(opcode, 26); + uint32_t imm3 = Bits32(opcode, 14, 12); + uint32_t imm8 = Bits32(opcode, 7, 0); + imm32 = (i << 11) | (imm3 << 8) | imm8; + + // if Rn == '1101' then SEE ADD (SP plus immediate); + if (n == 13) + return EmulateADDSPImm(opcode, eEncodingT4); + + // if BadReg(d) then UNPREDICTABLE; + if (BadReg(d)) + return false; + + break; + } + + default: + return false; + } + + uint64_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + //(result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + AddWithCarryResult res = AddWithCarry(Rn, imm32, 0); + + std::optional<RegisterInfo> reg_n = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + EmulateInstruction::Context context; + context.type = eContextArithmetic; + context.SetRegisterPlusOffset(*reg_n, imm32); + + // R[d] = result; + // if setflags then + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + // APSR.C = carry; + // APSR.V = overflow; + if (!WriteCoreRegOptionalFlags(context, res.result, d, setflags, + res.carry_out, res.overflow)) + return false; + } + return true; +} + +// This instruction adds an immediate value to a register value, and writes the +// result to the destination register. It can optionally update the condition +// flags based on the result. +bool EmulateInstructionARM::EmulateADDImmARM(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd, Rn; + uint32_t + imm32; // the immediate value to be added to the value obtained from Rn + bool setflags; + switch (encoding) { + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(val1, imm32, 0); + + EmulateInstruction::Context context; + if (Rd == 13) + context.type = EmulateInstruction::eContextAdjustStackPointer; + else if (Rd == GetFramePointerRegisterNumber()) + context.type = EmulateInstruction::eContextSetFramePointer; + else + context.type = EmulateInstruction::eContextRegisterPlusOffset; + + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, Rn); + context.SetRegisterPlusOffset(*dwarf_reg, imm32); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow)) + return false; + } + return true; +} + +// This instruction adds a register value and an optionally-shifted register +// value, and writes the result to the destination register. It can optionally +// update the condition flags based on the result. +bool EmulateInstructionARM::EmulateADDReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rn = Bits32(opcode, 5, 3); + Rm = Bits32(opcode, 8, 6); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 6, 3); + setflags = false; + shift_t = SRType_LSL; + shift_n = 0; + if (Rn == 15 && Rm == 15) + return false; + if (Rd == 15 && InITBlock() && !LastInITBlock()) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, shifted, 0); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + std::optional<RegisterInfo> op1_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rn); + std::optional<RegisterInfo> op2_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm); + context.SetRegisterRegisterOperands(*op1_reg, *op2_reg); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow)) + return false; + } + return true; +} + +// Compare Negative (immediate) adds a register value and an immediate value. +// It updates the condition flags based on the result, and discards the result. +bool EmulateInstructionARM::EmulateCMNImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rn; // the first operand + uint32_t imm32; // the immediate value to be compared with + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (Rn == 15) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, imm32, 0); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + return WriteFlags(context, res.result, res.carry_out, res.overflow); +} + +// Compare Negative (register) adds a register value and an optionally-shifted +// register value. It updates the condition flags based on the result, and +// discards the result. +bool EmulateInstructionARM::EmulateCMNReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if n == 15 || BadReg(m) then UNPREDICTABLE; + if (Rn == 15 || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, shifted, 0); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + return WriteFlags(context, res.result, res.carry_out, res.overflow); +} + +// Compare (immediate) subtracts an immediate value from a register value. It +// updates the condition flags based on the result, and discards the result. +bool EmulateInstructionARM::EmulateCMPImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rn; // the first operand + uint32_t imm32; // the immediate value to be compared with + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 10, 8); + imm32 = Bits32(opcode, 7, 0); + break; + case eEncodingT2: + Rn = Bits32(opcode, 19, 16); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (Rn == 15) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + return WriteFlags(context, res.result, res.carry_out, res.overflow); +} + +// Compare (register) subtracts an optionally-shifted register value from a +// register value. It updates the condition flags based on the result, and +// discards the result. +bool EmulateInstructionARM::EmulateCMPReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1'); + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 6, 3); + shift_t = SRType_LSL; + shift_n = 0; + if (Rn < 8 && Rm < 8) + return false; + if (Rn == 15 || Rm == 15) + return false; + break; + case eEncodingT3: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (Rn == 15 || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, ~shifted, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + return WriteFlags(context, res.result, res.carry_out, res.overflow); +} + +// Arithmetic Shift Right (immediate) shifts a register value right by an +// immediate number of bits, shifting in copies of its sign bit, and writes the +// result to the destination register. It can optionally update the condition +// flags based on the result. +bool EmulateInstructionARM::EmulateASRImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm(opcode, encoding, SRType_ASR); +} + +// Arithmetic Shift Right (register) shifts a register value right by a +// variable number of bits, shifting in copies of its sign bit, and writes the +// result to the destination register. The variable number of bits is read from +// the bottom byte of a register. It can optionally update the condition flags +// based on the result. +bool EmulateInstructionARM::EmulateASRReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[m]<7:0>); + (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftReg(opcode, encoding, SRType_ASR); +} + +// Logical Shift Left (immediate) shifts a register value left by an immediate +// number of bits, shifting in zeros, and writes the result to the destination +// register. It can optionally update the condition flags based on the result. +bool EmulateInstructionARM::EmulateLSLImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm(opcode, encoding, SRType_LSL); +} + +// Logical Shift Left (register) shifts a register value left by a variable +// number of bits, shifting in zeros, and writes the result to the destination +// register. The variable number of bits is read from the bottom byte of a +// register. It can optionally update the condition flags based on the result. +bool EmulateInstructionARM::EmulateLSLReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[m]<7:0>); + (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftReg(opcode, encoding, SRType_LSL); +} + +// Logical Shift Right (immediate) shifts a register value right by an +// immediate number of bits, shifting in zeros, and writes the result to the +// destination register. It can optionally update the condition flags based on +// the result. +bool EmulateInstructionARM::EmulateLSRImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm(opcode, encoding, SRType_LSR); +} + +// Logical Shift Right (register) shifts a register value right by a variable +// number of bits, shifting in zeros, and writes the result to the destination +// register. The variable number of bits is read from the bottom byte of a +// register. It can optionally update the condition flags based on the result. +bool EmulateInstructionARM::EmulateLSRReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[m]<7:0>); + (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftReg(opcode, encoding, SRType_LSR); +} + +// Rotate Right (immediate) provides the value of the contents of a register +// rotated by a constant value. The bits that are rotated off the right end are +// inserted into the vacated bit positions on the left. It can optionally +// update the condition flags based on the result. +bool EmulateInstructionARM::EmulateRORImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm(opcode, encoding, SRType_ROR); +} + +// Rotate Right (register) provides the value of the contents of a register +// rotated by a variable number of bits. The bits that are rotated off the +// right end are inserted into the vacated bit positions on the left. The +// variable number of bits is read from the bottom byte of a register. It can +// optionally update the condition flags based on the result. +bool EmulateInstructionARM::EmulateRORReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[m]<7:0>); + (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftReg(opcode, encoding, SRType_ROR); +} + +// Rotate Right with Extend provides the value of the contents of a register +// shifted right by one place, with the carry flag shifted into bit [31]. +// +// RRX can optionally update the condition flags based on the result. +// In that case, bit [0] is shifted into the carry flag. +bool EmulateInstructionARM::EmulateRRX(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry) = Shift_C(R[m], SRType_RRX, 1, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + return EmulateShiftImm(opcode, encoding, SRType_RRX); +} + +bool EmulateInstructionARM::EmulateShiftImm(const uint32_t opcode, + const ARMEncoding encoding, + ARM_ShifterType shift_type) { + // assert(shift_type == SRType_ASR + // || shift_type == SRType_LSL + // || shift_type == SRType_LSR + // || shift_type == SRType_ROR + // || shift_type == SRType_RRX); + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd; // the destination register + uint32_t Rm; // the first operand register + uint32_t imm5; // encoding for the shift amount + uint32_t carry; // the carry bit after the shift operation + bool setflags; + + // Special case handling! + // A8.6.139 ROR (immediate) -- Encoding T1 + ARMEncoding use_encoding = encoding; + if (shift_type == SRType_ROR && use_encoding == eEncodingT1) { + // Morph the T1 encoding from the ARM Architecture Manual into T2 + // encoding to have the same decoding of bit fields as the other Thumb2 + // shift operations. + use_encoding = eEncodingT2; + } + + switch (use_encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + imm5 = Bits32(opcode, 10, 6); + break; + case eEncodingT2: + // A8.6.141 RRX + // There's no imm form of RRX instructions. + if (shift_type == SRType_RRX) + return false; + + Rd = Bits32(opcode, 11, 8); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + imm5 = Bits32(opcode, 14, 12) << 2 | Bits32(opcode, 7, 6); + if (BadReg(Rd) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + imm5 = Bits32(opcode, 11, 7); + break; + default: + return false; + } + + // A8.6.139 ROR (immediate) + if (shift_type == SRType_ROR && imm5 == 0) + shift_type = SRType_RRX; + + // Get the first operand. + uint32_t value = ReadCoreReg(Rm, &success); + if (!success) + return false; + + // Decode the shift amount if not RRX. + uint32_t amt = + (shift_type == SRType_RRX ? 1 : DecodeImmShift(shift_type, imm5)); + + uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success); + if (!success) + return false; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +bool EmulateInstructionARM::EmulateShiftReg(const uint32_t opcode, + const ARMEncoding encoding, + ARM_ShifterType shift_type) { + // assert(shift_type == SRType_ASR + // || shift_type == SRType_LSL + // || shift_type == SRType_LSR + // || shift_type == SRType_ROR); + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand register + uint32_t + Rm; // the register whose bottom byte contains the amount to shift by + uint32_t carry; // the carry bit after the shift operation + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rn = Rd; + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 3, 0); + Rm = Bits32(opcode, 11, 8); + setflags = BitIsSet(opcode, 20); + if (Rd == 15 || Rn == 15 || Rm == 15) + return false; + break; + default: + return false; + } + + // Get the first operand. + uint32_t value = ReadCoreReg(Rn, &success); + if (!success) + return false; + // Get the Rm register content. + uint32_t val = ReadCoreReg(Rm, &success); + if (!success) + return false; + + // Get the shift amount. + uint32_t amt = Bits32(val, 7, 0); + + uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success); + if (!success) + return false; + + // The context specifies that an immediate is to be moved into Rd. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// LDM loads multiple registers from consecutive memory locations, using an +// address from a base register. Optionally the address just above the highest +// of those locations can be written back to the base register. +bool EmulateInstructionARM::EmulateLDM(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() + EncodingSpecificOperations(); NullCheckIfThumbEE (n); + address = R[n]; + + for i = 0 to 14 + if registers<i> == '1' then + R[i] = MemA[address, 4]; address = address + 4; + if registers<15> == '1' then + LoadWritePC (MemA[address, 4]); + + if wback && registers<n> == '0' then R[n] = R[n] + 4 * BitCount (registers); + if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 + +#endif + + bool success = false; + if (ConditionPassed(opcode)) { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + switch (encoding) { + case eEncodingT1: + // n = UInt(Rn); registers = '00000000':register_list; wback = + // (registers<n> == '0'); + n = Bits32(opcode, 10, 8); + registers = Bits32(opcode, 7, 0); + registers = registers & 0x00ff; // Make sure the top 8 bits are zeros. + wback = BitIsClear(registers, n); + // if BitCount(registers) < 1 then UNPREDICTABLE; + if (BitCount(registers) < 1) + return false; + break; + case eEncodingT2: + // if W == '1' && Rn == '1101' then SEE POP; + // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1'); + n = Bits32(opcode, 19, 16); + registers = Bits32(opcode, 15, 0); + registers = registers & 0xdfff; // Make sure bit 13 is zero. + wback = BitIsSet(opcode, 21); + + // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then + // UNPREDICTABLE; + if ((n == 15) || (BitCount(registers) < 2) || + (BitIsSet(opcode, 14) && BitIsSet(opcode, 15))) + return false; + + // if registers<15> == '1' && InITBlock() && !LastInITBlock() then + // UNPREDICTABLE; + if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock()) + return false; + + // if wback && registers<n> == '1' then UNPREDICTABLE; + if (wback && BitIsSet(registers, n)) + return false; + break; + + case eEncodingA1: + n = Bits32(opcode, 19, 16); + registers = Bits32(opcode, 15, 0); + wback = BitIsSet(opcode, 21); + if ((n == 15) || (BitCount(registers) < 1)) + return false; + break; + default: + return false; + } + + int32_t offset = 0; + const addr_t base_address = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + context.SetRegisterPlusOffset(*dwarf_reg, offset); + + for (int i = 0; i < 14; ++i) { + if (BitIsSet(registers, i)) { + context.type = EmulateInstruction::eContextRegisterPlusOffset; + context.SetRegisterPlusOffset(*dwarf_reg, offset); + if (wback && (n == 13)) // Pop Instruction + { + context.type = EmulateInstruction::eContextPopRegisterOffStack; + context.SetAddress(base_address + offset); + } + + // R[i] = MemA [address, 4]; address = address + 4; + uint32_t data = MemARead(context, base_address + offset, addr_byte_size, + 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, + data)) + return false; + + offset += addr_byte_size; + } + } + + if (BitIsSet(registers, 15)) { + // LoadWritePC (MemA [address, 4]); + context.type = EmulateInstruction::eContextRegisterPlusOffset; + context.SetRegisterPlusOffset(*dwarf_reg, offset); + uint32_t data = + MemARead(context, base_address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + + if (wback && BitIsClear(registers, n)) { + // R[n] = R[n] + 4 * BitCount (registers) + int32_t offset = addr_byte_size * BitCount(registers); + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetRegisterPlusOffset(*dwarf_reg, offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + base_address + offset)) + return false; + } + if (wback && BitIsSet(registers, n)) + // R[n] bits(32) UNKNOWN; + return WriteBits32Unknown(n); + } + return true; +} + +// LDMDA loads multiple registers from consecutive memory locations using an +// address from a base register. +// The consecutive memory locations end at this address and the address just +// below the lowest of those locations can optionally be written back to the +// base register. +bool EmulateInstructionARM::EmulateLDMDA(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + address = R[n] - 4*BitCount(registers) + 4; + + for i = 0 to 14 + if registers<i> == '1' then + R[i] = MemA[address,4]; address = address + 4; + + if registers<15> == '1' then + LoadWritePC(MemA[address,4]); + + if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers); + if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); + switch (encoding) { + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32(opcode, 19, 16); + registers = Bits32(opcode, 15, 0); + wback = BitIsSet(opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount(registers) < 1)) + return false; + + break; + + default: + return false; + } + // address = R[n] - 4*BitCount(registers) + 4; + + int32_t offset = 0; + addr_t Rn = ReadCoreReg(n, &success); + + if (!success) + return false; + + addr_t address = + Rn - (addr_byte_size * BitCount(registers)) + addr_byte_size; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + context.SetRegisterPlusOffset(*dwarf_reg, offset); + + // for i = 0 to 14 + for (int i = 0; i < 14; ++i) { + // if registers<i> == '1' then + if (BitIsSet(registers, i)) { + // R[i] = MemA[address,4]; address = address + 4; + context.SetRegisterPlusOffset(*dwarf_reg, Rn - (address + offset)); + uint32_t data = + MemARead(context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, + data)) + return false; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // LoadWritePC(MemA[address,4]); + if (BitIsSet(registers, 15)) { + context.SetRegisterPlusOffset(*dwarf_reg, offset); + uint32_t data = + MemARead(context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + + // if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers); + if (wback && BitIsClear(registers, n)) { + + offset = (addr_byte_size * BitCount(registers)) * -1; + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned(offset); + addr_t addr = Rn + offset; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + addr)) + return false; + } + + // if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; + if (wback && BitIsSet(registers, n)) + return WriteBits32Unknown(n); + } + return true; +} + +// LDMDB loads multiple registers from consecutive memory locations using an +// address from a base register. The +// consecutive memory locations end just below this address, and the address of +// the lowest of those locations can be optionally written back to the base +// register. +bool EmulateInstructionARM::EmulateLDMDB(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + address = R[n] - 4*BitCount(registers); + + for i = 0 to 14 + if registers<i> == '1' then + R[i] = MemA[address,4]; address = address + 4; + if registers<15> == '1' then + LoadWritePC(MemA[address,4]); + + if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers); + if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + switch (encoding) { + case eEncodingT1: + // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1'); + n = Bits32(opcode, 19, 16); + registers = Bits32(opcode, 15, 0); + registers = registers & 0xdfff; // Make sure bit 13 is a zero. + wback = BitIsSet(opcode, 21); + + // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then + // UNPREDICTABLE; + if ((n == 15) || (BitCount(registers) < 2) || + (BitIsSet(opcode, 14) && BitIsSet(opcode, 15))) + return false; + + // if registers<15> == '1' && InITBlock() && !LastInITBlock() then + // UNPREDICTABLE; + if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock()) + return false; + + // if wback && registers<n> == '1' then UNPREDICTABLE; + if (wback && BitIsSet(registers, n)) + return false; + + break; + + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32(opcode, 19, 16); + registers = Bits32(opcode, 15, 0); + wback = BitIsSet(opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount(registers) < 1)) + return false; + + break; + + default: + return false; + } + + // address = R[n] - 4*BitCount(registers); + + int32_t offset = 0; + addr_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + + if (!success) + return false; + + addr_t address = Rn - (addr_byte_size * BitCount(registers)); + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + context.SetRegisterPlusOffset(*dwarf_reg, Rn - address); + + for (int i = 0; i < 14; ++i) { + if (BitIsSet(registers, i)) { + // R[i] = MemA[address,4]; address = address + 4; + context.SetRegisterPlusOffset(*dwarf_reg, Rn - (address + offset)); + uint32_t data = + MemARead(context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, + data)) + return false; + + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // LoadWritePC(MemA[address,4]); + if (BitIsSet(registers, 15)) { + context.SetRegisterPlusOffset(*dwarf_reg, offset); + uint32_t data = + MemARead(context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + + // if wback && registers<n> == '0' then R[n] = R[n] - 4*BitCount(registers); + if (wback && BitIsClear(registers, n)) { + + offset = (addr_byte_size * BitCount(registers)) * -1; + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned(offset); + addr_t addr = Rn + offset; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + addr)) + return false; + } + + // if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only + // possible for encoding A1 + if (wback && BitIsSet(registers, n)) + return WriteBits32Unknown(n); + } + return true; +} + +// LDMIB loads multiple registers from consecutive memory locations using an +// address from a base register. The +// consecutive memory locations start just above this address, and thea ddress +// of the last of those locations can optinoally be written back to the base +// register. +bool EmulateInstructionARM::EmulateLDMIB(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + address = R[n] + 4; + + for i = 0 to 14 + if registers<i> == '1' then + R[i] = MemA[address,4]; address = address + 4; + if registers<15> == '1' then + LoadWritePC(MemA[address,4]); + + if wback && registers<n> == '0' then R[n] = R[n] + 4*BitCount(registers); + if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + switch (encoding) { + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32(opcode, 19, 16); + registers = Bits32(opcode, 15, 0); + wback = BitIsSet(opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount(registers) < 1)) + return false; + + break; + default: + return false; + } + // address = R[n] + 4; + + int32_t offset = 0; + addr_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + + if (!success) + return false; + + addr_t address = Rn + addr_byte_size; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterPlusOffset; + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + context.SetRegisterPlusOffset(*dwarf_reg, offset); + + for (int i = 0; i < 14; ++i) { + if (BitIsSet(registers, i)) { + // R[i] = MemA[address,4]; address = address + 4; + + context.SetRegisterPlusOffset(*dwarf_reg, offset + addr_byte_size); + uint32_t data = + MemARead(context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, + data)) + return false; + + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // LoadWritePC(MemA[address,4]); + if (BitIsSet(registers, 15)) { + context.SetRegisterPlusOffset(*dwarf_reg, offset); + uint32_t data = + MemARead(context, address + offset, addr_byte_size, 0, &success); + if (!success) + return false; + // In ARMv5T and above, this is an interworking branch. + if (!LoadWritePC(context, data)) + return false; + } + + // if wback && registers<n> == '0' then R[n] = R[n] + 4*BitCount(registers); + if (wback && BitIsClear(registers, n)) { + + offset = addr_byte_size * BitCount(registers); + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned(offset); + addr_t addr = Rn + offset; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + addr)) + return false; + } + + // if wback && registers<n> == '1' then R[n] = bits(32) UNKNOWN; // Only + // possible for encoding A1 + if (wback && BitIsSet(registers, n)) + return WriteBits32Unknown(n); + } + return true; +} + +// Load Register (immediate) calculates an address from a base register value +// and an immediate offset, loads a word from memory, and writes to a register. +// LDR (immediate, Thumb) +bool EmulateInstructionARM::EmulateLDRRtRnImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if (ConditionPassed()) + { + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + data = MemU[address,4]; + if wback then R[n] = offset_addr; + if t == 15 then + if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + elsif UnalignedSupport() || address<1:0> = '00' then + R[t] = data; + else R[t] = bits(32) UNKNOWN; // Can only apply before ARMv7 + } +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rt; // the destination register + uint32_t Rn; // the base register + uint32_t imm32; // the immediate offset used to form the address + addr_t offset_addr; // the offset address + addr_t address; // the calculated address + uint32_t data; // the literal data value from memory load + bool add, index, wback; + switch (encoding) { + case eEncodingT1: + Rt = Bits32(opcode, 2, 0); + Rn = Bits32(opcode, 5, 3); + imm32 = Bits32(opcode, 10, 6) << 2; // imm32 = ZeroExtend(imm5:'00', 32); + // index = TRUE; add = TRUE; wback = FALSE + add = true; + index = true; + wback = false; + + break; + + case eEncodingT2: + // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32); + Rt = Bits32(opcode, 10, 8); + Rn = 13; + imm32 = Bits32(opcode, 7, 0) << 2; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + break; + + case eEncodingT3: + // if Rn == '1111' then SEE LDR (literal); + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + Rt = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if ((Rt == 15) && InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingT4: + // if Rn == '1111' then SEE LDR (literal); + // if P == '1' && U == '1' && W == '0' then SEE LDRT; + // if Rn == '1101' && P == '0' && U == '1' && W == '1' && imm8 == + // '00000100' then SEE POP; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + Rt = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet(opcode, 10); + add = BitIsSet(opcode, 9); + wback = BitIsSet(opcode, 8); + + // if (wback && n == t) || (t == 15 && InITBlock() && !LastInITBlock()) + // then UNPREDICTABLE; + if ((wback && (Rn == Rt)) || + ((Rt == 15) && InITBlock() && !LastInITBlock())) + return false; + + break; + + default: + return false; + } + uint32_t base = ReadCoreReg(Rn, &success); + if (!success) + return false; + if (add) + offset_addr = base + imm32; + else + offset_addr = base - imm32; + + address = (index ? offset_addr : base); + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rn); + if (wback) { + EmulateInstruction::Context ctx; + if (Rn == 13) { + ctx.type = eContextAdjustStackPointer; + ctx.SetImmediateSigned((int32_t)(offset_addr - base)); + } else if (Rn == GetFramePointerRegisterNumber()) { + ctx.type = eContextSetFramePointer; + ctx.SetRegisterPlusOffset(*base_reg, (int32_t)(offset_addr - base)); + } else { + ctx.type = EmulateInstruction::eContextAdjustBaseRegister; + ctx.SetRegisterPlusOffset(*base_reg, (int32_t)(offset_addr - base)); + } + + if (!WriteRegisterUnsigned(ctx, eRegisterKindDWARF, dwarf_r0 + Rn, + offset_addr)) + return false; + } + + // Prepare to write to the Rt register. + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, (int32_t)(offset_addr - base)); + + // Read memory from the address. + data = MemURead(context, address, 4, 0, &success); + if (!success) + return false; + + if (Rt == 15) { + if (Bits32(address, 1, 0) == 0) { + if (!LoadWritePC(context, data)) + return false; + } else + return false; + } else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) { + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + Rt, + data)) + return false; + } else + WriteBits32Unknown(Rt); + } + return true; +} + +// STM (Store Multiple Increment After) stores multiple registers to consecutive +// memory locations using an address +// from a base register. The consecutive memory locations start at this +// address, and the address just above the last of those locations can +// optionally be written back to the base register. +bool EmulateInstructionARM::EmulateSTM(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + address = R[n]; + + for i = 0 to 14 + if registers<i> == '1' then + if i == n && wback && i != LowestSetBit(registers) then + MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings T1 and A1 + else + MemA[address,4] = R[i]; + address = address + 4; + + if registers<15> == '1' then // Only possible for encoding A1 + MemA[address,4] = PCStoreValue(); + if wback then R[n] = R[n] + 4*BitCount(registers); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // n = UInt(Rn); registers = '00000000':register_list; wback = TRUE; + n = Bits32(opcode, 10, 8); + registers = Bits32(opcode, 7, 0); + registers = registers & 0x00ff; // Make sure the top 8 bits are zeros. + wback = true; + + // if BitCount(registers) < 1 then UNPREDICTABLE; + if (BitCount(registers) < 1) + return false; + + break; + + case eEncodingT2: + // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1'); + n = Bits32(opcode, 19, 16); + registers = Bits32(opcode, 15, 0); + registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros. + wback = BitIsSet(opcode, 21); + + // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE; + if ((n == 15) || (BitCount(registers) < 2)) + return false; + + // if wback && registers<n> == '1' then UNPREDICTABLE; + if (wback && BitIsSet(registers, n)) + return false; + + break; + + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32(opcode, 19, 16); + registers = Bits32(opcode, 15, 0); + wback = BitIsSet(opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount(registers) < 1)) + return false; + + break; + + default: + return false; + } + + // address = R[n]; + int32_t offset = 0; + const addr_t address = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterStore; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + // for i = 0 to 14 + uint32_t lowest_set_bit = 14; + for (uint32_t i = 0; i < 14; ++i) { + // if registers<i> == '1' then + if (BitIsSet(registers, i)) { + if (i < lowest_set_bit) + lowest_set_bit = i; + // if i == n && wback && i != LowestSetBit(registers) then + if ((i == n) && wback && (i != lowest_set_bit)) + // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings + // T1 and A1 + WriteBits32UnknownToMemory(address + offset); + else { + // MemA[address,4] = R[i]; + uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i, + 0, &success); + if (!success) + return false; + + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i); + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, offset); + if (!MemAWrite(context, address + offset, data, addr_byte_size)) + return false; + } + + // address = address + 4; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then // Only possible for encoding A1 + // MemA[address,4] = PCStoreValue(); + if (BitIsSet(registers, 15)) { + std::optional<RegisterInfo> pc_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_pc); + context.SetRegisterPlusOffset(*pc_reg, 8); + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + if (!MemAWrite(context, address + offset, pc, addr_byte_size)) + return false; + } + + // if wback then R[n] = R[n] + 4*BitCount(registers); + if (wback) { + offset = addr_byte_size * BitCount(registers); + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned(offset); + addr_t data = address + offset; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + data)) + return false; + } + } + return true; +} + +// STMDA (Store Multiple Decrement After) stores multiple registers to +// consecutive memory locations using an address from a base register. The +// consecutive memory locations end at this address, and the address just below +// the lowest of those locations can optionally be written back to the base +// register. +bool EmulateInstructionARM::EmulateSTMDA(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + address = R[n] - 4*BitCount(registers) + 4; + + for i = 0 to 14 + if registers<i> == '1' then + if i == n && wback && i != LowestSetBit(registers) then + MemA[address,4] = bits(32) UNKNOWN; + else + MemA[address,4] = R[i]; + address = address + 4; + + if registers<15> == '1' then + MemA[address,4] = PCStoreValue(); + + if wback then R[n] = R[n] - 4*BitCount(registers); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); + switch (encoding) { + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32(opcode, 19, 16); + registers = Bits32(opcode, 15, 0); + wback = BitIsSet(opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || (BitCount(registers) < 1)) + return false; + break; + default: + return false; + } + + // address = R[n] - 4*BitCount(registers) + 4; + int32_t offset = 0; + addr_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + addr_t address = Rn - (addr_byte_size * BitCount(registers)) + 4; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterStore; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + // for i = 0 to 14 + uint32_t lowest_bit_set = 14; + for (uint32_t i = 0; i < 14; ++i) { + // if registers<i> == '1' then + if (BitIsSet(registers, i)) { + if (i < lowest_bit_set) + lowest_bit_set = i; + // if i == n && wback && i != LowestSetBit(registers) then + if ((i == n) && wback && (i != lowest_bit_set)) + // MemA[address,4] = bits(32) UNKNOWN; + WriteBits32UnknownToMemory(address + offset); + else { + // MemA[address,4] = R[i]; + uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i, + 0, &success); + if (!success) + return false; + + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i); + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + Rn - (address + offset)); + if (!MemAWrite(context, address + offset, data, addr_byte_size)) + return false; + } + + // address = address + 4; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // MemA[address,4] = PCStoreValue(); + if (BitIsSet(registers, 15)) { + std::optional<RegisterInfo> pc_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_pc); + context.SetRegisterPlusOffset(*pc_reg, 8); + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + if (!MemAWrite(context, address + offset, pc, addr_byte_size)) + return false; + } + + // if wback then R[n] = R[n] - 4*BitCount(registers); + if (wback) { + offset = (addr_byte_size * BitCount(registers)) * -1; + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned(offset); + addr_t data = Rn + offset; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + data)) + return false; + } + } + return true; +} + +// STMDB (Store Multiple Decrement Before) stores multiple registers to +// consecutive memory locations using an address from a base register. The +// consecutive memory locations end just below this address, and the address of +// the first of those locations can optionally be written back to the base +// register. +bool EmulateInstructionARM::EmulateSTMDB(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + address = R[n] - 4*BitCount(registers); + + for i = 0 to 14 + if registers<i> == '1' then + if i == n && wback && i != LowestSetBit(registers) then + MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding A1 + else + MemA[address,4] = R[i]; + address = address + 4; + + if registers<15> == '1' then // Only possible for encoding A1 + MemA[address,4] = PCStoreValue(); + + if wback then R[n] = R[n] - 4*BitCount(registers); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // if W == '1' && Rn == '1101' then SEE PUSH; + if ((BitIsSet(opcode, 21)) && (Bits32(opcode, 19, 16) == 13)) { + // See PUSH + } + // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1'); + n = Bits32(opcode, 19, 16); + registers = Bits32(opcode, 15, 0); + registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros. + wback = BitIsSet(opcode, 21); + // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE; + if ((n == 15) || BitCount(registers) < 2) + return false; + // if wback && registers<n> == '1' then UNPREDICTABLE; + if (wback && BitIsSet(registers, n)) + return false; + break; + + case eEncodingA1: + // if W == '1' && Rn == '1101' && BitCount(register_list) >= 2 then SEE + // PUSH; + if (BitIsSet(opcode, 21) && (Bits32(opcode, 19, 16) == 13) && + BitCount(Bits32(opcode, 15, 0)) >= 2) { + // See Push + } + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32(opcode, 19, 16); + registers = Bits32(opcode, 15, 0); + wback = BitIsSet(opcode, 21); + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) || BitCount(registers) < 1) + return false; + break; + + default: + return false; + } + + // address = R[n] - 4*BitCount(registers); + + int32_t offset = 0; + addr_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t address = Rn - (addr_byte_size * BitCount(registers)); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterStore; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + // for i = 0 to 14 + uint32_t lowest_set_bit = 14; + for (uint32_t i = 0; i < 14; ++i) { + // if registers<i> == '1' then + if (BitIsSet(registers, i)) { + if (i < lowest_set_bit) + lowest_set_bit = i; + // if i == n && wback && i != LowestSetBit(registers) then + if ((i == n) && wback && (i != lowest_set_bit)) + // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding + // A1 + WriteBits32UnknownToMemory(address + offset); + else { + // MemA[address,4] = R[i]; + uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i, + 0, &success); + if (!success) + return false; + + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i); + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + Rn - (address + offset)); + if (!MemAWrite(context, address + offset, data, addr_byte_size)) + return false; + } + + // address = address + 4; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then // Only possible for encoding A1 + // MemA[address,4] = PCStoreValue(); + if (BitIsSet(registers, 15)) { + std::optional<RegisterInfo> pc_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_pc); + context.SetRegisterPlusOffset(*pc_reg, 8); + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + if (!MemAWrite(context, address + offset, pc, addr_byte_size)) + return false; + } + + // if wback then R[n] = R[n] - 4*BitCount(registers); + if (wback) { + offset = (addr_byte_size * BitCount(registers)) * -1; + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned(offset); + addr_t data = Rn + offset; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + data)) + return false; + } + } + return true; +} + +// STMIB (Store Multiple Increment Before) stores multiple registers to +// consecutive memory locations using an address from a base register. The +// consecutive memory locations start just above this address, and the address +// of the last of those locations can optionally be written back to the base +// register. +bool EmulateInstructionARM::EmulateSTMIB(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + address = R[n] + 4; + + for i = 0 to 14 + if registers<i> == '1' then + if i == n && wback && i != LowestSetBit(registers) then + MemA[address,4] = bits(32) UNKNOWN; + else + MemA[address,4] = R[i]; + address = address + 4; + + if registers<15> == '1' then + MemA[address,4] = PCStoreValue(); + + if wback then R[n] = R[n] + 4*BitCount(registers); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t n; + uint32_t registers = 0; + bool wback; + const uint32_t addr_byte_size = GetAddressByteSize(); + + // EncodingSpecificOperations(); + switch (encoding) { + case eEncodingA1: + // n = UInt(Rn); registers = register_list; wback = (W == '1'); + n = Bits32(opcode, 19, 16); + registers = Bits32(opcode, 15, 0); + wback = BitIsSet(opcode, 21); + + // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; + if ((n == 15) && (BitCount(registers) < 1)) + return false; + break; + default: + return false; + } + // address = R[n] + 4; + + int32_t offset = 0; + addr_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + addr_t address = Rn + addr_byte_size; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextRegisterStore; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + uint32_t lowest_set_bit = 14; + // for i = 0 to 14 + for (uint32_t i = 0; i < 14; ++i) { + // if registers<i> == '1' then + if (BitIsSet(registers, i)) { + if (i < lowest_set_bit) + lowest_set_bit = i; + // if i == n && wback && i != LowestSetBit(registers) then + if ((i == n) && wback && (i != lowest_set_bit)) + // MemA[address,4] = bits(32) UNKNOWN; + WriteBits32UnknownToMemory(address + offset); + // else + else { + // MemA[address,4] = R[i]; + uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + i, + 0, &success); + if (!success) + return false; + + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i); + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + offset + addr_byte_size); + if (!MemAWrite(context, address + offset, data, addr_byte_size)) + return false; + } + + // address = address + 4; + offset += addr_byte_size; + } + } + + // if registers<15> == '1' then + // MemA[address,4] = PCStoreValue(); + if (BitIsSet(registers, 15)) { + std::optional<RegisterInfo> pc_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_pc); + context.SetRegisterPlusOffset(*pc_reg, 8); + const uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + if (!MemAWrite(context, address + offset, pc, addr_byte_size)) + return false; + } + + // if wback then R[n] = R[n] + 4*BitCount(registers); + if (wback) { + offset = addr_byte_size * BitCount(registers); + context.type = EmulateInstruction::eContextAdjustBaseRegister; + context.SetImmediateSigned(offset); + addr_t data = Rn + offset; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + data)) + return false; + } + } + return true; +} + +// STR (store immediate) calculates an address from a base register value and an +// immediate offset, and stores a word +// from a register to memory. It can use offset, post-indexed, or pre-indexed +// addressing. +bool EmulateInstructionARM::EmulateSTRThumb(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + if UnalignedSupport() || address<1:0> == '00' then + MemU[address,4] = R[t]; + else // Can only occur before ARMv7 + MemU[address,4] = bits(32) UNKNOWN; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + const uint32_t addr_byte_size = GetAddressByteSize(); + + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + // EncodingSpecificOperations (); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'00', 32); + t = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + imm32 = Bits32(opcode, 10, 6) << 2; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = false; + wback = false; + break; + + case eEncodingT2: + // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32); + t = Bits32(opcode, 10, 8); + n = 13; + imm32 = Bits32(opcode, 7, 0) << 2; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + break; + + case eEncodingT3: + // if Rn == '1111' then UNDEFINED; + if (Bits32(opcode, 19, 16) == 15) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + break; + + case eEncodingT4: + // if P == '1' && U == '1' && W == '0' then SEE STRT; + // if Rn == '1101' && P == '1' && U == '0' && W == '1' && imm8 == + // '00000100' then SEE PUSH; + // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED; + if ((Bits32(opcode, 19, 16) == 15) || + (BitIsClear(opcode, 10) && BitIsClear(opcode, 8))) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet(opcode, 10); + add = BitIsSet(opcode, 9); + wback = BitIsSet(opcode, 8); + + // if t == 15 || (wback && n == t) then UNPREDICTABLE; + if ((t == 15) || (wback && (n == t))) + return false; + break; + + default: + return false; + } + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t base_address = ReadCoreReg(n, &success); + if (!success) + return false; + + if (add) + offset_addr = base_address + imm32; + else + offset_addr = base_address - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = base_address; + + EmulateInstruction::Context context; + if (n == 13) + context.type = eContextPushRegisterOnStack; + else + context.type = eContextRegisterStore; + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + // if UnalignedSupport() || address<1:0> == '00' then + if (UnalignedSupport() || + (BitIsClear(address, 1) && BitIsClear(address, 0))) { + // MemU[address,4] = R[t]; + uint32_t data = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success); + if (!success) + return false; + + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); + int32_t offset = address - base_address; + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, offset); + if (!MemUWrite(context, address, data, addr_byte_size)) + return false; + } else { + // MemU[address,4] = bits(32) UNKNOWN; + WriteBits32UnknownToMemory(address); + } + + // if wback then R[n] = offset_addr; + if (wback) { + if (n == 13) + context.type = eContextAdjustStackPointer; + else + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + return true; +} + +// STR (Store Register) calculates an address from a base register value and an +// offset register value, stores a +// word from a register to memory. The offset register value can optionally +// be shifted. +bool EmulateInstructionARM::EmulateSTRRegister(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + if t == 15 then // Only possible for encoding A1 + data = PCStoreValue(); + else + data = R[t]; + if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() == InstrSet_ARM then + MemU[address,4] = data; + else // Can only occur before ARMv7 + MemU[address,4] = bits(32) UNKNOWN; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + const uint32_t addr_byte_size = GetAddressByteSize(); + + uint32_t t; + uint32_t n; + uint32_t m; + ARM_ShifterType shift_t; + uint32_t shift_n; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations (); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation + // in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + m = Bits32(opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + break; + + case eEncodingT2: + // if Rn == '1111' then UNDEFINED; + if (Bits32(opcode, 19, 16) == 15) + return false; + + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32(opcode, 5, 4); + + // if t == 15 || BadReg(m) then UNPREDICTABLE; + if ((t == 15) || (BadReg(m))) + return false; + break; + + case eEncodingA1: { + // if P == '0' && W == '1' then SEE STRT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || + // (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + uint32_t typ = Bits32(opcode, 6, 5); + uint32_t imm5 = Bits32(opcode, 11, 7); + shift_n = DecodeImmShift(typ, imm5, shift_t); + + // if m == 15 then UNPREDICTABLE; + if (m == 15) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + } + default: + return false; + } + + addr_t offset_addr; + addr_t address; + int32_t offset = 0; + + addr_t base_address = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + uint32_t Rm_data = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset = Shift(Rm_data, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + if (add) + offset_addr = base_address + offset; + else + offset_addr = base_address - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = base_address; + + uint32_t data; + // if t == 15 then // Only possible for encoding A1 + if (t == 15) + // data = PCStoreValue(); + data = ReadCoreReg(PC_REG, &success); + else + // data = R[t]; + data = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success); + + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + + // if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() == + // InstrSet_ARM then + if (UnalignedSupport() || + (BitIsClear(address, 1) && BitIsClear(address, 0)) || + CurrentInstrSet() == eModeARM) { + // MemU[address,4] = data; + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); + + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + address - base_address); + if (!MemUWrite(context, address, data, addr_byte_size)) + return false; + + } else + // MemU[address,4] = bits(32) UNKNOWN; + WriteBits32UnknownToMemory(address); + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextRegisterLoad; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + return true; +} + +bool EmulateInstructionARM::EmulateSTRBThumb(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemU[address,1] = R[t]<7:0>; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32); + t = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + imm32 = Bits32(opcode, 10, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + break; + + case eEncodingT2: + // if Rn == '1111' then UNDEFINED; + if (Bits32(opcode, 19, 16) == 15) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if BadReg(t) then UNPREDICTABLE; + if (BadReg(t)) + return false; + break; + + case eEncodingT3: + // if P == '1' && U == '1' && W == '0' then SEE STRBT; + // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED; + if (Bits32(opcode, 19, 16) == 15) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet(opcode, 10); + add = BitIsSet(opcode, 9); + wback = BitIsSet(opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE + if ((BadReg(t)) || (wback && (n == t))) + return false; + break; + + default: + return false; + } + + addr_t offset_addr; + addr_t address; + addr_t base_address = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + if (add) + offset_addr = base_address + imm32; + else + offset_addr = base_address - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = base_address; + + // MemU[address,1] = R[t]<7:0> + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + address - base_address); + + uint32_t data = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success); + if (!success) + return false; + + data = Bits32(data, 7, 0); + + if (!MemUWrite(context, address, data, 1)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextRegisterLoad; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + + return true; +} + +// STRH (register) calculates an address from a base register value and an +// offset register value, and stores a +// halfword from a register to memory. The offset register value can be +// shifted left by 0, 1, 2, or 3 bits. +bool EmulateInstructionARM::EmulateSTRHRegister(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + if UnalignedSupport() || address<0> == '0' then + MemU[address,2] = R[t]<15:0>; + else // Can only occur before ARMv7 + MemU[address,2] = bits(16) UNKNOWN; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation + // in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + m = Bits32(opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rn == '1111' then UNDEFINED; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + if (n == 15) + return false; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32(opcode, 5, 4); + + // if BadReg(t) || BadReg(m) then UNPREDICTABLE; + if (BadReg(t) || BadReg(m)) + return false; + + break; + + case eEncodingA1: + // if P == '0' && W == '1' then SEE STRHT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || + // (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + uint32_t Rm = ReadCoreReg(m, &success); + if (!success) + return false; + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + addr_t offset_addr; + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + + // if UnalignedSupport() || address<0> == '0' then + if (UnalignedSupport() || BitIsClear(address, 0)) { + // MemU[address,2] = R[t]<15:0>; + uint32_t Rt = ReadCoreReg(t, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> offset_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); + context.SetRegisterToRegisterPlusIndirectOffset(*base_reg, *offset_reg, + *data_reg); + + if (!MemUWrite(context, address, Bits32(Rt, 15, 0), 2)) + return false; + } else // Can only occur before ARMv7 + { + // MemU[address,2] = bits(16) UNKNOWN; + } + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + + return true; +} + +// Add with Carry (immediate) adds an immediate value and the carry flag value +// to a register value, and writes the result to the destination register. It +// can optionally update the condition flags based on the result. +bool EmulateInstructionARM::EmulateADCImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], imm32, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd, Rn; + uint32_t + imm32; // the immediate value to be added to the value obtained from Rn + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (BadReg(Rd) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + int32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(val1, imm32, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow)) + return false; + } + return true; +} + +// Add with Carry (register) adds a register value, the carry flag value, and +// an optionally-shifted register value, and writes the result to the +// destination register. It can optionally update the condition flags based on +// the result. +bool EmulateInstructionARM::EmulateADCReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], shifted, APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + switch (encoding) { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + int32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + int32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, shifted, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow)) + return false; + } + return true; +} + +// This instruction adds an immediate value to the PC value to form a PC- +// relative address, and writes the result to the destination register. +bool EmulateInstructionARM::EmulateADR(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = if add then (Align(PC,4) + imm32) else (Align(PC,4) - imm32); + if d == 15 then // Can only occur for ARM encodings + ALUWritePC(result); + else + R[d] = result; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd; + uint32_t imm32; // the immediate value to be added/subtracted to/from the PC + bool add; + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 10, 8); + imm32 = ThumbImm8Scaled(opcode); // imm32 = ZeroExtend(imm8:'00', 32) + add = true; + break; + case eEncodingT2: + case eEncodingT3: + Rd = Bits32(opcode, 11, 8); + imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) + add = (Bits32(opcode, 24, 21) == 0); // 0b0000 => ADD; 0b0101 => SUB + if (BadReg(Rd)) + return false; + break; + case eEncodingA1: + case eEncodingA2: + Rd = Bits32(opcode, 15, 12); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + add = (Bits32(opcode, 24, 21) == 0x4); // 0b0100 => ADD; 0b0010 => SUB + break; + default: + return false; + } + + // Read the PC value. + uint32_t pc = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + uint32_t result = (add ? Align(pc, 4) + imm32 : Align(pc, 4) - imm32); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreReg(context, result, Rd)) + return false; + } + return true; +} + +// This instruction performs a bitwise AND of a register value and an immediate +// value, and writes the result to the destination register. It can optionally +// update the condition flags based on the result. +bool EmulateInstructionARM::EmulateANDImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] AND imm32; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd, Rn; + uint32_t + imm32; // the immediate value to be ANDed to the value obtained from Rn + bool setflags; + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C( + opcode, APSR_C, + carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + // if Rd == '1111' && S == '1' then SEE TST (immediate); + if (Rd == 15 && setflags) + return EmulateTSTImm(opcode, eEncodingT1); + if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = + ARMExpandImm_C(opcode, APSR_C, + carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 & imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// This instruction performs a bitwise AND of a register value and an +// optionally-shifted register value, and writes the result to the destination +// register. It can optionally update the condition flags based on the result. +bool EmulateInstructionARM::EmulateANDReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] AND shifted; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; + switch (encoding) { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if Rd == '1111' && S == '1' then SEE TST (register); + if (Rd == 15 && setflags) + return EmulateTSTReg(opcode, eEncodingT2); + if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 & shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise Bit Clear (immediate) performs a bitwise AND of a register value and +// the complement of an immediate value, and writes the result to the +// destination register. It can optionally update the condition flags based on +// the result. +bool EmulateInstructionARM::EmulateBICImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] AND NOT(imm32); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd, Rn; + uint32_t imm32; // the immediate value to be bitwise inverted and ANDed to + // the value obtained from Rn + bool setflags; + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C( + opcode, APSR_C, + carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + if (BadReg(Rd) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = + ARMExpandImm_C(opcode, APSR_C, + carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 & ~imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise Bit Clear (register) performs a bitwise AND of a register value and +// the complement of an optionally-shifted register value, and writes the +// result to the destination register. It can optionally update the condition +// flags based on the result. +bool EmulateInstructionARM::EmulateBICReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] AND NOT(shifted); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; + switch (encoding) { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 & ~shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// LDR (immediate, ARM) calculates an address from a base register value and an +// immediate offset, loads a word +// from memory, and writes it to a register. It can use offset, post-indexed, +// or pre-indexed addressing. +bool EmulateInstructionARM::EmulateLDRImmediateARM(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + data = MemU[address,4]; + if wback then R[n] = offset_addr; + if t == 15 then + if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + elsif UnalignedSupport() || address<1:0> = '00' then + R[t] = data; + else // Can only apply before ARMv7 + R[t] = ROR(data, 8*UInt(address<1:0>)); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + const uint32_t addr_byte_size = GetAddressByteSize(); + + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + switch (encoding) { + case eEncodingA1: + // if Rn == '1111' then SEE LDR (literal); + // if P == '0' && W == '1' then SEE LDRT; + // if Rn == '1101' && P == '0' && U == '1' && W == '0' && imm12 == + // '000000000100' then SEE POP; + // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 11, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || + // (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); + + // if wback && n == t then UNPREDICTABLE; + if (wback && (n == t)) + return false; + + break; + + default: + return false; + } + + addr_t address; + addr_t offset_addr; + addr_t base_address = ReadCoreReg(n, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + if (add) + offset_addr = base_address + imm32; + else + offset_addr = base_address - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = base_address; + + // data = MemU[address,4]; + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - base_address); + + uint64_t data = MemURead(context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + + // if t == 15 then + if (t == 15) { + // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + if (BitIsClear(address, 1) && BitIsClear(address, 0)) { + // LoadWritePC (data); + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - base_address); + LoadWritePC(context, data); + } else + return false; + } + // elsif UnalignedSupport() || address<1:0> = '00' then + else if (UnalignedSupport() || + (BitIsClear(address, 1) && BitIsClear(address, 0))) { + // R[t] = data; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - base_address); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + data)) + return false; + } + // else // Can only apply before ARMv7 + else { + // R[t] = ROR(data, 8*UInt(address<1:0>)); + data = ROR(data, Bits32(address, 1, 0), &success); + if (!success) + return false; + context.type = eContextRegisterLoad; + context.SetImmediate(data); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + data)) + return false; + } + } + return true; +} + +// LDR (register) calculates an address from a base register value and an offset +// register value, loads a word +// from memory, and writes it to a register. The offset register value can +// optionally be shifted. +bool EmulateInstructionARM::EmulateLDRRegister(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + data = MemU[address,4]; + if wback then R[n] = offset_addr; + if t == 15 then + if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + elsif UnalignedSupport() || address<1:0> = '00' then + R[t] = data; + else // Can only apply before ARMv7 + if CurrentInstrSet() == InstrSet_ARM then + R[t] = ROR(data, 8*UInt(address<1:0>)); + else + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + const uint32_t addr_byte_size = GetAddressByteSize(); + + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + switch (encoding) { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation + // in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + m = Bits32(opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rn == '1111' then SEE LDR (literal); + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32(opcode, 5, 4); + + // if BadReg(m) then UNPREDICTABLE; + if (BadReg(m)) + return false; + + // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if ((t == 15) && InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingA1: { + // if P == '0' && W == '1' then SEE LDRT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || + // (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + uint32_t type = Bits32(opcode, 6, 5); + uint32_t imm5 = Bits32(opcode, 11, 7); + shift_n = DecodeImmShift(type, imm5, shift_t); + + // if m == 15 then UNPREDICTABLE; + if (m == 15) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + } break; + + default: + return false; + } + + uint32_t Rm = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + uint32_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); -- Note "The APSR is + // an application level alias for the CPSR". + addr_t offset = + Shift(Rm, shift_t, shift_n, Bit32(m_opcode_cpsr, APSR_C), &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,4]; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + + uint64_t data = MemURead(context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + + // if t == 15 then + if (t == 15) { + // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; + if (BitIsClear(address, 1) && BitIsClear(address, 0)) { + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + LoadWritePC(context, data); + } else + return false; + } + // elsif UnalignedSupport() || address<1:0> = '00' then + else if (UnalignedSupport() || + (BitIsClear(address, 1) && BitIsClear(address, 0))) { + // R[t] = data; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + data)) + return false; + } else // Can only apply before ARMv7 + { + // if CurrentInstrSet() == InstrSet_ARM then + if (CurrentInstrSet() == eModeARM) { + // R[t] = ROR(data, 8*UInt(address<1:0>)); + data = ROR(data, Bits32(address, 1, 0), &success); + if (!success) + return false; + context.type = eContextRegisterLoad; + context.SetImmediate(data); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + data)) + return false; + } else { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown(t); + } + } + } + return true; +} + +// LDRB (immediate, Thumb) +bool EmulateInstructionARM::EmulateLDRBImmediate(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + R[t] = ZeroExtend(MemU[address,1], 32); + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32); + t = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + imm32 = Bits32(opcode, 10, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + break; + + case eEncodingT2: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if Rt == '1111' then SEE PLD; + if (t == 15) + return false; // PLD is not implemented yet + + // if Rn == '1111' then SEE LDRB (literal); + if (n == 15) + return EmulateLDRBLiteral(opcode, eEncodingT1); + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingT3: + // if P == '1' && U == '1' && W == '0' then SEE LDRBT; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet(opcode, 10); + add = BitIsSet(opcode, 9); + wback = BitIsSet(opcode, 8); + + // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLD; + if (t == 15) + return false; // PLD is not implemented yet + + // if Rn == '1111' then SEE LDRB (literal); + if (n == 15) + return EmulateLDRBLiteral(opcode, eEncodingT1); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; + if (BadReg(t) || (wback && (n == t))) + return false; + + break; + + default: + return false; + } + + uint32_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t address; + addr_t offset_addr; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = ZeroExtend(MemU[address,1], 32); + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); + + uint64_t data = MemURead(context, address, 1, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + return true; +} + +// LDRB (literal) calculates an address from the PC value and an immediate +// offset, loads a byte from memory, +// zero-extends it to form a 32-bit word and writes it to a register. +bool EmulateInstructionARM::EmulateLDRBLiteral(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + R[t] = ZeroExtend(MemU[address,1], 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t imm32; + bool add; + switch (encoding) { + case eEncodingT1: + // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 11, 0); + add = BitIsSet(opcode, 23); + + // if Rt == '1111' then SEE PLD; + if (t == 15) + return false; // PLD is not implemented yet + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingA1: + // t == UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 11, 0); + add = BitIsSet(opcode, 23); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + break; + + default: + return false; + } + + // base = Align(PC,4); + uint32_t pc_val = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + uint32_t base = AlignPC(pc_val); + + addr_t address; + // address = if add then (base + imm32) else (base - imm32); + if (add) + address = base + imm32; + else + address = base - imm32; + + // R[t] = ZeroExtend(MemU[address,1], 32); + EmulateInstruction::Context context; + context.type = eContextRelativeBranchImmediate; + context.SetImmediate(address - base); + + uint64_t data = MemURead(context, address, 1, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + } + return true; +} + +// LDRB (register) calculates an address from a base register value and an +// offset rigister value, loads a byte from memory, zero-extends it to form a +// 32-bit word, and writes it to a register. The offset register value can +// optionally be shifted. +bool EmulateInstructionARM::EmulateLDRBRegister(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + R[t] = ZeroExtend(MemU[address,1],32); + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + m = Bits32(opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + break; + + case eEncodingT2: + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32(opcode, 5, 4); + + // if Rt == '1111' then SEE PLD; + if (t == 15) + return false; // PLD is not implemented yet + + // if Rn == '1111' then SEE LDRB (literal); + if (n == 15) + return EmulateLDRBLiteral(opcode, eEncodingT1); + + // if t == 13 || BadReg(m) then UNPREDICTABLE; + if ((t == 13) || BadReg(m)) + return false; + break; + + case eEncodingA1: { + // if P == '0' && W == '1' then SEE LDRBT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || + // (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + uint32_t type = Bits32(opcode, 6, 5); + uint32_t imm5 = Bits32(opcode, 11, 7); + shift_n = DecodeImmShift(type, imm5, shift_t); + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + } break; + + default: + return false; + } + + addr_t offset_addr; + addr_t address; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t Rm = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + uint32_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = ZeroExtend(MemU[address,1],32); + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + + uint64_t data = MemURead(context, address, 1, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + return true; +} + +// LDRH (immediate, Thumb) calculates an address from a base register value and +// an immediate offset, loads a +// halfword from memory, zero-extends it to form a 32-bit word, and writes it +// to a register. It can use offset, post-indexed, or pre-indexed addressing. +bool EmulateInstructionARM::EmulateLDRHImmediate(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + data = MemU[address,2]; + if wback then R[n] = offset_addr; + if UnalignedSupport() || address<0> = '0' then + R[t] = ZeroExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'0', 32); + t = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + imm32 = Bits32(opcode, 10, 6) << 1; + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + break; + + case eEncodingT2: + // if Rt == '1111' then SEE "Unallocated memory hints"; + // if Rn == '1111' then SEE LDRH (literal); + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + break; + + case eEncodingT3: + // if Rn == '1111' then SEE LDRH (literal); + // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE + // "Unallocated memory hints"; + // if P == '1' && U == '1' && W == '0' then SEE LDRHT; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet(opcode, 10); + add = BitIsSet(opcode, 9); + wback = BitIsSet(opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; + if (BadReg(t) || (wback && (n == t))) + return false; + break; + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,2]; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + + uint64_t data = MemURead(context, address, 2, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear(address, 0)) { + // R[t] = ZeroExtend(data, 32); + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + data)) + return false; + } else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown(t); + } + } + return true; +} + +// LDRH (literal) calculates an address from the PC value and an immediate +// offset, loads a halfword from memory, +// zero-extends it to form a 32-bit word, and writes it to a register. +bool EmulateInstructionARM::EmulateLDRHLiteral(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + data = MemU[address,2]; + if UnalignedSupport() || address<0> = '0' then + R[t] = ZeroExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t imm32; + bool add; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(15); + switch (encoding) { + case eEncodingT1: + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 11, 0); + add = BitIsSet(opcode, 23); + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingA1: { + uint32_t imm4H = Bits32(opcode, 11, 8); + uint32_t imm4L = Bits32(opcode, 3, 0); + + // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); + t = Bits32(opcode, 15, 12); + imm32 = (imm4H << 4) | imm4L; + add = BitIsSet(opcode, 23); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + break; + } + + default: + return false; + } + + // base = Align(PC,4); + uint64_t pc_value = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + addr_t base = AlignPC(pc_value); + addr_t address; + + // address = if add then (base + imm32) else (base - imm32); + if (add) + address = base + imm32; + else + address = base - imm32; + + // data = MemU[address,2]; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - base); + + uint64_t data = MemURead(context, address, 2, 0, &success); + if (!success) + return false; + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear(address, 0)) { + // R[t] = ZeroExtend(data, 32); + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - base); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + data)) + return false; + + } else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown(t); + } + } + return true; +} + +// LDRH (literal) calculates an address from a base register value and an offset +// register value, loads a halfword +// from memory, zero-extends it to form a 32-bit word, and writes it to a +// register. The offset register value can be shifted left by 0, 1, 2, or 3 +// bits. +bool EmulateInstructionARM::EmulateLDRHRegister(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + data = MemU[address,2]; + if wback then R[n] = offset_addr; + if UnalignedSupport() || address<0> = '0' then + R[t] = ZeroExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation + // in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + m = Bits32(opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rn == '1111' then SEE LDRH (literal); + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32(opcode, 5, 4); + + // if t == 13 || BadReg(m) then UNPREDICTABLE; + if ((t == 13) || BadReg(m)) + return false; + break; + + case eEncodingA1: + // if P == '0' && W == '1' then SEE LDRHT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || + // (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + + uint64_t Rm = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + uint64_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,2]; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> offset_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset(*base_reg, *offset_reg); + uint64_t data = MemURead(context, address, 2, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear(address, 0)) { + // R[t] = ZeroExtend(data, 32); + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset(*base_reg, *offset_reg); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + data)) + return false; + } else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown(t); + } + } + return true; +} + +// LDRSB (immediate) calculates an address from a base register value and an +// immediate offset, loads a byte from +// memory, sign-extends it to form a 32-bit word, and writes it to a register. +// It can use offset, post-indexed, or pre-indexed addressing. +bool EmulateInstructionARM::EmulateLDRSBImmediate(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + R[t] = SignExtend(MemU[address,1], 32); + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // if Rt == '1111' then SEE PLI; + // if Rn == '1111' then SEE LDRSB (literal); + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingT2: + // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLI; + // if Rn == '1111' then SEE LDRSB (literal); + // if P == '1' && U == '1' && W == '0' then SEE LDRSBT; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet(opcode, 10); + add = BitIsSet(opcode, 9); + wback = BitIsSet(opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; + if (((t == 13) || + ((t == 15) && (BitIsClear(opcode, 10) || BitIsSet(opcode, 9) || + BitIsSet(opcode, 8)))) || + (wback && (n == t))) + return false; + + break; + + case eEncodingA1: { + // if Rn == '1111' then SEE LDRSB (literal); + // if P == '0' && W == '1' then SEE LDRSBT; + // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + + uint32_t imm4H = Bits32(opcode, 11, 8); + uint32_t imm4L = Bits32(opcode, 3, 0); + imm32 = (imm4H << 4) | imm4L; + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || + // (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = (BitIsClear(opcode, 24) || BitIsSet(opcode, 21)); + + // if t == 15 || (wback && n == t) then UNPREDICTABLE; + if ((t == 15) || (wback && (n == t))) + return false; + + break; + } + + default: + return false; + } + + uint64_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = SignExtend(MemU[address,1], 32); + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + + uint64_t unsigned_data = MemURead(context, address, 1, 0, &success); + if (!success) + return false; + + int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + (uint64_t)signed_data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + + return true; +} + +// LDRSB (literal) calculates an address from the PC value and an immediate +// offset, loads a byte from memory, +// sign-extends it to form a 32-bit word, and writes tit to a register. +bool EmulateInstructionARM::EmulateLDRSBLiteral(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + R[t] = SignExtend(MemU[address,1], 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t imm32; + bool add; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(15); + switch (encoding) { + case eEncodingT1: + // if Rt == '1111' then SEE PLI; + // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 11, 0); + add = BitIsSet(opcode, 23); + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingA1: { + // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); + t = Bits32(opcode, 15, 12); + uint32_t imm4H = Bits32(opcode, 11, 8); + uint32_t imm4L = Bits32(opcode, 3, 0); + imm32 = (imm4H << 4) | imm4L; + add = BitIsSet(opcode, 23); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + + break; + } + + default: + return false; + } + + // base = Align(PC,4); + uint64_t pc_value = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + uint64_t base = AlignPC(pc_value); + + // address = if add then (base + imm32) else (base - imm32); + addr_t address; + if (add) + address = base + imm32; + else + address = base - imm32; + + // R[t] = SignExtend(MemU[address,1], 32); + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - base); + + uint64_t unsigned_data = MemURead(context, address, 1, 0, &success); + if (!success) + return false; + + int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + (uint64_t)signed_data)) + return false; + } + return true; +} + +// LDRSB (register) calculates an address from a base register value and an +// offset register value, loadsa byte from +// memory, sign-extends it to form a 32-bit word, and writes it to a register. +// The offset register value can be shifted left by 0, 1, 2, or 3 bits. +bool EmulateInstructionARM::EmulateLDRSBRegister(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + R[t] = SignExtend(MemU[address,1], 32); + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + m = Bits32(opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rt == '1111' then SEE PLI; + // if Rn == '1111' then SEE LDRSB (literal); + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32(opcode, 5, 4); + + // if t == 13 || BadReg(m) then UNPREDICTABLE; + if ((t == 13) || BadReg(m)) + return false; + break; + + case eEncodingA1: + // if P == '0' && W == '1' then SEE LDRSBT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || + // (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + break; + + default: + return false; + } + + uint64_t Rm = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + uint64_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = SignExtend(MemU[address,1], 32); + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> offset_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset(*base_reg, *offset_reg); + + uint64_t unsigned_data = MemURead(context, address, 1, 0, &success); + if (!success) + return false; + + int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + (uint64_t)signed_data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + return true; +} + +// LDRSH (immediate) calculates an address from a base register value and an +// immediate offset, loads a halfword from +// memory, sign-extends it to form a 32-bit word, and writes it to a register. +// It can use offset, post-indexed, or pre-indexed addressing. +bool EmulateInstructionARM::EmulateLDRSHImmediate(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + data = MemU[address,2]; + if wback then R[n] = offset_addr; + if UnalignedSupport() || address<0> = '0' then + R[t] = SignExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // if Rn == '1111' then SEE LDRSH (literal); + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 11, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingT2: + // if Rn == '1111' then SEE LDRSH (literal); + // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE + // "Unallocated memory hints"; + // if P == '1' && U == '1' && W == '0' then SEE LDRSHT; + // if P == '0' && W == '0' then UNDEFINED; + if (BitIsClear(opcode, 10) && BitIsClear(opcode, 8)) + return false; + + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 7, 0); + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet(opcode, 10); + add = BitIsSet(opcode, 9); + wback = BitIsSet(opcode, 8); + + // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; + if (BadReg(t) || (wback && (n == t))) + return false; + + break; + + case eEncodingA1: { + // if Rn == '1111' then SEE LDRSH (literal); + // if P == '0' && W == '1' then SEE LDRSHT; + // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + uint32_t imm4H = Bits32(opcode, 11, 8); + uint32_t imm4L = Bits32(opcode, 3, 0); + imm32 = (imm4H << 4) | imm4L; + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || + // (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); + + // if t == 15 || (wback && n == t) then UNPREDICTABLE; + if ((t == 15) || (wback && (n == t))) + return false; + + break; + } + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint64_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,2]; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + + uint64_t data = MemURead(context, address, 2, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear(address, 0)) { + // R[t] = SignExtend(data, 32); + int64_t signed_data = llvm::SignExtend64<16>(data); + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + (uint64_t)signed_data)) + return false; + } else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown(t); + } + } + return true; +} + +// LDRSH (literal) calculates an address from the PC value and an immediate +// offset, loads a halfword from memory, +// sign-extends it to from a 32-bit word, and writes it to a register. +bool EmulateInstructionARM::EmulateLDRSHLiteral(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(15); + base = Align(PC,4); + address = if add then (base + imm32) else (base - imm32); + data = MemU[address,2]; + if UnalignedSupport() || address<0> = '0' then + R[t] = SignExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t imm32; + bool add; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(15); + switch (encoding) { + case eEncodingT1: + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); + t = Bits32(opcode, 15, 12); + imm32 = Bits32(opcode, 11, 0); + add = BitIsSet(opcode, 23); + + // if t == 13 then UNPREDICTABLE; + if (t == 13) + return false; + + break; + + case eEncodingA1: { + // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); + t = Bits32(opcode, 15, 12); + uint32_t imm4H = Bits32(opcode, 11, 8); + uint32_t imm4L = Bits32(opcode, 3, 0); + imm32 = (imm4H << 4) | imm4L; + add = BitIsSet(opcode, 23); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + + break; + } + default: + return false; + } + + // base = Align(PC,4); + uint64_t pc_value = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + uint64_t base = AlignPC(pc_value); + + addr_t address; + // address = if add then (base + imm32) else (base - imm32); + if (add) + address = base + imm32; + else + address = base - imm32; + + // data = MemU[address,2]; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, imm32); + + uint64_t data = MemURead(context, address, 2, 0, &success); + if (!success) + return false; + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear(address, 0)) { + // R[t] = SignExtend(data, 32); + int64_t signed_data = llvm::SignExtend64<16>(data); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + (uint64_t)signed_data)) + return false; + } else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown(t); + } + } + return true; +} + +// LDRSH (register) calculates an address from a base register value and an +// offset register value, loads a halfword +// from memory, sign-extends it to form a 32-bit word, and writes it to a +// register. The offset register value can be shifted left by 0, 1, 2, or 3 +// bits. +bool EmulateInstructionARM::EmulateLDRSHRegister(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset = Shift(R[m], shift_t, shift_n, APSR.C); + offset_addr = if add then (R[n] + offset) else (R[n] - offset); + address = if index then offset_addr else R[n]; + data = MemU[address,2]; + if wback then R[n] = offset_addr; + if UnalignedSupport() || address<0> = '0' then + R[t] = SignExtend(data, 32); + else // Can only apply before ARMv7 + R[t] = bits(32) UNKNOWN; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + ARM_ShifterType shift_t; + uint32_t shift_n; + + // EncodingSpecificOperations(); NullCheckIfThumbEE(n); + switch (encoding) { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation + // in ThumbEE"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + m = Bits32(opcode, 8, 6); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // if Rn == '1111' then SEE LDRSH (literal); + // if Rt == '1111' then SEE "Unallocated memory hints"; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = TRUE; add = TRUE; wback = FALSE; + index = true; + add = true; + wback = false; + + // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); + shift_t = SRType_LSL; + shift_n = Bits32(opcode, 5, 4); + + // if t == 13 || BadReg(m) then UNPREDICTABLE; + if ((t == 13) || BadReg(m)) + return false; + + break; + + case eEncodingA1: + // if P == '0' && W == '1' then SEE LDRSHT; + // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || + // (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + // if t == 15 || m == 15 then UNPREDICTABLE; + if ((t == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + uint64_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + // offset = Shift(R[m], shift_t, shift_n, APSR.C); + addr_t offset = Shift(Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + addr_t offset_addr; + addr_t address; + + // offset_addr = if add then (R[n] + offset) else (R[n] - offset); + if (add) + offset_addr = Rn + offset; + else + offset_addr = Rn - offset; + + // address = if index then offset_addr else R[n]; + if (index) + address = offset_addr; + else + address = Rn; + + // data = MemU[address,2]; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> offset_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset(*base_reg, *offset_reg); + + uint64_t data = MemURead(context, address, 2, 0, &success); + if (!success) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + + // if UnalignedSupport() || address<0> = '0' then + if (UnalignedSupport() || BitIsClear(address, 0)) { + // R[t] = SignExtend(data, 32); + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset(*base_reg, *offset_reg); + + int64_t signed_data = llvm::SignExtend64<16>(data); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, + (uint64_t)signed_data)) + return false; + } else // Can only apply before ARMv7 + { + // R[t] = bits(32) UNKNOWN; + WriteBits32Unknown(t); + } + } + return true; +} + +// SXTB extracts an 8-bit value from a register, sign-extends it to 32 bits, and +// writes the result to the destination +// register. You can specifiy a rotation by 0, 8, 16, or 24 bits before +// extracting the 8-bit value. +bool EmulateInstructionARM::EmulateSXTB(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + rotated = ROR(R[m], rotation); + R[d] = SignExtend(rotated<7:0>, 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t d; + uint32_t m; + uint32_t rotation; + + // EncodingSpecificOperations(); + switch (encoding) { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); rotation = 0; + d = Bits32(opcode, 2, 0); + m = Bits32(opcode, 5, 3); + rotation = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32(opcode, 11, 8); + m = Bits32(opcode, 3, 0); + rotation = Bits32(opcode, 5, 4) << 3; + + // if BadReg(d) || BadReg(m) then UNPREDICTABLE; + if (BadReg(d) || BadReg(m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32(opcode, 15, 12); + m = Bits32(opcode, 3, 0); + rotation = Bits32(opcode, 11, 10) << 3; + + // if d == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15)) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // rotated = ROR(R[m], rotation); + uint64_t rotated = ROR(Rm, rotation, &success); + if (!success) + return false; + + // R[d] = SignExtend(rotated<7:0>, 32); + int64_t data = llvm::SignExtend64<8>(rotated); + + std::optional<RegisterInfo> source_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegister(*source_reg); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, + (uint64_t)data)) + return false; + } + return true; +} + +// SXTH extracts a 16-bit value from a register, sign-extends it to 32 bits, and +// writes the result to the destination +// register. You can specify a rotation by 0, 8, 16, or 24 bits before +// extracting the 16-bit value. +bool EmulateInstructionARM::EmulateSXTH(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + rotated = ROR(R[m], rotation); + R[d] = SignExtend(rotated<15:0>, 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t d; + uint32_t m; + uint32_t rotation; + + // EncodingSpecificOperations(); + switch (encoding) { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); rotation = 0; + d = Bits32(opcode, 2, 0); + m = Bits32(opcode, 5, 3); + rotation = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32(opcode, 11, 8); + m = Bits32(opcode, 3, 0); + rotation = Bits32(opcode, 5, 4) << 3; + + // if BadReg(d) || BadReg(m) then UNPREDICTABLE; + if (BadReg(d) || BadReg(m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32(opcode, 15, 12); + m = Bits32(opcode, 3, 0); + rotation = Bits32(opcode, 11, 10) << 3; + + // if d == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15)) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // rotated = ROR(R[m], rotation); + uint64_t rotated = ROR(Rm, rotation, &success); + if (!success) + return false; + + // R[d] = SignExtend(rotated<15:0>, 32); + std::optional<RegisterInfo> source_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegister(*source_reg); + + int64_t data = llvm::SignExtend64<16>(rotated); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, + (uint64_t)data)) + return false; + } + + return true; +} + +// UXTB extracts an 8-bit value from a register, zero-extends it to 32 bits, and +// writes the result to the destination +// register. You can specify a rotation by 0, 8, 16, or 24 bits before +// extracting the 8-bit value. +bool EmulateInstructionARM::EmulateUXTB(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + rotated = ROR(R[m], rotation); + R[d] = ZeroExtend(rotated<7:0>, 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t d; + uint32_t m; + uint32_t rotation; + + // EncodingSpecificOperations(); + switch (encoding) { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); rotation = 0; + d = Bits32(opcode, 2, 0); + m = Bits32(opcode, 5, 3); + rotation = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32(opcode, 11, 8); + m = Bits32(opcode, 3, 0); + rotation = Bits32(opcode, 5, 4) << 3; + + // if BadReg(d) || BadReg(m) then UNPREDICTABLE; + if (BadReg(d) || BadReg(m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32(opcode, 15, 12); + m = Bits32(opcode, 3, 0); + rotation = Bits32(opcode, 11, 10) << 3; + + // if d == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15)) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // rotated = ROR(R[m], rotation); + uint64_t rotated = ROR(Rm, rotation, &success); + if (!success) + return false; + + // R[d] = ZeroExtend(rotated<7:0>, 32); + std::optional<RegisterInfo> source_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegister(*source_reg); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, + Bits32(rotated, 7, 0))) + return false; + } + return true; +} + +// UXTH extracts a 16-bit value from a register, zero-extends it to 32 bits, and +// writes the result to the destination +// register. You can specify a rotation by 0, 8, 16, or 24 bits before +// extracting the 16-bit value. +bool EmulateInstructionARM::EmulateUXTH(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + rotated = ROR(R[m], rotation); + R[d] = ZeroExtend(rotated<15:0>, 32); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t d; + uint32_t m; + uint32_t rotation; + + switch (encoding) { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); rotation = 0; + d = Bits32(opcode, 2, 0); + m = Bits32(opcode, 5, 3); + rotation = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32(opcode, 11, 8); + m = Bits32(opcode, 3, 0); + rotation = Bits32(opcode, 5, 4) << 3; + + // if BadReg(d) || BadReg(m) then UNPREDICTABLE; + if (BadReg(d) || BadReg(m)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); + d = Bits32(opcode, 15, 12); + m = Bits32(opcode, 3, 0); + rotation = Bits32(opcode, 11, 10) << 3; + + // if d == 15 || m == 15 then UNPREDICTABLE; + if ((d == 15) || (m == 15)) + return false; + + break; + + default: + return false; + } + + uint64_t Rm = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + m, 0, &success); + if (!success) + return false; + + // rotated = ROR(R[m], rotation); + uint64_t rotated = ROR(Rm, rotation, &success); + if (!success) + return false; + + // R[d] = ZeroExtend(rotated<15:0>, 32); + std::optional<RegisterInfo> source_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegister(*source_reg); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, + Bits32(rotated, 15, 0))) + return false; + } + return true; +} + +// RFE (Return From Exception) loads the PC and the CPSR from the word at the +// specified address and the following +// word respectively. +bool EmulateInstructionARM::EmulateRFE(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE then + UNPREDICTABLE; + else + address = if increment then R[n] else R[n]-8; + if wordhigher then address = address+4; + CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE); + BranchWritePC(MemA[address,4]); + if wback then R[n] = if increment then R[n]+8 else R[n]-8; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t n; + bool wback; + bool increment; + bool wordhigher; + + // EncodingSpecificOperations(); + switch (encoding) { + case eEncodingT1: + // n = UInt(Rn); wback = (W == '1'); increment = FALSE; wordhigher = + // FALSE; + n = Bits32(opcode, 19, 16); + wback = BitIsSet(opcode, 21); + increment = false; + wordhigher = false; + + // if n == 15 then UNPREDICTABLE; + if (n == 15) + return false; + + // if InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingT2: + // n = UInt(Rn); wback = (W == '1'); increment = TRUE; wordhigher = FALSE; + n = Bits32(opcode, 19, 16); + wback = BitIsSet(opcode, 21); + increment = true; + wordhigher = false; + + // if n == 15 then UNPREDICTABLE; + if (n == 15) + return false; + + // if InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingA1: + // n = UInt(Rn); + n = Bits32(opcode, 19, 16); + + // wback = (W == '1'); inc = (U == '1'); wordhigher = (P == U); + wback = BitIsSet(opcode, 21); + increment = BitIsSet(opcode, 23); + wordhigher = (Bit32(opcode, 24) == Bit32(opcode, 23)); + + // if n == 15 then UNPREDICTABLE; + if (n == 15) + return false; + + break; + + default: + return false; + } + + // if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE + // then + if (!CurrentModeIsPrivileged()) + // UNPREDICTABLE; + return false; + else { + uint64_t Rn = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + n, 0, &success); + if (!success) + return false; + + addr_t address; + // address = if increment then R[n] else R[n]-8; + if (increment) + address = Rn; + else + address = Rn - 8; + + // if wordhigher then address = address+4; + if (wordhigher) + address = address + 4; + + // CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE); + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + EmulateInstruction::Context context; + context.type = eContextReturnFromException; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + + uint64_t data = MemARead(context, address + 4, 4, 0, &success); + if (!success) + return false; + + CPSRWriteByInstr(data, 15, true); + + // BranchWritePC(MemA[address,4]); + uint64_t data2 = MemARead(context, address, 4, 0, &success); + if (!success) + return false; + + BranchWritePC(context, data2); + + // if wback then R[n] = if increment then R[n]+8 else R[n]-8; + if (wback) { + context.type = eContextAdjustBaseRegister; + if (increment) { + context.SetOffset(8); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + Rn + 8)) + return false; + } else { + context.SetOffset(-8); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + Rn - 8)) + return false; + } + } // if wback + } + } // if ConditionPassed() + return true; +} + +// Bitwise Exclusive OR (immediate) performs a bitwise exclusive OR of a +// register value and an immediate value, and writes the result to the +// destination register. It can optionally update the condition flags based on +// the result. +bool EmulateInstructionARM::EmulateEORImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] EOR imm32; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd, Rn; + uint32_t + imm32; // the immediate value to be ORed to the value obtained from Rn + bool setflags; + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C( + opcode, APSR_C, + carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + // if Rd == '1111' && S == '1' then SEE TEQ (immediate); + if (Rd == 15 && setflags) + return EmulateTEQImm(opcode, eEncodingT1); + if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = + ARMExpandImm_C(opcode, APSR_C, + carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 ^ imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise Exclusive OR (register) performs a bitwise exclusive OR of a +// register value and an optionally-shifted register value, and writes the +// result to the destination register. It can optionally update the condition +// flags based on the result. +bool EmulateInstructionARM::EmulateEORReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] EOR shifted; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; + switch (encoding) { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if Rd == '1111' && S == '1' then SEE TEQ (register); + if (Rd == 15 && setflags) + return EmulateTEQReg(opcode, eEncodingT1); + if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 ^ shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise OR (immediate) performs a bitwise (inclusive) OR of a register value +// and an immediate value, and writes the result to the destination register. +// It can optionally update the condition flags based on the result. +bool EmulateInstructionARM::EmulateORRImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] OR imm32; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd, Rn; + uint32_t + imm32; // the immediate value to be ORed to the value obtained from Rn + bool setflags; + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm_C( + opcode, APSR_C, + carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + // if Rn == '1111' then SEE MOV (immediate); + if (Rn == 15) + return EmulateMOVRdImm(opcode, eEncodingT2); + if (BadReg(Rd) || Rn == 13) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = + ARMExpandImm_C(opcode, APSR_C, + carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 | imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Bitwise OR (register) performs a bitwise (inclusive) OR of a register value +// and an optionally-shifted register value, and writes the result to the +// destination register. It can optionally update the condition flags based on +// the result. +bool EmulateInstructionARM::EmulateORRReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] OR shifted; + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd, Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + bool setflags; + uint32_t carry; + switch (encoding) { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if Rn == '1111' then SEE MOV (register); + if (Rn == 15) + return EmulateMOVRdRm(opcode, eEncodingT3); + if (BadReg(Rd) || Rn == 13 || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 | shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) + return false; + } + return true; +} + +// Reverse Subtract (immediate) subtracts a register value from an immediate +// value, and writes the result to the destination register. It can optionally +// update the condition flags based on the result. +bool EmulateInstructionARM::EmulateRSBImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t + imm32; // the immediate value to be added to the value obtained from Rn + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rn = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + imm32 = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (BadReg(Rd) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(~reg_val, imm32, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow); +} + +// Reverse Subtract (register) subtracts a register value from an optionally- +// shifted register value, and writes the result to the destination register. +// It can optionally update the condition flags based on the result. +bool EmulateInstructionARM::EmulateRSBReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE; + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(~val1, shifted, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow); +} + +// Reverse Subtract with Carry (immediate) subtracts a register value and the +// value of NOT (Carry flag) from an immediate value, and writes the result to +// the destination register. It can optionally update the condition flags based +// on the result. +bool EmulateInstructionARM::EmulateRSCImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, APSR.C); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t + imm32; // the immediate value to be added to the value obtained from Rn + switch (encoding) { + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(~reg_val, imm32, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow); +} + +// Reverse Subtract with Carry (register) subtracts a register value and the +// value of NOT (Carry flag) from an optionally-shifted register value, and +// writes the result to the destination register. It can optionally update the +// condition flags based on the result. +bool EmulateInstructionARM::EmulateRSCReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, APSR.C); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(~val1, shifted, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow); +} + +// Subtract with Carry (immediate) subtracts an immediate value and the value +// of +// NOT (Carry flag) from a register value, and writes the result to the +// destination register. +// It can optionally update the condition flags based on the result. +bool EmulateInstructionARM::EmulateSBCImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t + imm32; // the immediate value to be added to the value obtained from Rn + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + if (BadReg(Rd) || BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow); +} + +// Subtract with Carry (register) subtracts an optionally-shifted register +// value and the value of +// NOT (Carry flag) from a register value, and writes the result to the +// destination register. +// It can optionally update the condition flags based on the result. +bool EmulateInstructionARM::EmulateSBCReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), APSR.C); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + uint32_t Rm; // the second operand + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + switch (encoding) { + case eEncodingT1: + Rd = Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + // Read the register value from register Rn. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the register value from register Rm. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + AddWithCarryResult res = AddWithCarry(val1, ~shifted, APSR_C); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow); +} + +// This instruction subtracts an immediate value from a register value, and +// writes the result to the destination register. It can optionally update the +// condition flags based on the result. +bool EmulateInstructionARM::EmulateSUBImmThumb(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t imm32; // the immediate value to be subtracted from the value + // obtained from Rn + switch (encoding) { + case eEncodingT1: + Rd = Bits32(opcode, 2, 0); + Rn = Bits32(opcode, 5, 3); + setflags = !InITBlock(); + imm32 = Bits32(opcode, 8, 6); // imm32 = ZeroExtend(imm3, 32) + break; + case eEncodingT2: + Rd = Rn = Bits32(opcode, 10, 8); + setflags = !InITBlock(); + imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32) + break; + case eEncodingT3: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) + + // if Rd == '1111' && S == '1' then SEE CMP (immediate); + if (Rd == 15 && setflags) + return EmulateCMPImm(opcode, eEncodingT2); + + // if Rn == '1101' then SEE SUB (SP minus immediate); + if (Rn == 13) + return EmulateSUBSPImm(opcode, eEncodingT2); + + // if d == 13 || (d == 15 && S == '0') || n == 15 then UNPREDICTABLE; + if (Rd == 13 || (Rd == 15 && !setflags) || Rn == 15) + return false; + break; + case eEncodingT4: + Rd = Bits32(opcode, 11, 8); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) + + // if Rn == '1111' then SEE ADR; + if (Rn == 15) + return EmulateADR(opcode, eEncodingT2); + + // if Rn == '1101' then SEE SUB (SP minus immediate); + if (Rn == 13) + return EmulateSUBSPImm(opcode, eEncodingT3); + + if (BadReg(Rd)) + return false; + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + return WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow); +} + +// This instruction subtracts an immediate value from a register value, and +// writes the result to the destination register. It can optionally update the +// condition flags based on the result. +bool EmulateInstructionARM::EmulateSUBImmARM(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); + if d == 15 then + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rd; // the destination register + uint32_t Rn; // the first operand + bool setflags; + uint32_t imm32; // the immediate value to be subtracted from the value + // obtained from Rn + switch (encoding) { + case eEncodingA1: + Rd = Bits32(opcode, 15, 12); + Rn = Bits32(opcode, 19, 16); + setflags = BitIsSet(opcode, 20); + imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) + + // if Rn == '1111' && S == '0' then SEE ADR; + if (Rn == 15 && !setflags) + return EmulateADR(opcode, eEncodingA2); + + // if Rn == '1101' then SEE SUB (SP minus immediate); + if (Rn == 13) + return EmulateSUBSPImm(opcode, eEncodingA1); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (Rd == 15 && setflags) + return EmulateSUBSPcLrEtc(opcode, encoding); + break; + default: + return false; + } + // Read the register value from the operand register Rn. + uint32_t reg_val = ReadCoreReg(Rn, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); + + EmulateInstruction::Context context; + if (Rd == 13) + context.type = EmulateInstruction::eContextAdjustStackPointer; + else + context.type = EmulateInstruction::eContextRegisterPlusOffset; + + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, Rn); + int64_t imm32_signed = imm32; + context.SetRegisterPlusOffset(*dwarf_reg, -imm32_signed); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, + res.carry_out, res.overflow)) + return false; + } + return true; +} + +// Test Equivalence (immediate) performs a bitwise exclusive OR operation on a +// register value and an immediate value. It updates the condition flags based +// on the result, and discards the result. +bool EmulateInstructionARM::EmulateTEQImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] EOR imm32; + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rn; + uint32_t + imm32; // the immediate value to be ANDed to the value obtained from Rn + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + imm32 = ThumbExpandImm_C( + opcode, APSR_C, + carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + if (BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + imm32 = + ARMExpandImm_C(opcode, APSR_C, + carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 ^ imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteFlags(context, result, carry)) + return false; + } + return true; +} + +// Test Equivalence (register) performs a bitwise exclusive OR operation on a +// register value and an optionally-shifted register value. It updates the +// condition flags based on the result, and discards the result. +bool EmulateInstructionARM::EmulateTEQReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] EOR shifted; + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + uint32_t carry; + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 ^ shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteFlags(context, result, carry)) + return false; + } + return true; +} + +// Test (immediate) performs a bitwise AND operation on a register value and an +// immediate value. It updates the condition flags based on the result, and +// discards the result. +bool EmulateInstructionARM::EmulateTSTImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + result = R[n] AND imm32; + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rn; + uint32_t + imm32; // the immediate value to be ANDed to the value obtained from Rn + uint32_t carry; // the carry bit after ARM/Thumb Expand operation + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 19, 16); + imm32 = ThumbExpandImm_C( + opcode, APSR_C, + carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) + if (BadReg(Rn)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + imm32 = + ARMExpandImm_C(opcode, APSR_C, + carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + uint32_t result = val1 & imm32; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteFlags(context, result, carry)) + return false; + } + return true; +} + +// Test (register) performs a bitwise AND operation on a register value and an +// optionally-shifted register value. It updates the condition flags based on +// the result, and discards the result. +bool EmulateInstructionARM::EmulateTSTReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + // ARM pseudo code... + if ConditionPassed() then + EncodingSpecificOperations(); + (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); + result = R[n] AND shifted; + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + // APSR.V unchanged +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t Rn, Rm; + ARM_ShifterType shift_t; + uint32_t shift_n; // the shift applied to the value read from Rm + uint32_t carry; + switch (encoding) { + case eEncodingT1: + Rn = Bits32(opcode, 2, 0); + Rm = Bits32(opcode, 5, 3); + shift_t = SRType_LSL; + shift_n = 0; + break; + case eEncodingT2: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + if (BadReg(Rn) || BadReg(Rm)) + return false; + break; + case eEncodingA1: + Rn = Bits32(opcode, 19, 16); + Rm = Bits32(opcode, 3, 0); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + default: + return false; + } + + // Read the first operand. + uint32_t val1 = ReadCoreReg(Rn, &success); + if (!success) + return false; + + // Read the second operand. + uint32_t val2 = ReadCoreReg(Rm, &success); + if (!success) + return false; + + uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); + if (!success) + return false; + uint32_t result = val1 & shifted; + + EmulateInstruction::Context context; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs(); + + if (!WriteFlags(context, result, carry)) + return false; + } + return true; +} + +// A8.6.216 SUB (SP minus register) +bool EmulateInstructionARM::EmulateSUBSPReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t d; + uint32_t m; + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; + + switch (encoding) { + case eEncodingT1: + // d = UInt(Rd); m = UInt(Rm); setflags = (S == '1'); + d = Bits32(opcode, 11, 8); + m = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + + // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + + // if d == 13 && (shift_t != SRType_LSL || shift_n > 3) then + // UNPREDICTABLE; + if ((d == 13) && ((shift_t != SRType_LSL) || (shift_n > 3))) + return false; + + // if d == 15 || BadReg(m) then UNPREDICTABLE; + if ((d == 15) || BadReg(m)) + return false; + break; + + case eEncodingA1: + // d = UInt(Rd); m = UInt(Rm); setflags = (S == '1'); + d = Bits32(opcode, 15, 12); + m = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if (d == 15 && setflags) + EmulateSUBSPcLrEtc(opcode, encoding); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + shift_n = DecodeImmShiftARM(opcode, shift_t); + break; + + default: + return false; + } + + // shifted = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t Rm = ReadCoreReg(m, &success); + if (!success) + return false; + + uint32_t shifted = Shift(Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), '1'); + uint32_t sp_val = ReadCoreReg(SP_REG, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(sp_val, ~shifted, 1); + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + std::optional<RegisterInfo> sp_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_sp); + std::optional<RegisterInfo> dwarf_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + context.SetRegisterRegisterOperands(*sp_reg, *dwarf_reg); + + if (!WriteCoreRegOptionalFlags(context, res.result, dwarf_r0 + d, setflags, + res.carry_out, res.overflow)) + return false; + } + return true; +} + +// A8.6.7 ADD (register-shifted register) +bool EmulateInstructionARM::EmulateADDRegShift(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + shift_n = UInt(R[s]<7:0>); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t d; + uint32_t n; + uint32_t m; + uint32_t s; + bool setflags; + ARM_ShifterType shift_t; + + switch (encoding) { + case eEncodingA1: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); s = UInt(Rs); + d = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + s = Bits32(opcode, 11, 8); + + // setflags = (S == '1'); shift_t = DecodeRegShift(type); + setflags = BitIsSet(opcode, 20); + shift_t = DecodeRegShift(Bits32(opcode, 6, 5)); + + // if d == 15 || n == 15 || m == 15 || s == 15 then UNPREDICTABLE; + if ((d == 15) || (n == 15) || (m == 15) || (s == 15)) + return false; + break; + + default: + return false; + } + + // shift_n = UInt(R[s]<7:0>); + uint32_t Rs = ReadCoreReg(s, &success); + if (!success) + return false; + + uint32_t shift_n = Bits32(Rs, 7, 0); + + // shifted = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t Rm = ReadCoreReg(m, &success); + if (!success) + return false; + + uint32_t shifted = Shift(Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(Rn, shifted, 0); + + // R[d] = result; + EmulateInstruction::Context context; + context.type = eContextArithmetic; + std::optional<RegisterInfo> reg_n = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> reg_m = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + + context.SetRegisterRegisterOperands(*reg_n, *reg_m); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, + res.result)) + return false; + + // if setflags then + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + // APSR.C = carry; + // APSR.V = overflow; + if (setflags) + return WriteFlags(context, res.result, res.carry_out, res.overflow); + } + return true; +} + +// A8.6.213 SUB (register) +bool EmulateInstructionARM::EmulateSUBReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + shifted = Shift(R[m], shift_t, shift_n, APSR.C); + (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1'); + if d == 15 then // Can only occur for ARM encoding + ALUWritePC(result); // setflags is always FALSE here + else + R[d] = result; + if setflags then + APSR.N = result<31>; + APSR.Z = IsZeroBit(result); + APSR.C = carry; + APSR.V = overflow; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t d; + uint32_t n; + uint32_t m; + bool setflags; + ARM_ShifterType shift_t; + uint32_t shift_n; + + switch (encoding) { + case eEncodingT1: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = !InITBlock(); + d = Bits32(opcode, 2, 0); + n = Bits32(opcode, 5, 3); + m = Bits32(opcode, 8, 6); + setflags = !InITBlock(); + + // (shift_t, shift_n) = (SRType_LSL, 0); + shift_t = SRType_LSL; + shift_n = 0; + + break; + + case eEncodingT2: + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S =="1"); + d = Bits32(opcode, 11, 8); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + + // if Rd == "1111" && S == "1" then SEE CMP (register); + if (d == 15 && setflags == 1) + return EmulateCMPImm(opcode, eEncodingT3); + + // if Rn == "1101" then SEE SUB (SP minus register); + if (n == 13) + return EmulateSUBSPReg(opcode, eEncodingT1); + + // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2); + shift_n = DecodeImmShiftThumb(opcode, shift_t); + + // if d == 13 || (d == 15 && S == '0') || n == 15 || BadReg(m) then + // UNPREDICTABLE; + if ((d == 13) || ((d == 15) && BitIsClear(opcode, 20)) || (n == 15) || + BadReg(m)) + return false; + + break; + + case eEncodingA1: + // if Rn == '1101' then SEE SUB (SP minus register); + // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == '1'); + d = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + setflags = BitIsSet(opcode, 20); + + // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related + // instructions; + if ((d == 15) && setflags) + EmulateSUBSPcLrEtc(opcode, encoding); + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + break; + + default: + return false; + } + + // shifted = Shift(R[m], shift_t, shift_n, APSR.C); + uint32_t Rm = ReadCoreReg(m, &success); + if (!success) + return false; + + uint32_t shifted = Shift(Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + + // (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1'); + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + AddWithCarryResult res = AddWithCarry(Rn, ~shifted, 1); + + // if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); + // // setflags is always FALSE here else + // R[d] = result; + // if setflags then + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + // APSR.C = carry; + // APSR.V = overflow; + + EmulateInstruction::Context context; + context.type = eContextArithmetic; + std::optional<RegisterInfo> reg_n = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> reg_m = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + context.SetRegisterRegisterOperands(*reg_n, *reg_m); + + if (!WriteCoreRegOptionalFlags(context, res.result, dwarf_r0 + d, setflags, + res.carry_out, res.overflow)) + return false; + } + return true; +} + +// A8.6.202 STREX +// Store Register Exclusive calculates an address from a base register value +// and an immediate offset, and stores a word from a register to memory if the +// executing processor has exclusive access to the memory addressed. +bool EmulateInstructionARM::EmulateSTREX(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + address = R[n] + imm32; + if ExclusiveMonitorsPass(address,4) then + MemA[address,4] = R[t]; + R[d] = 0; + else + R[d] = 1; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t d; + uint32_t t; + uint32_t n; + uint32_t imm32; + const uint32_t addr_byte_size = GetAddressByteSize(); + + switch (encoding) { + case eEncodingT1: + // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 = + // ZeroExtend(imm8:'00', + // 32); + d = Bits32(opcode, 11, 8); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 7, 0) << 2; + + // if BadReg(d) || BadReg(t) || n == 15 then UNPREDICTABLE; + if (BadReg(d) || BadReg(t) || (n == 15)) + return false; + + // if d == n || d == t then UNPREDICTABLE; + if ((d == n) || (d == t)) + return false; + + break; + + case eEncodingA1: + // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 = Zeros(32); // Zero + // offset + d = Bits32(opcode, 15, 12); + t = Bits32(opcode, 3, 0); + n = Bits32(opcode, 19, 16); + imm32 = 0; + + // if d == 15 || t == 15 || n == 15 then UNPREDICTABLE; + if ((d == 15) || (t == 15) || (n == 15)) + return false; + + // if d == n || d == t then UNPREDICTABLE; + if ((d == n) || (d == t)) + return false; + + break; + + default: + return false; + } + + // address = R[n] + imm32; + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + addr_t address = Rn + imm32; + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, imm32); + + // if ExclusiveMonitorsPass(address,4) then if (ExclusiveMonitorsPass + // (address, addr_byte_size)) -- For now, for the sake of emulation, we + // will say this + // always return + // true. + if (true) { + // MemA[address,4] = R[t]; + uint32_t Rt = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_r0 + t, 0, &success); + if (!success) + return false; + + if (!MemAWrite(context, address, Rt, addr_byte_size)) + return false; + + // R[d] = 0; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, 0)) + return false; + } +#if 0 // unreachable because if true + else + { + // R[d] = 1; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 1)) + return false; + } +#endif // unreachable because if true + } + return true; +} + +// A8.6.197 STRB (immediate, ARM) +bool EmulateInstructionARM::EmulateSTRBImmARM(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemU[address,1] = R[t]<7:0>; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + switch (encoding) { + case eEncodingA1: + // if P == '0' && W == '1' then SEE STRBT; + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 11, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); + + // if t == 15 then UNPREDICTABLE; + if (t == 15) + return false; + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + // MemU[address,1] = R[t]<7:0>; + uint32_t Rt = ReadCoreReg(t, &success); + if (!success) + return false; + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); + + if (!MemUWrite(context, address, Bits32(Rt, 7, 0), 1)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + return true; +} + +// A8.6.194 STR (immediate, ARM) +bool EmulateInstructionARM::EmulateSTRImmARM(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + const uint32_t addr_byte_size = GetAddressByteSize(); + + switch (encoding) { + case eEncodingA1: + // if P == '0' && W == '1' then SEE STRT; + // if Rn == '1101' && P == '1' && U == '0' && W == '1' && imm12 == + // '000000000100' then SEE PUSH; + // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); + t = Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 11, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); + + // if wback && (n == 15 || n == t) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t))) + return false; + + break; + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); + + // MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; + uint32_t Rt = ReadCoreReg(t, &success); + if (!success) + return false; + + if (t == 15) { + uint32_t pc_value = ReadCoreReg(PC_REG, &success); + if (!success) + return false; + + if (!MemUWrite(context, address, pc_value, addr_byte_size)) + return false; + } else { + if (!MemUWrite(context, address, Rt, addr_byte_size)) + return false; + } + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetImmediate(offset_addr); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + return true; +} + +// A8.6.66 LDRD (immediate) +// Load Register Dual (immediate) calculates an address from a base register +// value and an immediate offset, loads two words from memory, and writes them +// to two registers. It can use offset, post-indexed, or pre-indexed +// addressing. +bool EmulateInstructionARM::EmulateLDRDImmediate(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + R[t] = MemA[address,4]; + R[t2] = MemA[address+4,4]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t t2; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + switch (encoding) { + case eEncodingT1: + // if P == '0' && W == '0' then SEE 'Related encodings'; + // if Rn == '1111' then SEE LDRD (literal); + // t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 = + // ZeroExtend(imm8:'00', 32); + t = Bits32(opcode, 15, 12); + t2 = Bits32(opcode, 11, 8); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 7, 0) << 2; + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = BitIsSet(opcode, 21); + + // if wback && (n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == t) || (n == t2))) + return false; + + // if BadReg(t) || BadReg(t2) || t == t2 then UNPREDICTABLE; + if (BadReg(t) || BadReg(t2) || (t == t2)) + return false; + + break; + + case eEncodingA1: + // if Rn == '1111' then SEE LDRD (literal); + // if Rt<0> == '1' then UNPREDICTABLE; + // t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, + // 32); + t = Bits32(opcode, 15, 12); + if (BitIsSet(t, 0)) + return false; + t2 = t + 1; + n = Bits32(opcode, 19, 16); + imm32 = (Bits32(opcode, 11, 8) << 4) | Bits32(opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); + + // if P == '0' && W == '1' then UNPREDICTABLE; + if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21)) + return false; + + // if wback && (n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == t) || (n == t2))) + return false; + + // if t2 == 15 then UNPREDICTABLE; + if (t2 == 15) + return false; + + break; + + default: + return false; + } + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + // R[t] = MemA[address,4]; + + EmulateInstruction::Context context; + if (n == 13) + context.type = eContextPopRegisterOffStack; + else + context.type = eContextRegisterLoad; + context.SetAddress(address); + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t data = MemARead(context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + // R[t2] = MemA[address+4,4]; + context.SetAddress(address + 4); + data = MemARead(context, address + 4, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t2, + data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + return true; +} + +// A8.6.68 LDRD (register) +// Load Register Dual (register) calculates an address from a base register +// value and a register offset, loads two words from memory, and writes them to +// two registers. It can use offset, post-indexed or pre-indexed addressing. +bool EmulateInstructionARM::EmulateLDRDRegister(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); + address = if index then offset_addr else R[n]; + R[t] = MemA[address,4]; + R[t2] = MemA[address+4,4]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t t2; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + + switch (encoding) { + case eEncodingA1: + // if Rt<0> == '1' then UNPREDICTABLE; + // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + if (BitIsSet(t, 0)) + return false; + t2 = t + 1; + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); + + // if P == '0' && W == '1' then UNPREDICTABLE; + if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21)) + return false; + + // if t2 == 15 || m == 15 || m == t || m == t2 then UNPREDICTABLE; + if ((t2 == 15) || (m == 15) || (m == t) || (m == t2)) + return false; + + // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t) || (n == t2))) + return false; + + // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE; + if ((ArchVersion() < 6) && wback && (m == n)) + return false; + break; + + default: + return false; + } + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + uint32_t Rm = ReadCoreReg(m, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); + addr_t offset_addr; + if (add) + offset_addr = Rn + Rm; + else + offset_addr = Rn - Rm; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + EmulateInstruction::Context context; + if (n == 13) + context.type = eContextPopRegisterOffStack; + else + context.type = eContextRegisterLoad; + context.SetAddress(address); + + // R[t] = MemA[address,4]; + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t data = MemARead(context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t, data)) + return false; + + // R[t2] = MemA[address+4,4]; + + data = MemARead(context, address + 4, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + t2, + data)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + return true; +} + +// A8.6.200 STRD (immediate) +// Store Register Dual (immediate) calculates an address from a base register +// value and an immediate offset, and stores two words from two registers to +// memory. It can use offset, post-indexed, or pre-indexed addressing. +bool EmulateInstructionARM::EmulateSTRDImm(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); NullCheckIfThumbEE(n); + offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + address = if index then offset_addr else R[n]; + MemA[address,4] = R[t]; + MemA[address+4,4] = R[t2]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t t2; + uint32_t n; + uint32_t imm32; + bool index; + bool add; + bool wback; + + switch (encoding) { + case eEncodingT1: + // if P == '0' && W == '0' then SEE 'Related encodings'; + // t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 = + // ZeroExtend(imm8:'00', 32); + t = Bits32(opcode, 15, 12); + t2 = Bits32(opcode, 11, 8); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 7, 0) << 2; + + // index = (P == '1'); add = (U == '1'); wback = (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = BitIsSet(opcode, 21); + + // if wback && (n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == t) || (n == t2))) + return false; + + // if n == 15 || BadReg(t) || BadReg(t2) then UNPREDICTABLE; + if ((n == 15) || BadReg(t) || BadReg(t2)) + return false; + + break; + + case eEncodingA1: + // if Rt<0> == '1' then UNPREDICTABLE; + // t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, + // 32); + t = Bits32(opcode, 15, 12); + if (BitIsSet(t, 0)) + return false; + + t2 = t + 1; + n = Bits32(opcode, 19, 16); + imm32 = (Bits32(opcode, 11, 8) << 4) | Bits32(opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); + + // if P == '0' && W == '1' then UNPREDICTABLE; + if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21)) + return false; + + // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t) || (n == t2))) + return false; + + // if t2 == 15 then UNPREDICTABLE; + if (t2 == 15) + return false; + + break; + + default: + return false; + } + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); + addr_t offset_addr; + if (add) + offset_addr = Rn + imm32; + else + offset_addr = Rn - imm32; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + + // MemA[address,4] = R[t]; + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); + + uint32_t data = ReadCoreReg(t, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + if (n == 13) + context.type = eContextPushRegisterOnStack; + else + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); + + const uint32_t addr_byte_size = GetAddressByteSize(); + + if (!MemAWrite(context, address, data, addr_byte_size)) + return false; + + // MemA[address+4,4] = R[t2]; + data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t2); + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + (address + 4) - Rn); + + data = ReadCoreReg(t2, &success); + if (!success) + return false; + + if (!MemAWrite(context, address + 4, data, addr_byte_size)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + if (n == 13) + context.type = eContextAdjustStackPointer; + else + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + return true; +} + +// A8.6.201 STRD (register) +bool EmulateInstructionARM::EmulateSTRDReg(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); + address = if index then offset_addr else R[n]; + MemA[address,4] = R[t]; + MemA[address+4,4] = R[t2]; + if wback then R[n] = offset_addr; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t t; + uint32_t t2; + uint32_t n; + uint32_t m; + bool index; + bool add; + bool wback; + + switch (encoding) { + case eEncodingA1: + // if Rt<0> == '1' then UNPREDICTABLE; + // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm); + t = Bits32(opcode, 15, 12); + if (BitIsSet(t, 0)) + return false; + + t2 = t + 1; + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); + index = BitIsSet(opcode, 24); + add = BitIsSet(opcode, 23); + wback = BitIsClear(opcode, 24) || BitIsSet(opcode, 21); + + // if P == '0' && W == '1' then UNPREDICTABLE; + if (BitIsClear(opcode, 24) && BitIsSet(opcode, 21)) + return false; + + // if t2 == 15 || m == 15 then UNPREDICTABLE; + if ((t2 == 15) || (m == 15)) + return false; + + // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; + if (wback && ((n == 15) || (n == t) || (n == t2))) + return false; + + // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE; + if ((ArchVersion() < 6) && wback && (m == n)) + return false; + + break; + + default: + return false; + } + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + uint32_t Rm = ReadCoreReg(m, &success); + if (!success) + return false; + + // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); + addr_t offset_addr; + if (add) + offset_addr = Rn + Rm; + else + offset_addr = Rn - Rm; + + // address = if index then offset_addr else R[n]; + addr_t address; + if (index) + address = offset_addr; + else + address = Rn; + // MemA[address,4] = R[t]; + uint32_t Rt = ReadCoreReg(t, &success); + if (!success) + return false; + + EmulateInstruction::Context context; + if (t == 13) + context.type = eContextPushRegisterOnStack; + else + context.type = eContextRegisterStore; + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> offset_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m); + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t); + context.SetRegisterToRegisterPlusIndirectOffset(*base_reg, *offset_reg, + *data_reg); + + const uint32_t addr_byte_size = GetAddressByteSize(); + + if (!MemAWrite(context, address, Rt, addr_byte_size)) + return false; + + // MemA[address+4,4] = R[t2]; + uint32_t Rt2 = ReadCoreReg(t2, &success); + if (!success) + return false; + + data_reg = GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + t2); + + context.SetRegisterToRegisterPlusIndirectOffset(*base_reg, *offset_reg, + *data_reg); + + if (!MemAWrite(context, address + 4, Rt2, addr_byte_size)) + return false; + + // if wback then R[n] = offset_addr; + if (wback) { + context.type = eContextAdjustBaseRegister; + context.SetAddress(offset_addr); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + offset_addr)) + return false; + } + } + return true; +} + +// A8.6.319 VLDM +// Vector Load Multiple loads multiple extension registers from consecutive +// memory locations using an address from an ARM core register. +bool EmulateInstructionARM::EmulateVLDM(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); + address = if add then R[n] else R[n]-imm32; + if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; + for r = 0 to regs-1 + if single_regs then + S[d+r] = MemA[address,4]; address = address+4; + else + word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8; + // Combine the word-aligned words in the correct order for + // current endianness. + D[d+r] = if BigEndian() then word1:word2 else word2:word1; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + bool single_regs; + bool add; + bool wback; + uint32_t d; + uint32_t n; + uint32_t imm32; + uint32_t regs; + + switch (encoding) { + case eEncodingT1: + case eEncodingA1: + // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings'; + // if P == '0' && U == '1' && W == '1' && Rn == '1101' then SEE VPOP; + // if P == '1' && W == '0' then SEE VLDR; + // if P == U && W == '1' then UNDEFINED; + if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21)) + return false; + + // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with + // !), 101 (DB with !) + // single_regs = FALSE; add = (U == '1'); wback = (W == '1'); + single_regs = false; + add = BitIsSet(opcode, 23); + wback = BitIsSet(opcode, 21); + + // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:'00', 32); + d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 7, 0) << 2; + + // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see 'FLDMX'. + regs = Bits32(opcode, 7, 0) / 2; + + // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then + // UNPREDICTABLE; + if (n == 15 && (wback || CurrentInstrSet() != eModeARM)) + return false; + + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if ((regs == 0) || (regs > 16) || ((d + regs) > 32)) + return false; + + break; + + case eEncodingT2: + case eEncodingA2: + // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings'; + // if P == '0' && U == '1' && W == '1' && Rn == '1101' then SEE VPOP; + // if P == '1' && W == '0' then SEE VLDR; + // if P == U && W == '1' then UNDEFINED; + if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21)) + return false; + + // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with + // !), 101 (DB with !) single_regs = TRUE; add = (U == '1'); wback = (W + // == '1'); d = + // UInt(Vd:D); n = UInt(Rn); + single_regs = true; + add = BitIsSet(opcode, 23); + wback = BitIsSet(opcode, 21); + d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22); + n = Bits32(opcode, 19, 16); + + // imm32 = ZeroExtend(imm8:'00', 32); regs = UInt(imm8); + imm32 = Bits32(opcode, 7, 0) << 2; + regs = Bits32(opcode, 7, 0); + + // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then + // UNPREDICTABLE; + if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM))) + return false; + + // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE; + if ((regs == 0) || ((d + regs) > 32)) + return false; + break; + + default: + return false; + } + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + // address = if add then R[n] else R[n]-imm32; + addr_t address; + if (add) + address = Rn; + else + address = Rn - imm32; + + // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; + EmulateInstruction::Context context; + + if (wback) { + uint32_t value; + if (add) + value = Rn + imm32; + else + value = Rn - imm32; + + context.type = eContextAdjustBaseRegister; + context.SetImmediateSigned(value - Rn); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + value)) + return false; + } + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; + + context.type = eContextRegisterLoad; + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) { + if (single_regs) { + // S[d+r] = MemA[address,4]; address = address+4; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + + uint32_t data = MemARead(context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, + start_reg + d + r, data)) + return false; + + address = address + 4; + } else { + // word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = + // address+8; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + uint32_t word1 = + MemARead(context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + context.SetRegisterPlusOffset(*base_reg, (address + 4) - Rn); + uint32_t word2 = + MemARead(context, address + 4, addr_byte_size, 0, &success); + if (!success) + return false; + + address = address + 8; + // // Combine the word-aligned words in the correct order for current + // endianness. + // D[d+r] = if BigEndian() then word1:word2 else word2:word1; + uint64_t data; + if (GetByteOrder() == eByteOrderBig) { + data = word1; + data = (data << 32) | word2; + } else { + data = word2; + data = (data << 32) | word1; + } + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, + start_reg + d + r, data)) + return false; + } + } + } + return true; +} + +// A8.6.399 VSTM +// Vector Store Multiple stores multiple extension registers to consecutive +// memory locations using an address from an +// ARM core register. +bool EmulateInstructionARM::EmulateVSTM(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); + address = if add then R[n] else R[n]-imm32; + if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; + for r = 0 to regs-1 + if single_regs then + MemA[address,4] = S[d+r]; address = address+4; + else + // Store as two word-aligned words in the correct order for + // current endianness. + MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; + MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; + address = address+8; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + bool single_regs; + bool add; + bool wback; + uint32_t d; + uint32_t n; + uint32_t imm32; + uint32_t regs; + + switch (encoding) { + case eEncodingT1: + case eEncodingA1: + // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings'; + // if P == '1' && U == '0' && W == '1' && Rn == '1101' then SEE VPUSH; + // if P == '1' && W == '0' then SEE VSTR; + // if P == U && W == '1' then UNDEFINED; + if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21)) + return false; + + // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with + // !), 101 (DB with !) + // single_regs = FALSE; add = (U == '1'); wback = (W == '1'); + single_regs = false; + add = BitIsSet(opcode, 23); + wback = BitIsSet(opcode, 21); + + // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:'00', 32); + d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + imm32 = Bits32(opcode, 7, 0) << 2; + + // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see 'FSTMX'. + regs = Bits32(opcode, 7, 0) / 2; + + // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then + // UNPREDICTABLE; + if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM))) + return false; + + // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; + if ((regs == 0) || (regs > 16) || ((d + regs) > 32)) + return false; + + break; + + case eEncodingT2: + case eEncodingA2: + // if P == '0' && U == '0' && W == '0' then SEE 'Related encodings'; + // if P == '1' && U == '0' && W == '1' && Rn == '1101' then SEE VPUSH; + // if P == '1' && W == '0' then SEE VSTR; + // if P == U && W == '1' then UNDEFINED; + if ((Bit32(opcode, 24) == Bit32(opcode, 23)) && BitIsSet(opcode, 21)) + return false; + + // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with + // !), 101 (DB with !) single_regs = TRUE; add = (U == '1'); wback = (W + // == '1'); d = + // UInt(Vd:D); n = UInt(Rn); + single_regs = true; + add = BitIsSet(opcode, 23); + wback = BitIsSet(opcode, 21); + d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22); + n = Bits32(opcode, 19, 16); + + // imm32 = ZeroExtend(imm8:'00', 32); regs = UInt(imm8); + imm32 = Bits32(opcode, 7, 0) << 2; + regs = Bits32(opcode, 7, 0); + + // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then + // UNPREDICTABLE; + if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM))) + return false; + + // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE; + if ((regs == 0) || ((d + regs) > 32)) + return false; + + break; + + default: + return false; + } + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + // address = if add then R[n] else R[n]-imm32; + addr_t address; + if (add) + address = Rn; + else + address = Rn - imm32; + + EmulateInstruction::Context context; + // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; + if (wback) { + uint32_t value; + if (add) + value = Rn + imm32; + else + value = Rn - imm32; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset(*base_reg, value - Rn); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + value)) + return false; + } + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; + + context.type = eContextRegisterStore; + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) { + + if (single_regs) { + // MemA[address,4] = S[d+r]; address = address+4; + uint32_t data = ReadRegisterUnsigned(eRegisterKindDWARF, + start_reg + d + r, 0, &success); + if (!success) + return false; + + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, start_reg + d + r); + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + address - Rn); + if (!MemAWrite(context, address, data, addr_byte_size)) + return false; + + address = address + 4; + } else { + // // Store as two word-aligned words in the correct order for current + // endianness. MemA[address,4] = if BigEndian() then D[d+r]<63:32> else + // D[d+r]<31:0>; + // MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else + // D[d+r]<63:32>; + uint64_t data = ReadRegisterUnsigned(eRegisterKindDWARF, + start_reg + d + r, 0, &success); + if (!success) + return false; + + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, start_reg + d + r); + + if (GetByteOrder() == eByteOrderBig) { + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + address - Rn); + if (!MemAWrite(context, address, Bits64(data, 63, 32), + addr_byte_size)) + return false; + + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + (address + 4) - Rn); + if (!MemAWrite(context, address + 4, Bits64(data, 31, 0), + addr_byte_size)) + return false; + } else { + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + address - Rn); + if (!MemAWrite(context, address, Bits64(data, 31, 0), addr_byte_size)) + return false; + + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + (address + 4) - Rn); + if (!MemAWrite(context, address + 4, Bits64(data, 63, 32), + addr_byte_size)) + return false; + } + // address = address+8; + address = address + 8; + } + } + } + return true; +} + +// A8.6.320 +// This instruction loads a single extension register from memory, using an +// address from an ARM core register, with an optional offset. +bool EmulateInstructionARM::EmulateVLDR(const uint32_t opcode, + ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); + base = if n == 15 then Align(PC,4) else R[n]; + address = if add then (base + imm32) else (base - imm32); + if single_reg then + S[d] = MemA[address,4]; + else + word1 = MemA[address,4]; word2 = MemA[address+4,4]; + // Combine the word-aligned words in the correct order for current + // endianness. + D[d] = if BigEndian() then word1:word2 else word2:word1; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + bool single_reg; + bool add; + uint32_t imm32; + uint32_t d; + uint32_t n; + + switch (encoding) { + case eEncodingT1: + case eEncodingA1: + // single_reg = FALSE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00', + // 32); + single_reg = false; + add = BitIsSet(opcode, 23); + imm32 = Bits32(opcode, 7, 0) << 2; + + // d = UInt(D:Vd); n = UInt(Rn); + d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + + break; + + case eEncodingT2: + case eEncodingA2: + // single_reg = TRUE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00', 32); + single_reg = true; + add = BitIsSet(opcode, 23); + imm32 = Bits32(opcode, 7, 0) << 2; + + // d = UInt(Vd:D); n = UInt(Rn); + d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22); + n = Bits32(opcode, 19, 16); + + break; + + default: + return false; + } + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + // base = if n == 15 then Align(PC,4) else R[n]; + uint32_t base; + if (n == 15) + base = AlignPC(Rn); + else + base = Rn; + + // address = if add then (base + imm32) else (base - imm32); + addr_t address; + if (add) + address = base + imm32; + else + address = base - imm32; + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0; + + EmulateInstruction::Context context; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - base); + + if (single_reg) { + // S[d] = MemA[address,4]; + uint32_t data = MemARead(context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, start_reg + d, + data)) + return false; + } else { + // word1 = MemA[address,4]; word2 = MemA[address+4,4]; + uint32_t word1 = MemARead(context, address, addr_byte_size, 0, &success); + if (!success) + return false; + + context.SetRegisterPlusOffset(*base_reg, (address + 4) - base); + uint32_t word2 = + MemARead(context, address + 4, addr_byte_size, 0, &success); + if (!success) + return false; + // // Combine the word-aligned words in the correct order for current + // endianness. + // D[d] = if BigEndian() then word1:word2 else word2:word1; + uint64_t data64; + if (GetByteOrder() == eByteOrderBig) { + data64 = word1; + data64 = (data64 << 32) | word2; + } else { + data64 = word2; + data64 = (data64 << 32) | word1; + } + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, start_reg + d, + data64)) + return false; + } + } + return true; +} + +// A8.6.400 VSTR +// This instruction stores a signle extension register to memory, using an +// address from an ARM core register, with an optional offset. +bool EmulateInstructionARM::EmulateVSTR(const uint32_t opcode, + ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); + address = if add then (R[n] + imm32) else (R[n] - imm32); + if single_reg then + MemA[address,4] = S[d]; + else + // Store as two word-aligned words in the correct order for current + // endianness. + MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>; + MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + bool single_reg; + bool add; + uint32_t imm32; + uint32_t d; + uint32_t n; + + switch (encoding) { + case eEncodingT1: + case eEncodingA1: + // single_reg = FALSE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00', + // 32); + single_reg = false; + add = BitIsSet(opcode, 23); + imm32 = Bits32(opcode, 7, 0) << 2; + + // d = UInt(D:Vd); n = UInt(Rn); + d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + + // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE; + if ((n == 15) && (CurrentInstrSet() != eModeARM)) + return false; + + break; + + case eEncodingT2: + case eEncodingA2: + // single_reg = TRUE; add = (U == '1'); imm32 = ZeroExtend(imm8:'00', 32); + single_reg = true; + add = BitIsSet(opcode, 23); + imm32 = Bits32(opcode, 7, 0) << 2; + + // d = UInt(Vd:D); n = UInt(Rn); + d = (Bits32(opcode, 15, 12) << 1) | Bit32(opcode, 22); + n = Bits32(opcode, 19, 16); + + // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE; + if ((n == 15) && (CurrentInstrSet() != eModeARM)) + return false; + + break; + + default: + return false; + } + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + // address = if add then (R[n] + imm32) else (R[n] - imm32); + addr_t address; + if (add) + address = Rn + imm32; + else + address = Rn - imm32; + + const uint32_t addr_byte_size = GetAddressByteSize(); + uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0; + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, start_reg + d); + EmulateInstruction::Context context; + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); + + if (single_reg) { + // MemA[address,4] = S[d]; + uint32_t data = + ReadRegisterUnsigned(eRegisterKindDWARF, start_reg + d, 0, &success); + if (!success) + return false; + + if (!MemAWrite(context, address, data, addr_byte_size)) + return false; + } else { + // // Store as two word-aligned words in the correct order for current + // endianness. + // MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>; + // MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>; + uint64_t data = + ReadRegisterUnsigned(eRegisterKindDWARF, start_reg + d, 0, &success); + if (!success) + return false; + + if (GetByteOrder() == eByteOrderBig) { + if (!MemAWrite(context, address, Bits64(data, 63, 32), addr_byte_size)) + return false; + + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + (address + 4) - Rn); + if (!MemAWrite(context, address + 4, Bits64(data, 31, 0), + addr_byte_size)) + return false; + } else { + if (!MemAWrite(context, address, Bits64(data, 31, 0), addr_byte_size)) + return false; + + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + (address + 4) - Rn); + if (!MemAWrite(context, address + 4, Bits64(data, 63, 32), + addr_byte_size)) + return false; + } + } + } + return true; +} + +// A8.6.307 VLDI1 (multiple single elements) This instruction loads elements +// from memory into one, two, three or four registers, without de-interleaving. +// Every element of each register is loaded. +bool EmulateInstructionARM::EmulateVLD1Multiple(const uint32_t opcode, + ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); + for r = 0 to regs-1 + for e = 0 to elements-1 + Elem[D[d+r],e,esize] = MemU[address,ebytes]; + address = address + ebytes; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t regs; + uint32_t alignment; + uint32_t ebytes; + uint32_t esize; + uint32_t elements; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) { + case eEncodingT1: + case eEncodingA1: { + // case type of + // when '0111' + // regs = 1; if align<1> == '1' then UNDEFINED; + // when '1010' + // regs = 2; if align == '11' then UNDEFINED; + // when '0110' + // regs = 3; if align<1> == '1' then UNDEFINED; + // when '0010' + // regs = 4; + // otherwise + // SEE 'Related encodings'; + uint32_t type = Bits32(opcode, 11, 8); + uint32_t align = Bits32(opcode, 5, 4); + if (type == 7) // '0111' + { + regs = 1; + if (BitIsSet(align, 1)) + return false; + } else if (type == 10) // '1010' + { + regs = 2; + if (align == 3) + return false; + + } else if (type == 6) // '0110' + { + regs = 3; + if (BitIsSet(align, 1)) + return false; + } else if (type == 2) // '0010' + { + regs = 4; + } else + return false; + + // alignment = if align == '00' then 1 else 4 << UInt(align); + if (align == 0) + alignment = 1; + else + alignment = 4 << align; + + // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes; + ebytes = 1 << Bits32(opcode, 7, 6); + esize = 8 * ebytes; + elements = 8 / ebytes; + + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 15); + m = Bits32(opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + // if d+regs > 32 then UNPREDICTABLE; + if ((d + regs) > 32) + return false; + } break; + + default: + return false; + } + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then + // GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); + if (wback) { + uint32_t Rm = ReadCoreReg(m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = 8 * regs; + + uint32_t value = Rn + offset; + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset(*base_reg, offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + value)) + return false; + } + + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) { + // for e = 0 to elements-1 + uint64_t assembled_data = 0; + for (uint32_t e = 0; e < elements; ++e) { + // Elem[D[d+r],e,esize] = MemU[address,ebytes]; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset(*base_reg, address - Rn); + uint64_t data = MemURead(context, address, ebytes, 0, &success); + if (!success) + return false; + + assembled_data = + (data << (e * esize)) | + assembled_data; // New data goes to the left of existing data + + // address = address + ebytes; + address = address + ebytes; + } + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_d0 + d + r, + assembled_data)) + return false; + } + } + return true; +} + +// A8.6.308 VLD1 (single element to one lane) +// +bool EmulateInstructionARM::EmulateVLD1Single(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + Elem[D[d],index,esize] = MemU[address,ebytes]; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t ebytes; + uint32_t esize; + uint32_t index; + uint32_t alignment; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) { + case eEncodingT1: + case eEncodingA1: { + uint32_t size = Bits32(opcode, 11, 10); + uint32_t index_align = Bits32(opcode, 7, 4); + // if size == '11' then SEE VLD1 (single element to all lanes); + if (size == 3) + return EmulateVLD1SingleAll(opcode, encoding); + // case size of + if (size == 0) // when '00' + { + // if index_align<0> != '0' then UNDEFINED; + if (BitIsClear(index_align, 0)) + return false; + + // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1; + ebytes = 1; + esize = 8; + index = Bits32(index_align, 3, 1); + alignment = 1; + } else if (size == 1) // when '01' + { + // if index_align<1> != '0' then UNDEFINED; + if (BitIsClear(index_align, 1)) + return false; + + // ebytes = 2; esize = 16; index = UInt(index_align<3:2>); + ebytes = 2; + esize = 16; + index = Bits32(index_align, 3, 2); + + // alignment = if index_align<0> == '0' then 1 else 2; + if (BitIsClear(index_align, 0)) + alignment = 1; + else + alignment = 2; + } else if (size == 2) // when '10' + { + // if index_align<2> != '0' then UNDEFINED; + if (BitIsClear(index_align, 2)) + return false; + + // if index_align<1:0> != '00' && index_align<1:0> != '11' then + // UNDEFINED; + if ((Bits32(index_align, 1, 0) != 0) && + (Bits32(index_align, 1, 0) != 3)) + return false; + + // ebytes = 4; esize = 32; index = UInt(index_align<3>); + ebytes = 4; + esize = 32; + index = Bit32(index_align, 3); + + // alignment = if index_align<1:0> == '00' then 1 else 4; + if (Bits32(index_align, 1, 0) == 0) + alignment = 1; + else + alignment = 4; + } else { + return false; + } + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15 + // then UNPREDICTABLE; + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + if (n == 15) + return false; + + } break; + + default: + return false; + } + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then + // GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + if (wback) { + uint32_t Rm = ReadCoreReg(m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = ebytes; + + uint32_t value = Rn + offset; + + context.type = eContextAdjustBaseRegister; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + context.SetRegisterPlusOffset(*base_reg, offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + value)) + return false; + } + + // Elem[D[d],index,esize] = MemU[address,ebytes]; + uint32_t element = MemURead(context, address, esize, 0, &success); + if (!success) + return false; + + element = element << (index * esize); + + uint64_t reg_data = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_d0 + d, 0, &success); + if (!success) + return false; + + uint64_t all_ones = -1; + uint64_t mask = all_ones + << ((index + 1) * esize); // mask is all 1's to left of + // where 'element' goes, & all 0's + // at element & to the right of element. + if (index > 0) + mask = mask | Bits64(all_ones, (index * esize) - 1, + 0); // add 1's to the right of where 'element' goes. + // now mask should be 0's where element goes & 1's everywhere else. + + uint64_t masked_reg = + reg_data & mask; // Take original reg value & zero out 'element' bits + reg_data = + masked_reg & element; // Put 'element' into those bits in reg_data. + + context.type = eContextRegisterLoad; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + d, + reg_data)) + return false; + } + return true; +} + +// A8.6.391 VST1 (multiple single elements) Vector Store (multiple single +// elements) stores elements to memory from one, two, three, or four registers, +// without interleaving. Every element of each register is stored. +bool EmulateInstructionARM::EmulateVST1Multiple(const uint32_t opcode, + ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); + for r = 0 to regs-1 + for e = 0 to elements-1 + MemU[address,ebytes] = Elem[D[d+r],e,esize]; + address = address + ebytes; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t regs; + uint32_t alignment; + uint32_t ebytes; + uint32_t esize; + uint32_t elements; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) { + case eEncodingT1: + case eEncodingA1: { + uint32_t type = Bits32(opcode, 11, 8); + uint32_t align = Bits32(opcode, 5, 4); + + // case type of + if (type == 7) // when '0111' + { + // regs = 1; if align<1> == '1' then UNDEFINED; + regs = 1; + if (BitIsSet(align, 1)) + return false; + } else if (type == 10) // when '1010' + { + // regs = 2; if align == '11' then UNDEFINED; + regs = 2; + if (align == 3) + return false; + } else if (type == 6) // when '0110' + { + // regs = 3; if align<1> == '1' then UNDEFINED; + regs = 3; + if (BitIsSet(align, 1)) + return false; + } else if (type == 2) // when '0010' + // regs = 4; + regs = 4; + else // otherwise + // SEE 'Related encodings'; + return false; + + // alignment = if align == '00' then 1 else 4 << UInt(align); + if (align == 0) + alignment = 1; + else + alignment = 4 << align; + + // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes; + ebytes = 1 << Bits32(opcode, 7, 6); + esize = 8 * ebytes; + elements = 8 / ebytes; + + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + // if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE; + if ((d + regs) > 32) + return false; + + if (n == 15) + return false; + + } break; + + default: + return false; + } + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then + // GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); + if (wback) { + uint32_t Rm = ReadCoreReg(m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = 8 * regs; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset(*base_reg, offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + Rn + offset)) + return false; + } + + context.type = eContextRegisterStore; + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) { + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_d0 + d + r); + uint64_t register_data = ReadRegisterUnsigned( + eRegisterKindDWARF, dwarf_d0 + d + r, 0, &success); + if (!success) + return false; + + // for e = 0 to elements-1 + for (uint32_t e = 0; e < elements; ++e) { + // MemU[address,ebytes] = Elem[D[d+r],e,esize]; + uint64_t word = Bits64(register_data, ((e + 1) * esize) - 1, e * esize); + + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, + address - Rn); + if (!MemUWrite(context, address, word, ebytes)) + return false; + + // address = address + ebytes; + address = address + ebytes; + } + } + } + return true; +} + +// A8.6.392 VST1 (single element from one lane) This instruction stores one +// element to memory from one element of a register. +bool EmulateInstructionARM::EmulateVST1Single(const uint32_t opcode, + ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + MemU[address,ebytes] = Elem[D[d],index,esize]; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t ebytes; + uint32_t esize; + uint32_t index; + uint32_t alignment; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) { + case eEncodingT1: + case eEncodingA1: { + uint32_t size = Bits32(opcode, 11, 10); + uint32_t index_align = Bits32(opcode, 7, 4); + + // if size == '11' then UNDEFINED; + if (size == 3) + return false; + + // case size of + if (size == 0) // when '00' + { + // if index_align<0> != '0' then UNDEFINED; + if (BitIsClear(index_align, 0)) + return false; + // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1; + ebytes = 1; + esize = 8; + index = Bits32(index_align, 3, 1); + alignment = 1; + } else if (size == 1) // when '01' + { + // if index_align<1> != '0' then UNDEFINED; + if (BitIsClear(index_align, 1)) + return false; + + // ebytes = 2; esize = 16; index = UInt(index_align<3:2>); + ebytes = 2; + esize = 16; + index = Bits32(index_align, 3, 2); + + // alignment = if index_align<0> == '0' then 1 else 2; + if (BitIsClear(index_align, 0)) + alignment = 1; + else + alignment = 2; + } else if (size == 2) // when '10' + { + // if index_align<2> != '0' then UNDEFINED; + if (BitIsClear(index_align, 2)) + return false; + + // if index_align<1:0> != '00' && index_align<1:0> != '11' then + // UNDEFINED; + if ((Bits32(index_align, 1, 0) != 0) && + (Bits32(index_align, 1, 0) != 3)) + return false; + + // ebytes = 4; esize = 32; index = UInt(index_align<3>); + ebytes = 4; + esize = 32; + index = Bit32(index_align, 3); + + // alignment = if index_align<1:0> == '00' then 1 else 4; + if (Bits32(index_align, 1, 0) == 0) + alignment = 1; + else + alignment = 4; + } else { + return false; + } + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15 + // then UNPREDICTABLE; + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + if (n == 15) + return false; + } break; + + default: + return false; + } + + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then + // GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + if (wback) { + uint32_t Rm = ReadCoreReg(m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = ebytes; + + context.type = eContextAdjustBaseRegister; + context.SetRegisterPlusOffset(*base_reg, offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + Rn + offset)) + return false; + } + + // MemU[address,ebytes] = Elem[D[d],index,esize]; + uint64_t register_data = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_d0 + d, 0, &success); + if (!success) + return false; + + uint64_t word = + Bits64(register_data, ((index + 1) * esize) - 1, index * esize); + + std::optional<RegisterInfo> data_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_d0 + d); + context.type = eContextRegisterStore; + context.SetRegisterToRegisterPlusOffset(*data_reg, *base_reg, address - Rn); + + if (!MemUWrite(context, address, word, ebytes)) + return false; + } + return true; +} + +// A8.6.309 VLD1 (single element to all lanes) This instruction loads one +// element from memory into every element of one or two vectors. +bool EmulateInstructionARM::EmulateVLD1SingleAll(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); + address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); + if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + replicated_element = Replicate(MemU[address,ebytes], elements); + for r = 0 to regs-1 + D[d+r] = replicated_element; +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t ebytes; + uint32_t elements; + uint32_t regs; + uint32_t alignment; + uint32_t d; + uint32_t n; + uint32_t m; + bool wback; + bool register_index; + + switch (encoding) { + case eEncodingT1: + case eEncodingA1: { + // if size == '11' || (size == '00' && a == '1') then UNDEFINED; + uint32_t size = Bits32(opcode, 7, 6); + if ((size == 3) || ((size == 0) && BitIsSet(opcode, 4))) + return false; + + // ebytes = 1 << UInt(size); elements = 8 DIV ebytes; regs = if T == '0' + // then 1 else 2; + ebytes = 1 << size; + elements = 8 / ebytes; + if (BitIsClear(opcode, 5)) + regs = 1; + else + regs = 2; + + // alignment = if a == '0' then 1 else ebytes; + if (BitIsClear(opcode, 4)) + alignment = 1; + else + alignment = ebytes; + + // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); + d = (Bit32(opcode, 22) << 4) | Bits32(opcode, 15, 12); + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + + // wback = (m != 15); register_index = (m != 15 && m != 13); + wback = (m != 15); + register_index = ((m != 15) && (m != 13)); + + // if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE; + if ((d + regs) > 32) + return false; + + if (n == 15) + return false; + } break; + + default: + return false; + } + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + // address = R[n]; if (address MOD alignment) != 0 then + // GenerateAlignmentException(); + addr_t address = Rn; + if ((address % alignment) != 0) + return false; + + EmulateInstruction::Context context; + // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); + if (wback) { + uint32_t Rm = ReadCoreReg(m, &success); + if (!success) + return false; + + uint32_t offset; + if (register_index) + offset = Rm; + else + offset = ebytes; + + context.type = eContextAdjustBaseRegister; + std::optional<RegisterInfo> base_reg = + GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n); + context.SetRegisterPlusOffset(*base_reg, offset); + + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + n, + Rn + offset)) + return false; + } + + // replicated_element = Replicate(MemU[address,ebytes], elements); + + context.type = eContextRegisterLoad; + uint64_t word = MemURead(context, address, ebytes, 0, &success); + if (!success) + return false; + + uint64_t replicated_element = 0; + uint32_t esize = ebytes * 8; + for (uint32_t e = 0; e < elements; ++e) + replicated_element = + (replicated_element << esize) | Bits64(word, esize - 1, 0); + + // for r = 0 to regs-1 + for (uint32_t r = 0; r < regs; ++r) { + // D[d+r] = replicated_element; + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_d0 + d + r, + replicated_element)) + return false; + } + } + return true; +} + +// B6.2.13 SUBS PC, LR and related instructions The SUBS PC, LR, #<const? +// instruction provides an exception return without the use of the stack. It +// subtracts the immediate constant from the LR, branches to the resulting +// address, and also copies the SPSR to the CPSR. +bool EmulateInstructionARM::EmulateSUBSPcLrEtc(const uint32_t opcode, + const ARMEncoding encoding) { +#if 0 + if ConditionPassed() then + EncodingSpecificOperations(); + if CurrentInstrSet() == InstrSet_ThumbEE then + UNPREDICTABLE; + operand2 = if register_form then Shift(R[m], shift_t, shift_n, APSR.C) else imm32; + case opcode of + when '0000' result = R[n] AND operand2; // AND + when '0001' result = R[n] EOR operand2; // EOR + when '0010' (result, -, -) = AddWithCarry(R[n], NOT(operand2), '1'); // SUB + when '0011' (result, -, -) = AddWithCarry(NOT(R[n]), operand2, '1'); // RSB + when '0100' (result, -, -) = AddWithCarry(R[n], operand2, '0'); // ADD + when '0101' (result, -, -) = AddWithCarry(R[n], operand2, APSR.c); // ADC + when '0110' (result, -, -) = AddWithCarry(R[n], NOT(operand2), APSR.C); // SBC + when '0111' (result, -, -) = AddWithCarry(NOT(R[n]), operand2, APSR.C); // RSC + when '1100' result = R[n] OR operand2; // ORR + when '1101' result = operand2; // MOV + when '1110' result = R[n] AND NOT(operand2); // BIC + when '1111' result = NOT(operand2); // MVN + CPSRWriteByInstr(SPSR[], '1111', TRUE); + BranchWritePC(result); +#endif + + bool success = false; + + if (ConditionPassed(opcode)) { + uint32_t n; + uint32_t m; + uint32_t imm32; + bool register_form; + ARM_ShifterType shift_t; + uint32_t shift_n; + uint32_t code; + + switch (encoding) { + case eEncodingT1: + // if CurrentInstrSet() == InstrSet_ThumbEE then UNPREDICTABLE n = 14; + // imm32 = ZeroExtend(imm8, 32); register_form = FALSE; opcode = '0010'; + // // = SUB + n = 14; + imm32 = Bits32(opcode, 7, 0); + register_form = false; + code = 2; + + // if InITBlock() && !LastInITBlock() then UNPREDICTABLE; + if (InITBlock() && !LastInITBlock()) + return false; + + break; + + case eEncodingA1: + // n = UInt(Rn); imm32 = ARMExpandImm(imm12); register_form = FALSE; + n = Bits32(opcode, 19, 16); + imm32 = ARMExpandImm(opcode); + register_form = false; + code = Bits32(opcode, 24, 21); + + break; + + case eEncodingA2: + // n = UInt(Rn); m = UInt(Rm); register_form = TRUE; + n = Bits32(opcode, 19, 16); + m = Bits32(opcode, 3, 0); + register_form = true; + + // (shift_t, shift_n) = DecodeImmShift(type, imm5); + shift_n = DecodeImmShiftARM(opcode, shift_t); + + break; + + default: + return false; + } + + // operand2 = if register_form then Shift(R[m], shift_t, shift_n, APSR.C) + // else imm32; + uint32_t operand2; + if (register_form) { + uint32_t Rm = ReadCoreReg(m, &success); + if (!success) + return false; + + operand2 = Shift(Rm, shift_t, shift_n, APSR_C, &success); + if (!success) + return false; + } else { + operand2 = imm32; + } + + uint32_t Rn = ReadCoreReg(n, &success); + if (!success) + return false; + + AddWithCarryResult result; + + // case opcode of + switch (code) { + case 0: // when '0000' + // result = R[n] AND operand2; // AND + result.result = Rn & operand2; + break; + + case 1: // when '0001' + // result = R[n] EOR operand2; // EOR + result.result = Rn ^ operand2; + break; + + case 2: // when '0010' + // (result, -, -) = AddWithCarry(R[n], NOT(operand2), '1'); // SUB + result = AddWithCarry(Rn, ~(operand2), 1); + break; + + case 3: // when '0011' + // (result, -, -) = AddWithCarry(NOT(R[n]), operand2, '1'); // RSB + result = AddWithCarry(~(Rn), operand2, 1); + break; + + case 4: // when '0100' + // (result, -, -) = AddWithCarry(R[n], operand2, '0'); // ADD + result = AddWithCarry(Rn, operand2, 0); + break; + + case 5: // when '0101' + // (result, -, -) = AddWithCarry(R[n], operand2, APSR.c); // ADC + result = AddWithCarry(Rn, operand2, APSR_C); + break; + + case 6: // when '0110' + // (result, -, -) = AddWithCarry(R[n], NOT(operand2), APSR.C); // SBC + result = AddWithCarry(Rn, ~(operand2), APSR_C); + break; + + case 7: // when '0111' + // (result, -, -) = AddWithCarry(NOT(R[n]), operand2, APSR.C); // RSC + result = AddWithCarry(~(Rn), operand2, APSR_C); + break; + + case 10: // when '1100' + // result = R[n] OR operand2; // ORR + result.result = Rn | operand2; + break; + + case 11: // when '1101' + // result = operand2; // MOV + result.result = operand2; + break; + + case 12: // when '1110' + // result = R[n] AND NOT(operand2); // BIC + result.result = Rn & ~(operand2); + break; + + case 15: // when '1111' + // result = NOT(operand2); // MVN + result.result = ~(operand2); + break; + + default: + return false; + } + // CPSRWriteByInstr(SPSR[], '1111', TRUE); + + // For now, in emulation mode, we don't have access to the SPSR, so we will + // use the CPSR instead, and hope for the best. + uint32_t spsr = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_cpsr, 0, &success); + if (!success) + return false; + + CPSRWriteByInstr(spsr, 15, true); + + // BranchWritePC(result); + EmulateInstruction::Context context; + context.type = eContextAdjustPC; + context.SetImmediate(result.result); + + BranchWritePC(context, result.result); + } + return true; +} + +EmulateInstructionARM::ARMOpcode * +EmulateInstructionARM::GetARMOpcodeForInstruction(const uint32_t opcode, + uint32_t arm_isa) { + static ARMOpcode g_arm_opcodes[] = { + // Prologue instructions + + // push register(s) + {0x0fff0000, 0x092d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulatePUSH, "push <registers>"}, + {0x0fff0fff, 0x052d0004, ARMvAll, eEncodingA2, No_VFP, eSize32, + &EmulateInstructionARM::EmulatePUSH, "push <register>"}, + + // set r7 to point to a stack offset + {0x0ffff000, 0x028d7000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADDRdSPImm, "add r7, sp, #<const>"}, + {0x0ffff000, 0x024c7000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBR7IPImm, "sub r7, ip, #<const>"}, + // copy the stack pointer to ip + {0x0fffffff, 0x01a0c00d, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMOVRdSP, "mov ip, sp"}, + {0x0ffff000, 0x028dc000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADDRdSPImm, "add ip, sp, #<const>"}, + {0x0ffff000, 0x024dc000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBIPSPImm, "sub ip, sp, #<const>"}, + + // adjust the stack pointer + {0x0ffff000, 0x024dd000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBSPImm, "sub sp, sp, #<const>"}, + {0x0fef0010, 0x004d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBSPReg, + "sub{s}<c> <Rd>, sp, <Rm>{,<shift>}"}, + + // push one register + // if Rn == '1101' && imm12 == '000000000100' then SEE PUSH; + {0x0e5f0000, 0x040d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRRtSP, "str Rt, [sp, #-imm12]!"}, + + // vector push consecutive extension register(s) + {0x0fbf0f00, 0x0d2d0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateVPUSH, "vpush.64 <list>"}, + {0x0fbf0f00, 0x0d2d0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateVPUSH, "vpush.32 <list>"}, + + // Epilogue instructions + + {0x0fff0000, 0x08bd0000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulatePOP, "pop <registers>"}, + {0x0fff0fff, 0x049d0004, ARMvAll, eEncodingA2, No_VFP, eSize32, + &EmulateInstructionARM::EmulatePOP, "pop <register>"}, + {0x0fbf0f00, 0x0cbd0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateVPOP, "vpop.64 <list>"}, + {0x0fbf0f00, 0x0cbd0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateVPOP, "vpop.32 <list>"}, + + // Supervisor Call (previously Software Interrupt) + {0x0f000000, 0x0f000000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSVC, "svc #imm24"}, + + // Branch instructions + // To resolve ambiguity, "blx <label>" should come before "b #imm24" and + // "bl <label>". + {0xfe000000, 0xfa000000, ARMV5_ABOVE, eEncodingA2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBLXImmediate, "blx <label>"}, + {0x0f000000, 0x0a000000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateB, "b #imm24"}, + {0x0f000000, 0x0b000000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBLXImmediate, "bl <label>"}, + {0x0ffffff0, 0x012fff30, ARMV5_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBLXRm, "blx <Rm>"}, + // for example, "bx lr" + {0x0ffffff0, 0x012fff10, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBXRm, "bx <Rm>"}, + // bxj + {0x0ffffff0, 0x012fff20, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBXJRm, "bxj <Rm>"}, + + // Data-processing instructions + // adc (immediate) + {0x0fe00000, 0x02a00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADCImm, "adc{s}<c> <Rd>, <Rn>, #const"}, + // adc (register) + {0x0fe00010, 0x00a00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADCReg, + "adc{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // add (immediate) + {0x0fe00000, 0x02800000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADDImmARM, + "add{s}<c> <Rd>, <Rn>, #const"}, + // add (register) + {0x0fe00010, 0x00800000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADDReg, + "add{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // add (register-shifted register) + {0x0fe00090, 0x00800010, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADDRegShift, + "add{s}<c> <Rd>, <Rn>, <Rm>, <type> <RS>"}, + // adr + {0x0fff0000, 0x028f0000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADR, "add<c> <Rd>, PC, #<const>"}, + {0x0fff0000, 0x024f0000, ARMvAll, eEncodingA2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADR, "sub<c> <Rd>, PC, #<const>"}, + // and (immediate) + {0x0fe00000, 0x02000000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateANDImm, "and{s}<c> <Rd>, <Rn>, #const"}, + // and (register) + {0x0fe00010, 0x00000000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateANDReg, + "and{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // bic (immediate) + {0x0fe00000, 0x03c00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBICImm, "bic{s}<c> <Rd>, <Rn>, #const"}, + // bic (register) + {0x0fe00010, 0x01c00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBICReg, + "bic{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // eor (immediate) + {0x0fe00000, 0x02200000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateEORImm, "eor{s}<c> <Rd>, <Rn>, #const"}, + // eor (register) + {0x0fe00010, 0x00200000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateEORReg, + "eor{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // orr (immediate) + {0x0fe00000, 0x03800000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateORRImm, "orr{s}<c> <Rd>, <Rn>, #const"}, + // orr (register) + {0x0fe00010, 0x01800000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateORRReg, + "orr{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // rsb (immediate) + {0x0fe00000, 0x02600000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRSBImm, "rsb{s}<c> <Rd>, <Rn>, #<const>"}, + // rsb (register) + {0x0fe00010, 0x00600000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRSBReg, + "rsb{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // rsc (immediate) + {0x0fe00000, 0x02e00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRSCImm, "rsc{s}<c> <Rd>, <Rn>, #<const>"}, + // rsc (register) + {0x0fe00010, 0x00e00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRSCReg, + "rsc{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // sbc (immediate) + {0x0fe00000, 0x02c00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSBCImm, "sbc{s}<c> <Rd>, <Rn>, #<const>"}, + // sbc (register) + {0x0fe00010, 0x00c00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSBCReg, + "sbc{s}<c> <Rd>, <Rn>, <Rm> {,<shift>}"}, + // sub (immediate, ARM) + {0x0fe00000, 0x02400000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBImmARM, + "sub{s}<c> <Rd>, <Rn>, #<const>"}, + // sub (sp minus immediate) + {0x0fef0000, 0x024d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBSPImm, "sub{s}<c> <Rd>, sp, #<const>"}, + // sub (register) + {0x0fe00010, 0x00400000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBReg, + "sub{s}<c> <Rd>, <Rn>, <Rm>{,<shift>}"}, + // teq (immediate) + {0x0ff0f000, 0x03300000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateTEQImm, "teq<c> <Rn>, #const"}, + // teq (register) + {0x0ff0f010, 0x01300000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateTEQReg, "teq<c> <Rn>, <Rm> {,<shift>}"}, + // tst (immediate) + {0x0ff0f000, 0x03100000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateTSTImm, "tst<c> <Rn>, #const"}, + // tst (register) + {0x0ff0f010, 0x01100000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateTSTReg, "tst<c> <Rn>, <Rm> {,<shift>}"}, + + // mov (immediate) + {0x0fef0000, 0x03a00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMOVRdImm, "mov{s}<c> <Rd>, #<const>"}, + {0x0ff00000, 0x03000000, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMOVRdImm, "movw<c> <Rd>, #<imm16>"}, + // mov (register) + {0x0fef0ff0, 0x01a00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMOVRdRm, "mov{s}<c> <Rd>, <Rm>"}, + // mvn (immediate) + {0x0fef0000, 0x03e00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMVNImm, "mvn{s}<c> <Rd>, #<const>"}, + // mvn (register) + {0x0fef0010, 0x01e00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMVNReg, + "mvn{s}<c> <Rd>, <Rm> {,<shift>}"}, + // cmn (immediate) + {0x0ff0f000, 0x03700000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateCMNImm, "cmn<c> <Rn>, #<const>"}, + // cmn (register) + {0x0ff0f010, 0x01700000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateCMNReg, "cmn<c> <Rn>, <Rm> {,<shift>}"}, + // cmp (immediate) + {0x0ff0f000, 0x03500000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateCMPImm, "cmp<c> <Rn>, #<const>"}, + // cmp (register) + {0x0ff0f010, 0x01500000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateCMPReg, "cmp<c> <Rn>, <Rm> {,<shift>}"}, + // asr (immediate) + {0x0fef0070, 0x01a00040, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateASRImm, "asr{s}<c> <Rd>, <Rm>, #imm"}, + // asr (register) + {0x0fef00f0, 0x01a00050, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateASRReg, "asr{s}<c> <Rd>, <Rn>, <Rm>"}, + // lsl (immediate) + {0x0fef0070, 0x01a00000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLSLImm, "lsl{s}<c> <Rd>, <Rm>, #imm"}, + // lsl (register) + {0x0fef00f0, 0x01a00010, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLSLReg, "lsl{s}<c> <Rd>, <Rn>, <Rm>"}, + // lsr (immediate) + {0x0fef0070, 0x01a00020, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLSRImm, "lsr{s}<c> <Rd>, <Rm>, #imm"}, + // lsr (register) + {0x0fef00f0, 0x01a00050, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLSRReg, "lsr{s}<c> <Rd>, <Rn>, <Rm>"}, + // rrx is a special case encoding of ror (immediate) + {0x0fef0ff0, 0x01a00060, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRRX, "rrx{s}<c> <Rd>, <Rm>"}, + // ror (immediate) + {0x0fef0070, 0x01a00060, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRORImm, "ror{s}<c> <Rd>, <Rm>, #imm"}, + // ror (register) + {0x0fef00f0, 0x01a00070, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRORReg, "ror{s}<c> <Rd>, <Rn>, <Rm>"}, + // mul + {0x0fe000f0, 0x00000090, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMUL, "mul{s}<c> <Rd>,<R>,<Rm>"}, + + // subs pc, lr and related instructions + {0x0e10f000, 0x0210f000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBSPcLrEtc, + "<opc>S<c> PC,#<const> | <Rn>,#<const>"}, + {0x0e10f010, 0x0010f000, ARMvAll, eEncodingA2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBSPcLrEtc, + "<opc>S<c> PC,<Rn>,<Rm{,<shift>}"}, + + // Load instructions + {0x0fd00000, 0x08900000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDM, "ldm<c> <Rn>{!} <registers>"}, + {0x0fd00000, 0x08100000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDMDA, "ldmda<c> <Rn>{!} <registers>"}, + {0x0fd00000, 0x09100000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDMDB, "ldmdb<c> <Rn>{!} <registers>"}, + {0x0fd00000, 0x09900000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDMIB, "ldmib<c> <Rn<{!} <registers>"}, + {0x0e500000, 0x04100000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRImmediateARM, + "ldr<c> <Rt> [<Rn> {#+/-<imm12>}]"}, + {0x0e500010, 0x06100000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRRegister, + "ldr<c> <Rt> [<Rn> +/-<Rm> {<shift>}] {!}"}, + {0x0e5f0000, 0x045f0000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRBLiteral, "ldrb<c> <Rt>, [...]"}, + {0xfe500010, 0x06500000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRBRegister, + "ldrb<c> <Rt>, [<Rn>,+/-<Rm>{, <shift>}]{!}"}, + {0x0e5f00f0, 0x005f00b0, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRHLiteral, "ldrh<c> <Rt>, <label>"}, + {0x0e5000f0, 0x001000b0, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRHRegister, + "ldrh<c> <Rt>,[<Rn>,+/-<Rm>]{!}"}, + {0x0e5000f0, 0x005000d0, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSBImmediate, + "ldrsb<c> <Rt>, [<Rn>{,#+/-<imm8>}]"}, + {0x0e5f00f0, 0x005f00d0, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSBLiteral, "ldrsb<c> <Rt> <label>"}, + {0x0e5000f0, 0x001000d0, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSBRegister, + "ldrsb<c> <Rt>,[<Rn>,+/-<Rm>]{!}"}, + {0x0e5000f0, 0x005000f0, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSHImmediate, + "ldrsh<c> <Rt>,[<Rn>{,#+/-<imm8>}]"}, + {0x0e5f00f0, 0x005f00f0, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSHLiteral, "ldrsh<c> <Rt>,<label>"}, + {0x0e5000f0, 0x001000f0, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSHRegister, + "ldrsh<c> <Rt>,[<Rn>,+/-<Rm>]{!}"}, + {0x0e5000f0, 0x004000d0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRDImmediate, + "ldrd<c> <Rt>, <Rt2>, [<Rn>,#+/-<imm8>]!"}, + {0x0e500ff0, 0x000000d0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRDRegister, + "ldrd<c> <Rt>, <Rt2>, [<Rn>, +/-<Rm>]{!}"}, + {0x0e100f00, 0x0c100b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32, + &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"}, + {0x0e100f00, 0x0c100a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32, + &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"}, + {0x0f300f00, 0x0d100b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32, + &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Dd>, [<Rn>{,#+/-<imm>}]"}, + {0x0f300f00, 0x0d100a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32, + &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Sd>, [<Rn>{,#+/-<imm>}]"}, + {0xffb00000, 0xf4200000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32, + &EmulateInstructionARM::EmulateVLD1Multiple, + "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + {0xffb00300, 0xf4a00000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32, + &EmulateInstructionARM::EmulateVLD1Single, + "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + {0xffb00f00, 0xf4a00c00, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32, + &EmulateInstructionARM::EmulateVLD1SingleAll, + "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + + // Store instructions + {0x0fd00000, 0x08800000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTM, "stm<c> <Rn>{!} <registers>"}, + {0x0fd00000, 0x08000000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTMDA, "stmda<c> <Rn>{!} <registers>"}, + {0x0fd00000, 0x09000000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTMDB, "stmdb<c> <Rn>{!} <registers>"}, + {0x0fd00000, 0x09800000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTMIB, "stmib<c> <Rn>{!} <registers>"}, + {0x0e500010, 0x06000000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRRegister, + "str<c> <Rt> [<Rn> +/-<Rm> {<shift>}]{!}"}, + {0x0e5000f0, 0x000000b0, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRHRegister, + "strh<c> <Rt>,[<Rn>,+/-<Rm>[{!}"}, + {0x0ff00ff0, 0x01800f90, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTREX, "strex<c> <Rd>, <Rt>, [<Rn>]"}, + {0x0e500000, 0x04400000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRBImmARM, + "strb<c> <Rt>,[<Rn>,#+/-<imm12>]!"}, + {0x0e500000, 0x04000000, ARMvAll, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRImmARM, + "str<c> <Rt>,[<Rn>,#+/-<imm12>]!"}, + {0x0e5000f0, 0x004000f0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRDImm, + "strd<c> <Rt>, <Rt2>, [<Rn> #+/-<imm8>]!"}, + {0x0e500ff0, 0x000000f0, ARMV5TE_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRDReg, + "strd<c> <Rt>, <Rt2>, [<Rn>, +/-<Rm>]{!}"}, + {0x0e100f00, 0x0c000b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32, + &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!} <list>"}, + {0x0e100f00, 0x0c000a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32, + &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!} <list>"}, + {0x0f300f00, 0x0d000b00, ARMvAll, eEncodingA1, VFPv2_ABOVE, eSize32, + &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Dd> [<Rn>{,#+/-<imm>}]"}, + {0x0f300f00, 0x0d000a00, ARMvAll, eEncodingA2, VFPv2v3, eSize32, + &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Sd> [<Rn>{,#+/-<imm>}]"}, + {0xffb00000, 0xf4000000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32, + &EmulateInstructionARM::EmulateVST1Multiple, + "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + {0xffb00300, 0xf4800000, ARMvAll, eEncodingA1, AdvancedSIMD, eSize32, + &EmulateInstructionARM::EmulateVST1Single, + "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + + // Other instructions + {0x0fff00f0, 0x06af00f0, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSXTB, "sxtb<c> <Rd>,<Rm>{,<rotation>}"}, + {0x0fff00f0, 0x06bf0070, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSXTH, "sxth<c> <Rd>,<Rm>{,<rotation>}"}, + {0x0fff00f0, 0x06ef0070, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateUXTB, "uxtb<c> <Rd>,<Rm>{,<rotation>}"}, + {0x0fff00f0, 0x06ff0070, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateUXTH, "uxth<c> <Rd>,<Rm>{,<rotation>}"}, + {0xfe500000, 0xf8100000, ARMV6_ABOVE, eEncodingA1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRFE, "rfe{<amode>} <Rn>{!}"} + + }; + static const size_t k_num_arm_opcodes = std::size(g_arm_opcodes); + + for (size_t i = 0; i < k_num_arm_opcodes; ++i) { + if ((g_arm_opcodes[i].mask & opcode) == g_arm_opcodes[i].value && + (g_arm_opcodes[i].variants & arm_isa) != 0) + return &g_arm_opcodes[i]; + } + return nullptr; +} + +EmulateInstructionARM::ARMOpcode * +EmulateInstructionARM::GetThumbOpcodeForInstruction(const uint32_t opcode, + uint32_t arm_isa) { + + static ARMOpcode g_thumb_opcodes[] = { + // Prologue instructions + + // push register(s) + {0xfffffe00, 0x0000b400, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulatePUSH, "push <registers>"}, + {0xffff0000, 0xe92d0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulatePUSH, "push.w <registers>"}, + {0xffff0fff, 0xf84d0d04, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulatePUSH, "push.w <register>"}, + + // set r7 to point to a stack offset + {0xffffff00, 0x0000af00, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateADDRdSPImm, "add r7, sp, #imm"}, + // copy the stack pointer to r7 + {0xffffffff, 0x0000466f, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateMOVRdSP, "mov r7, sp"}, + // move from high register to low register (comes after "mov r7, sp" to + // resolve ambiguity) + {0xffffffc0, 0x00004640, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateMOVLowHigh, "mov r0-r7, r8-r15"}, + + // PC-relative load into register (see also EmulateADDSPRm) + {0xfffff800, 0x00004800, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLDRRtPCRelative, "ldr <Rt>, [PC, #imm]"}, + + // adjust the stack pointer + {0xffffff87, 0x00004485, ARMvAll, eEncodingT2, No_VFP, eSize16, + &EmulateInstructionARM::EmulateADDSPRm, "add sp, <Rm>"}, + {0xffffff80, 0x0000b080, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSUBSPImm, "sub sp, sp, #imm"}, + {0xfbef8f00, 0xf1ad0d00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBSPImm, "sub.w sp, sp, #<const>"}, + {0xfbff8f00, 0xf2ad0d00, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBSPImm, "subw sp, sp, #imm12"}, + {0xffef8000, 0xebad0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBSPReg, + "sub{s}<c> <Rd>, sp, <Rm>{,<shift>}"}, + + // vector push consecutive extension register(s) + {0xffbf0f00, 0xed2d0b00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateVPUSH, "vpush.64 <list>"}, + {0xffbf0f00, 0xed2d0a00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateVPUSH, "vpush.32 <list>"}, + + // Epilogue instructions + + {0xfffff800, 0x0000a800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateADDSPImm, "add<c> <Rd>, sp, #imm"}, + {0xffffff80, 0x0000b000, ARMvAll, eEncodingT2, No_VFP, eSize16, + &EmulateInstructionARM::EmulateADDSPImm, "add sp, #imm"}, + {0xfffffe00, 0x0000bc00, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulatePOP, "pop <registers>"}, + {0xffff0000, 0xe8bd0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulatePOP, "pop.w <registers>"}, + {0xffff0fff, 0xf85d0d04, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulatePOP, "pop.w <register>"}, + {0xffbf0f00, 0xecbd0b00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateVPOP, "vpop.64 <list>"}, + {0xffbf0f00, 0xecbd0a00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateVPOP, "vpop.32 <list>"}, + + // Supervisor Call (previously Software Interrupt) + {0xffffff00, 0x0000df00, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSVC, "svc #imm8"}, + + // If Then makes up to four following instructions conditional. + // The next 5 opcode _must_ come before the if then instruction + {0xffffffff, 0x0000bf00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateNop, "nop"}, + {0xffffffff, 0x0000bf10, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateNop, "nop YIELD (yield hint)"}, + {0xffffffff, 0x0000bf20, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateNop, "nop WFE (wait for event hint)"}, + {0xffffffff, 0x0000bf30, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateNop, "nop WFI (wait for interrupt hint)"}, + {0xffffffff, 0x0000bf40, ARMV7_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateNop, "nop SEV (send event hint)"}, + {0xffffff00, 0x0000bf00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateIT, "it{<x>{<y>{<z>}}} <firstcond>"}, + + // Branch instructions + // To resolve ambiguity, "b<c> #imm8" should come after "svc #imm8". + {0xfffff000, 0x0000d000, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateB, "b<c> #imm8 (outside IT)"}, + {0xfffff800, 0x0000e000, ARMvAll, eEncodingT2, No_VFP, eSize16, + &EmulateInstructionARM::EmulateB, "b<c> #imm11 (outside or last in IT)"}, + {0xf800d000, 0xf0008000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateB, "b<c>.w #imm8 (outside IT)"}, + {0xf800d000, 0xf0009000, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32, + &EmulateInstructionARM::EmulateB, + "b<c>.w #imm8 (outside or last in IT)"}, + // J1 == J2 == 1 + {0xf800d000, 0xf000d000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBLXImmediate, "bl <label>"}, + // J1 == J2 == 1 + {0xf800d001, 0xf000c000, ARMV5_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBLXImmediate, "blx <label>"}, + {0xffffff87, 0x00004780, ARMV5_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateBLXRm, "blx <Rm>"}, + // for example, "bx lr" + {0xffffff87, 0x00004700, ARMvAll, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBXRm, "bx <Rm>"}, + // bxj + {0xfff0ffff, 0xf3c08f00, ARMV5J_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBXJRm, "bxj <Rm>"}, + // compare and branch + {0xfffff500, 0x0000b100, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateCB, "cb{n}z <Rn>, <label>"}, + // table branch byte + {0xfff0fff0, 0xe8d0f000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateTB, "tbb<c> <Rn>, <Rm>"}, + // table branch halfword + {0xfff0fff0, 0xe8d0f010, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateTB, "tbh<c> <Rn>, <Rm>, lsl #1"}, + + // Data-processing instructions + // adc (immediate) + {0xfbe08000, 0xf1400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADCImm, "adc{s}<c> <Rd>, <Rn>, #<const>"}, + // adc (register) + {0xffffffc0, 0x00004140, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateADCReg, "adcs|adc<c> <Rdn>, <Rm>"}, + {0xffe08000, 0xeb400000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADCReg, + "adc{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // add (register) + {0xfffffe00, 0x00001800, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateADDReg, "adds|add<c> <Rd>, <Rn>, <Rm>"}, + // Make sure "add sp, <Rm>" comes before this instruction, so there's no + // ambiguity decoding the two. + {0xffffff00, 0x00004400, ARMvAll, eEncodingT2, No_VFP, eSize16, + &EmulateInstructionARM::EmulateADDReg, "add<c> <Rdn>, <Rm>"}, + // adr + {0xfffff800, 0x0000a000, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateADR, "add<c> <Rd>, PC, #<const>"}, + {0xfbff8000, 0xf2af0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADR, "sub<c> <Rd>, PC, #<const>"}, + {0xfbff8000, 0xf20f0000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADR, "add<c> <Rd>, PC, #<const>"}, + // and (immediate) + {0xfbe08000, 0xf0000000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateANDImm, "and{s}<c> <Rd>, <Rn>, #<const>"}, + // and (register) + {0xffffffc0, 0x00004000, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateANDReg, "ands|and<c> <Rdn>, <Rm>"}, + {0xffe08000, 0xea000000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateANDReg, + "and{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // bic (immediate) + {0xfbe08000, 0xf0200000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBICImm, "bic{s}<c> <Rd>, <Rn>, #<const>"}, + // bic (register) + {0xffffffc0, 0x00004380, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateBICReg, "bics|bic<c> <Rdn>, <Rm>"}, + {0xffe08000, 0xea200000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateBICReg, + "bic{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // eor (immediate) + {0xfbe08000, 0xf0800000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateEORImm, "eor{s}<c> <Rd>, <Rn>, #<const>"}, + // eor (register) + {0xffffffc0, 0x00004040, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateEORReg, "eors|eor<c> <Rdn>, <Rm>"}, + {0xffe08000, 0xea800000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateEORReg, + "eor{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // orr (immediate) + {0xfbe08000, 0xf0400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateORRImm, "orr{s}<c> <Rd>, <Rn>, #<const>"}, + // orr (register) + {0xffffffc0, 0x00004300, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateORRReg, "orrs|orr<c> <Rdn>, <Rm>"}, + {0xffe08000, 0xea400000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateORRReg, + "orr{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // rsb (immediate) + {0xffffffc0, 0x00004240, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateRSBImm, "rsbs|rsb<c> <Rd>, <Rn>, #0"}, + {0xfbe08000, 0xf1c00000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRSBImm, + "rsb{s}<c>.w <Rd>, <Rn>, #<const>"}, + // rsb (register) + {0xffe08000, 0xea400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRSBReg, + "rsb{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // sbc (immediate) + {0xfbe08000, 0xf1600000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSBCImm, "sbc{s}<c> <Rd>, <Rn>, #<const>"}, + // sbc (register) + {0xffffffc0, 0x00004180, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSBCReg, "sbcs|sbc<c> <Rdn>, <Rm>"}, + {0xffe08000, 0xeb600000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSBCReg, + "sbc{s}<c>.w <Rd>, <Rn>, <Rm> {,<shift>}"}, + // add (immediate, Thumb) + {0xfffffe00, 0x00001c00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateADDImmThumb, + "adds|add<c> <Rd>,<Rn>,#<imm3>"}, + {0xfffff800, 0x00003000, ARMV4T_ABOVE, eEncodingT2, No_VFP, eSize16, + &EmulateInstructionARM::EmulateADDImmThumb, "adds|add<c> <Rdn>,#<imm8>"}, + {0xfbe08000, 0xf1000000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADDImmThumb, + "add{s}<c>.w <Rd>,<Rn>,#<const>"}, + {0xfbf08000, 0xf2000000, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32, + &EmulateInstructionARM::EmulateADDImmThumb, + "addw<c> <Rd>,<Rn>,#<imm12>"}, + // sub (immediate, Thumb) + {0xfffffe00, 0x00001e00, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSUBImmThumb, + "subs|sub<c> <Rd>, <Rn> #imm3"}, + {0xfffff800, 0x00003800, ARMvAll, eEncodingT2, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSUBImmThumb, "subs|sub<c> <Rdn>, #imm8"}, + {0xfbe08000, 0xf1a00000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBImmThumb, + "sub{s}<c>.w <Rd>, <Rn>, #<const>"}, + {0xfbf08000, 0xf2a00000, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBImmThumb, + "subw<c> <Rd>, <Rn>, #imm12"}, + // sub (sp minus immediate) + {0xfbef8000, 0xf1ad0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBSPImm, "sub{s}.w <Rd>, sp, #<const>"}, + {0xfbff8000, 0xf2ad0000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBSPImm, "subw<c> <Rd>, sp, #imm12"}, + // sub (register) + {0xfffffe00, 0x00001a00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSUBReg, "subs|sub<c> <Rd>, <Rn>, <Rm>"}, + {0xffe08000, 0xeba00000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBReg, + "sub{s}<c>.w <Rd>, <Rn>, <Rm>{,<shift>}"}, + // teq (immediate) + {0xfbf08f00, 0xf0900f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateTEQImm, "teq<c> <Rn>, #<const>"}, + // teq (register) + {0xfff08f00, 0xea900f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateTEQReg, "teq<c> <Rn>, <Rm> {,<shift>}"}, + // tst (immediate) + {0xfbf08f00, 0xf0100f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateTSTImm, "tst<c> <Rn>, #<const>"}, + // tst (register) + {0xffffffc0, 0x00004200, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateTSTReg, "tst<c> <Rdn>, <Rm>"}, + {0xfff08f00, 0xea100f00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateTSTReg, "tst<c>.w <Rn>, <Rm> {,<shift>}"}, + + // move from high register to high register + {0xffffff00, 0x00004600, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateMOVRdRm, "mov<c> <Rd>, <Rm>"}, + // move from low register to low register + {0xffffffc0, 0x00000000, ARMvAll, eEncodingT2, No_VFP, eSize16, + &EmulateInstructionARM::EmulateMOVRdRm, "movs <Rd>, <Rm>"}, + // mov{s}<c>.w <Rd>, <Rm> + {0xffeff0f0, 0xea4f0000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMOVRdRm, "mov{s}<c>.w <Rd>, <Rm>"}, + // move immediate + {0xfffff800, 0x00002000, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateMOVRdImm, "movs|mov<c> <Rd>, #imm8"}, + {0xfbef8000, 0xf04f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMOVRdImm, "mov{s}<c>.w <Rd>, #<const>"}, + {0xfbf08000, 0xf2400000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMOVRdImm, "movw<c> <Rd>,#<imm16>"}, + // mvn (immediate) + {0xfbef8000, 0xf06f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMVNImm, "mvn{s} <Rd>, #<const>"}, + // mvn (register) + {0xffffffc0, 0x000043c0, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateMVNReg, "mvns|mvn<c> <Rd>, <Rm>"}, + {0xffef8000, 0xea6f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMVNReg, + "mvn{s}<c>.w <Rd>, <Rm> {,<shift>}"}, + // cmn (immediate) + {0xfbf08f00, 0xf1100f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateCMNImm, "cmn<c> <Rn>, #<const>"}, + // cmn (register) + {0xffffffc0, 0x000042c0, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateCMNReg, "cmn<c> <Rn>, <Rm>"}, + {0xfff08f00, 0xeb100f00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateCMNReg, "cmn<c> <Rn>, <Rm> {,<shift>}"}, + // cmp (immediate) + {0xfffff800, 0x00002800, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateCMPImm, "cmp<c> <Rn>, #imm8"}, + {0xfbf08f00, 0xf1b00f00, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateCMPImm, "cmp<c>.w <Rn>, #<const>"}, + // cmp (register) (Rn and Rm both from r0-r7) + {0xffffffc0, 0x00004280, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateCMPReg, "cmp<c> <Rn>, <Rm>"}, + // cmp (register) (Rn and Rm not both from r0-r7) + {0xffffff00, 0x00004500, ARMvAll, eEncodingT2, No_VFP, eSize16, + &EmulateInstructionARM::EmulateCMPReg, "cmp<c> <Rn>, <Rm>"}, + {0xfff08f00, 0xebb00f00, ARMvAll, eEncodingT3, No_VFP, eSize16, + &EmulateInstructionARM::EmulateCMPReg, + "cmp<c>.w <Rn>, <Rm> {, <shift>}"}, + // asr (immediate) + {0xfffff800, 0x00001000, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateASRImm, "asrs|asr<c> <Rd>, <Rm>, #imm"}, + {0xffef8030, 0xea4f0020, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateASRImm, "asr{s}<c>.w <Rd>, <Rm>, #imm"}, + // asr (register) + {0xffffffc0, 0x00004100, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateASRReg, "asrs|asr<c> <Rdn>, <Rm>"}, + {0xffe0f0f0, 0xfa40f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateASRReg, "asr{s}<c>.w <Rd>, <Rn>, <Rm>"}, + // lsl (immediate) + {0xfffff800, 0x00000000, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLSLImm, "lsls|lsl<c> <Rd>, <Rm>, #imm"}, + {0xffef8030, 0xea4f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLSLImm, "lsl{s}<c>.w <Rd>, <Rm>, #imm"}, + // lsl (register) + {0xffffffc0, 0x00004080, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLSLReg, "lsls|lsl<c> <Rdn>, <Rm>"}, + {0xffe0f0f0, 0xfa00f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLSLReg, "lsl{s}<c>.w <Rd>, <Rn>, <Rm>"}, + // lsr (immediate) + {0xfffff800, 0x00000800, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLSRImm, "lsrs|lsr<c> <Rd>, <Rm>, #imm"}, + {0xffef8030, 0xea4f0010, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLSRImm, "lsr{s}<c>.w <Rd>, <Rm>, #imm"}, + // lsr (register) + {0xffffffc0, 0x000040c0, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLSRReg, "lsrs|lsr<c> <Rdn>, <Rm>"}, + {0xffe0f0f0, 0xfa20f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLSRReg, "lsr{s}<c>.w <Rd>, <Rn>, <Rm>"}, + // rrx is a special case encoding of ror (immediate) + {0xffeff0f0, 0xea4f0030, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRRX, "rrx{s}<c>.w <Rd>, <Rm>"}, + // ror (immediate) + {0xffef8030, 0xea4f0030, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRORImm, "ror{s}<c>.w <Rd>, <Rm>, #imm"}, + // ror (register) + {0xffffffc0, 0x000041c0, ARMvAll, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateRORReg, "rors|ror<c> <Rdn>, <Rm>"}, + {0xffe0f0f0, 0xfa60f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRORReg, "ror{s}<c>.w <Rd>, <Rn>, <Rm>"}, + // mul + {0xffffffc0, 0x00004340, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateMUL, "muls <Rdm>,<Rn>,<Rdm>"}, + // mul + {0xfff0f0f0, 0xfb00f000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateMUL, "mul<c> <Rd>,<Rn>,<Rm>"}, + + // subs pc, lr and related instructions + {0xffffff00, 0xf3de8f00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSUBSPcLrEtc, "SUBS<c> PC, LR, #<imm8>"}, + + // RFE instructions *** IMPORTANT *** THESE MUST BE LISTED **BEFORE** THE + // LDM.. Instructions in this table; + // otherwise the wrong instructions will be selected. + + {0xffd0ffff, 0xe810c000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRFE, "rfedb<c> <Rn>{!}"}, + {0xffd0ffff, 0xe990c000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateRFE, "rfe{ia}<c> <Rn>{!}"}, + + // Load instructions + {0xfffff800, 0x0000c800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLDM, "ldm<c> <Rn>{!} <registers>"}, + {0xffd02000, 0xe8900000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDM, "ldm<c>.w <Rn>{!} <registers>"}, + {0xffd00000, 0xe9100000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDMDB, "ldmdb<c> <Rn>{!} <registers>"}, + {0xfffff800, 0x00006800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLDRRtRnImm, "ldr<c> <Rt>, [<Rn>{,#imm}]"}, + {0xfffff800, 0x00009800, ARMV4T_ABOVE, eEncodingT2, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLDRRtRnImm, "ldr<c> <Rt>, [SP{,#imm}]"}, + {0xfff00000, 0xf8d00000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRRtRnImm, + "ldr<c>.w <Rt>, [<Rn>{,#imm12}]"}, + {0xfff00800, 0xf8500800, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRRtRnImm, + "ldr<c> <Rt>, [<Rn>{,#+/-<imm8>}]{!}"}, + // Thumb2 PC-relative load into register + {0xff7f0000, 0xf85f0000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRRtPCRelative, + "ldr<c>.w <Rt>, [PC, +/-#imm}]"}, + {0xfffffe00, 0x00005800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLDRRegister, "ldr<c> <Rt>, [<Rn>, <Rm>]"}, + {0xfff00fc0, 0xf8500000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRRegister, + "ldr<c>.w <Rt>, [<Rn>,<Rm>{,LSL #<imm2>}]"}, + {0xfffff800, 0x00007800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLDRBImmediate, + "ldrb<c> <Rt>,[<Rn>{,#<imm5>}]"}, + {0xfff00000, 0xf8900000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRBImmediate, + "ldrb<c>.w <Rt>,[<Rn>{,#<imm12>}]"}, + {0xfff00800, 0xf8100800, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRBImmediate, + "ldrb<c> <Rt>,[<Rn>, #+/-<imm8>]{!}"}, + {0xff7f0000, 0xf81f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRBLiteral, "ldrb<c> <Rt>,[...]"}, + {0xfffffe00, 0x00005c00, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLDRBRegister, "ldrb<c> <Rt>,[<Rn>,<Rm>]"}, + {0xfff00fc0, 0xf8100000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRBRegister, + "ldrb<c>.w <Rt>,[<Rn>,<Rm>{,LSL #imm2>}]"}, + {0xfffff800, 0x00008800, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLDRHImmediate, + "ldrh<c> <Rt>, [<Rn>{,#<imm>}]"}, + {0xfff00000, 0xf8b00000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRHImmediate, + "ldrh<c>.w <Rt>,[<Rn>{,#<imm12>}]"}, + {0xfff00800, 0xf8300800, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRHImmediate, + "ldrh<c> <Rt>,[<Rn>,#+/-<imm8>]{!}"}, + {0xff7f0000, 0xf83f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRHLiteral, "ldrh<c> <Rt>, <label>"}, + {0xfffffe00, 0x00005a00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLDRHRegister, + "ldrh<c> <Rt>, [<Rn>,<Rm>]"}, + {0xfff00fc0, 0xf8300000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRHRegister, + "ldrh<c>.w <Rt>,[<Rn>,<Rm>{,LSL #<imm2>}]"}, + {0xfff00000, 0xf9900000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSBImmediate, + "ldrsb<c> <Rt>,[<Rn>,#<imm12>]"}, + {0xfff00800, 0xf9100800, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSBImmediate, + "ldrsb<c> <Rt>,[<Rn>,#+/-<imm8>]"}, + {0xff7f0000, 0xf91f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSBLiteral, "ldrsb<c> <Rt>, <label>"}, + {0xfffffe00, 0x00005600, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLDRSBRegister, + "ldrsb<c> <Rt>,[<Rn>,<Rm>]"}, + {0xfff00fc0, 0xf9100000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSBRegister, + "ldrsb<c>.w <Rt>,[<Rn>,<Rm>{,LSL #imm2>}]"}, + {0xfff00000, 0xf9b00000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSHImmediate, + "ldrsh<c> <Rt>,[<Rn>,#<imm12>]"}, + {0xfff00800, 0xf9300800, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSHImmediate, + "ldrsh<c> <Rt>,[<Rn>,#+/-<imm8>]"}, + {0xff7f0000, 0xf93f0000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSHLiteral, "ldrsh<c> <Rt>,<label>"}, + {0xfffffe00, 0x00005e00, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateLDRSHRegister, + "ldrsh<c> <Rt>,[<Rn>,<Rm>]"}, + {0xfff00fc0, 0xf9300000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRSHRegister, + "ldrsh<c>.w <Rt>,[<Rn>,<Rm>{,LSL #<imm2>}]"}, + {0xfe500000, 0xe8500000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateLDRDImmediate, + "ldrd<c> <Rt>, <Rt2>, [<Rn>,#+/-<imm>]!"}, + {0xfe100f00, 0xec100b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32, + &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"}, + {0xfe100f00, 0xec100a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32, + &EmulateInstructionARM::EmulateVLDM, "vldm{mode}<c> <Rn>{!}, <list>"}, + {0xffe00f00, 0xed100b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32, + &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Dd>, [<Rn>{,#+/-<imm>}]"}, + {0xff300f00, 0xed100a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32, + &EmulateInstructionARM::EmulateVLDR, "vldr<c> <Sd>, {<Rn>{,#+/-<imm>}]"}, + {0xffb00000, 0xf9200000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32, + &EmulateInstructionARM::EmulateVLD1Multiple, + "vld1<c>.<size> <list>, [<Rn>{@<align>}],<Rm>"}, + {0xffb00300, 0xf9a00000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32, + &EmulateInstructionARM::EmulateVLD1Single, + "vld1<c>.<size> <list>, [<Rn>{@<align>}],<Rm>"}, + {0xffb00f00, 0xf9a00c00, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32, + &EmulateInstructionARM::EmulateVLD1SingleAll, + "vld1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + + // Store instructions + {0xfffff800, 0x0000c000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSTM, "stm<c> <Rn>{!} <registers>"}, + {0xffd00000, 0xe8800000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTM, "stm<c>.w <Rn>{!} <registers>"}, + {0xffd00000, 0xe9000000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTMDB, "stmdb<c> <Rn>{!} <registers>"}, + {0xfffff800, 0x00006000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSTRThumb, "str<c> <Rt>, [<Rn>{,#<imm>}]"}, + {0xfffff800, 0x00009000, ARMV4T_ABOVE, eEncodingT2, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSTRThumb, "str<c> <Rt>, [SP,#<imm>]"}, + {0xfff00000, 0xf8c00000, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRThumb, + "str<c>.w <Rt>, [<Rn>,#<imm12>]"}, + {0xfff00800, 0xf8400800, ARMV6T2_ABOVE, eEncodingT4, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRThumb, + "str<c> <Rt>, [<Rn>,#+/-<imm8>]"}, + {0xfffffe00, 0x00005000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSTRRegister, "str<c> <Rt> ,{<Rn>, <Rm>]"}, + {0xfff00fc0, 0xf8400000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRRegister, + "str<c>.w <Rt>, [<Rn>, <Rm> {lsl #imm2>}]"}, + {0xfffff800, 0x00007000, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSTRBThumb, + "strb<c> <Rt>, [<Rn>, #<imm5>]"}, + {0xfff00000, 0xf8800000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRBThumb, + "strb<c>.w <Rt>, [<Rn>, #<imm12>]"}, + {0xfff00800, 0xf8000800, ARMV6T2_ABOVE, eEncodingT3, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRBThumb, + "strb<c> <Rt> ,[<Rn>, #+/-<imm8>]{!}"}, + {0xfffffe00, 0x00005200, ARMV4T_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSTRHRegister, "strh<c> <Rt>,[<Rn>,<Rm>]"}, + {0xfff00fc0, 0xf8200000, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRHRegister, + "strh<c>.w <Rt>,[<Rn>,<Rm>{,LSL #<imm2>}]"}, + {0xfff00000, 0xe8400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTREX, + "strex<c> <Rd>, <Rt>, [<Rn{,#<imm>}]"}, + {0xfe500000, 0xe8400000, ARMV6T2_ABOVE, eEncodingT1, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSTRDImm, + "strd<c> <Rt>, <Rt2>, [<Rn>, #+/-<imm>]!"}, + {0xfe100f00, 0xec000b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32, + &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!}, <list>"}, + {0xfea00f00, 0xec000a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32, + &EmulateInstructionARM::EmulateVSTM, "vstm{mode}<c> <Rn>{!}, <list>"}, + {0xff300f00, 0xed000b00, ARMvAll, eEncodingT1, VFPv2_ABOVE, eSize32, + &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Dd>, [<Rn>{,#+/-<imm>}]"}, + {0xff300f00, 0xed000a00, ARMvAll, eEncodingT2, VFPv2v3, eSize32, + &EmulateInstructionARM::EmulateVSTR, "vstr<c> <Sd>, [<Rn>{,#+/-<imm>}]"}, + {0xffb00000, 0xf9000000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32, + &EmulateInstructionARM::EmulateVST1Multiple, + "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + {0xffb00300, 0xf9800000, ARMvAll, eEncodingT1, AdvancedSIMD, eSize32, + &EmulateInstructionARM::EmulateVST1Single, + "vst1<c>.<size> <list>, [<Rn>{@<align>}], <Rm>"}, + + // Other instructions + {0xffffffc0, 0x0000b240, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSXTB, "sxtb<c> <Rd>,<Rm>"}, + {0xfffff080, 0xfa4ff080, ARMV6_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSXTB, "sxtb<c>.w <Rd>,<Rm>{,<rotation>}"}, + {0xffffffc0, 0x0000b200, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateSXTH, "sxth<c> <Rd>,<Rm>"}, + {0xfffff080, 0xfa0ff080, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateSXTH, "sxth<c>.w <Rd>,<Rm>{,<rotation>}"}, + {0xffffffc0, 0x0000b2c0, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateUXTB, "uxtb<c> <Rd>,<Rm>"}, + {0xfffff080, 0xfa5ff080, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateUXTB, "uxtb<c>.w <Rd>,<Rm>{,<rotation>}"}, + {0xffffffc0, 0x0000b280, ARMV6_ABOVE, eEncodingT1, No_VFP, eSize16, + &EmulateInstructionARM::EmulateUXTH, "uxth<c> <Rd>,<Rm>"}, + {0xfffff080, 0xfa1ff080, ARMV6T2_ABOVE, eEncodingT2, No_VFP, eSize32, + &EmulateInstructionARM::EmulateUXTH, "uxth<c>.w <Rd>,<Rm>{,<rotation>}"}, + }; + + const size_t k_num_thumb_opcodes = std::size(g_thumb_opcodes); + for (size_t i = 0; i < k_num_thumb_opcodes; ++i) { + if ((g_thumb_opcodes[i].mask & opcode) == g_thumb_opcodes[i].value && + (g_thumb_opcodes[i].variants & arm_isa) != 0) + return &g_thumb_opcodes[i]; + } + return nullptr; +} + +bool EmulateInstructionARM::SetArchitecture(const ArchSpec &arch) { + m_arch = arch; + m_arm_isa = 0; + llvm::StringRef arch_cstr = arch.GetArchitectureName(); + if (arch_cstr.equals_insensitive("armv4t")) + m_arm_isa = ARMv4T; + else if (arch_cstr.equals_insensitive("armv5tej")) + m_arm_isa = ARMv5TEJ; + else if (arch_cstr.equals_insensitive("armv5te")) + m_arm_isa = ARMv5TE; + else if (arch_cstr.equals_insensitive("armv5t")) + m_arm_isa = ARMv5T; + else if (arch_cstr.equals_insensitive("armv6k")) + m_arm_isa = ARMv6K; + else if (arch_cstr.equals_insensitive("armv6t2")) + m_arm_isa = ARMv6T2; + else if (arch_cstr.equals_insensitive("armv7s")) + m_arm_isa = ARMv7S; + else if (arch_cstr.equals_insensitive("arm")) + m_arm_isa = ARMvAll; + else if (arch_cstr.equals_insensitive("thumb")) + m_arm_isa = ARMvAll; + else if (arch_cstr.starts_with_insensitive("armv4")) + m_arm_isa = ARMv4; + else if (arch_cstr.starts_with_insensitive("armv6")) + m_arm_isa = ARMv6; + else if (arch_cstr.starts_with_insensitive("armv7")) + m_arm_isa = ARMv7; + else if (arch_cstr.starts_with_insensitive("armv8")) + m_arm_isa = ARMv8; + return m_arm_isa != 0; +} + +bool EmulateInstructionARM::SetInstruction(const Opcode &insn_opcode, + const Address &inst_addr, + Target *target) { + if (EmulateInstruction::SetInstruction(insn_opcode, inst_addr, target)) { + if (m_arch.GetTriple().getArch() == llvm::Triple::thumb || + m_arch.IsAlwaysThumbInstructions()) + m_opcode_mode = eModeThumb; + else { + AddressClass addr_class = inst_addr.GetAddressClass(); + + if ((addr_class == AddressClass::eCode) || + (addr_class == AddressClass::eUnknown)) + m_opcode_mode = eModeARM; + else if (addr_class == AddressClass::eCodeAlternateISA) + m_opcode_mode = eModeThumb; + else + return false; + } + if (m_opcode_mode == eModeThumb || m_arch.IsAlwaysThumbInstructions()) + m_opcode_cpsr = CPSR_MODE_USR | MASK_CPSR_T; + else + m_opcode_cpsr = CPSR_MODE_USR; + return true; + } + return false; +} + +bool EmulateInstructionARM::ReadInstruction() { + bool success = false; + m_opcode_cpsr = ReadRegisterUnsigned(eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_FLAGS, 0, &success); + if (success) { + addr_t pc = + ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, + LLDB_INVALID_ADDRESS, &success); + if (success) { + Context read_inst_context; + read_inst_context.type = eContextReadOpcode; + read_inst_context.SetNoArgs(); + + if ((m_opcode_cpsr & MASK_CPSR_T) || m_arch.IsAlwaysThumbInstructions()) { + m_opcode_mode = eModeThumb; + uint32_t thumb_opcode = MemARead(read_inst_context, pc, 2, 0, &success); + + if (success) { + if ((thumb_opcode & 0xe000) != 0xe000 || + ((thumb_opcode & 0x1800u) == 0)) { + m_opcode.SetOpcode16(thumb_opcode, GetByteOrder()); + } else { + m_opcode.SetOpcode32( + (thumb_opcode << 16) | + MemARead(read_inst_context, pc + 2, 2, 0, &success), + GetByteOrder()); + } + } + } else { + m_opcode_mode = eModeARM; + m_opcode.SetOpcode32(MemARead(read_inst_context, pc, 4, 0, &success), + GetByteOrder()); + } + + if (!m_ignore_conditions) { + // If we are not ignoreing the conditions then init the it session from + // the current value of cpsr. + uint32_t it = (Bits32(m_opcode_cpsr, 15, 10) << 2) | + Bits32(m_opcode_cpsr, 26, 25); + if (it != 0) + m_it_session.InitIT(it); + } + } + } + if (!success) { + m_opcode_mode = eModeInvalid; + m_addr = LLDB_INVALID_ADDRESS; + } + return success; +} + +uint32_t EmulateInstructionARM::ArchVersion() { return m_arm_isa; } + +bool EmulateInstructionARM::ConditionPassed(const uint32_t opcode) { + // If we are ignoring conditions, then always return true. this allows us to + // iterate over disassembly code and still emulate an instruction even if we + // don't have all the right bits set in the CPSR register... + if (m_ignore_conditions) + return true; + + const uint32_t cond = CurrentCond(opcode); + if (cond == UINT32_MAX) + return false; + + bool result = false; + switch (UnsignedBits(cond, 3, 1)) { + case 0: + if (m_opcode_cpsr == 0) + result = true; + else + result = (m_opcode_cpsr & MASK_CPSR_Z) != 0; + break; + case 1: + if (m_opcode_cpsr == 0) + result = true; + else + result = (m_opcode_cpsr & MASK_CPSR_C) != 0; + break; + case 2: + if (m_opcode_cpsr == 0) + result = true; + else + result = (m_opcode_cpsr & MASK_CPSR_N) != 0; + break; + case 3: + if (m_opcode_cpsr == 0) + result = true; + else + result = (m_opcode_cpsr & MASK_CPSR_V) != 0; + break; + case 4: + if (m_opcode_cpsr == 0) + result = true; + else + result = ((m_opcode_cpsr & MASK_CPSR_C) != 0) && + ((m_opcode_cpsr & MASK_CPSR_Z) == 0); + break; + case 5: + if (m_opcode_cpsr == 0) + result = true; + else { + bool n = (m_opcode_cpsr & MASK_CPSR_N); + bool v = (m_opcode_cpsr & MASK_CPSR_V); + result = n == v; + } + break; + case 6: + if (m_opcode_cpsr == 0) + result = true; + else { + bool n = (m_opcode_cpsr & MASK_CPSR_N); + bool v = (m_opcode_cpsr & MASK_CPSR_V); + result = n == v && ((m_opcode_cpsr & MASK_CPSR_Z) == 0); + } + break; + case 7: + // Always execute (cond == 0b1110, or the special 0b1111 which gives + // opcodes different meanings, but always means execution happens. + return true; + } + + if (cond & 1) + result = !result; + return result; +} + +uint32_t EmulateInstructionARM::CurrentCond(const uint32_t opcode) { + switch (m_opcode_mode) { + case eModeInvalid: + break; + + case eModeARM: + return UnsignedBits(opcode, 31, 28); + + case eModeThumb: + // For T1 and T3 encodings of the Branch instruction, it returns the 4-bit + // 'cond' field of the encoding. + { + const uint32_t byte_size = m_opcode.GetByteSize(); + if (byte_size == 2) { + if (Bits32(opcode, 15, 12) == 0x0d && Bits32(opcode, 11, 8) != 0x0f) + return Bits32(opcode, 11, 8); + } else if (byte_size == 4) { + if (Bits32(opcode, 31, 27) == 0x1e && Bits32(opcode, 15, 14) == 0x02 && + Bits32(opcode, 12, 12) == 0x00 && Bits32(opcode, 25, 22) <= 0x0d) { + return Bits32(opcode, 25, 22); + } + } else + // We have an invalid thumb instruction, let's bail out. + break; + + return m_it_session.GetCond(); + } + } + return UINT32_MAX; // Return invalid value +} + +bool EmulateInstructionARM::InITBlock() { + return CurrentInstrSet() == eModeThumb && m_it_session.InITBlock(); +} + +bool EmulateInstructionARM::LastInITBlock() { + return CurrentInstrSet() == eModeThumb && m_it_session.LastInITBlock(); +} + +bool EmulateInstructionARM::BadMode(uint32_t mode) { + + switch (mode) { + case 16: + return false; // '10000' + case 17: + return false; // '10001' + case 18: + return false; // '10010' + case 19: + return false; // '10011' + case 22: + return false; // '10110' + case 23: + return false; // '10111' + case 27: + return false; // '11011' + case 31: + return false; // '11111' + default: + return true; + } + return true; +} + +bool EmulateInstructionARM::CurrentModeIsPrivileged() { + uint32_t mode = Bits32(m_opcode_cpsr, 4, 0); + + if (BadMode(mode)) + return false; + + if (mode == 16) + return false; + + return true; +} + +void EmulateInstructionARM::CPSRWriteByInstr(uint32_t value, uint32_t bytemask, + bool affect_execstate) { + bool privileged = CurrentModeIsPrivileged(); + + uint32_t tmp_cpsr = Bits32(m_opcode_cpsr, 23, 20) << 20; + + if (BitIsSet(bytemask, 3)) { + tmp_cpsr = tmp_cpsr | (Bits32(value, 31, 27) << 27); + if (affect_execstate) + tmp_cpsr = tmp_cpsr | (Bits32(value, 26, 24) << 24); + } + + if (BitIsSet(bytemask, 2)) { + tmp_cpsr = tmp_cpsr | (Bits32(value, 19, 16) << 16); + } + + if (BitIsSet(bytemask, 1)) { + if (affect_execstate) + tmp_cpsr = tmp_cpsr | (Bits32(value, 15, 10) << 10); + tmp_cpsr = tmp_cpsr | (Bit32(value, 9) << 9); + if (privileged) + tmp_cpsr = tmp_cpsr | (Bit32(value, 8) << 8); + } + + if (BitIsSet(bytemask, 0)) { + if (privileged) + tmp_cpsr = tmp_cpsr | (Bits32(value, 7, 6) << 6); + if (affect_execstate) + tmp_cpsr = tmp_cpsr | (Bit32(value, 5) << 5); + if (privileged) + tmp_cpsr = tmp_cpsr | Bits32(value, 4, 0); + } + + m_opcode_cpsr = tmp_cpsr; +} + +bool EmulateInstructionARM::BranchWritePC(const Context &context, + uint32_t addr) { + addr_t target; + + // Check the current instruction set. + if (CurrentInstrSet() == eModeARM) + target = addr & 0xfffffffc; + else + target = addr & 0xfffffffe; + + return WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_PC, target); +} + +// As a side effect, BXWritePC sets context.arg2 to eModeARM or eModeThumb by +// inspecting addr. +bool EmulateInstructionARM::BXWritePC(Context &context, uint32_t addr) { + addr_t target; + // If the CPSR is changed due to switching between ARM and Thumb ISETSTATE, + // we want to record it and issue a WriteRegister callback so the clients can + // track the mode changes accordingly. + bool cpsr_changed = false; + + if (BitIsSet(addr, 0)) { + if (CurrentInstrSet() != eModeThumb) { + SelectInstrSet(eModeThumb); + cpsr_changed = true; + } + target = addr & 0xfffffffe; + context.SetISA(eModeThumb); + } else if (BitIsClear(addr, 1)) { + if (CurrentInstrSet() != eModeARM) { + SelectInstrSet(eModeARM); + cpsr_changed = true; + } + target = addr & 0xfffffffc; + context.SetISA(eModeARM); + } else + return false; // address<1:0> == '10' => UNPREDICTABLE + + if (cpsr_changed) { + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr)) + return false; + } + return WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_PC, target); +} + +// Dispatches to either BXWritePC or BranchWritePC based on architecture +// versions. +bool EmulateInstructionARM::LoadWritePC(Context &context, uint32_t addr) { + if (ArchVersion() >= ARMv5T) + return BXWritePC(context, addr); + else + return BranchWritePC((const Context)context, addr); +} + +// Dispatches to either BXWritePC or BranchWritePC based on architecture +// versions and current instruction set. +bool EmulateInstructionARM::ALUWritePC(Context &context, uint32_t addr) { + if (ArchVersion() >= ARMv7 && CurrentInstrSet() == eModeARM) + return BXWritePC(context, addr); + else + return BranchWritePC((const Context)context, addr); +} + +EmulateInstructionARM::Mode EmulateInstructionARM::CurrentInstrSet() { + return m_opcode_mode; +} + +// Set the 'T' bit of our CPSR. The m_opcode_mode gets updated when the next +// ReadInstruction() is performed. This function has a side effect of updating +// the m_new_inst_cpsr member variable if necessary. +bool EmulateInstructionARM::SelectInstrSet(Mode arm_or_thumb) { + m_new_inst_cpsr = m_opcode_cpsr; + switch (arm_or_thumb) { + default: + return false; + case eModeARM: + // Clear the T bit. + m_new_inst_cpsr &= ~MASK_CPSR_T; + break; + case eModeThumb: + // Set the T bit. + m_new_inst_cpsr |= MASK_CPSR_T; + break; + } + return true; +} + +// This function returns TRUE if the processor currently provides support for +// unaligned memory accesses, or FALSE otherwise. This is always TRUE in ARMv7, +// controllable by the SCTLR.U bit in ARMv6, and always FALSE before ARMv6. +bool EmulateInstructionARM::UnalignedSupport() { + return (ArchVersion() >= ARMv7); +} + +// The main addition and subtraction instructions can produce status +// information about both unsigned carry and signed overflow conditions. This +// status information can be used to synthesize multi-word additions and +// subtractions. +EmulateInstructionARM::AddWithCarryResult +EmulateInstructionARM::AddWithCarry(uint32_t x, uint32_t y, uint8_t carry_in) { + uint32_t result; + uint8_t carry_out; + uint8_t overflow; + + uint64_t unsigned_sum = x + y + carry_in; + int64_t signed_sum = (int32_t)x + (int32_t)y + (int32_t)carry_in; + + result = UnsignedBits(unsigned_sum, 31, 0); + // carry_out = (result == unsigned_sum ? 0 : 1); + overflow = ((int32_t)result == signed_sum ? 0 : 1); + + if (carry_in) + carry_out = ((int32_t)x >= (int32_t)(~y)) ? 1 : 0; + else + carry_out = ((int32_t)x > (int32_t)y) ? 1 : 0; + + AddWithCarryResult res = {result, carry_out, overflow}; + return res; +} + +uint32_t EmulateInstructionARM::ReadCoreReg(uint32_t num, bool *success) { + lldb::RegisterKind reg_kind; + uint32_t reg_num; + switch (num) { + case SP_REG: + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_SP; + break; + case LR_REG: + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_RA; + break; + case PC_REG: + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_PC; + break; + default: + if (num < SP_REG) { + reg_kind = eRegisterKindDWARF; + reg_num = dwarf_r0 + num; + } else { + // assert(0 && "Invalid register number"); + *success = false; + return UINT32_MAX; + } + break; + } + + // Read our register. + uint32_t val = ReadRegisterUnsigned(reg_kind, reg_num, 0, success); + + // When executing an ARM instruction , PC reads as the address of the current + // instruction plus 8. When executing a Thumb instruction , PC reads as the + // address of the current instruction plus 4. + if (num == 15) { + if (CurrentInstrSet() == eModeARM) + val += 8; + else + val += 4; + } + + return val; +} + +// Write the result to the ARM core register Rd, and optionally update the +// condition flags based on the result. +// +// This helper method tries to encapsulate the following pseudocode from the +// ARM Architecture Reference Manual: +// +// if d == 15 then // Can only occur for encoding A1 +// ALUWritePC(result); // setflags is always FALSE here +// else +// R[d] = result; +// if setflags then +// APSR.N = result<31>; +// APSR.Z = IsZeroBit(result); +// APSR.C = carry; +// // APSR.V unchanged +// +// In the above case, the API client does not pass in the overflow arg, which +// defaults to ~0u. +bool EmulateInstructionARM::WriteCoreRegOptionalFlags( + Context &context, const uint32_t result, const uint32_t Rd, bool setflags, + const uint32_t carry, const uint32_t overflow) { + if (Rd == 15) { + if (!ALUWritePC(context, result)) + return false; + } else { + lldb::RegisterKind reg_kind; + uint32_t reg_num; + switch (Rd) { + case SP_REG: + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_SP; + break; + case LR_REG: + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_RA; + break; + default: + reg_kind = eRegisterKindDWARF; + reg_num = dwarf_r0 + Rd; + } + if (!WriteRegisterUnsigned(context, reg_kind, reg_num, result)) + return false; + if (setflags) + return WriteFlags(context, result, carry, overflow); + } + return true; +} + +// This helper method tries to encapsulate the following pseudocode from the +// ARM Architecture Reference Manual: +// +// APSR.N = result<31>; +// APSR.Z = IsZeroBit(result); +// APSR.C = carry; +// APSR.V = overflow +// +// Default arguments can be specified for carry and overflow parameters, which +// means not to update the respective flags. +bool EmulateInstructionARM::WriteFlags(Context &context, const uint32_t result, + const uint32_t carry, + const uint32_t overflow) { + m_new_inst_cpsr = m_opcode_cpsr; + SetBit32(m_new_inst_cpsr, CPSR_N_POS, Bit32(result, CPSR_N_POS)); + SetBit32(m_new_inst_cpsr, CPSR_Z_POS, result == 0 ? 1 : 0); + if (carry != ~0u) + SetBit32(m_new_inst_cpsr, CPSR_C_POS, carry); + if (overflow != ~0u) + SetBit32(m_new_inst_cpsr, CPSR_V_POS, overflow); + if (m_new_inst_cpsr != m_opcode_cpsr) { + if (!WriteRegisterUnsigned(context, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr)) + return false; + } + return true; +} + +bool EmulateInstructionARM::EvaluateInstruction(uint32_t evaluate_options) { + ARMOpcode *opcode_data = nullptr; + + if (m_opcode_mode == eModeThumb) + opcode_data = + GetThumbOpcodeForInstruction(m_opcode.GetOpcode32(), m_arm_isa); + else if (m_opcode_mode == eModeARM) + opcode_data = GetARMOpcodeForInstruction(m_opcode.GetOpcode32(), m_arm_isa); + + const bool auto_advance_pc = + evaluate_options & eEmulateInstructionOptionAutoAdvancePC; + m_ignore_conditions = + evaluate_options & eEmulateInstructionOptionIgnoreConditions; + + bool success = false; + if (m_opcode_cpsr == 0 || !m_ignore_conditions) { + m_opcode_cpsr = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_cpsr, 0, &success); + } + + // Only return false if we are unable to read the CPSR if we care about + // conditions + if (!success && !m_ignore_conditions) + return false; + + uint32_t orig_pc_value = 0; + if (auto_advance_pc) { + orig_pc_value = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc, 0, &success); + if (!success) + return false; + } + + // Call the Emulate... function if we managed to decode the opcode. + if (opcode_data) { + success = (this->*opcode_data->callback)(m_opcode.GetOpcode32(), + opcode_data->encoding); + if (!success) + return false; + } + + // Advance the ITSTATE bits to their values for the next instruction if we + // haven't just executed an IT instruction what initialized it. + if (m_opcode_mode == eModeThumb && m_it_session.InITBlock() && + (opcode_data == nullptr || + opcode_data->callback != &EmulateInstructionARM::EmulateIT)) + m_it_session.ITAdvance(); + + if (auto_advance_pc) { + uint32_t after_pc_value = + ReadRegisterUnsigned(eRegisterKindDWARF, dwarf_pc, 0, &success); + if (!success) + return false; + + if (after_pc_value == orig_pc_value) { + after_pc_value += m_opcode.GetByteSize(); + + EmulateInstruction::Context context; + context.type = eContextAdvancePC; + context.SetNoArgs(); + if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_pc, + after_pc_value)) + return false; + } + } + return true; +} + +EmulateInstruction::InstructionCondition +EmulateInstructionARM::GetInstructionCondition() { + const uint32_t cond = CurrentCond(m_opcode.GetOpcode32()); + if (cond == 0xe || cond == 0xf || cond == UINT32_MAX) + return EmulateInstruction::UnconditionalCondition; + return cond; +} + +bool EmulateInstructionARM::TestEmulation(Stream &out_stream, ArchSpec &arch, + OptionValueDictionary *test_data) { + if (!test_data) { + out_stream.Printf("TestEmulation: Missing test data.\n"); + return false; + } + + static constexpr llvm::StringLiteral opcode_key("opcode"); + static constexpr llvm::StringLiteral before_key("before_state"); + static constexpr llvm::StringLiteral after_key("after_state"); + + OptionValueSP value_sp = test_data->GetValueForKey(opcode_key); + + uint32_t test_opcode; + if ((value_sp.get() == nullptr) || + (value_sp->GetType() != OptionValue::eTypeUInt64)) { + out_stream.Printf("TestEmulation: Error reading opcode from test file.\n"); + return false; + } + test_opcode = value_sp->GetValueAs<uint64_t>().value_or(0); + + if (arch.GetTriple().getArch() == llvm::Triple::thumb || + arch.IsAlwaysThumbInstructions()) { + m_opcode_mode = eModeThumb; + if (test_opcode < 0x10000) + m_opcode.SetOpcode16(test_opcode, endian::InlHostByteOrder()); + else + m_opcode.SetOpcode32(test_opcode, endian::InlHostByteOrder()); + } else if (arch.GetTriple().getArch() == llvm::Triple::arm) { + m_opcode_mode = eModeARM; + m_opcode.SetOpcode32(test_opcode, endian::InlHostByteOrder()); + } else { + out_stream.Printf("TestEmulation: Invalid arch.\n"); + return false; + } + + EmulationStateARM before_state; + EmulationStateARM after_state; + + value_sp = test_data->GetValueForKey(before_key); + if ((value_sp.get() == nullptr) || + (value_sp->GetType() != OptionValue::eTypeDictionary)) { + out_stream.Printf("TestEmulation: Failed to find 'before' state.\n"); + return false; + } + + OptionValueDictionary *state_dictionary = value_sp->GetAsDictionary(); + if (!before_state.LoadStateFromDictionary(state_dictionary)) { + out_stream.Printf("TestEmulation: Failed loading 'before' state.\n"); + return false; + } + + value_sp = test_data->GetValueForKey(after_key); + if ((value_sp.get() == nullptr) || + (value_sp->GetType() != OptionValue::eTypeDictionary)) { + out_stream.Printf("TestEmulation: Failed to find 'after' state.\n"); + return false; + } + + state_dictionary = value_sp->GetAsDictionary(); + if (!after_state.LoadStateFromDictionary(state_dictionary)) { + out_stream.Printf("TestEmulation: Failed loading 'after' state.\n"); + return false; + } + + SetBaton((void *)&before_state); + SetCallbacks(&EmulationStateARM::ReadPseudoMemory, + &EmulationStateARM::WritePseudoMemory, + &EmulationStateARM::ReadPseudoRegister, + &EmulationStateARM::WritePseudoRegister); + + bool success = EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC); + if (!success) { + out_stream.Printf("TestEmulation: EvaluateInstruction() failed.\n"); + return false; + } + + success = before_state.CompareState(after_state, out_stream); + if (!success) + out_stream.Printf("TestEmulation: State after emulation does not match " + "'after' state.\n"); + + return success; +} +// +// +// const char * +// EmulateInstructionARM::GetRegisterName (uint32_t reg_kind, uint32_t reg_num) +//{ +// if (reg_kind == eRegisterKindGeneric) +// { +// switch (reg_num) +// { +// case LLDB_REGNUM_GENERIC_PC: return "pc"; +// case LLDB_REGNUM_GENERIC_SP: return "sp"; +// case LLDB_REGNUM_GENERIC_FP: return "fp"; +// case LLDB_REGNUM_GENERIC_RA: return "lr"; +// case LLDB_REGNUM_GENERIC_FLAGS: return "cpsr"; +// default: return NULL; +// } +// } +// else if (reg_kind == eRegisterKindDWARF) +// { +// return GetARMDWARFRegisterName (reg_num); +// } +// return NULL; +//} +// +bool EmulateInstructionARM::CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) { + unwind_plan.Clear(); + unwind_plan.SetRegisterKind(eRegisterKindDWARF); + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + // Our previous Call Frame Address is the stack pointer + row->GetCFAValue().SetIsRegisterPlusOffset(dwarf_sp, 0); + + unwind_plan.AppendRow(row); + unwind_plan.SetSourceName("EmulateInstructionARM"); + unwind_plan.SetSourcedFromCompiler(eLazyBoolNo); + unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); + unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); + unwind_plan.SetReturnAddressRegister(dwarf_lr); + return true; +} |