Files
llvm/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp
Greg Clayton e5b3498eef Added the start of the CFI row production using the
emulate instruction classes.

llvm-svn: 130556
2011-04-29 22:50:31 +00:00

13440 lines
503 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//===-- EmulateInstructionARM.cpp -------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <stdlib.h>
#include "EmulateInstructionARM.h"
#include "EmulationStateARM.h"
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/ConstString.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Stream.h"
#include "lldb/Symbol/UnwindPlan.h"
#include "Plugins/Process/Utility/ARMDefines.h"
#include "Plugins/Process/Utility/ARMUtils.h"
#include "Utility/ARM_DWARF_Registers.h"
#include "llvm/Support/MathExtras.h" // for SignExtend32 template function
// and CountTrailingZeros_32 function
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
//
//----------------------------------------------------------------------
// A8.6.50
// Valid return values are {1, 2, 3, 4}, with 0 signifying an error condition.
static unsigned short CountITSize(unsigned ITMask) {
// First count the trailing zeros of the IT mask.
unsigned TZ = llvm::CountTrailingZeros_32(ITMask);
if (TZ > 3)
{
printf("Encoding error: IT Mask '0000'\n");
return 0;
}
return (4 - TZ);
}
// Init ITState. Note that at least one bit is always 1 in mask.
bool ITSession::InitIT(unsigned short 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)
{
printf("Encoding error: IT FirstCond '1111'\n");
return false;
}
if (FirstCond == 0xE && ITCounter != 1)
{
printf("Encoding error: IT FirstCond '1110' && Mask != '1000'\n");
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 ARMv8 (1u << 9)
#define ARMvAll (0xffffffffu)
#define ARMV4T_ABOVE (ARMv4T|ARMv5T|ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv8)
#define ARMV5_ABOVE (ARMv5T|ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv8)
#define ARMV5TE_ABOVE (ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv8)
#define ARMV5J_ABOVE (ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv8)
#define ARMV6_ABOVE (ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv8)
#define ARMV6T2_ABOVE (ARMv6T2|ARMv7|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);
}
const char *
EmulateInstructionARM::GetPluginNameStatic ()
{
return "lldb.emulate-instruction.arm";
}
const char *
EmulateInstructionARM::GetPluginDescriptionStatic ()
{
return "Emulate instructions for the ARM architecture.";
}
EmulateInstruction *
EmulateInstructionARM::CreateInstance (const ArchSpec &arch, InstructionType inst_type)
{
if (EmulateInstructionARM::SupportsEmulatingIntructionsOfTypeStatic(inst_type))
{
if (arch.GetTriple().getArch() == llvm::Triple::arm)
{
std::auto_ptr<EmulateInstructionARM> emulate_insn_ap (new EmulateInstructionARM (arch));
if (emulate_insn_ap.get())
return emulate_insn_ap.release();
}
else if (arch.GetTriple().getArch() == llvm::Triple::thumb)
{
std::auto_ptr<EmulateInstructionARM> emulate_insn_ap (new EmulateInstructionARM (arch));
if (emulate_insn_ap.get())
return emulate_insn_ap.release();
}
}
return NULL;
}
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();
if (!MemAWrite (context, address, random_data, addr_byte_size))
return false;
return true;
}
// 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 (uint32_t reg_kind, uint32_t reg_num, RegisterInfo &reg_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;
}
// 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.SetRegisterPlusOffset (reg_info, 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.SetRegisterPlusOffset (sp_reg, addr - sp);
data = MemARead(context, addr, 4, 0, &success);
if (!success)
return false;
RegisterInfo reg_info;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, reg_info);
if (!WriteRegisterUnsigned(context, reg_info, 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;
context.type = EmulateInstruction::eContextAdjustStackPointer;
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;
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;
context.type = EmulateInstruction::eContextRegisterLoad;
RegisterInfo dwarf_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg);
context.SetRegister (dwarf_reg);
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; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C.
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);
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);
setflags = false;
imm32 = (Bits32 (opcode, 7, 0) << 2);
break;
case eEncodingT2:
// d = 13; setflags = FALSE; imm32 = ZeroExtend(imm7:'00', 32);
d = 13;
setflags = false;
imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32)
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::eContextAdjustStackPointer;
RegisterInfo sp_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg);
context.SetRegisterPlusOffset (sp_reg, sp_offset);
if (d == 15)
{
if (!ALUWritePC (context, addr))
return false;
}
else
{
if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, addr))
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;
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;
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;
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;
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;
}
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;
GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg);
context.SetRegisterPlusOffset (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 accomodate 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.SetRegisterPlusOffset (sp_reg, addr - sp);
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;
}
// 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));
target = pc + imm32;
context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32);
break;
case eEncodingT3:
// The 'cond' field is handled in EmulateInstructionARM::CurrentCond().
{
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 (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;
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(Rm, &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);
// if Rn == '1101' then SEE ADD (SP plus 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 BadReg(d) || n == 15 then UNPREDICTABLE;
if (BadReg (d) || (n == 15))
return false;
break;
case eEncodingT4:
{
// if Rn == '1111' then SEE ADR;
// if Rn == '1101' then SEE ADD (SP plus immediate);
// 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 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;
context.type = eContextArithmetic;
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);
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
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 ();
if (!WriteFlags(context, res.result, res.carry_out, res.overflow))
return false;
return true;
}
// 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);
AddWithCarryResult res = AddWithCarry(val1, shifted, 0);
EmulateInstruction::Context context;
context.type = EmulateInstruction::eContextImmediate;
context.SetNoArgs();
if (!WriteFlags(context, res.result, res.carry_out, res.overflow))
return false;
return true;
}
// 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 ();
if (!WriteFlags(context, res.result, res.carry_out, res.overflow))
return false;
return true;
}
// 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 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);
AddWithCarryResult res = AddWithCarry(val1, ~shifted, 1);
EmulateInstruction::Context context;
context.type = EmulateInstruction::eContextImmediate;
context.SetNoArgs();
if (!WriteFlags(context, res.result, res.carry_out, res.overflow))
return false;
return true;
}
// 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:
// Due to the above special case handling!
assert(shift_type != SRType_ROR);
Rd = Bits32(opcode, 2, 0);
Rm = Bits32(opcode, 5, 3);
setflags = !InITBlock();
imm5 = Bits32(opcode, 10, 6);
break;
case eEncodingT2:
// A8.6.141 RRX
assert(shift_type != SRType_RRX);
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);
// 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);
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);
// 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;
RegisterInfo dwarf_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg);
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;
// 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;
RegisterInfo dwarf_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg);
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))
{
if (!success)
return false;
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 lcoations 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;
RegisterInfo dwarf_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg);
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))
{
if (!success)
return false;
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;
RegisterInfo dwarf_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg);
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))
{
if (!success)
return false;
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);
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rn, base_reg);
if (wback)
{
EmulateInstruction::Context ctx;
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 teh 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;
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
// for i = 0 to 14
int lowest_set_bit = 14;
for (int 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;
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg);
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))
{
RegisterInfo pc_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg);
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;
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
// for i = 0 to 14
int lowest_bit_set = 14;
for (int 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;
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg);
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))
{
RegisterInfo pc_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg);
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;
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
// for i = 0 to 14
uint32_t lowest_set_bit = 14;
for (int 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;
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg);
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))
{
RegisterInfo pc_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg);
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;
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
uint32_t lowest_set_bit = 14;
// for i = 0 to 14
for (int 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;
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg);
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))
{
RegisterInfo pc_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg);
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) calcualtes 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;
context.type = eContextRegisterStore;
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
// 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;
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg);
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)
{
context.type = eContextRegisterLoad;
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);
// 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;
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg);
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>
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg);
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 alue 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);
// 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;
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
RegisterInfo offset_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
// 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;
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
RegisterInfo offset_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg);
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);
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)
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);
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);
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];
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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));
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 resgister. 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));
// 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];
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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));
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:
// if Rt == '1111' then SEE PLD;
// if Rn == '1111' then SEE LDRB (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 Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLD;
// if Rn == '1111' then SEE LDRB (literal);
// 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 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);
RegisterInfo base_reg;
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg);
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:
// if Rt == '1111' then SEE PLD;
// 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(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:
// if Rt == '1111' then SEE PLD;
// if Rn == '1111' then SEE LDRB (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 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);
// 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);
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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];
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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) caculates 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];
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg);
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);
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];
RegisterInfo base_reg;
RegisterInfo offset_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
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);
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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);
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg);
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);
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);
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
RegisterInfo offset_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
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];
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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];
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg);
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:
break;
}
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);
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];
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
RegisterInfo offset_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
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);
// R[d] = SignExtend(rotated<7:0>, 32);
int64_t data = llvm::SignExtend64<8>(rotated);
RegisterInfo source_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg);
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);
// R[d] = SignExtend(rotated<15:0>, 32);
RegisterInfo source_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg);
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-extneds 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);
// R[d] = ZeroExtend(rotated<7:0>, 32);
RegisterInfo source_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg);
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);
// R[d] = ZeroExtend(rotated<15:0>, 32);
RegisterInfo source_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg);
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);
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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);
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);
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 ();
if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow))
return false;
return true;
}
// 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);
AddWithCarryResult res = AddWithCarry(~val1, shifted, 1);
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;
}
// 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 ();
if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow))
return false;
return true;
}
// 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);
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;
}
// 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 ();
if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow))
return false;
return true;
}
// 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);
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 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 ();
if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow))
return false;
return true;
}
// 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;
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;
context.type = EmulateInstruction::eContextImmediate;
context.SetNoArgs ();
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);
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);
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);
// (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;
RegisterInfo sp_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg);
RegisterInfo dwarf_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, dwarf_reg);
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) || (m == 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);
// (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;
RegisterInfo reg_n;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, reg_n);
RegisterInfo reg_m;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, reg_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:
// if Rd == 1111 && S == 1 then SEE CMP (register);
// if Rn == 1101 then SEE SUB (SP minus register);
// 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);
// (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);
// (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;
RegisterInfo reg_n;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, reg_n);
RegisterInfo reg_m;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, reg_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;
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg);
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;
}
else
{
// R[d] = 1;
if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 1))
return false;
}
}
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;
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg);
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;
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg);
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];
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
EmulateInstruction::Context context;
context.type = eContextRegisterLoad;
context.SetRegisterPlusOffset (base_reg, address - Rn);
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.SetRegisterPlusOffset (base_reg, (address + 4) - Rn);
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;
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
uint32_t Rm = ReadCoreReg (m, &success);
if (!success)
return false;
RegisterInfo offset_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
// 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;
context.type = eContextRegisterLoad;
context.SetRegisterPlusIndirectOffset (base_reg, offset_reg);
// 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;
}
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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];
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg);
uint32_t data = ReadCoreReg (t, &success);
if (!success)
return false;
EmulateInstruction::Context context;
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];
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t2, data_reg);
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)
{
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;
}
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
RegisterInfo offset_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg);
RegisterInfo data_reg;
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;
context.type = eContextRegisterStore;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg);
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;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t2, data_reg);
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;
}
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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;
// 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;
}
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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 (int 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;
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, start_reg + d + r, data_reg);
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;
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, start_reg + d + r, data_reg);
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 instruciton loads a single extension register fronm 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;
}
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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;
}
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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;
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, start_reg + d, data_reg);
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;
}
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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 (int r = 0; r < regs; ++r)
{
// for e = 0 to elements-1
uint64_t assembled_data = 0;
for (int 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;
}
// 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;
}
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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;
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 regsiters, 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 = 0;
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;
}
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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;
}
RegisterInfo data_reg;
context.type = eContextRegisterStore;
// for r = 0 to regs-1
for (int r = 0; r < regs; ++r)
{
GetRegisterInfo (eRegisterKindDWARF, dwarf_d0 + d + r, data_reg);
uint64_t register_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_d0 + d + r, 0, &success);
if (!success)
return false;
// for e = 0 to elements-1
for (int 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;
}
// 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;
}
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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);
RegisterInfo data_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_d0 + d, data_reg);
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:
break;
}
RegisterInfo base_reg;
GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg);
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;
}
// 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;
uint32_t esize = ebytes * 8;
for (int e = 0; e < elements; ++e)
replicated_element = (replicated_element << esize) | Bits64 (word, esize - 1, 0);
// for r = 0 to regs-1
for (int 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);
}
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
//----------------------------------------------------------------------
{ 0x0f000000, 0x0a000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateB, "b #imm24"},
// To resolve ambiguity, "blx <label>" should come before "bl <label>".
{ 0xfe000000, 0xfa000000, ARMV5_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateBLXImmediate, "blx <label>"},
{ 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 = sizeof(g_arm_opcodes)/sizeof(ARMOpcode);
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 NULL;
}
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.
//----------------------------------------------------------------------
{ 0xffffff00, 0x0000bf00, ARMvAll, 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, eEncodingA1, 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>"},
// 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, 0xfa000000, 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 = sizeof(g_thumb_opcodes)/sizeof(ARMOpcode);
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 NULL;
}
bool
EmulateInstructionARM::SetArchitecture (const ArchSpec &arch)
{
m_arch = arch;
m_arm_isa = 0;
const char *arch_cstr = arch.GetArchitectureName ();
if (arch_cstr)
{
if (0 == ::strcasecmp(arch_cstr, "armv4t")) m_arm_isa = ARMv4T;
else if (0 == ::strcasecmp(arch_cstr, "armv4")) m_arm_isa = ARMv4;
else if (0 == ::strcasecmp(arch_cstr, "armv5tej")) m_arm_isa = ARMv5TEJ;
else if (0 == ::strcasecmp(arch_cstr, "armv5te")) m_arm_isa = ARMv5TE;
else if (0 == ::strcasecmp(arch_cstr, "armv5t")) m_arm_isa = ARMv5T;
else if (0 == ::strcasecmp(arch_cstr, "armv6k")) m_arm_isa = ARMv6K;
else if (0 == ::strcasecmp(arch_cstr, "armv6")) m_arm_isa = ARMv6;
else if (0 == ::strcasecmp(arch_cstr, "armv6t2")) m_arm_isa = ARMv6T2;
else if (0 == ::strcasecmp(arch_cstr, "armv7")) m_arm_isa = ARMv7;
else if (0 == ::strcasecmp(arch_cstr, "armv8")) m_arm_isa = ARMv8;
else if (0 == ::strcasecmp(arch_cstr, "arm")) m_arm_isa = ARMvAll;
else if (0 == ::strcasecmp(arch_cstr, "thumb")) m_arm_isa = ARMvAll;
}
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_opcode_mode = eModeThumb;
else
{
AddressClass addr_class = inst_addr.GetAddressClass();
if ((addr_class == eAddressClassCode) || (addr_class == eAddressClassUnknown))
m_opcode_mode = eModeARM;
else if (addr_class == eAddressClassCodeAlternateISA)
m_opcode_mode = eModeThumb;
else
return false;
}
if (m_opcode_mode == eModeThumb)
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_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);
}
else
{
m_opcode.SetOpcode32 ((thumb_opcode << 16) | MemARead(read_inst_context, pc + 2, 2, 0, &success));
}
}
}
else
{
m_opcode_mode = eModeARM;
m_opcode.SetOpcode32 (MemARead(read_inst_context, pc, 4, 0, &success));
}
}
}
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)
return true;
result = (m_opcode_cpsr & MASK_CPSR_Z) != 0;
break;
case 1:
if (m_opcode_cpsr == 0)
return true;
result = (m_opcode_cpsr & MASK_CPSR_C) != 0;
break;
case 2:
if (m_opcode_cpsr == 0)
return true;
result = (m_opcode_cpsr & MASK_CPSR_N) != 0;
break;
case 3:
if (m_opcode_cpsr == 0)
return true;
result = (m_opcode_cpsr & MASK_CPSR_V) != 0;
break;
case 4:
if (m_opcode_cpsr == 0)
return true;
result = ((m_opcode_cpsr & MASK_CPSR_C) != 0) && ((m_opcode_cpsr & MASK_CPSR_Z) == 0);
break;
case 5:
if (m_opcode_cpsr == 0)
return 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)
return 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:
result = true;
break;
}
if (cond & 1)
result = !result;
return result;
}
uint32_t
EmulateInstructionARM::CurrentCond (const uint32_t opcode)
{
switch (m_opcode_mode)
{
default:
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, 7) != 0x0f)
return Bits32(opcode, 11, 7);
}
else
{
assert (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);
}
}
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 = 0;
tmp_cpsr = 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;
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, target))
return false;
return true;
}
// 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;
}
if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, target))
return false;
return true;
}
// 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;
eModeARM:
// Clear the T bit.
m_new_inst_cpsr &= ~MASK_CPSR_T;
break;
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)
{
uint32_t reg_kind, 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
{
uint32_t reg_kind, 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)
{
// Advance the ITSTATE bits to their values for the next instruction.
if (m_opcode_mode == eModeThumb && m_it_session.InITBlock())
m_it_session.ITAdvance();
ARMOpcode *opcode_data = NULL;
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);
if (opcode_data == NULL)
return false;
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 == false)
{
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 == false && m_ignore_conditions == false)
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.
success = (this->*opcode_data->callback) (m_opcode.GetOpcode32(), opcode_data->encoding);
if (!success)
return false;
if (auto_advance_pc)
{
uint32_t after_pc_value = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_pc, 0, &success);
if (!success)
return false;
if (auto_advance_pc && (after_pc_value == orig_pc_value))
{
if (opcode_data->size == eSize32)
after_pc_value += 4;
else if (opcode_data->size == eSize16)
after_pc_value += 2;
EmulateInstruction::Context context;
context.type = eContextAdvancePC;
context.SetNoArgs();
if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_pc, after_pc_value))
return false;
}
}
return true;
}
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 ConstString opcode_key ("opcode");
static ConstString before_key ("before_state");
static ConstString after_key ("after_state");
OptionValueSP value_sp = test_data->GetValueForKey (opcode_key);
uint32_t test_opcode;
if ((value_sp.get() == NULL) || (value_sp->GetType() != OptionValue::eTypeUInt64))
{
out_stream->Printf ("TestEmulation: Error reading opcode from test file.\n");
return false;
}
test_opcode = value_sp->GetUInt64Value ();
if (arch.GetTriple().getArch() == llvm::Triple::arm)
{
m_opcode_mode = eModeARM;
m_opcode.SetOpcode32 (test_opcode);
}
else if (arch.GetTriple().getArch() == llvm::Triple::thumb)
{
m_opcode_mode = eModeThumb;
if (test_opcode < 0x10000)
m_opcode.SetOpcode16 (test_opcode);
else
m_opcode.SetOpcode32 (test_opcode);
}
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() == NULL) || (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() == NULL) || (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);
if (!success)
out_stream->Printf ("TestEmulation: 'before' and 'after' states do not match.\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.SetRegisterKind (eRegisterKindDWARF);
UnwindPlan::Row row;
UnwindPlan::Row::RegisterLocation regloc;
// Our previous Call Frame Address is the stack pointer
row.SetCFARegister (dwarf_sp);
// Our previous PC is in the LR
regloc.SetInRegister(dwarf_lr);
row.SetRegisterInfo (dwarf_pc, regloc);
unwind_plan.AppendRow (row);
// All other registers are the same.
unwind_plan.SetSourceName ("EmulateInstructionARM");
return true;
}