//===-- EmulateInstructionARM.cpp -------------------------------*- C++ -*-===//
//
// 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 <stdlib.h>
#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/ConstString.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;
// 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 bool GetARMDWARFRegisterInfo(unsigned reg_num, RegisterInfo ®_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 false;
}
return true;
}
// 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::countTrailingZeros(ITMask);
if (TZ > 3) {
#ifdef LLDB_CONFIGURATION_DEBUG
printf("Encoding error: IT Mask '0000'\n");
#endif
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) {
#ifdef LLDB_CONFIGURATION_DEBUG
printf("Encoding error: IT FirstCond '1111'\n");
#endif
return false;
}
if (FirstCond == 0xE && ITCounter != 1) {
#ifdef LLDB_CONFIGURATION_DEBUG
printf("Encoding error: IT FirstCond '1110' && Mask != '1000'\n");
#endif
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);
}
ConstString EmulateInstructionARM::GetPluginNameStatic() {
static ConstString g_name("arm");
return g_name;
}
const char *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;
}
bool EmulateInstructionARM::GetRegisterInfo(lldb::RegisterKind reg_kind,
uint32_t reg_num,
RegisterInfo ®_info) {
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 false;
}
}
if (reg_kind == eRegisterKindDWARF)
return GetARMDWARFRegisterInfo(reg_num, reg_info);
return false;
}
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:
// NEED_BRIDGEOS_TRIPLE 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.
*/
uint32_t fp_regnum = 11;
if (is_apple)
fp_regnum = 7;
if (m_opcode_mode == eModeThumb)
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.
*/
uint32_t fp_regnum = dwarf_r11;
if (is_apple)
fp_regnum = dwarf_r7;
if (m_opcode_mode == eModeThumb)
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;
RegisterInfo reg_info;
RegisterInfo sp_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
for (i = 0; i < 15; ++i) {
if (BitIsSet(registers, i)) {
GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + i, reg_info);
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)) {
GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, reg_info);
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;
RegisterInfo sp_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
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;
RegisterInfo sp_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
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;
RegisterInfo sp_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
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
context.type = EmulateInstruction::eContextRegisterPlusOffset;
RegisterInfo dwarf_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
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>;
RegisterInfo op1_reg;
RegisterInfo op2_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, op1_reg);
GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + m, op2_reg);
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;
RegisterInfo pc_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_pc, pc_reg);
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;
RegisterInfo sp_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
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;
RegisterInfo sp_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
RegisterInfo other_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, other_reg);
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;
RegisterInfo dwarf_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
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;
RegisterInfo dwarf_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
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;
RegisterInfo dwarf_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
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;
RegisterInfo dwarf_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_r12, dwarf_reg);
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;
RegisterInfo dwarf_reg;
GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, dwarf_reg);
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;
RegisterInfo sp_reg;
RegisterInfo dwarf_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rt, dwarf_reg);
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;
RegisterInfo dwarf_reg;
RegisterInfo sp_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
for (i = 0; i < regs; ++i) {
GetRegisterInfo(eRegisterKindDWARF, start_reg + d + i, dwarf_reg);
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;
RegisterInfo dwarf_reg;
RegisterInfo sp_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_sp, sp_reg);
for (i = 0; i < regs; ++i) {
GetRegisterInfo(eRegisterKindDWARF, start_reg + d + i, dwarf_reg);
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);
RegisterInfo reg_n;
GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + n, reg_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;
RegisterInfo dwarf_reg;
GetRegisterInfo(eRegisterKindDWARF, Rn, dwarf_reg);
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;
RegisterInfo op1_reg;
RegisterInfo op2_reg;
GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rn, op1_reg);
GetRegisterInfo(eRegisterKindDWARF, dwarf_r0 + Rm, op2_reg);
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
|