early-access version 1255

This commit is contained in:
pineappleEA
2020-12-28 15:15:37 +00:00
parent 84b39492d1
commit 78b48028e1
6254 changed files with 1868140 additions and 0 deletions

190
externals/dynarmic/src/frontend/A32/FPSCR.h vendored Executable file
View File

@@ -0,0 +1,190 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <optional>
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/fp/rounding_mode.h"
namespace Dynarmic::A32 {
/**
* Representation of the Floating-Point Status and Control Register.
*/
class FPSCR final
{
public:
FPSCR() = default;
FPSCR(const FPSCR&) = default;
FPSCR(FPSCR&&) = default;
explicit FPSCR(u32 data) : value{data & mask} {}
FPSCR& operator=(const FPSCR&) = default;
FPSCR& operator=(FPSCR&&) = default;
FPSCR& operator=(u32 data) {
value = data & mask;
return *this;
}
/// Negative condition flag.
bool N() const {
return Common::Bit<31>(value);
}
/// Zero condition flag.
bool Z() const {
return Common::Bit<30>(value);
}
/// Carry condition flag.
bool C() const {
return Common::Bit<29>(value);
}
/// Overflow condition flag.
bool V() const {
return Common::Bit<28>(value);
}
/// Cumulative saturation flag.
bool QC() const {
return Common::Bit<27>(value);
}
/// Alternate half-precision control flag.
bool AHP() const {
return Common::Bit<26>(value);
}
/// Default NaN mode control bit.
bool DN() const {
return Common::Bit<25>(value);
}
/// Flush-to-zero mode control bit.
bool FTZ() const {
return Common::Bit<24>(value);
}
/// Rounding mode control field.
FP::RoundingMode RMode() const {
return static_cast<FP::RoundingMode>(Common::Bits<22, 23>(value));
}
/// Indicates the stride of a vector.
std::optional<size_t> Stride() const {
switch (Common::Bits<20, 21>(value)) {
case 0b00:
return 1;
case 0b11:
return 2;
default:
return std::nullopt;
}
}
/// Indicates the length of a vector.
size_t Len() const {
return Common::Bits<16, 18>(value) + 1;
}
/// Input denormal exception trap enable flag.
bool IDE() const {
return Common::Bit<15>(value);
}
/// Inexact exception trap enable flag.
bool IXE() const {
return Common::Bit<12>(value);
}
/// Underflow exception trap enable flag.
bool UFE() const {
return Common::Bit<11>(value);
}
/// Overflow exception trap enable flag.
bool OFE() const {
return Common::Bit<10>(value);
}
/// Division by zero exception trap enable flag.
bool DZE() const {
return Common::Bit<9>(value);
}
/// Invalid operation exception trap enable flag.
bool IOE() const {
return Common::Bit<8>(value);
}
/// Input denormal cumulative exception bit.
bool IDC() const {
return Common::Bit<7>(value);
}
/// Inexact cumulative exception bit.
bool IXC() const {
return Common::Bit<4>(value);
}
/// Underflow cumulative exception bit.
bool UFC() const {
return Common::Bit<3>(value);
}
/// Overflow cumulative exception bit.
bool OFC() const {
return Common::Bit<2>(value);
}
/// Division by zero cumulative exception bit.
bool DZC() const {
return Common::Bit<1>(value);
}
/// Invalid operation cumulative exception bit.
bool IOC() const {
return Common::Bit<0>(value);
}
/**
* Whether or not the FPSCR indicates RunFast mode.
*
* RunFast mode is enabled when:
* - Flush-to-zero is enabled
* - Default NaNs are enabled.
* - All exception enable bits are cleared.
*/
bool InRunFastMode() const {
constexpr u32 runfast_mask = 0x03001F00;
constexpr u32 expected = 0x03000000;
return (value & runfast_mask) == expected;
}
/// Gets the underlying raw value within the FPSCR.
u32 Value() const {
return value;
}
private:
// Bits 5-6, 13-14, and 19 are reserved.
static constexpr u32 mask = 0xFFF79F9F;
u32 value = 0;
};
inline bool operator==(FPSCR lhs, FPSCR rhs) {
return lhs.Value() == rhs.Value();
}
inline bool operator!=(FPSCR lhs, FPSCR rhs) {
return !operator==(lhs, rhs);
}
} // namespace Dynarmic::A32

71
externals/dynarmic/src/frontend/A32/ITState.h vendored Executable file
View File

@@ -0,0 +1,71 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include "common/common_types.h"
#include "common/bit_util.h"
#include "frontend/ir/cond.h"
namespace Dynarmic::A32 {
class ITState final {
public:
ITState() = default;
explicit ITState(u8 data) : value(data) {}
ITState& operator=(u8 data) {
value = data;
return *this;
}
IR::Cond Cond() const {
return static_cast<IR::Cond>(Common::Bits<4, 7>(value));
}
void Cond(IR::Cond cond) {
value = Common::ModifyBits<4, 7>(value, static_cast<u8>(cond));
}
u8 Mask() const {
return Common::Bits<0, 3>(value);
}
void Mask(u8 mask) {
value = Common::ModifyBits<0, 3>(value, mask);
}
bool IsInITBlock() const {
return Mask() != 0b0000;
}
bool IsLastInITBlock() const {
return Mask() == 0b1000;
}
ITState Advance() const {
ITState result{*this};
result.Mask(result.Mask() << 1);
if (result.Mask() == 0) {
return ITState{0};
}
return result;
}
u8 Value() const {
return value;
}
private:
u8 value;
};
inline bool operator==(ITState lhs, ITState rhs) {
return lhs.Value() == rhs.Value();
}
inline bool operator!=(ITState lhs, ITState rhs) {
return !operator==(lhs, rhs);
}
} // namespace Dynarmic::A32

222
externals/dynarmic/src/frontend/A32/PSR.h vendored Executable file
View File

@@ -0,0 +1,222 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include "common/bit_util.h"
#include "common/common_types.h"
#include "frontend/A32/ITState.h"
namespace Dynarmic::A32 {
/**
* Program Status Register
*
* | Bit(s) | Description |
* |:-------:|:----------------------------------------------|
* | N | Negative |
* | Z | Zero |
* | C | Carry |
* | V | Overflow |
* | Q | Sticky overflow for DSP-oriented instructions |
* | IT[1:0] | Lower two bits of the If-Then execution state |
* | J | Jazelle bit |
* | GE | Greater-than or Equal |
* | IT[7:2] | Upper six bits of the If-Then execution state |
* | E | Endian (0 is little endian, 1 is big endian) |
* | A | Imprecise data abort (disables them when set) |
* | I | IRQ interrupts (disabled when set) |
* | F | FIQ interrupts (disabled when set) |
* | T | Thumb bit |
* | M | Current processor mode |
*/
class PSR final {
public:
/// Valid processor modes that may be indicated.
enum class Mode : u32 {
User = 0b10000,
FIQ = 0b10001,
IRQ = 0b10010,
Supervisor = 0b10011,
Monitor = 0b10110,
Abort = 0b10111,
Hypervisor = 0b11010,
Undefined = 0b11011,
System = 0b11111
};
/// Instruction sets that may be signified through a PSR.
enum class InstructionSet {
ARM,
Jazelle,
Thumb,
ThumbEE
};
PSR() = default;
explicit PSR(u32 data) : value{data & mask} {}
PSR& operator=(u32 data) {
value = data & mask;
return *this;
}
bool N() const {
return Common::Bit<31>(value);
}
void N(bool set) {
value = Common::ModifyBit<31>(value, set);
}
bool Z() const {
return Common::Bit<30>(value);
}
void Z(bool set) {
value = Common::ModifyBit<30>(value, set);
}
bool C() const {
return Common::Bit<29>(value);
}
void C(bool set) {
value = Common::ModifyBit<29>(value, set);
}
bool V() const {
return Common::Bit<28>(value);
}
void V(bool set) {
value = Common::ModifyBit<28>(value, set);
}
bool Q() const {
return Common::Bit<27>(value);
}
void Q(bool set) {
value = Common::ModifyBit<27>(value, set);
}
bool J() const {
return Common::Bit<24>(value);
}
void J(bool set) {
value = Common::ModifyBit<24>(value, set);
}
u32 GE() const {
return Common::Bits<16, 19>(value);
}
void GE(u32 data) {
value = Common::ModifyBits<16, 19>(value, data);
}
ITState IT() const {
return ITState{static_cast<u8>((value & 0x6000000) >> 25 | (value & 0xFC00) >> 8)};
}
void IT(ITState it_state) {
const u32 data = it_state.Value();
value = (value & ~0x000FC00) | (data & 0b11111100) << 8;
value = (value & ~0x6000000) | (data & 0b00000011) << 25;
}
bool E() const {
return Common::Bit<9>(value);
}
void E(bool set) {
value = Common::ModifyBit<9>(value, set);
}
bool A() const {
return Common::Bit<8>(value);
}
void A(bool set) {
value = Common::ModifyBit<8>(value, set);
}
bool I() const {
return Common::Bit<7>(value);
}
void I(bool set) {
value = Common::ModifyBit<7>(value, set);
}
bool F() const {
return Common::Bit<6>(value);
}
void F(bool set) {
value = Common::ModifyBit<6>(value, set);
}
bool T() const {
return Common::Bit<5>(value);
}
void T(bool set) {
value = Common::ModifyBit<5>(value, set);
}
Mode M() const {
return static_cast<Mode>(Common::Bits<0, 4>(value));
}
void M(Mode mode) {
value = Common::ModifyBits<0, 4>(value, static_cast<u32>(mode));
}
u32 Value() const {
return value;
}
InstructionSet CurrentInstructionSet() const {
const bool j_bit = J();
const bool t_bit = T();
if (j_bit && t_bit)
return InstructionSet::ThumbEE;
if (t_bit)
return InstructionSet::Thumb;
if (j_bit)
return InstructionSet::Jazelle;
return InstructionSet::ARM;
}
void CurrentInstructionSet(InstructionSet instruction_set) {
switch (instruction_set) {
case InstructionSet::ARM:
T(false);
J(false);
break;
case InstructionSet::Jazelle:
T(false);
J(true);
break;
case InstructionSet::Thumb:
T(true);
J(false);
break;
case InstructionSet::ThumbEE:
T(true);
J(true);
break;
}
}
private:
// Bits 20-23 are reserved and should be zero.
static constexpr u32 mask = 0xFF0FFFFF;
u32 value = 0;
};
inline bool operator==(PSR lhs, PSR rhs) {
return lhs.Value() == rhs.Value();
}
inline bool operator!=(PSR lhs, PSR rhs) {
return !operator==(lhs, rhs);
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,53 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*
* Original version of table by Lioncash.
*/
#pragma once
#include <algorithm>
#include <functional>
#include <optional>
#include <vector>
#include "common/bit_util.h"
#include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic::A32 {
template <typename Visitor>
using ArmMatcher = Decoder::Matcher<Visitor, u32>;
template <typename V>
std::vector<ArmMatcher<V>> GetArmDecodeTable() {
std::vector<ArmMatcher<V>> table = {
#define INST(fn, name, bitstring) Decoder::detail::detail<ArmMatcher<V>>::GetMatcher(&V::fn, name, bitstring),
#include "arm.inc"
#undef INST
};
// If a matcher has more bits in its mask it is more specific, so it should come first.
std::stable_sort(table.begin(), table.end(), [](const auto& matcher1, const auto& matcher2) {
return Common::BitCount(matcher1.GetMask()) > Common::BitCount(matcher2.GetMask());
});
return table;
}
template<typename V>
std::optional<std::reference_wrapper<const ArmMatcher<V>>> DecodeArm(u32 instruction) {
static const auto table = GetArmDecodeTable<V>();
const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); };
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
return iter != table.end() ? std::optional<std::reference_wrapper<const ArmMatcher<V>>>(*iter) : std::nullopt;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,316 @@
// Barrier instructions
INST(arm_DMB, "DMB", "1111010101111111111100000101oooo") // v7
INST(arm_DSB, "DSB", "1111010101111111111100000100oooo") // v7
INST(arm_ISB, "ISB", "1111010101111111111100000110oooo") // v7
// Branch instructions
INST(arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv") // v5
INST(arm_BLX_reg, "BLX (reg)", "cccc000100101111111111110011mmmm") // v5
INST(arm_B, "B", "cccc1010vvvvvvvvvvvvvvvvvvvvvvvv") // all
INST(arm_BL, "BL", "cccc1011vvvvvvvvvvvvvvvvvvvvvvvv") // all
INST(arm_BX, "BX", "cccc000100101111111111110001mmmm") // v4T
INST(arm_BXJ, "BXJ", "cccc000100101111111111110010mmmm") // v5J
// CRC32 instructions
INST(arm_CRC32, "CRC32", "cccc00010zz0nnnndddd00000100mmmm") // v8
INST(arm_CRC32C, "CRC32C", "cccc00010zz0nnnndddd00100100mmmm") // v8
// Coprocessor instructions
INST(arm_CDP, "CDP", "cccc1110ooooNNNNDDDDppppooo0MMMM") // v2 (CDP2: v5)
INST(arm_LDC, "LDC", "cccc110pudw1nnnnDDDDppppvvvvvvvv") // v2 (LDC2: v5)
INST(arm_MCR, "MCR", "cccc1110ooo0NNNNttttppppooo1MMMM") // v2 (MCR2: v5)
INST(arm_MCRR, "MCRR", "cccc11000100uuuuttttppppooooMMMM") // v5E (MCRR2: v6)
INST(arm_MRC, "MRC", "cccc1110ooo1NNNNttttppppooo1MMMM") // v2 (MRC2: v5)
INST(arm_MRRC, "MRRC", "cccc11000101uuuuttttppppooooMMMM") // v5E (MRRC2: v6)
INST(arm_STC, "STC", "cccc110pudw0nnnnDDDDppppvvvvvvvv") // v2 (STC2: v5)
// Data Processing instructions
INST(arm_ADC_imm, "ADC (imm)", "cccc0010101Snnnnddddrrrrvvvvvvvv") // all
INST(arm_ADC_reg, "ADC (reg)", "cccc0000101Snnnnddddvvvvvrr0mmmm") // all
INST(arm_ADC_rsr, "ADC (rsr)", "cccc0000101Snnnnddddssss0rr1mmmm") // all
INST(arm_ADD_imm, "ADD (imm)", "cccc0010100Snnnnddddrrrrvvvvvvvv") // all
INST(arm_ADD_reg, "ADD (reg)", "cccc0000100Snnnnddddvvvvvrr0mmmm") // all
INST(arm_ADD_rsr, "ADD (rsr)", "cccc0000100Snnnnddddssss0rr1mmmm") // all
INST(arm_AND_imm, "AND (imm)", "cccc0010000Snnnnddddrrrrvvvvvvvv") // all
INST(arm_AND_reg, "AND (reg)", "cccc0000000Snnnnddddvvvvvrr0mmmm") // all
INST(arm_AND_rsr, "AND (rsr)", "cccc0000000Snnnnddddssss0rr1mmmm") // all
INST(arm_BIC_imm, "BIC (imm)", "cccc0011110Snnnnddddrrrrvvvvvvvv") // all
INST(arm_BIC_reg, "BIC (reg)", "cccc0001110Snnnnddddvvvvvrr0mmmm") // all
INST(arm_BIC_rsr, "BIC (rsr)", "cccc0001110Snnnnddddssss0rr1mmmm") // all
INST(arm_CMN_imm, "CMN (imm)", "cccc00110111nnnn0000rrrrvvvvvvvv") // all
INST(arm_CMN_reg, "CMN (reg)", "cccc00010111nnnn0000vvvvvrr0mmmm") // all
INST(arm_CMN_rsr, "CMN (rsr)", "cccc00010111nnnn0000ssss0rr1mmmm") // all
INST(arm_CMP_imm, "CMP (imm)", "cccc00110101nnnn0000rrrrvvvvvvvv") // all
INST(arm_CMP_reg, "CMP (reg)", "cccc00010101nnnn0000vvvvvrr0mmmm") // all
INST(arm_CMP_rsr, "CMP (rsr)", "cccc00010101nnnn0000ssss0rr1mmmm") // all
INST(arm_EOR_imm, "EOR (imm)", "cccc0010001Snnnnddddrrrrvvvvvvvv") // all
INST(arm_EOR_reg, "EOR (reg)", "cccc0000001Snnnnddddvvvvvrr0mmmm") // all
INST(arm_EOR_rsr, "EOR (rsr)", "cccc0000001Snnnnddddssss0rr1mmmm") // all
INST(arm_MOV_imm, "MOV (imm)", "cccc0011101S0000ddddrrrrvvvvvvvv") // all
INST(arm_MOV_reg, "MOV (reg)", "cccc0001101S0000ddddvvvvvrr0mmmm") // all
INST(arm_MOV_rsr, "MOV (rsr)", "cccc0001101S0000ddddssss0rr1mmmm") // all
INST(arm_MVN_imm, "MVN (imm)", "cccc0011111S0000ddddrrrrvvvvvvvv") // all
INST(arm_MVN_reg, "MVN (reg)", "cccc0001111S0000ddddvvvvvrr0mmmm") // all
INST(arm_MVN_rsr, "MVN (rsr)", "cccc0001111S0000ddddssss0rr1mmmm") // all
INST(arm_ORR_imm, "ORR (imm)", "cccc0011100Snnnnddddrrrrvvvvvvvv") // all
INST(arm_ORR_reg, "ORR (reg)", "cccc0001100Snnnnddddvvvvvrr0mmmm") // all
INST(arm_ORR_rsr, "ORR (rsr)", "cccc0001100Snnnnddddssss0rr1mmmm") // all
INST(arm_RSB_imm, "RSB (imm)", "cccc0010011Snnnnddddrrrrvvvvvvvv") // all
INST(arm_RSB_reg, "RSB (reg)", "cccc0000011Snnnnddddvvvvvrr0mmmm") // all
INST(arm_RSB_rsr, "RSB (rsr)", "cccc0000011Snnnnddddssss0rr1mmmm") // all
INST(arm_RSC_imm, "RSC (imm)", "cccc0010111Snnnnddddrrrrvvvvvvvv") // all
INST(arm_RSC_reg, "RSC (reg)", "cccc0000111Snnnnddddvvvvvrr0mmmm") // all
INST(arm_RSC_rsr, "RSC (rsr)", "cccc0000111Snnnnddddssss0rr1mmmm") // all
INST(arm_SBC_imm, "SBC (imm)", "cccc0010110Snnnnddddrrrrvvvvvvvv") // all
INST(arm_SBC_reg, "SBC (reg)", "cccc0000110Snnnnddddvvvvvrr0mmmm") // all
INST(arm_SBC_rsr, "SBC (rsr)", "cccc0000110Snnnnddddssss0rr1mmmm") // all
INST(arm_SUB_imm, "SUB (imm)", "cccc0010010Snnnnddddrrrrvvvvvvvv") // all
INST(arm_SUB_reg, "SUB (reg)", "cccc0000010Snnnnddddvvvvvrr0mmmm") // all
INST(arm_SUB_rsr, "SUB (rsr)", "cccc0000010Snnnnddddssss0rr1mmmm") // all
INST(arm_TEQ_imm, "TEQ (imm)", "cccc00110011nnnn0000rrrrvvvvvvvv") // all
INST(arm_TEQ_reg, "TEQ (reg)", "cccc00010011nnnn0000vvvvvrr0mmmm") // all
INST(arm_TEQ_rsr, "TEQ (rsr)", "cccc00010011nnnn0000ssss0rr1mmmm") // all
INST(arm_TST_imm, "TST (imm)", "cccc00110001nnnn0000rrrrvvvvvvvv") // all
INST(arm_TST_reg, "TST (reg)", "cccc00010001nnnn0000vvvvvrr0mmmm") // all
INST(arm_TST_rsr, "TST (rsr)", "cccc00010001nnnn0000ssss0rr1mmmm") // all
// Exception Generating instructions
INST(arm_BKPT, "BKPT", "cccc00010010vvvvvvvvvvvv0111vvvv") // v5
INST(arm_SVC, "SVC", "cccc1111vvvvvvvvvvvvvvvvvvvvvvvv") // all
INST(arm_UDF, "UDF", "111001111111------------1111----") // all
// Extension instructions
INST(arm_SXTB, "SXTB", "cccc011010101111ddddrr000111mmmm") // v6
INST(arm_SXTB16, "SXTB16", "cccc011010001111ddddrr000111mmmm") // v6
INST(arm_SXTH, "SXTH", "cccc011010111111ddddrr000111mmmm") // v6
INST(arm_SXTAB, "SXTAB", "cccc01101010nnnnddddrr000111mmmm") // v6
INST(arm_SXTAB16, "SXTAB16", "cccc01101000nnnnddddrr000111mmmm") // v6
INST(arm_SXTAH, "SXTAH", "cccc01101011nnnnddddrr000111mmmm") // v6
INST(arm_UXTB, "UXTB", "cccc011011101111ddddrr000111mmmm") // v6
INST(arm_UXTB16, "UXTB16", "cccc011011001111ddddrr000111mmmm") // v6
INST(arm_UXTH, "UXTH", "cccc011011111111ddddrr000111mmmm") // v6
INST(arm_UXTAB, "UXTAB", "cccc01101110nnnnddddrr000111mmmm") // v6
INST(arm_UXTAB16, "UXTAB16", "cccc01101100nnnnddddrr000111mmmm") // v6
INST(arm_UXTAH, "UXTAH", "cccc01101111nnnnddddrr000111mmmm") // v6
// Hint instructions
INST(arm_PLD_imm, "PLD (imm)", "11110101uz01nnnn1111iiiiiiiiiiii") // v5E for PLD; v7 for PLDW
INST(arm_PLD_reg, "PLD (reg)", "11110111uz01nnnn1111iiiiitt0mmmm") // v5E for PLD; v7 for PLDW
INST(arm_SEV, "SEV", "----0011001000001111000000000100") // v6K
INST(arm_SEVL, "SEVL", "----0011001000001111000000000101") // v8
INST(arm_WFE, "WFE", "----0011001000001111000000000010") // v6K
INST(arm_WFI, "WFI", "----0011001000001111000000000011") // v6K
INST(arm_YIELD, "YIELD", "----0011001000001111000000000001") // v6K
INST(arm_NOP, "Reserved Hint", "----0011001000001111------------")
INST(arm_NOP, "Reserved Hint", "----001100100000111100000000----")
// Synchronization Primitive instructions
INST(arm_CLREX, "CLREX", "11110101011111111111000000011111") // v6K
INST(arm_SWP, "SWP", "cccc00010000nnnntttt00001001uuuu") // v2S (v6: Deprecated)
INST(arm_SWPB, "SWPB", "cccc00010100nnnntttt00001001uuuu") // v2S (v6: Deprecated)
INST(arm_STL, "STL", "cccc00011000nnnn111111001001tttt") // v8
INST(arm_STLEX, "STLEX", "cccc00011000nnnndddd11101001tttt") // v8
INST(arm_STREX, "STREX", "cccc00011000nnnndddd11111001mmmm") // v6
INST(arm_LDA, "LDA", "cccc00011001nnnndddd110010011111") // v8
INST(arm_LDAEX, "LDAEX", "cccc00011001nnnndddd111010011111") // v8
INST(arm_LDREX, "LDREX", "cccc00011001nnnndddd111110011111") // v6
INST(arm_STLEXD, "STLEXD", "cccc00011010nnnndddd11101001mmmm") // v8
INST(arm_STREXD, "STREXD", "cccc00011010nnnndddd11111001mmmm") // v6K
INST(arm_LDAEXD, "LDAEXD", "cccc00011011nnnndddd111010011111") // v8
INST(arm_LDREXD, "LDREXD", "cccc00011011nnnndddd111110011111") // v6K
INST(arm_STLB, "STLB", "cccc00011100nnnn111111001001tttt") // v8
INST(arm_STLEXB, "STLEXB", "cccc00011100nnnndddd11101001mmmm") // v8
INST(arm_STREXB, "STREXB", "cccc00011100nnnndddd11111001mmmm") // v6K
INST(arm_LDAB, "LDAB", "cccc00011101nnnndddd110010011111") // v8
INST(arm_LDAEXB, "LDAEXB", "cccc00011101nnnndddd111010011111") // v8
INST(arm_LDREXB, "LDREXB", "cccc00011101nnnndddd111110011111") // v6K
INST(arm_STLH, "STLH", "cccc00011110nnnn111111001001mmmm") // v8
INST(arm_STLEXH, "STLEXH", "cccc00011110nnnndddd11101001mmmm") // v8
INST(arm_STREXH, "STREXH", "cccc00011110nnnndddd11111001mmmm") // v6K
INST(arm_LDAH, "LDAH", "cccc00011111nnnndddd110010011111") // v8
INST(arm_LDAEXH, "LDAEXH", "cccc00011111nnnndddd111010011111") // v8
INST(arm_LDREXH, "LDREXH", "cccc00011111nnnndddd111110011111") // v6K
// Load/Store instructions
INST(arm_LDRBT, "LDRBT (A1)", "----0100-111--------------------")
INST(arm_LDRBT, "LDRBT (A2)", "----0110-111---------------0----")
INST(arm_LDRHT, "LDRHT (A1)", "----0000-111------------1011----")
INST(arm_LDRHT, "LDRHT (A1)", "----0000-1111111--------1011----")
INST(arm_LDRHT, "LDRHT (A2)", "----0000-011--------00001011----")
INST(arm_LDRSBT, "LDRSBT (A1)", "----0000-111------------1101----")
INST(arm_LDRSBT, "LDRSBT (A2)", "----0000-011--------00001101----")
INST(arm_LDRSHT, "LDRSHT (A1)", "----0000-111------------1111----")
INST(arm_LDRSHT, "LDRSHT (A2)", "----0000-011--------00001111----")
INST(arm_LDRT, "LDRT (A1)", "----0100-011--------------------")
INST(arm_LDRT, "LDRT (A2)", "----0110-011---------------0----")
INST(arm_STRBT, "STRBT (A1)", "----0100-110--------------------")
INST(arm_STRBT, "STRBT (A2)", "----0110-110---------------0----")
INST(arm_STRHT, "STRHT (A1)", "----0000-110------------1011----")
INST(arm_STRHT, "STRHT (A2)", "----0000-010--------00001011----")
INST(arm_STRT, "STRT (A1)", "----0100-010--------------------")
INST(arm_STRT, "STRT (A2)", "----0110-010---------------0----")
INST(arm_LDR_lit, "LDR (lit)", "cccc0101u0011111ttttvvvvvvvvvvvv")
INST(arm_LDR_imm, "LDR (imm)", "cccc010pu0w1nnnnttttvvvvvvvvvvvv")
INST(arm_LDR_reg, "LDR (reg)", "cccc011pu0w1nnnnttttvvvvvrr0mmmm")
INST(arm_LDRB_lit, "LDRB (lit)", "cccc0101u1011111ttttvvvvvvvvvvvv")
INST(arm_LDRB_imm, "LDRB (imm)", "cccc010pu1w1nnnnttttvvvvvvvvvvvv")
INST(arm_LDRB_reg, "LDRB (reg)", "cccc011pu1w1nnnnttttvvvvvrr0mmmm")
INST(arm_LDRD_lit, "LDRD (lit)", "cccc0001u1001111ttttvvvv1101vvvv")
INST(arm_LDRD_imm, "LDRD (imm)", "cccc000pu1w0nnnnttttvvvv1101vvvv") // v5E
INST(arm_LDRD_reg, "LDRD (reg)", "cccc000pu0w0nnnntttt00001101mmmm") // v5E
INST(arm_LDRH_lit, "LDRH (lit)", "cccc000pu1w11111ttttvvvv1011vvvv")
INST(arm_LDRH_imm, "LDRH (imm)", "cccc000pu1w1nnnnttttvvvv1011vvvv")
INST(arm_LDRH_reg, "LDRH (reg)", "cccc000pu0w1nnnntttt00001011mmmm")
INST(arm_LDRSB_lit, "LDRSB (lit)", "cccc0001u1011111ttttvvvv1101vvvv")
INST(arm_LDRSB_imm, "LDRSB (imm)", "cccc000pu1w1nnnnttttvvvv1101vvvv")
INST(arm_LDRSB_reg, "LDRSB (reg)", "cccc000pu0w1nnnntttt00001101mmmm")
INST(arm_LDRSH_lit, "LDRSH (lit)", "cccc0001u1011111ttttvvvv1111vvvv")
INST(arm_LDRSH_imm, "LDRSH (imm)", "cccc000pu1w1nnnnttttvvvv1111vvvv")
INST(arm_LDRSH_reg, "LDRSH (reg)", "cccc000pu0w1nnnntttt00001111mmmm")
INST(arm_STR_imm, "STR (imm)", "cccc010pu0w0nnnnttttvvvvvvvvvvvv")
INST(arm_STR_reg, "STR (reg)", "cccc011pu0w0nnnnttttvvvvvrr0mmmm")
INST(arm_STRB_imm, "STRB (imm)", "cccc010pu1w0nnnnttttvvvvvvvvvvvv")
INST(arm_STRB_reg, "STRB (reg)", "cccc011pu1w0nnnnttttvvvvvrr0mmmm")
INST(arm_STRD_imm, "STRD (imm)", "cccc000pu1w0nnnnttttvvvv1111vvvv") // v5E
INST(arm_STRD_reg, "STRD (reg)", "cccc000pu0w0nnnntttt00001111mmmm") // v5E
INST(arm_STRH_imm, "STRH (imm)", "cccc000pu1w0nnnnttttvvvv1011vvvv")
INST(arm_STRH_reg, "STRH (reg)", "cccc000pu0w0nnnntttt00001011mmmm")
// Load/Store Multiple instructions
INST(arm_LDM, "LDM", "cccc100010w1nnnnxxxxxxxxxxxxxxxx") // all
INST(arm_LDMDA, "LDMDA", "cccc100000w1nnnnxxxxxxxxxxxxxxxx") // all
INST(arm_LDMDB, "LDMDB", "cccc100100w1nnnnxxxxxxxxxxxxxxxx") // all
INST(arm_LDMIB, "LDMIB", "cccc100110w1nnnnxxxxxxxxxxxxxxxx") // all
INST(arm_LDM_usr, "LDM (usr reg)", "----100--101--------------------") // all
INST(arm_LDM_eret, "LDM (exce ret)", "----100--1-1----1---------------") // all
INST(arm_STM, "STM", "cccc100010w0nnnnxxxxxxxxxxxxxxxx") // all
INST(arm_STMDA, "STMDA", "cccc100000w0nnnnxxxxxxxxxxxxxxxx") // all
INST(arm_STMDB, "STMDB", "cccc100100w0nnnnxxxxxxxxxxxxxxxx") // all
INST(arm_STMIB, "STMIB", "cccc100110w0nnnnxxxxxxxxxxxxxxxx") // all
INST(arm_STM_usr, "STM (usr reg)", "----100--100--------------------") // all
// Miscellaneous instructions
INST(arm_BFC, "BFC", "cccc0111110vvvvvddddvvvvv0011111") // v6T2
INST(arm_BFI, "BFI", "cccc0111110vvvvvddddvvvvv001nnnn") // v6T2
INST(arm_CLZ, "CLZ", "cccc000101101111dddd11110001mmmm") // v5
INST(arm_MOVT, "MOVT", "cccc00110100vvvvddddvvvvvvvvvvvv") // v6T2
INST(arm_MOVW, "MOVW", "cccc00110000vvvvddddvvvvvvvvvvvv") // v6T2
INST(arm_NOP, "NOP", "----0011001000001111000000000000") // v6K
INST(arm_SBFX, "SBFX", "cccc0111101wwwwwddddvvvvv101nnnn") // v6T2
INST(arm_SEL, "SEL", "cccc01101000nnnndddd11111011mmmm") // v6
INST(arm_UBFX, "UBFX", "cccc0111111wwwwwddddvvvvv101nnnn") // v6T2
// Unsigned Sum of Absolute Differences instructions
INST(arm_USAD8, "USAD8", "cccc01111000dddd1111mmmm0001nnnn") // v6
INST(arm_USADA8, "USADA8", "cccc01111000ddddaaaammmm0001nnnn") // v6
// Packing instructions
INST(arm_PKHBT, "PKHBT", "cccc01101000nnnnddddvvvvv001mmmm") // v6K
INST(arm_PKHTB, "PKHTB", "cccc01101000nnnnddddvvvvv101mmmm") // v6K
// Reversal instructions
INST(arm_RBIT, "RBIT", "cccc011011111111dddd11110011mmmm") // v6T2
INST(arm_REV, "REV", "cccc011010111111dddd11110011mmmm") // v6
INST(arm_REV16, "REV16", "cccc011010111111dddd11111011mmmm") // v6
INST(arm_REVSH, "REVSH", "cccc011011111111dddd11111011mmmm") // v6
// Saturation instructions
INST(arm_SSAT, "SSAT", "cccc0110101vvvvvddddvvvvvr01nnnn") // v6
INST(arm_SSAT16, "SSAT16", "cccc01101010vvvvdddd11110011nnnn") // v6
INST(arm_USAT, "USAT", "cccc0110111vvvvvddddvvvvvr01nnnn") // v6
INST(arm_USAT16, "USAT16", "cccc01101110vvvvdddd11110011nnnn") // v6
// Divide instructions
INST(arm_SDIV, "SDIV", "cccc01110001dddd1111mmmm0001nnnn") // v7a
INST(arm_UDIV, "UDIV", "cccc01110011dddd1111mmmm0001nnnn") // v7a
// Multiply (Normal) instructions
INST(arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn") // v2
INST(arm_MLS, "MLS", "cccc00000110ddddaaaammmm1001nnnn") // v6T2
INST(arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn") // v2
// Multiply (Long) instructions
INST(arm_SMLAL, "SMLAL", "cccc0000111Sddddaaaammmm1001nnnn") // v3M
INST(arm_SMULL, "SMULL", "cccc0000110Sddddaaaammmm1001nnnn") // v3M
INST(arm_UMAAL, "UMAAL", "cccc00000100ddddaaaammmm1001nnnn") // v6
INST(arm_UMLAL, "UMLAL", "cccc0000101Sddddaaaammmm1001nnnn") // v3M
INST(arm_UMULL, "UMULL", "cccc0000100Sddddaaaammmm1001nnnn") // v3M
// Multiply (Halfword) instructions
INST(arm_SMLALxy, "SMLALXY", "cccc00010100ddddaaaammmm1xy0nnnn") // v5xP
INST(arm_SMLAxy, "SMLAXY", "cccc00010000ddddaaaammmm1xy0nnnn") // v5xP
INST(arm_SMULxy, "SMULXY", "cccc00010110dddd0000mmmm1xy0nnnn") // v5xP
// Multiply (Word by Halfword) instructions
INST(arm_SMLAWy, "SMLAWY", "cccc00010010ddddaaaammmm1y00nnnn") // v5xP
INST(arm_SMULWy, "SMULWY", "cccc00010010dddd0000mmmm1y10nnnn") // v5xP
// Multiply (Most Significant Word) instructions
INST(arm_SMMUL, "SMMUL", "cccc01110101dddd1111mmmm00R1nnnn") // v6
INST(arm_SMMLA, "SMMLA", "cccc01110101ddddaaaammmm00R1nnnn") // v6
INST(arm_SMMLS, "SMMLS", "cccc01110101ddddaaaammmm11R1nnnn") // v6
// Multiply (Dual) instructions
INST(arm_SMLAD, "SMLAD", "cccc01110000ddddaaaammmm00M1nnnn") // v6
INST(arm_SMLALD, "SMLALD", "cccc01110100ddddaaaammmm00M1nnnn") // v6
INST(arm_SMLSD, "SMLSD", "cccc01110000ddddaaaammmm01M1nnnn") // v6
INST(arm_SMLSLD, "SMLSLD", "cccc01110100ddddaaaammmm01M1nnnn") // v6
INST(arm_SMUAD, "SMUAD", "cccc01110000dddd1111mmmm00M1nnnn") // v6
INST(arm_SMUSD, "SMUSD", "cccc01110000dddd1111mmmm01M1nnnn") // v6
// Parallel Add/Subtract (Modulo) instructions
INST(arm_SADD8, "SADD8", "cccc01100001nnnndddd11111001mmmm") // v6
INST(arm_SADD16, "SADD16", "cccc01100001nnnndddd11110001mmmm") // v6
INST(arm_SASX, "SASX", "cccc01100001nnnndddd11110011mmmm") // v6
INST(arm_SSAX, "SSAX", "cccc01100001nnnndddd11110101mmmm") // v6
INST(arm_SSUB8, "SSUB8", "cccc01100001nnnndddd11111111mmmm") // v6
INST(arm_SSUB16, "SSUB16", "cccc01100001nnnndddd11110111mmmm") // v6
INST(arm_UADD8, "UADD8", "cccc01100101nnnndddd11111001mmmm") // v6
INST(arm_UADD16, "UADD16", "cccc01100101nnnndddd11110001mmmm") // v6
INST(arm_UASX, "UASX", "cccc01100101nnnndddd11110011mmmm") // v6
INST(arm_USAX, "USAX", "cccc01100101nnnndddd11110101mmmm") // v6
INST(arm_USUB8, "USUB8", "cccc01100101nnnndddd11111111mmmm") // v6
INST(arm_USUB16, "USUB16", "cccc01100101nnnndddd11110111mmmm") // v6
// Parallel Add/Subtract (Saturating) instructions
INST(arm_QADD8, "QADD8", "cccc01100010nnnndddd11111001mmmm") // v6
INST(arm_QADD16, "QADD16", "cccc01100010nnnndddd11110001mmmm") // v6
INST(arm_QASX, "QASX", "cccc01100010nnnndddd11110011mmmm") // v6
INST(arm_QSAX, "QSAX", "cccc01100010nnnndddd11110101mmmm") // v6
INST(arm_QSUB8, "QSUB8", "cccc01100010nnnndddd11111111mmmm") // v6
INST(arm_QSUB16, "QSUB16", "cccc01100010nnnndddd11110111mmmm") // v6
INST(arm_UQADD8, "UQADD8", "cccc01100110nnnndddd11111001mmmm") // v6
INST(arm_UQADD16, "UQADD16", "cccc01100110nnnndddd11110001mmmm") // v6
INST(arm_UQASX, "UQASX", "cccc01100110nnnndddd11110011mmmm") // v6
INST(arm_UQSAX, "UQSAX", "cccc01100110nnnndddd11110101mmmm") // v6
INST(arm_UQSUB8, "UQSUB8", "cccc01100110nnnndddd11111111mmmm") // v6
INST(arm_UQSUB16, "UQSUB16", "cccc01100110nnnndddd11110111mmmm") // v6
// Parallel Add/Subtract (Halving) instructions
INST(arm_SHADD8, "SHADD8", "cccc01100011nnnndddd11111001mmmm") // v6
INST(arm_SHADD16, "SHADD16", "cccc01100011nnnndddd11110001mmmm") // v6
INST(arm_SHASX, "SHASX", "cccc01100011nnnndddd11110011mmmm") // v6
INST(arm_SHSAX, "SHSAX", "cccc01100011nnnndddd11110101mmmm") // v6
INST(arm_SHSUB8, "SHSUB8", "cccc01100011nnnndddd11111111mmmm") // v6
INST(arm_SHSUB16, "SHSUB16", "cccc01100011nnnndddd11110111mmmm") // v6
INST(arm_UHADD8, "UHADD8", "cccc01100111nnnndddd11111001mmmm") // v6
INST(arm_UHADD16, "UHADD16", "cccc01100111nnnndddd11110001mmmm") // v6
INST(arm_UHASX, "UHASX", "cccc01100111nnnndddd11110011mmmm") // v6
INST(arm_UHSAX, "UHSAX", "cccc01100111nnnndddd11110101mmmm") // v6
INST(arm_UHSUB8, "UHSUB8", "cccc01100111nnnndddd11111111mmmm") // v6
INST(arm_UHSUB16, "UHSUB16", "cccc01100111nnnndddd11110111mmmm") // v6
// Saturated Add/Subtract instructions
INST(arm_QADD, "QADD", "cccc00010000nnnndddd00000101mmmm") // v5xP
INST(arm_QSUB, "QSUB", "cccc00010010nnnndddd00000101mmmm") // v5xP
INST(arm_QDADD, "QDADD", "cccc00010100nnnndddd00000101mmmm") // v5xP
INST(arm_QDSUB, "QDSUB", "cccc00010110nnnndddd00000101mmmm") // v5xP
// Status Register Access instructions
INST(arm_CPS, "CPS", "111100010000---00000000---0-----") // v6
INST(arm_SETEND, "SETEND", "1111000100000001000000e000000000") // v6
INST(arm_MRS, "MRS", "cccc000100001111dddd000000000000") // v3
INST(arm_MSR_imm, "MSR (imm)", "cccc00110010mmmm1111rrrrvvvvvvvv") // v3
INST(arm_MSR_reg, "MSR (reg)", "cccc00010010mmmm111100000000nnnn") // v3
INST(arm_RFE, "RFE", "1111100--0-1----0000101000000000") // v6
INST(arm_SRS, "SRS", "1111100--1-0110100000101000-----") // v6

View File

@@ -0,0 +1,77 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2020 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <algorithm>
#include <functional>
#include <optional>
#include <set>
#include <vector>
#include "common/bit_util.h"
#include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic::A32 {
template <typename Visitor>
using ASIMDMatcher = Decoder::Matcher<Visitor, u32>;
template <typename V>
std::vector<ASIMDMatcher<V>> GetASIMDDecodeTable() {
std::vector<ASIMDMatcher<V>> table = {
#define INST(fn, name, bitstring) Decoder::detail::detail<ASIMDMatcher<V>>::GetMatcher(&V::fn, name, bitstring),
#include "asimd.inc"
#undef INST
};
// Exceptions to the rule of thumb.
const std::set<std::string> comes_first{
"VBIC, VMOV, VMVN, VORR (immediate)",
"VEXT",
"VTBL",
"VTBX",
"VDUP (scalar)",
};
const std::set<std::string> comes_last{
"VMLA (scalar)",
"VMLAL (scalar)",
"VQDMLAL/VQDMLSL (scalar)",
"VMUL (scalar)",
"VMULL (scalar)",
"VQDMULL (scalar)",
"VQDMULH (scalar)",
"VQRDMULH (scalar)",
};
const auto sort_begin = std::stable_partition(table.begin(), table.end(), [&](const auto& matcher) {
return comes_first.count(matcher.GetName()) > 0;
});
const auto sort_end = std::stable_partition(table.begin(), table.end(), [&](const auto& matcher) {
return comes_last.count(matcher.GetName()) == 0;
});
// If a matcher has more bits in its mask it is more specific, so it should come first.
std::stable_sort(sort_begin, sort_end, [](const auto& matcher1, const auto& matcher2) {
return Common::BitCount(matcher1.GetMask()) > Common::BitCount(matcher2.GetMask());
});
return table;
}
template<typename V>
std::optional<std::reference_wrapper<const ASIMDMatcher<V>>> DecodeASIMD(u32 instruction) {
static const auto table = GetASIMDDecodeTable<V>();
const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); };
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
return iter != table.end() ? std::optional<std::reference_wrapper<const ASIMDMatcher<V>>>(*iter) : std::nullopt;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,167 @@
// Three registers of the same length
INST(asimd_VHADD, "VHADD", "1111001U0Dzznnnndddd0000NQM0mmmm") // ASIMD
INST(asimd_VQADD, "VQADD", "1111001U0Dzznnnndddd0000NQM1mmmm") // ASIMD
INST(asimd_VRHADD, "VRHADD", "1111001U0Dzznnnndddd0001NQM0mmmm") // ASIMD
INST(asimd_VAND_reg, "VAND (register)", "111100100D00nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VBIC_reg, "VBIC (register)", "111100100D01nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VORR_reg, "VORR (register)", "111100100D10nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VORN_reg, "VORN (register)", "111100100D11nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VEOR_reg, "VEOR (register)", "111100110D00nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VBSL, "VBSL", "111100110D01nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VBIT, "VBIT", "111100110D10nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VBIF, "VBIF", "111100110D11nnnndddd0001NQM1mmmm") // ASIMD
INST(asimd_VHSUB, "VHSUB", "1111001U0Dzznnnndddd0010NQM0mmmm") // ASIMD
INST(asimd_VQSUB, "VQSUB", "1111001U0Dzznnnndddd0010NQM1mmmm") // ASIMD
INST(asimd_VCGT_reg, "VCGT (register)", "1111001U0Dzznnnndddd0011NQM0mmmm") // ASIMD
INST(asimd_VCGE_reg, "VCGE (register)", "1111001U0Dzznnnndddd0011NQM1mmmm") // ASIMD
INST(asimd_VSHL_reg, "VSHL (register)", "1111001U0Dzznnnndddd0100NQM0mmmm") // ASIMD
INST(asimd_VQSHL_reg, "VQSHL (register)", "1111001U0Dzznnnndddd0100NQM1mmmm") // ASIMD
INST(asimd_VRSHL, "VRSHL", "1111001U0Dzznnnndddd0101NQM0mmmm") // ASIMD
//INST(asimd_VQRSHL, "VQRSHL", "1111001U0-CC--------0101---1----") // ASIMD
INST(asimd_VMAX, "VMAX/VMIN (integer)", "1111001U0Dzznnnnmmmm0110NQMommmm") // ASIMD
INST(asimd_VABD, "VABD", "1111001U0Dzznnnndddd0111NQM0mmmm") // ASIMD
INST(asimd_VABA, "VABA", "1111001U0Dzznnnndddd0111NQM1mmmm") // ASIMD
INST(asimd_VADD_int, "VADD (integer)", "111100100Dzznnnndddd1000NQM0mmmm") // ASIMD
INST(asimd_VSUB_int, "VSUB (integer)", "111100110Dzznnnndddd1000NQM0mmmm") // ASIMD
INST(asimd_VTST, "VTST", "111100100Dzznnnndddd1000NQM1mmmm") // ASIMD
INST(asimd_VCEQ_reg, "VCEG (register)", "111100110Dzznnnndddd1000NQM1mmmm") // ASIMD
INST(asimd_VMLA, "VMLA/VMLS", "1111001o0Dzznnnndddd1001NQM0mmmm") // ASIMD
INST(asimd_VMUL, "VMUL", "1111001P0Dzznnnndddd1001NQM1mmmm") // ASIMD
INST(asimd_VPMAX_int, "VPMAX/VPMIN (integer)", "1111001U0Dzznnnndddd1010NQMommmm") // ASIMD
INST(asimd_VQDMULH, "VQDMULH", "111100100Dzznnnndddd1011NQM0mmmm") // ASIMD
INST(asimd_VQRDMULH, "VQRDMULH", "111100110Dzznnnndddd1011NQM0mmmm") // ASIMD
INST(asimd_VPADD, "VPADD", "111100100Dzznnnndddd1011NQM1mmmm") // ASIMD
INST(asimd_VFMA, "VFMA", "111100100D0znnnndddd1100NQM1mmmm") // ASIMD
INST(asimd_VFMS, "VFMS", "111100100D1znnnndddd1100NQM1mmmm") // ASIMD
INST(asimd_VADD_float, "VADD (floating-point)", "111100100D0znnnndddd1101NQM0mmmm") // ASIMD
INST(asimd_VSUB_float, "VSUB (floating-point)", "111100100D1znnnndddd1101NQM0mmmm") // ASIMD
INST(asimd_VPADD_float, "VPADD (floating-point)", "111100110D0znnnndddd1101NQM0mmmm") // ASIMD
INST(asimd_VABD_float, "VABD (floating-point)", "111100110D1znnnndddd1101NQM0mmmm") // ASIMD
INST(asimd_VMLA_float, "VMLA (floating-point)", "111100100D0znnnndddd1101NQM1mmmm") // ASIMD
INST(asimd_VMLS_float, "VMLS (floating-point)", "111100100D1znnnndddd1101NQM1mmmm") // ASIMD
INST(asimd_VMUL_float, "VMUL (floating-point)", "111100110D0znnnndddd1101NQM1mmmm") // ASIMD
INST(asimd_VCEQ_reg_float, "VCEQ (register)", "111100100D0znnnndddd1110NQM0mmmm") // ASIMD
INST(asimd_VCGE_reg_float, "VCGE (register)", "111100110D0znnnndddd1110NQM0mmmm") // ASIMD
INST(asimd_VCGT_reg_float, "VCGT (register)", "111100110D1znnnndddd1110NQM0mmmm") // ASIMD
INST(asimd_VACGE, "VACGE", "111100110Doznnnndddd1110NQM1mmmm") // ASIMD
INST(asimd_VMAX_float, "VMAX (floating-point)", "111100100D0znnnndddd1111NQM0mmmm") // ASIMD
INST(asimd_VMIN_float, "VMIN (floating-point)", "111100100D1znnnndddd1111NQM0mmmm") // ASIMD
INST(asimd_VPMAX_float, "VPMAX (floating-point)", "111100110D0znnnndddd1111NQM0mmmm") // ASIMD
INST(asimd_VPMIN_float, "VPMIN (floating-point)", "111100110D1znnnndddd1111NQM0mmmm") // ASIMD
INST(asimd_VRECPS, "VRECPS", "111100100D0znnnndddd1111NQM1mmmm") // ASIMD
INST(asimd_VRSQRTS, "VRSQRTS", "111100100D1znnnndddd1111NQM1mmmm") // ASIMD
// Three registers of different lengths
INST(asimd_VADDL, "VADDL/VADDW", "1111001U1Dzznnnndddd000oN0M0mmmm") // ASIMD
INST(asimd_VSUBL, "VSUBL/VSUBW", "1111001U1Dzznnnndddd001oN0M0mmmm") // ASIMD
//INST(asimd_VADDHN, "VADDHN", "111100101-----------0100-0-0----") // ASIMD
//INST(asimd_VRADDHN, "VRADDHN", "111100111-----------0100-0-0----") // ASIMD
INST(asimd_VABAL, "VABAL", "1111001U1Dzznnnndddd0101N0M0mmmm") // ASIMD
//INST(asimd_VSUBHN, "VSUBHN", "111100101-----------0110-0-0----") // ASIMD
//INST(asimd_VRSUBHN, "VRSUBHN", "111100111-----------0110-0-0----") // ASIMD
INST(asimd_VABDL, "VABDL", "1111001U1Dzznnnndddd0111N0M0mmmm") // ASIMD
INST(asimd_VMLAL, "VMLAL/VMLSL", "1111001U1Dzznnnndddd10o0N0M0mmmm") // ASIMD
//INST(asimd_VQDMLAL, "VQDMLAL", "111100101-----------10-1-0-0----") // ASIMD
INST(asimd_VMULL, "VMULL", "1111001U1Dzznnnndddd11P0N0M0mmmm") // ASIMD
//INST(asimd_VQDMULL, "VQDMULL", "111100101-----------1101-0-0----") // ASIMD
// Two registers and a scalar
INST(asimd_VMLA_scalar, "VMLA (scalar)", "1111001Q1Dzznnnndddd0o0FN1M0mmmm") // ASIMD
INST(asimd_VMLAL_scalar, "VMLAL (scalar)", "1111001U1dzznnnndddd0o10N1M0mmmm") // ASIMD
//INST(asimd_VQDMLAL_scalar, "VQDMLAL/VQDMLSL (scalar)", "111100101-BB--------0x11-1-0----") // ASIMD
INST(asimd_VMUL_scalar, "VMUL (scalar)", "1111001Q1Dzznnnndddd100FN1M0mmmm") // ASIMD
INST(asimd_VMULL_scalar, "VMULL (scalar)", "1111001U1Dzznnnndddd1010N1M0mmmm") // ASIMD
INST(asimd_VQDMULL_scalar, "VQDMULL (scalar)", "111100101Dzznnnndddd1011N1M0mmmm") // ASIMD
INST(asimd_VQDMULH_scalar, "VQDMULH (scalar)", "1111001Q1Dzznnnndddd1100N1M0mmmm") // ASIMD
INST(asimd_VQRDMULH_scalar, "VQRDMULH (scalar)", "1111001Q1Dzznnnndddd1101N1M0mmmm") // ASIMD
// Two registers and a shift amount
INST(asimd_SHR, "SHR", "1111001U1Diiiiiidddd0000LQM1mmmm") // ASIMD
INST(asimd_SRA, "SRA", "1111001U1Diiiiiidddd0001LQM1mmmm") // ASIMD
INST(asimd_VRSHR, "VRSHR", "1111001U1Diiiiiidddd0010LQM1mmmm") // ASIMD
INST(asimd_VRSRA, "VRSRA", "1111001U1Diiiiiidddd0011LQM1mmmm") // ASIMD
INST(asimd_VSRI, "VSRI", "111100111Diiiiiidddd0100LQM1mmmm") // ASIMD
INST(asimd_VSHL, "VSHL", "111100101Diiiiiidddd0101LQM1mmmm") // ASIMD
INST(asimd_VSLI, "VSLI", "111100111Diiiiiidddd0101LQM1mmmm") // ASIMD
INST(asimd_VQSHL, "VQSHL" , "1111001U1Diiiiiidddd011oLQM1mmmm") // ASIMD
INST(asimd_VSHRN, "VSHRN", "111100101Diiiiiidddd100000M1mmmm") // ASIMD
INST(asimd_VRSHRN, "VRSHRN", "111100101Diiiiiidddd100001M1mmmm") // ASIMD
INST(asimd_VQSHRUN, "VQSHRUN", "111100111Diiiiiidddd100000M1mmmm") // ASIMD
INST(asimd_VQRSHRUN, "VQRSHRUN", "111100111Diiiiiidddd100001M1mmmm") // ASIMD
INST(asimd_VQSHRN, "VQSHRN", "1111001U1Diiiiiidddd100100M1mmmm") // ASIMD
INST(asimd_VQRSHRN, "VQRSHRN", "1111001U1Diiiiiidddd100101M1mmmm") // ASIMD
INST(asimd_VSHLL, "VSHLL", "1111001U1Diiiiiidddd101000M1mmmm") // ASIMD
INST(asimd_VCVT_fixed, "VCVT (fixed-point)", "1111001U1Diiiiiidddd111o0QM1mmmm") // ASIMD
// Two registers, miscellaneous
INST(asimd_VREV, "VREV{16,32,64}", "111100111D11zz00dddd000ooQM0mmmm") // ASIMD
INST(asimd_VPADDL, "VPADDL", "111100111D11zz00dddd0010oQM0mmmm") // ASIMD
INST(asimd_VCLS, "VCLS", "111100111D11zz00dddd01000QM0mmmm") // ASIMD
INST(asimd_VCLZ, "VCLZ", "111100111D11zz00dddd01001QM0mmmm") // ASIMD
INST(asimd_VCNT, "VCNT", "111100111D11zz00dddd01010QM0mmmm") // ASIMD
INST(asimd_VMVN_reg, "VMVN_reg", "111100111D11zz00dddd01011QM0mmmm") // ASIMD
INST(asimd_VPADAL, "VPADAL", "111100111D11zz00dddd0110oQM0mmmm") // ASIMD
INST(asimd_VQABS, "VQABS", "111100111D11zz00dddd01110QM0mmmm") // ASIMD
INST(asimd_VQNEG, "VQNEG", "111100111D11zz00dddd01111QM0mmmm") // ASIMD
INST(asimd_VCGT_zero, "VCGT (zero)", "111100111D11zz01dddd0F000QM0mmmm") // ASIMD
INST(asimd_VCGE_zero, "VCGE (zero)", "111100111D11zz01dddd0F001QM0mmmm") // ASIMD
INST(asimd_VCEQ_zero, "VCEQ (zero)", "111100111D11zz01dddd0F010QM0mmmm") // ASIMD
INST(asimd_VCLE_zero, "VCLE (zero)", "111100111D11zz01dddd0F011QM0mmmm") // ASIMD
INST(asimd_VCLT_zero, "VCLT (zero)", "111100111D11zz01dddd0F100QM0mmmm") // ASIMD
INST(arm_UDF, "UNALLOCATED", "111100111-11--01----01101--0----") // v8
INST(asimd_VABS, "VABS", "111100111D11zz01dddd0F110QM0mmmm") // ASIMD
INST(asimd_VNEG, "VNEG", "111100111D11zz01dddd0F111QM0mmmm") // ASIMD
INST(asimd_VSWP, "VSWP", "111100111D110010dddd00000QM0mmmm") // ASIMD
INST(arm_UDF, "UNALLOCATED", "111100111-11--10----00000--0----") // ASIMD
INST(asimd_VTRN, "VTRN", "111100111D11zz10dddd00001QM0mmmm") // ASIMD
INST(asimd_VUZP, "VUZP", "111100111D11zz10dddd00010QM0mmmm") // ASIMD
INST(asimd_VZIP, "VZIP", "111100111D11zz10dddd00011QM0mmmm") // ASIMD
INST(asimd_VMOVN, "VMOVN", "111100111D11zz10dddd001000M0mmmm") // ASIMD
INST(asimd_VQMOVUN, "VQMOVUN", "111100111D11zz10dddd001001M0mmmm") // ASIMD
INST(asimd_VQMOVN, "VQMOVN", "111100111D11zz10dddd00101oM0mmmm") // ASIMD
INST(asimd_VSHLL_max, "VSHLL_max", "111100111D11zz10dddd001100M0mmmm") // ASIMD
INST(arm_UDF, "UNALLOCATED (VRINTN)", "111100111-11--10----01000--0----")
INST(arm_UDF, "UNALLOCATED (VRINTX)", "111100111-11--10----01001--0----")
INST(arm_UDF, "UNALLOCATED (VRINTA)", "111100111-11--10----01010--0----")
INST(arm_UDF, "UNALLOCATED (VRINTZ)", "111100111-11--10----01011--0----")
INST(arm_UDF, "UNALLOCATED (VRINTM)", "111100111-11--10----01101--0----")
INST(arm_UDF, "UNALLOCATED (VRINTP)", "111100111-11--10----01111--0----")
INST(arm_UDF, "UNALLOCATED (VCVT half)", "111100111-11--10----011-00-0----") // ASIMD
INST(arm_UDF, "UNALLOCATED", "111100111-11--10----011-01-0----") // ASIMD
INST(arm_UDF, "UNALLOCATED (VCVTA)", "111100111-11--11----0000---0----")
INST(arm_UDF, "UNALLOCATED (VCVTN)", "111100111-11--11----0001---0----")
INST(arm_UDF, "UNALLOCATED (VCVTP)", "111100111-11--11----0010---0----")
INST(arm_UDF, "UNALLOCATED (VCVTM)", "111100111-11--11----0011---0----")
INST(asimd_VRECPE, "VRECPE", "111100111D11zz11dddd010F0QM0mmmm") // ASIMD
INST(asimd_VRSQRTE, "VRSQRTE", "111100111D11zz11dddd010F1QM0mmmm") // ASIMD
INST(asimd_VCVT_integer, "VCVT (integer)", "111100111D11zz11dddd011oUQM0mmmm") // ASIMD
// Two registers, cryptography
INST(v8_AESE, "AESE", "111100111D11zz00dddd001100M0mmmm") // v8
INST(v8_AESD, "AESD", "111100111D11zz00dddd001101M0mmmm") // v8
INST(v8_AESMC, "AESMC", "111100111D11zz00dddd001110M0mmmm") // v8
INST(v8_AESIMC, "AESIMC", "111100111D11zz00dddd001111M0mmmm") // v8
INST(arm_UDF, "UNALLOCATED", "111100111-11--01----001010-0----") // v8
INST(arm_UDF, "UNALLOCATED (SHA1H)", "111100111-11--01----001011-0----") // v8
INST(arm_UDF, "UNALLOCATED (SHA1SU1)", "111100111-11--10----001110-0----") // v8
INST(arm_UDF, "UNALLOCATED (SHA256SU0)", "111100111-11--10----001111-0----") // v8
// One register and modified immediate
INST(asimd_VMOV_imm, "VBIC, VMOV, VMVN, VORR (immediate)", "1111001a1D000bcdVVVVmmmm0Qo1efgh") // ASIMD
// Miscellaneous
INST(asimd_VEXT, "VEXT", "111100101D11nnnnddddiiiiNQM0mmmm") // ASIMD
INST(asimd_VTBL, "VTBL", "111100111D11nnnndddd10zzN0M0mmmm") // ASIMD
INST(asimd_VTBX, "VTBX", "111100111D11nnnndddd10zzN1M0mmmm") // ASIMD
INST(asimd_VDUP_scalar, "VDUP (scalar)", "111100111D11iiiidddd11000QM0mmmm") // ASIMD
INST(arm_UDF, "UNALLOCATED", "111100111-11--------11-----0----") // ASIMD
// Advanced SIMD load/store structures
INST(v8_VST_multiple, "VST{1-4} (multiple)", "111101000D00nnnnddddxxxxzzaammmm") // v8
INST(v8_VLD_multiple, "VLD{1-4} (multiple)", "111101000D10nnnnddddxxxxzzaammmm") // v8
INST(arm_UDF, "UNALLOCATED", "111101000--0--------1011--------") // v8
INST(arm_UDF, "UNALLOCATED", "111101000--0--------11----------") // v8
INST(arm_UDF, "UNALLOCATED", "111101001-00--------11----------") // v8
INST(v8_VLD_all_lanes, "VLD{1-4} (all lanes)", "111101001D10nnnndddd11nnzzTammmm") // v8
INST(v8_VST_single, "VST{1-4} (single)", "111101001D00nnnnddddzzNNaaaammmm") // v8
INST(v8_VLD_single, "VLD{1-4} (single)", "111101001D10nnnnddddzzNNaaaammmm") // v8

View File

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

View File

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

View File

@@ -0,0 +1,58 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2032 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <algorithm>
#include <functional>
#include <optional>
#include <vector>
#include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic::A32 {
template <typename Visitor>
using VFPMatcher = Decoder::Matcher<Visitor, u32>;
template<typename V>
std::optional<std::reference_wrapper<const VFPMatcher<V>>> DecodeVFP(u32 instruction) {
using Table = std::vector<VFPMatcher<V>>;
static const struct Tables {
Table unconditional;
Table conditional;
} tables = []{
Table list = {
#define INST(fn, name, bitstring) Decoder::detail::detail<VFPMatcher<V>>::GetMatcher(&V::fn, name, bitstring),
#include "vfp.inc"
#undef INST
};
const auto division = std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) {
return (matcher.GetMask() & 0xF0000000) == 0xF0000000;
});
return Tables{
Table{list.begin(), division},
Table{division, list.end()},
};
}();
const bool is_unconditional = (instruction & 0xF0000000) == 0xF0000000;
const Table& table = is_unconditional ? tables.unconditional : tables.conditional;
const auto matches_instruction = [instruction](const auto& matcher){ return matcher.Matches(instruction); };
auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
return iter != table.end() ? std::optional<std::reference_wrapper<const VFPMatcher<V>>>(*iter) : std::nullopt;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,71 @@
// Floating-point three-register data processing instructions
INST(vfp_VMLA, "VMLA", "cccc11100D00nnnndddd101zN0M0mmmm") // VFPv2
INST(vfp_VMLS, "VMLS", "cccc11100D00nnnndddd101zN1M0mmmm") // VFPv2
INST(vfp_VNMLS, "VNMLS", "cccc11100D01nnnndddd101zN0M0mmmm") // VFPv2
INST(vfp_VNMLA, "VNMLA", "cccc11100D01nnnndddd101zN1M0mmmm") // VFPv2
INST(vfp_VMUL, "VMUL", "cccc11100D10nnnndddd101zN0M0mmmm") // VFPv2
INST(vfp_VNMUL, "VNMUL", "cccc11100D10nnnndddd101zN1M0mmmm") // VFPv2
INST(vfp_VADD, "VADD", "cccc11100D11nnnndddd101zN0M0mmmm") // VFPv2
INST(vfp_VSUB, "VSUB", "cccc11100D11nnnndddd101zN1M0mmmm") // VFPv2
INST(vfp_VDIV, "VDIV", "cccc11101D00nnnndddd101zN0M0mmmm") // VFPv2
INST(vfp_VFNMS, "VFNMS", "cccc11101D01nnnndddd101zN0M0mmmm") // VFPv4
INST(vfp_VFNMA, "VFNMA", "cccc11101D01nnnndddd101zN1M0mmmm") // VFPv4
INST(vfp_VFMA, "VFMA", "cccc11101D10nnnndddd101zN0M0mmmm") // VFPv4
INST(vfp_VFMS, "VFMS", "cccc11101D10nnnndddd101zN1M0mmmm") // VFPv4
INST(vfp_VSEL, "VSEL", "111111100Dccnnnndddd101zN0M0mmmm") // VFPv5
INST(vfp_VMAXNM, "VMAXNNM", "111111101D00nnnndddd101zN0M0mmmm") // VFPv5
INST(vfp_VMINNM, "VMINNM", "111111101D00nnnndddd101zN1M0mmmm") // VFPv5
// Other floating-point data-processing instructions
INST(vfp_VMOV_imm, "VMOV (immediate)", "cccc11101D11vvvvdddd101z0000vvvv") // VFPv3
INST(vfp_VMOV_reg, "VMOV (reg)", "cccc11101D110000dddd101z01M0mmmm") // VFPv2
INST(vfp_VABS, "VABS", "cccc11101D110000dddd101z11M0mmmm") // VFPv2
INST(vfp_VNEG, "VNEG", "cccc11101D110001dddd101z01M0mmmm") // VFPv2
INST(vfp_VSQRT, "VSQRT", "cccc11101D110001dddd101z11M0mmmm") // VFPv2
INST(vfp_VCVTB, "VCVTB", "cccc11101D11001odddd101z01M0mmmm") // VFPv3HP
INST(vfp_VCVTT, "VCVTT", "cccc11101D11001odddd101z11M0mmmm") // VFPv3HP
INST(vfp_VCMP, "VCMP", "cccc11101D110100dddd101zE1M0mmmm") // VFPv2
INST(vfp_VCMP_zero, "VCMP (with zero)", "cccc11101D110101dddd101zE1000000") // VFPv2
INST(vfp_VRINTR, "VRINTR", "cccc11101D110110dddd101z01M0mmmm") // VFPv5
INST(vfp_VRINTZ, "VRINTZ", "cccc11101D110110dddd101z11M0mmmm") // VFPv5
INST(vfp_VRINTX, "VRINTX", "cccc11101D110111dddd101z01M0mmmm") // VFPv5
INST(vfp_VCVT_f_to_f, "VCVT (f32<->f64)", "cccc11101D110111dddd101z11M0mmmm") // VFPv2
INST(vfp_VCVT_from_int, "VCVT (from int)", "cccc11101D111000dddd101zs1M0mmmm") // VFPv2
INST(vfp_VCVT_from_fixed, "VCVT (from fixed)", "cccc11101D11101Udddd101zx1i0vvvv") // VFPv3
INST(vfp_VCVT_to_u32, "VCVT (to u32)", "cccc11101D111100dddd101zr1M0mmmm") // VFPv2
INST(vfp_VCVT_to_s32, "VCVT (to s32)", "cccc11101D111101dddd101zr1M0mmmm") // VFPv2
INST(vfp_VCVT_to_fixed, "VCVT (to fixed)", "cccc11101D11111Udddd101zx1i0vvvv") // VFPv3
INST(vfp_VRINT_rm, "VRINT{A,N,P,M}", "111111101D1110mmdddd101z01M0mmmm") // VFPv5
INST(vfp_VCVT_rm, "VCVT{A,N,P,M}", "111111101D1111mmdddd101zU1M0mmmm") // VFPv5
// Floating-point move instructions
INST(vfp_VMOV_u32_f64, "VMOV (core to f64)", "cccc11100000ddddtttt1011D0010000") // VFPv2
INST(vfp_VMOV_f64_u32, "VMOV (f64 to core)", "cccc11100001nnnntttt1011N0010000") // VFPv2
INST(vfp_VMOV_u32_f32, "VMOV (core to f32)", "cccc11100000nnnntttt1010N0010000") // VFPv2
INST(vfp_VMOV_f32_u32, "VMOV (f32 to core)", "cccc11100001nnnntttt1010N0010000") // VFPv2
INST(vfp_VMOV_2u32_2f32, "VMOV (2xcore to 2xf32)", "cccc11000100uuuutttt101000M1mmmm") // VFPv2
INST(vfp_VMOV_2f32_2u32, "VMOV (2xf32 to 2xcore)", "cccc11000101uuuutttt101000M1mmmm") // VFPv2
INST(vfp_VMOV_2u32_f64, "VMOV (2xcore to f64)", "cccc11000100uuuutttt101100M1mmmm") // VFPv2
INST(vfp_VMOV_f64_2u32, "VMOV (f64 to 2xcore)", "cccc11000101uuuutttt101100M1mmmm") // VFPv2
INST(vfp_VMOV_from_i32, "VMOV (core to i32)" , "cccc111000i0nnnntttt1011N0010000") // VFPv4
INST(vfp_VMOV_from_i16, "VMOV (core to i16)" , "cccc111000i0nnnntttt1011Ni110000") // ASIMD
INST(vfp_VMOV_from_i8, "VMOV (core to i8)", "cccc111001i0nnnntttt1011Nii10000") // ASIMD
INST(vfp_VMOV_to_i32, "VMOV (i32 to core)" , "cccc111000i1nnnntttt1011N0010000") // VFPv4
INST(vfp_VMOV_to_i16, "VMOV (i16 to core)" , "cccc1110U0i1nnnntttt1011Ni110000") // ASIMD
INST(vfp_VMOV_to_i8, "VMOV (i8 to core)", "cccc1110U1i1nnnntttt1011Nii10000") // ASIMD
INST(vfp_VDUP, "VDUP (from core)", "cccc11101BQ0ddddtttt1011D0E10000") // ASIMD
// Floating-point system register access
INST(vfp_VMSR, "VMSR", "cccc111011100001tttt101000010000") // VFPv2
INST(vfp_VMRS, "VMRS", "cccc111011110001tttt101000010000") // VFPv2
// Extension register load-store instructions
INST(vfp_VPUSH, "VPUSH", "cccc11010D101101dddd101zvvvvvvvv") // VFPv2
INST(vfp_VPOP, "VPOP", "cccc11001D111101dddd101zvvvvvvvv") // VFPv2
INST(vfp_VLDR, "VLDR", "cccc1101UD01nnnndddd101zvvvvvvvv") // VFPv2
INST(vfp_VSTR, "VSTR", "cccc1101UD00nnnndddd101zvvvvvvvv") // VFPv2
INST(arm_UDF, "Undefined VSTM/VLDM", "----11000-0---------101---------") // VFPv2
INST(vfp_VSTM_a1, "VSTM (A1)", "cccc110puDw0nnnndddd1011vvvvvvvv") // VFPv2
INST(vfp_VSTM_a2, "VSTM (A2)", "cccc110puDw0nnnndddd1010vvvvvvvv") // VFPv2
INST(vfp_VLDM_a1, "VLDM (A1)", "cccc110puDw1nnnndddd1011vvvvvvvv") // VFPv2
INST(vfp_VLDM_a2, "VLDM (A2)", "cccc110puDw1nnnndddd1010vvvvvvvv") // VFPv2

View File

@@ -0,0 +1,17 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <string>
#include "common/common_types.h"
namespace Dynarmic::A32 {
std::string DisassembleArm(u32 instruction);
std::string DisassembleThumb16(u16 instruction);
} // namespace Dynarmic::A32

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,376 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <cstdlib>
#include <string>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include "common/bit_util.h"
#include "common/string_util.h"
#include "frontend/imm.h"
#include "frontend/A32/decoder/thumb16.h"
#include "frontend/A32/disassembler/disassembler.h"
#include "frontend/A32/types.h"
namespace Dynarmic::A32 {
class DisassemblerVisitor {
public:
using instruction_return_type = std::string;
std::string thumb16_LSL_imm(Imm<5> imm5, Reg m, Reg d) {
return fmt::format("lsls {}, {}, #{}", d, m, imm5.ZeroExtend());
}
std::string thumb16_LSR_imm(Imm<5> imm5, Reg m, Reg d) {
const u32 shift = imm5 != 0 ? imm5.ZeroExtend() : 32U;
return fmt::format("lsrs {}, {}, #{}", d, m, shift);
}
std::string thumb16_ASR_imm(Imm<5> imm5, Reg m, Reg d) {
const u32 shift = imm5 != 0 ? imm5.ZeroExtend() : 32U;
return fmt::format("asrs {}, {}, #{}", d, m, shift);
}
std::string thumb16_ADD_reg_t1(Reg m, Reg n, Reg d) {
return fmt::format("adds {}, {}, {}", d, n, m);
}
std::string thumb16_SUB_reg(Reg m, Reg n, Reg d) {
return fmt::format("subs {}, {}, {}", d, n, m);
}
std::string thumb16_ADD_imm_t1(Imm<3> imm3, Reg n, Reg d) {
return fmt::format("adds {}, {}, #{}", d, n, imm3.ZeroExtend());
}
std::string thumb16_SUB_imm_t1(Imm<3> imm3, Reg n, Reg d) {
return fmt::format("subs {}, {}, #{}", d, n, imm3.ZeroExtend());
}
std::string thumb16_MOV_imm(Reg d, Imm<8> imm8) {
return fmt::format("movs {}, #{}", d, imm8.ZeroExtend());
}
std::string thumb16_CMP_imm(Reg n, Imm<8> imm8) {
return fmt::format("cmp {}, #{}", n, imm8.ZeroExtend());
}
std::string thumb16_ADD_imm_t2(Reg d_n, Imm<8> imm8) {
return fmt::format("adds {}, #{}", d_n, imm8.ZeroExtend());
}
std::string thumb16_SUB_imm_t2(Reg d_n, Imm<8> imm8) {
return fmt::format("subs {}, #{}", d_n, imm8.ZeroExtend());
}
std::string thumb16_AND_reg(Reg m, Reg d_n) {
return fmt::format("ands {}, {}", d_n, m);
}
std::string thumb16_EOR_reg(Reg m, Reg d_n) {
return fmt::format("eors {}, {}", d_n, m);
}
std::string thumb16_LSL_reg(Reg m, Reg d_n) {
return fmt::format("lsls {}, {}", d_n, m);
}
std::string thumb16_LSR_reg(Reg m, Reg d_n) {
return fmt::format("lsrs {}, {}", d_n, m);
}
std::string thumb16_ASR_reg(Reg m, Reg d_n) {
return fmt::format("asrs {}, {}", d_n, m);
}
std::string thumb16_ADC_reg(Reg m, Reg d_n) {
return fmt::format("adcs {}, {}", d_n, m);
}
std::string thumb16_SBC_reg(Reg m, Reg d_n) {
return fmt::format("sbcs {}, {}", d_n, m);
}
std::string thumb16_ROR_reg(Reg m, Reg d_n) {
return fmt::format("rors {}, {}", d_n, m);
}
std::string thumb16_TST_reg(Reg m, Reg n) {
return fmt::format("tst {}, {}", n, m);
}
std::string thumb16_RSB_imm(Reg n, Reg d) {
// Pre-UAL syntax: NEGS <Rd>, <Rn>
return fmt::format("rsbs {}, {}, #0", d, n);
}
std::string thumb16_CMP_reg_t1(Reg m, Reg n) {
return fmt::format("cmp {}, {}", n, m);
}
std::string thumb16_CMN_reg(Reg m, Reg n) {
return fmt::format("cmn {}, {}", n, m);
}
std::string thumb16_ORR_reg(Reg m, Reg d_n) {
return fmt::format("orrs {}, {}", d_n, m);
}
std::string thumb16_MUL_reg(Reg n, Reg d_m) {
return fmt::format("muls {}, {}, {}", d_m, n, d_m);
}
std::string thumb16_BIC_reg(Reg m, Reg d_n) {
return fmt::format("bics {}, {}", d_n, m);
}
std::string thumb16_MVN_reg(Reg m, Reg d) {
return fmt::format("mvns {}, {}", d, m);
}
std::string thumb16_ADD_reg_t2(bool d_n_hi, Reg m, Reg d_n_lo) {
const Reg d_n = d_n_hi ? (d_n_lo + 8) : d_n_lo;
return fmt::format("add {}, {}", d_n, m);
}
std::string thumb16_CMP_reg_t2(bool n_hi, Reg m, Reg n_lo) {
const Reg n = n_hi ? (n_lo + 8) : n_lo;
return fmt::format("cmp {}, {}", n, m);
}
std::string thumb16_MOV_reg(bool d_hi, Reg m, Reg d_lo) {
const Reg d = d_hi ? (d_lo + 8) : d_lo;
return fmt::format("mov {}, {}", d, m);
}
std::string thumb16_LDR_literal(Reg t, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend() << 2;
return fmt::format("ldr {}, [pc, #{}]", t, imm32);
}
std::string thumb16_STR_reg(Reg m, Reg n, Reg t) {
return fmt::format("str {}, [{}, {}]", t, n, m);
}
std::string thumb16_STRH_reg(Reg m, Reg n, Reg t) {
return fmt::format("strh {}, [{}, {}]", t, n, m);
}
std::string thumb16_STRB_reg(Reg m, Reg n, Reg t) {
return fmt::format("strb {}, [{}, {}]", t, n, m);
}
std::string thumb16_LDRSB_reg(Reg m, Reg n, Reg t) {
return fmt::format("ldrsb {}, [{}, {}]", t, n, m);
}
std::string thumb16_LDR_reg(Reg m, Reg n, Reg t) {
return fmt::format("ldr {}, [{}, {}]", t, n, m);
}
std::string thumb16_LDRH_reg(Reg m, Reg n, Reg t) {
return fmt::format("ldrh {}, [%s, %s]", t, n, m);
}
std::string thumb16_LDRB_reg(Reg m, Reg n, Reg t) {
return fmt::format("ldrb {}, [{}, {}]", t, n, m);
}
std::string thumb16_LDRSH_reg(Reg m, Reg n, Reg t) {
return fmt::format("ldrsh {}, [{}, {}]", t, n, m);
}
std::string thumb16_STR_imm_t1(Imm<5> imm5, Reg n, Reg t) {
const u32 imm32 = imm5.ZeroExtend() << 2;
return fmt::format("str {}, [{}, #{}]", t, n, imm32);
}
std::string thumb16_LDR_imm_t1(Imm<5> imm5, Reg n, Reg t) {
const u32 imm32 = imm5.ZeroExtend() << 2;
return fmt::format("ldr {}, [{}, #{}]", t, n, imm32);
}
std::string thumb16_STRB_imm(Imm<5> imm5, Reg n, Reg t) {
const u32 imm32 = imm5.ZeroExtend();
return fmt::format("strb {}, [{}, #{}]", t, n, imm32);
}
std::string thumb16_LDRB_imm(Imm<5> imm5, Reg n, Reg t) {
const u32 imm32 = imm5.ZeroExtend();
return fmt::format("ldrb {}, [{}, #{}]", t, n, imm32);
}
std::string thumb16_STRH_imm(Imm<5> imm5, Reg n, Reg t) {
const u32 imm32 = imm5.ZeroExtend() << 1;
return fmt::format("strh {}, [{}, #{}]", t, n, imm32);
}
std::string thumb16_LDRH_imm(Imm<5> imm5, Reg n, Reg t) {
const u32 imm32 = imm5.ZeroExtend() << 1;
return fmt::format("ldrh {}, [{}, #{}]", t, n, imm32);
}
std::string thumb16_STR_imm_t2(Reg t, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend() << 2;
return fmt::format("str {}, [sp, #{}]", t, imm32);
}
std::string thumb16_LDR_imm_t2(Reg t, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend() << 2;
return fmt::format("ldr {}, [sp, #{}]", t, imm32);
}
std::string thumb16_ADR(Reg d, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend() << 2;
return fmt::format("adr {}, +#{}", d, imm32);
}
std::string thumb16_ADD_sp_t1(Reg d, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend() << 2;
return fmt::format("add {}, sp, #{}", d, imm32);
}
std::string thumb16_ADD_sp_t2(Imm<7> imm7) {
const u32 imm32 = imm7.ZeroExtend() << 2;
return fmt::format("add sp, sp, #{}", imm32);
}
std::string thumb16_SUB_sp(Imm<7> imm7) {
const u32 imm32 = imm7.ZeroExtend() << 2;
return fmt::format("sub sp, sp, #{}", imm32);
}
std::string thumb16_NOP() {
return "nop";
}
std::string thumb16_SEV() {
return "sev";
}
std::string thumb16_SEVL() {
return "sevl";
}
std::string thumb16_WFE() {
return "wfe";
}
std::string thumb16_WFI() {
return "wfi";
}
std::string thumb16_YIELD() {
return "yield";
}
std::string thumb16_SXTH(Reg m, Reg d) {
return fmt::format("sxth {}, {}", d, m);
}
std::string thumb16_SXTB(Reg m, Reg d) {
return fmt::format("sxtb {}, {}", d, m);
}
std::string thumb16_UXTH(Reg m, Reg d) {
return fmt::format("uxth {}, {}", d, m);
}
std::string thumb16_UXTB(Reg m, Reg d) {
return fmt::format("uxtb {}, {}", d, m);
}
std::string thumb16_PUSH(bool M, RegList reg_list) {
if (M) reg_list |= 1 << 14;
return fmt::format("push {{{}}}", RegListToString(reg_list));
}
std::string thumb16_POP(bool P, RegList reg_list) {
if (P) reg_list |= 1 << 15;
return fmt::format("pop {{{}}}", RegListToString(reg_list));
}
std::string thumb16_SETEND(bool E) {
return fmt::format("setend {}", E ? "BE" : "LE");
}
std::string thumb16_CPS(bool im, bool a, bool i, bool f) {
return fmt::format("cps{} {}{}{}", im ? "id" : "ie", a ? "a" : "", i ? "i" : "", f ? "f" : "");
}
std::string thumb16_REV(Reg m, Reg d) {
return fmt::format("rev {}, {}", d, m);
}
std::string thumb16_REV16(Reg m, Reg d) {
return fmt::format("rev16 {}, {}", d, m);
}
std::string thumb16_REVSH(Reg m, Reg d) {
return fmt::format("revsh {}, {}", d, m);
}
std::string thumb16_BKPT(Imm<8> imm8) {
return fmt::format("bkpt #{}", imm8.ZeroExtend());
}
std::string thumb16_STMIA(Reg n, RegList reg_list) {
return fmt::format("stm {}!, {{{}}}", n, RegListToString(reg_list));
}
std::string thumb16_LDMIA(Reg n, RegList reg_list) {
const bool write_back = !Common::Bit(static_cast<size_t>(n), reg_list);
return fmt::format("ldm {}{}, {{{}}}", n, write_back ? "!" : "", RegListToString(reg_list));
}
std::string thumb16_BX(Reg m) {
return fmt::format("bx {}", m);
}
std::string thumb16_BLX_reg(Reg m) {
return fmt::format("blx {}", m);
}
std::string thumb16_CBZ_CBNZ(bool nonzero, Imm<1> i, Imm<5> imm5, Reg n) {
const char* const name = nonzero ? "cbnz" : "cbz";
const u32 imm = concatenate(i, imm5, Imm<1>{0}).ZeroExtend();
return fmt::format("{} {}, #{}", name, n, imm);
}
std::string thumb16_UDF() {
return fmt::format("udf");
}
std::string thumb16_SVC(Imm<8> imm8) {
return fmt::format("svc #{}", imm8.ZeroExtend());
}
std::string thumb16_B_t1(Cond cond, Imm<8> imm8) {
const s32 imm32 = static_cast<s32>((imm8.SignExtend<u32>() << 1) + 4);
return fmt::format("b{} {}#{}",
CondToString(cond),
Common::SignToChar(imm32),
abs(imm32));
}
std::string thumb16_B_t2(Imm<11> imm11) {
const s32 imm32 = static_cast<s32>((imm11.SignExtend<u32>() << 1) + 4);
return fmt::format("b {}#{}",
Common::SignToChar(imm32),
abs(imm32));
}
};
std::string DisassembleThumb16(u16 instruction) {
DisassemblerVisitor visitor;
auto decoder = DecodeThumb16<DisassemblerVisitor>(instruction);
return !decoder ? fmt::format("UNKNOWN: {:x}", instruction) : decoder->get().call(visitor, instruction);
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,400 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/assert.h"
#include "frontend/A32/ir_emitter.h"
#include "frontend/A32/types.h"
#include "frontend/ir/opcodes.h"
namespace Dynarmic::A32 {
using Opcode = IR::Opcode;
u32 IREmitter::PC() const {
const u32 offset = current_location.TFlag() ? 4 : 8;
return current_location.PC() + offset;
}
u32 IREmitter::AlignPC(size_t alignment) const {
const u32 pc = PC();
return static_cast<u32>(pc - pc % alignment);
}
IR::U32 IREmitter::GetRegister(Reg reg) {
if (reg == A32::Reg::PC) {
return Imm32(PC());
}
return Inst<IR::U32>(Opcode::A32GetRegister, IR::Value(reg));
}
IR::U32U64 IREmitter::GetExtendedRegister(ExtReg reg) {
if (A32::IsSingleExtReg(reg)) {
return Inst<IR::U32U64>(Opcode::A32GetExtendedRegister32, IR::Value(reg));
}
if (A32::IsDoubleExtReg(reg)) {
return Inst<IR::U32U64>(Opcode::A32GetExtendedRegister64, IR::Value(reg));
}
ASSERT_FALSE("Invalid reg.");
}
IR::U128 IREmitter::GetVector(ExtReg reg) {
ASSERT(A32::IsDoubleExtReg(reg) || A32::IsQuadExtReg(reg));
return Inst<IR::U128>(Opcode::A32GetVector, IR::Value(reg));
}
void IREmitter::SetRegister(const Reg reg, const IR::U32& value) {
ASSERT(reg != A32::Reg::PC);
Inst(Opcode::A32SetRegister, IR::Value(reg), value);
}
void IREmitter::SetExtendedRegister(const ExtReg reg, const IR::U32U64& value) {
if (A32::IsSingleExtReg(reg)) {
Inst(Opcode::A32SetExtendedRegister32, IR::Value(reg), value);
} else if (A32::IsDoubleExtReg(reg)) {
Inst(Opcode::A32SetExtendedRegister64, IR::Value(reg), value);
} else {
ASSERT_FALSE("Invalid reg.");
}
}
void IREmitter::SetVector(ExtReg reg, const IR::U128& value) {
ASSERT(A32::IsDoubleExtReg(reg) || A32::IsQuadExtReg(reg));
Inst(Opcode::A32SetVector, IR::Value(reg), value);
}
void IREmitter::ALUWritePC(const IR::U32& value) {
// This behaviour is ARM version-dependent.
// The below implementation is for ARMv6k
BranchWritePC(value);
}
void IREmitter::BranchWritePC(const IR::U32& value) {
if (!current_location.TFlag()) {
const auto new_pc = And(value, Imm32(0xFFFFFFFC));
Inst(Opcode::A32SetRegister, IR::Value(A32::Reg::PC), new_pc);
} else {
const auto new_pc = And(value, Imm32(0xFFFFFFFE));
Inst(Opcode::A32SetRegister, IR::Value(A32::Reg::PC), new_pc);
}
}
void IREmitter::BXWritePC(const IR::U32& value) {
Inst(Opcode::A32BXWritePC, value);
}
void IREmitter::LoadWritePC(const IR::U32& value) {
// This behaviour is ARM version-dependent.
// The below implementation is for ARMv6k
BXWritePC(value);
}
void IREmitter::CallSupervisor(const IR::U32& value) {
Inst(Opcode::A32CallSupervisor, value);
}
void IREmitter::ExceptionRaised(const Exception exception) {
Inst(Opcode::A32ExceptionRaised, Imm32(current_location.PC()), Imm64(static_cast<u64>(exception)));
}
IR::U32 IREmitter::GetCpsr() {
return Inst<IR::U32>(Opcode::A32GetCpsr);
}
void IREmitter::SetCpsr(const IR::U32& value) {
Inst(Opcode::A32SetCpsr, value);
}
void IREmitter::SetCpsrNZCV(const IR::U32& value) {
Inst(Opcode::A32SetCpsrNZCV, value);
}
void IREmitter::SetCpsrNZCVQ(const IR::U32& value) {
Inst(Opcode::A32SetCpsrNZCVQ, value);
}
void IREmitter::SetCheckBit(const IR::U1& value) {
Inst(Opcode::A32SetCheckBit, value);
}
IR::U1 IREmitter::GetCFlag() {
return Inst<IR::U1>(Opcode::A32GetCFlag);
}
void IREmitter::SetNFlag(const IR::U1& value) {
Inst(Opcode::A32SetNFlag, value);
}
void IREmitter::SetZFlag(const IR::U1& value) {
Inst(Opcode::A32SetZFlag, value);
}
void IREmitter::SetCFlag(const IR::U1& value) {
Inst(Opcode::A32SetCFlag, value);
}
void IREmitter::SetVFlag(const IR::U1& value) {
Inst(Opcode::A32SetVFlag, value);
}
void IREmitter::OrQFlag(const IR::U1& value) {
Inst(Opcode::A32OrQFlag, value);
}
IR::U32 IREmitter::GetGEFlags() {
return Inst<IR::U32>(Opcode::A32GetGEFlags);
}
void IREmitter::SetGEFlags(const IR::U32& value) {
Inst(Opcode::A32SetGEFlags, value);
}
void IREmitter::SetGEFlagsCompressed(const IR::U32& value) {
Inst(Opcode::A32SetGEFlagsCompressed, value);
}
void IREmitter::DataSynchronizationBarrier() {
Inst(Opcode::A32DataSynchronizationBarrier);
}
void IREmitter::DataMemoryBarrier() {
Inst(Opcode::A32DataMemoryBarrier);
}
void IREmitter::InstructionSynchronizationBarrier() {
Inst(Opcode::A32InstructionSynchronizationBarrier);
}
IR::U32 IREmitter::GetFpscr() {
return Inst<IR::U32>(Opcode::A32GetFpscr);
}
void IREmitter::SetFpscr(const IR::U32& new_fpscr) {
Inst(Opcode::A32SetFpscr, new_fpscr);
}
IR::U32 IREmitter::GetFpscrNZCV() {
return Inst<IR::U32>(Opcode::A32GetFpscrNZCV);
}
void IREmitter::SetFpscrNZCV(const IR::NZCV& new_fpscr_nzcv) {
Inst(Opcode::A32SetFpscrNZCV, new_fpscr_nzcv);
}
void IREmitter::ClearExclusive() {
Inst(Opcode::A32ClearExclusive);
}
IR::UAny IREmitter::ReadMemory(size_t bitsize, const IR::U32& vaddr) {
switch (bitsize) {
case 8:
return ReadMemory8(vaddr);
case 16:
return ReadMemory16(vaddr);
case 32:
return ReadMemory32(vaddr);
case 64:
return ReadMemory64(vaddr);
}
ASSERT_FALSE("Invalid bitsize");
}
IR::U8 IREmitter::ReadMemory8(const IR::U32& vaddr) {
return Inst<IR::U8>(Opcode::A32ReadMemory8, vaddr);
}
IR::U16 IREmitter::ReadMemory16(const IR::U32& vaddr) {
const auto value = Inst<IR::U16>(Opcode::A32ReadMemory16, vaddr);
return current_location.EFlag() ? ByteReverseHalf(value) : value;
}
IR::U32 IREmitter::ReadMemory32(const IR::U32& vaddr) {
const auto value = Inst<IR::U32>(Opcode::A32ReadMemory32, vaddr);
return current_location.EFlag() ? ByteReverseWord(value) : value;
}
IR::U64 IREmitter::ReadMemory64(const IR::U32& vaddr) {
const auto value = Inst<IR::U64>(Opcode::A32ReadMemory64, vaddr);
return current_location.EFlag() ? ByteReverseDual(value) : value;
}
IR::U8 IREmitter::ExclusiveReadMemory8(const IR::U32& vaddr) {
return Inst<IR::U8>(Opcode::A32ExclusiveReadMemory8, vaddr);
}
IR::U16 IREmitter::ExclusiveReadMemory16(const IR::U32& vaddr) {
const auto value = Inst<IR::U16>(Opcode::A32ExclusiveReadMemory16, vaddr);
return current_location.EFlag() ? ByteReverseHalf(value) : value;
}
IR::U32 IREmitter::ExclusiveReadMemory32(const IR::U32& vaddr) {
const auto value = Inst<IR::U32>(Opcode::A32ExclusiveReadMemory32, vaddr);
return current_location.EFlag() ? ByteReverseWord(value) : value;
}
std::pair<IR::U32, IR::U32> IREmitter::ExclusiveReadMemory64(const IR::U32& vaddr) {
const auto value = Inst<IR::U64>(Opcode::A32ExclusiveReadMemory64, vaddr);
const auto lo = LeastSignificantWord(value);
const auto hi = MostSignificantWord(value).result;
if (current_location.EFlag()) {
// DO NOT SWAP hi AND lo IN BIG ENDIAN MODE, THIS IS CORRECT BEHAVIOUR
return std::make_pair(ByteReverseWord(lo), ByteReverseWord(hi));
}
return std::make_pair(lo, hi);
}
void IREmitter::WriteMemory(size_t bitsize, const IR::U32& vaddr, const IR::UAny& value) {
switch (bitsize) {
case 8:
return WriteMemory8(vaddr, value);
case 16:
return WriteMemory16(vaddr, value);
case 32:
return WriteMemory32(vaddr, value);
case 64:
return WriteMemory64(vaddr, value);
}
ASSERT_FALSE("Invalid bitsize");
}
void IREmitter::WriteMemory8(const IR::U32& vaddr, const IR::U8& value) {
Inst(Opcode::A32WriteMemory8, vaddr, value);
}
void IREmitter::WriteMemory16(const IR::U32& vaddr, const IR::U16& value) {
if (current_location.EFlag()) {
const auto v = ByteReverseHalf(value);
Inst(Opcode::A32WriteMemory16, vaddr, v);
} else {
Inst(Opcode::A32WriteMemory16, vaddr, value);
}
}
void IREmitter::WriteMemory32(const IR::U32& vaddr, const IR::U32& value) {
if (current_location.EFlag()) {
const auto v = ByteReverseWord(value);
Inst(Opcode::A32WriteMemory32, vaddr, v);
} else {
Inst(Opcode::A32WriteMemory32, vaddr, value);
}
}
void IREmitter::WriteMemory64(const IR::U32& vaddr, const IR::U64& value) {
if (current_location.EFlag()) {
const auto v = ByteReverseDual(value);
Inst(Opcode::A32WriteMemory64, vaddr, v);
} else {
Inst(Opcode::A32WriteMemory64, vaddr, value);
}
}
IR::U32 IREmitter::ExclusiveWriteMemory8(const IR::U32& vaddr, const IR::U8& value) {
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory8, vaddr, value);
}
IR::U32 IREmitter::ExclusiveWriteMemory16(const IR::U32& vaddr, const IR::U16& value) {
if (current_location.EFlag()) {
const auto v = ByteReverseHalf(value);
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory16, vaddr, v);
} else {
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory16, vaddr, value);
}
}
IR::U32 IREmitter::ExclusiveWriteMemory32(const IR::U32& vaddr, const IR::U32& value) {
if (current_location.EFlag()) {
const auto v = ByteReverseWord(value);
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory32, vaddr, v);
} else {
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory32, vaddr, value);
}
}
IR::U32 IREmitter::ExclusiveWriteMemory64(const IR::U32& vaddr, const IR::U32& value_lo, const IR::U32& value_hi) {
if (current_location.EFlag()) {
const auto vlo = ByteReverseWord(value_lo);
const auto vhi = ByteReverseWord(value_hi);
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory64, vaddr, Pack2x32To1x64(vlo, vhi));
} else {
return Inst<IR::U32>(Opcode::A32ExclusiveWriteMemory64, vaddr, Pack2x32To1x64(value_lo, value_hi));
}
}
void IREmitter::CoprocInternalOperation(size_t coproc_no, bool two, size_t opc1, CoprocReg CRd, CoprocReg CRn, CoprocReg CRm, size_t opc2) {
ASSERT(coproc_no <= 15);
const IR::Value::CoprocessorInfo coproc_info{static_cast<u8>(coproc_no),
static_cast<u8>(two ? 1 : 0),
static_cast<u8>(opc1),
static_cast<u8>(CRd),
static_cast<u8>(CRn),
static_cast<u8>(CRm),
static_cast<u8>(opc2)};
Inst(Opcode::A32CoprocInternalOperation, IR::Value(coproc_info));
}
void IREmitter::CoprocSendOneWord(size_t coproc_no, bool two, size_t opc1, CoprocReg CRn, CoprocReg CRm, size_t opc2, const IR::U32& word) {
ASSERT(coproc_no <= 15);
const IR::Value::CoprocessorInfo coproc_info{static_cast<u8>(coproc_no),
static_cast<u8>(two ? 1 : 0),
static_cast<u8>(opc1),
static_cast<u8>(CRn),
static_cast<u8>(CRm),
static_cast<u8>(opc2)};
Inst(Opcode::A32CoprocSendOneWord, IR::Value(coproc_info), word);
}
void IREmitter::CoprocSendTwoWords(size_t coproc_no, bool two, size_t opc, CoprocReg CRm, const IR::U32& word1, const IR::U32& word2) {
ASSERT(coproc_no <= 15);
const IR::Value::CoprocessorInfo coproc_info{static_cast<u8>(coproc_no),
static_cast<u8>(two ? 1 : 0),
static_cast<u8>(opc),
static_cast<u8>(CRm)};
Inst(Opcode::A32CoprocSendTwoWords, IR::Value(coproc_info), word1, word2);
}
IR::U32 IREmitter::CoprocGetOneWord(size_t coproc_no, bool two, size_t opc1, CoprocReg CRn, CoprocReg CRm, size_t opc2) {
ASSERT(coproc_no <= 15);
const IR::Value::CoprocessorInfo coproc_info{static_cast<u8>(coproc_no),
static_cast<u8>(two ? 1 : 0),
static_cast<u8>(opc1),
static_cast<u8>(CRn),
static_cast<u8>(CRm),
static_cast<u8>(opc2)};
return Inst<IR::U32>(Opcode::A32CoprocGetOneWord, IR::Value(coproc_info));
}
IR::U64 IREmitter::CoprocGetTwoWords(size_t coproc_no, bool two, size_t opc, CoprocReg CRm) {
ASSERT(coproc_no <= 15);
const IR::Value::CoprocessorInfo coproc_info{static_cast<u8>(coproc_no),
static_cast<u8>(two ? 1 : 0),
static_cast<u8>(opc),
static_cast<u8>(CRm)};
return Inst<IR::U64>(Opcode::A32CoprocGetTwoWords, IR::Value(coproc_info));
}
void IREmitter::CoprocLoadWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option) {
ASSERT(coproc_no <= 15);
const IR::Value::CoprocessorInfo coproc_info{static_cast<u8>(coproc_no),
static_cast<u8>(two ? 1 : 0),
static_cast<u8>(long_transfer ? 1 : 0),
static_cast<u8>(CRd),
static_cast<u8>(has_option ? 1 : 0),
static_cast<u8>(option)};
Inst(Opcode::A32CoprocLoadWords, IR::Value(coproc_info), address);
}
void IREmitter::CoprocStoreWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option) {
ASSERT(coproc_no <= 15);
const IR::Value::CoprocessorInfo coproc_info{static_cast<u8>(coproc_no),
static_cast<u8>(two ? 1 : 0),
static_cast<u8>(long_transfer ? 1 : 0),
static_cast<u8>(CRd),
static_cast<u8>(has_option ? 1 : 0),
static_cast<u8>(option)};
Inst(Opcode::A32CoprocStoreWords, IR::Value(coproc_info), address);
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,104 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <utility>
#include "common/common_types.h"
#include "frontend/A32/location_descriptor.h"
#include "frontend/ir/ir_emitter.h"
#include "frontend/ir/value.h"
namespace Dynarmic::A32 {
enum class CoprocReg;
enum class Exception;
enum class ExtReg;
enum class Reg;
/**
* Convenience class to construct a basic block of the intermediate representation.
* `block` is the resulting block.
* The user of this class updates `current_location` as appropriate.
*/
class IREmitter : public IR::IREmitter {
public:
explicit IREmitter(IR::Block& block, LocationDescriptor descriptor) : IR::IREmitter(block), current_location(descriptor) {}
LocationDescriptor current_location;
u32 PC() const;
u32 AlignPC(size_t alignment) const;
IR::U32 GetRegister(Reg source_reg);
IR::U32U64 GetExtendedRegister(ExtReg source_reg);
IR::U128 GetVector(ExtReg source_reg);
void SetRegister(Reg dest_reg, const IR::U32& value);
void SetExtendedRegister(ExtReg dest_reg, const IR::U32U64& value);
void SetVector(ExtReg dest_reg, const IR::U128& value);
void ALUWritePC(const IR::U32& value);
void BranchWritePC(const IR::U32& value);
void BXWritePC(const IR::U32& value);
void LoadWritePC(const IR::U32& value);
void CallSupervisor(const IR::U32& value);
void ExceptionRaised(Exception exception);
IR::U32 GetCpsr();
void SetCpsr(const IR::U32& value);
void SetCpsrNZCV(const IR::U32& value);
void SetCpsrNZCVQ(const IR::U32& value);
void SetCheckBit(const IR::U1& value);
IR::U1 GetCFlag();
void SetNFlag(const IR::U1& value);
void SetZFlag(const IR::U1& value);
void SetCFlag(const IR::U1& value);
void SetVFlag(const IR::U1& value);
void OrQFlag(const IR::U1& value);
IR::U32 GetGEFlags();
void SetGEFlags(const IR::U32& value);
void SetGEFlagsCompressed(const IR::U32& value);
void DataSynchronizationBarrier();
void DataMemoryBarrier();
void InstructionSynchronizationBarrier();
IR::U32 GetFpscr();
void SetFpscr(const IR::U32& new_fpscr);
IR::U32 GetFpscrNZCV();
void SetFpscrNZCV(const IR::NZCV& new_fpscr_nzcv);
void ClearExclusive();
IR::UAny ReadMemory(size_t bitsize, const IR::U32& vaddr);
IR::U8 ReadMemory8(const IR::U32& vaddr);
IR::U16 ReadMemory16(const IR::U32& vaddr);
IR::U32 ReadMemory32(const IR::U32& vaddr);
IR::U64 ReadMemory64(const IR::U32& vaddr);
IR::U8 ExclusiveReadMemory8(const IR::U32& vaddr);
IR::U16 ExclusiveReadMemory16(const IR::U32& vaddr);
IR::U32 ExclusiveReadMemory32(const IR::U32& vaddr);
std::pair<IR::U32, IR::U32> ExclusiveReadMemory64(const IR::U32& vaddr);
void WriteMemory(size_t bitsize, const IR::U32& vaddr, const IR::UAny& value);
void WriteMemory8(const IR::U32& vaddr, const IR::U8& value);
void WriteMemory16(const IR::U32& vaddr, const IR::U16& value);
void WriteMemory32(const IR::U32& vaddr, const IR::U32& value);
void WriteMemory64(const IR::U32& vaddr, const IR::U64& value);
IR::U32 ExclusiveWriteMemory8(const IR::U32& vaddr, const IR::U8& value);
IR::U32 ExclusiveWriteMemory16(const IR::U32& vaddr, const IR::U16& value);
IR::U32 ExclusiveWriteMemory32(const IR::U32& vaddr, const IR::U32& value);
IR::U32 ExclusiveWriteMemory64(const IR::U32& vaddr, const IR::U32& value_lo, const IR::U32& value_hi);
void CoprocInternalOperation(size_t coproc_no, bool two, size_t opc1, CoprocReg CRd, CoprocReg CRn, CoprocReg CRm, size_t opc2);
void CoprocSendOneWord(size_t coproc_no, bool two, size_t opc1, CoprocReg CRn, CoprocReg CRm, size_t opc2, const IR::U32& word);
void CoprocSendTwoWords(size_t coproc_no, bool two, size_t opc, CoprocReg CRm, const IR::U32& word1, const IR::U32& word2);
IR::U32 CoprocGetOneWord(size_t coproc_no, bool two, size_t opc1, CoprocReg CRn, CoprocReg CRm, size_t opc2);
IR::U64 CoprocGetTwoWords(size_t coproc_no, bool two, size_t opc, CoprocReg CRm);
void CoprocLoadWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option);
void CoprocStoreWords(size_t coproc_no, bool two, bool long_transfer, CoprocReg CRd, const IR::U32& address, bool has_option, u8 option);
};
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,22 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <ostream>
#include <fmt/format.h>
#include "frontend/A32/location_descriptor.h"
namespace Dynarmic::A32 {
std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) {
o << fmt::format("{{{:08x},{},{},{:08x}{}}}",
descriptor.PC(),
descriptor.TFlag() ? "T" : "!T",
descriptor.EFlag() ? "E" : "!E",
descriptor.FPSCR().Value(),
descriptor.SingleStepping() ? ",step" : "");
return o;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,149 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <functional>
#include <iosfwd>
#include <tuple>
#include "common/common_types.h"
#include "frontend/A32/FPSCR.h"
#include "frontend/A32/PSR.h"
#include "frontend/A32/ITState.h"
#include "frontend/ir/location_descriptor.h"
namespace Dynarmic::A32 {
/**
* LocationDescriptor describes the location of a basic block.
* The location is not solely based on the PC because other flags influence the way
* instructions should be translated. The CPSR.T flag is most notable since it
* tells us if the processor is in Thumb or Arm mode.
*/
class LocationDescriptor {
public:
// Indicates bits that should be preserved within descriptors.
static constexpr u32 CPSR_MODE_MASK = 0x0600FE20;
static constexpr u32 FPSCR_MODE_MASK = 0x07F70000;
LocationDescriptor(u32 arm_pc, PSR cpsr, FPSCR fpscr, bool single_stepping = false)
: arm_pc(arm_pc)
, cpsr(cpsr.Value() & CPSR_MODE_MASK)
, fpscr(fpscr.Value() & FPSCR_MODE_MASK)
, single_stepping(single_stepping)
{}
explicit LocationDescriptor(const IR::LocationDescriptor& o) {
arm_pc = static_cast<u32>(o.Value());
cpsr.T((o.Value() >> 32) & 1);
cpsr.E((o.Value() >> 32) & 2);
fpscr = (o.Value() >> 32) & FPSCR_MODE_MASK;
cpsr.IT(ITState{static_cast<u8>(o.Value() >> 40)});
single_stepping = (o.Value() >> 32) & 4;
}
u32 PC() const { return arm_pc; }
bool TFlag() const { return cpsr.T(); }
bool EFlag() const { return cpsr.E(); }
ITState IT() const { return cpsr.IT(); }
A32::PSR CPSR() const { return cpsr; }
A32::FPSCR FPSCR() const { return fpscr; }
bool SingleStepping() const { return single_stepping; }
bool operator == (const LocationDescriptor& o) const {
return std::tie(arm_pc, cpsr, fpscr, single_stepping) == std::tie(o.arm_pc, o.cpsr, o.fpscr, single_stepping);
}
bool operator != (const LocationDescriptor& o) const {
return !operator==(o);
}
LocationDescriptor SetPC(u32 new_arm_pc) const {
return LocationDescriptor(new_arm_pc, cpsr, fpscr, single_stepping);
}
LocationDescriptor AdvancePC(int amount) const {
return LocationDescriptor(static_cast<u32>(arm_pc + amount), cpsr, fpscr, single_stepping);
}
LocationDescriptor SetTFlag(bool new_tflag) const {
PSR new_cpsr = cpsr;
new_cpsr.T(new_tflag);
return LocationDescriptor(arm_pc, new_cpsr, fpscr, single_stepping);
}
LocationDescriptor SetEFlag(bool new_eflag) const {
PSR new_cpsr = cpsr;
new_cpsr.E(new_eflag);
return LocationDescriptor(arm_pc, new_cpsr, fpscr, single_stepping);
}
LocationDescriptor SetFPSCR(u32 new_fpscr) const {
return LocationDescriptor(arm_pc, cpsr, A32::FPSCR{new_fpscr & FPSCR_MODE_MASK}, single_stepping);
}
LocationDescriptor AdvanceIT() const {
PSR new_cpsr = cpsr;
new_cpsr.IT(new_cpsr.IT().Advance());
return LocationDescriptor(arm_pc, new_cpsr, fpscr, single_stepping);
}
LocationDescriptor SetSingleStepping(bool new_single_stepping) const {
return LocationDescriptor(arm_pc, cpsr, fpscr, new_single_stepping);
}
u64 UniqueHash() const noexcept {
// This value MUST BE UNIQUE.
// This calculation has to match up with EmitX64::EmitTerminalPopRSBHint
const u64 pc_u64 = arm_pc;
const u64 fpscr_u64 = fpscr.Value();
const u64 t_u64 = cpsr.T() ? 1 : 0;
const u64 e_u64 = cpsr.E() ? 2 : 0;
const u64 single_stepping_u64 = single_stepping ? 4 : 0;
const u64 it_u64 = u64(cpsr.IT().Value()) << 8;
const u64 upper = (fpscr_u64 | t_u64 | e_u64 | single_stepping_u64 | it_u64) << 32;
return pc_u64 | upper;
}
operator IR::LocationDescriptor() const {
return IR::LocationDescriptor{UniqueHash()};
}
private:
u32 arm_pc; ///< Current program counter value.
PSR cpsr; ///< Current program status register.
A32::FPSCR fpscr; ///< Floating point status control register.
bool single_stepping;
};
/**
* Provides a string representation of a LocationDescriptor.
*
* @param o Output stream
* @param descriptor The descriptor to get a string representation of
*/
std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor);
} // namespace Dynarmic::A32
namespace std {
template <>
struct less<Dynarmic::A32::LocationDescriptor> {
bool operator()(const Dynarmic::A32::LocationDescriptor& x, const Dynarmic::A32::LocationDescriptor& y) const noexcept {
return x.UniqueHash() < y.UniqueHash();
}
};
template <>
struct hash<Dynarmic::A32::LocationDescriptor> {
size_t operator()(const Dynarmic::A32::LocationDescriptor& x) const noexcept {
return std::hash<u64>()(x.UniqueHash());
}
};
} // namespace std

View File

@@ -0,0 +1,374 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2020 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
#include <optional>
#include <tuple>
#include "common/bit_util.h"
namespace Dynarmic::A32 {
namespace {
std::optional<std::tuple<size_t, size_t, size_t>> DecodeType(Imm<4> type, size_t size, size_t align) {
switch (type.ZeroExtend()) {
case 0b0111: // VST1 A1 / VLD1 A1
if (Common::Bit<1>(align)) {
return std::nullopt;
}
return std::tuple<size_t, size_t, size_t>{1, 1, 0};
case 0b1010: // VST1 A2 / VLD1 A2
if (align == 0b11) {
return std::nullopt;
}
return std::tuple<size_t, size_t, size_t>{1, 2, 0};
case 0b0110: // VST1 A3 / VLD1 A3
if (Common::Bit<1>(align)) {
return std::nullopt;
}
return std::tuple<size_t, size_t, size_t>{1, 3, 0};
case 0b0010: // VST1 A4 / VLD1 A4
return std::tuple<size_t, size_t, size_t>{1, 4, 0};
case 0b1000: // VST2 A1 / VLD2 A1
if (size == 0b11 || align == 0b11) {
return std::nullopt;
}
return std::tuple<size_t, size_t, size_t>{2, 1, 1};
case 0b1001: // VST2 A1 / VLD2 A1
if (size == 0b11 || align == 0b11) {
return std::nullopt;
}
return std::tuple<size_t, size_t, size_t>{2, 1, 2};
case 0b0011: // VST2 A2 / VLD2 A2
if (size == 0b11) {
return std::nullopt;
}
return std::tuple<size_t, size_t, size_t>{2, 2, 2};
case 0b0100: // VST3 / VLD3
if (size == 0b11 || Common::Bit<1>(align)) {
return std::nullopt;
}
return std::tuple<size_t, size_t, size_t>{3, 1, 1};
case 0b0101: // VST3 / VLD3
if (size == 0b11 || Common::Bit<1>(align)) {
return std::nullopt;
}
return std::tuple<size_t, size_t, size_t>{3, 1, 2};
case 0b0000: // VST4 / VLD4
if (size == 0b11) {
return std::nullopt;
}
return std::tuple<size_t, size_t, size_t>{4, 1, 1};
case 0b0001: // VST4 / VLD4
if (size == 0b11) {
return std::nullopt;
}
return std::tuple<size_t, size_t, size_t>{4, 1, 2};
}
ASSERT_FALSE("Decode error");
}
} // anoynmous namespace
bool ArmTranslatorVisitor::v8_VST_multiple(bool D, Reg n, size_t Vd, Imm<4> type, size_t size, size_t align, Reg m) {
if (type == 0b1011 || type.Bits<2, 3>() == 0b11) {
return DecodeError();
}
const auto decoded_type = DecodeType(type, size, align);
if (!decoded_type) {
return UndefinedInstruction();
}
const auto [nelem, regs, inc] = *decoded_type;
const ExtReg d = ToExtRegD(Vd, D);
const size_t d_last = RegNumber(d) + inc * (nelem - 1);
if (n == Reg::R15 || d_last + regs > 32) {
return UnpredictableInstruction();
}
[[maybe_unused]] const size_t alignment = align == 0 ? 1 : 4 << align;
const size_t ebytes = static_cast<size_t>(1) << size;
const size_t elements = 8 / ebytes;
const bool wback = m != Reg::R15;
const bool register_index = m != Reg::R15 && m != Reg::R13;
IR::U32 address = ir.GetRegister(n);
for (size_t r = 0; r < regs; r++) {
for (size_t e = 0; e < elements; e++) {
for (size_t i = 0; i < nelem; i++) {
const ExtReg ext_reg = d + i * inc + r;
const IR::U64 shifted_element = ir.LogicalShiftRight(ir.GetExtendedRegister(ext_reg), ir.Imm8(static_cast<u8>(e * ebytes * 8)));
const IR::UAny element = ir.LeastSignificant(8 * ebytes, shifted_element);
ir.WriteMemory(8 * ebytes, address, element);
address = ir.Add(address, ir.Imm32(static_cast<u32>(ebytes)));
}
}
}
if (wback) {
if (register_index) {
ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.GetRegister(m)));
} else {
ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.Imm32(static_cast<u32>(8 * nelem * regs))));
}
}
return true;
}
bool ArmTranslatorVisitor::v8_VLD_multiple(bool D, Reg n, size_t Vd, Imm<4> type, size_t size, size_t align, Reg m) {
if (type == 0b1011 || type.Bits<2, 3>() == 0b11) {
return DecodeError();
}
const auto decoded_type = DecodeType(type, size, align);
if (!decoded_type) {
return UndefinedInstruction();
}
const auto [nelem, regs, inc] = *decoded_type;
const ExtReg d = ToExtRegD(Vd, D);
const size_t d_last = RegNumber(d) + inc * (nelem - 1);
if (n == Reg::R15 || d_last + regs > 32) {
return UnpredictableInstruction();
}
[[maybe_unused]] const size_t alignment = align == 0 ? 1 : 4 << align;
const size_t ebytes = static_cast<size_t>(1) << size;
const size_t elements = 8 / ebytes;
const bool wback = m != Reg::R15;
const bool register_index = m != Reg::R15 && m != Reg::R13;
for (size_t r = 0; r < regs; r++) {
for (size_t i = 0; i < nelem; i++) {
const ExtReg ext_reg = d + i * inc + r;
ir.SetExtendedRegister(ext_reg, ir.Imm64(0));
}
}
IR::U32 address = ir.GetRegister(n);
for (size_t r = 0; r < regs; r++) {
for (size_t e = 0; e < elements; e++) {
for (size_t i = 0; i < nelem; i++) {
const IR::U64 element = ir.ZeroExtendToLong(ir.ReadMemory(ebytes * 8, address));
const IR::U64 shifted_element = ir.LogicalShiftLeft(element, ir.Imm8(static_cast<u8>(e * ebytes * 8)));
const ExtReg ext_reg = d + i * inc + r;
ir.SetExtendedRegister(ext_reg, ir.Or(ir.GetExtendedRegister(ext_reg), shifted_element));
address = ir.Add(address, ir.Imm32(static_cast<u32>(ebytes)));
}
}
}
if (wback) {
if (register_index) {
ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.GetRegister(m)));
} else {
ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.Imm32(static_cast<u32>(8 * nelem * regs))));
}
}
return true;
}
bool ArmTranslatorVisitor::v8_VLD_all_lanes(bool D, Reg n, size_t Vd, size_t nn, size_t sz, bool T, bool a, Reg m) {
const size_t nelem = nn + 1;
if (nelem == 1 && (sz == 0b11 || (sz == 0b00 && a))) {
return UndefinedInstruction();
}
if (nelem == 2 && sz == 0b11) {
return UndefinedInstruction();
}
if (nelem == 3 && (sz == 0b11 || a)) {
return UndefinedInstruction();
}
if (nelem == 4 && (sz == 0b11 && !a)) {
return UndefinedInstruction();
}
const size_t ebytes = sz == 0b11 ? 4 : (1 << sz);
const size_t inc = T ? 2 : 1;
const size_t regs = nelem == 1 ? inc : 1;
[[maybe_unused]] const size_t alignment = [&]() -> size_t {
if (a && nelem == 1) {
return ebytes;
}
if (a && nelem == 2) {
return ebytes * 2;
}
if (a && nelem == 4) {
return sz >= 0b10 ? 2 * ebytes : 4 * ebytes;
}
return 1;
}();
const ExtReg d = ToExtRegD(Vd, D);
const size_t d_last = RegNumber(d) + inc * (nelem - 1);
if (n == Reg::R15 || d_last + regs > 32) {
return UnpredictableInstruction();
}
const bool wback = m != Reg::R15;
const bool register_index = m != Reg::R15 && m != Reg::R13;
auto address = ir.GetRegister(n);
for (size_t i = 0; i < nelem; i++) {
const auto element = ir.ReadMemory(ebytes * 8, address);
const auto replicated_element = ir.VectorBroadcast(ebytes * 8, element);
for (size_t r = 0; r < regs; r++) {
const ExtReg ext_reg = d + i * inc + r;
ir.SetVector(ext_reg, replicated_element);
}
address = ir.Add(address, ir.Imm32(static_cast<u32>(ebytes)));
}
if (wback) {
if (register_index) {
ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.GetRegister(m)));
} else {
ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.Imm32(static_cast<u32>(nelem * ebytes))));
}
}
return true;
}
bool ArmTranslatorVisitor::v8_VST_single(bool D, Reg n, size_t Vd, size_t sz, size_t nn, size_t index_align, Reg m) {
const size_t nelem = nn + 1;
if (sz == 0b11) {
return DecodeError();
}
if (nelem == 1 && Common::Bit(sz, index_align)) {
return UndefinedInstruction();
}
const size_t ebytes = size_t(1) << sz;
const size_t index = Common::Bits(sz + 1, 3, index_align);
const size_t inc = (sz != 0 && Common::Bit(sz, index_align)) ? 2 : 1;
const size_t a = Common::Bits(0, sz ? sz - 1 : 0, index_align);
if (nelem == 1 && inc == 2) {
return UndefinedInstruction();
}
if (nelem == 1 && sz == 2 && (a != 0b00 && a != 0b11)) {
return UndefinedInstruction();
}
if (nelem == 2 && Common::Bit<1>(a)) {
return UndefinedInstruction();
}
if (nelem == 3 && a != 0b00) {
return UndefinedInstruction();
}
if (nelem == 4 && a == 0b11) {
return UndefinedInstruction();
}
// TODO: alignment
const ExtReg d = ToExtRegD(Vd, D);
const size_t d_last = RegNumber(d) + inc * (nelem - 1);
if (n == Reg::R15 || d_last + 1 > 32) {
return UnpredictableInstruction();
}
const bool wback = m != Reg::R15;
const bool register_index = m != Reg::R15 && m != Reg::R13;
auto address = ir.GetRegister(n);
for (size_t i = 0; i < nelem; i++) {
const ExtReg ext_reg = d + i * inc;
const auto element = ir.VectorGetElement(ebytes * 8, ir.GetVector(ext_reg), index);
ir.WriteMemory(ebytes * 8, address, element);
address = ir.Add(address, ir.Imm32(static_cast<u32>(ebytes)));
}
if (wback) {
if (register_index) {
ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.GetRegister(m)));
} else {
ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.Imm32(static_cast<u32>(nelem * ebytes))));
}
}
return true;
}
bool ArmTranslatorVisitor::v8_VLD_single(bool D, Reg n, size_t Vd, size_t sz, size_t nn, size_t index_align, Reg m) {
const size_t nelem = nn + 1;
if (sz == 0b11) {
return DecodeError();
}
if (nelem == 1 && Common::Bit(sz, index_align)) {
return UndefinedInstruction();
}
const size_t ebytes = size_t(1) << sz;
const size_t index = Common::Bits(sz + 1, 3, index_align);
const size_t inc = (sz != 0 && Common::Bit(sz, index_align)) ? 2 : 1;
const size_t a = Common::Bits(0, sz ? sz - 1 : 0, index_align);
if (nelem == 1 && inc == 2) {
return UndefinedInstruction();
}
if (nelem == 1 && sz == 2 && (a != 0b00 && a != 0b11)) {
return UndefinedInstruction();
}
if (nelem == 2 && Common::Bit<1>(a)) {
return UndefinedInstruction();
}
if (nelem == 3 && a != 0b00) {
return UndefinedInstruction();
}
if (nelem == 4 && a == 0b11) {
return UndefinedInstruction();
}
// TODO: alignment
const ExtReg d = ToExtRegD(Vd, D);
const size_t d_last = RegNumber(d) + inc * (nelem - 1);
if (n == Reg::R15 || d_last + 1 > 32) {
return UnpredictableInstruction();
}
const bool wback = m != Reg::R15;
const bool register_index = m != Reg::R15 && m != Reg::R13;
auto address = ir.GetRegister(n);
for (size_t i = 0; i < nelem; i++) {
const auto element = ir.ReadMemory(ebytes * 8, address);
const ExtReg ext_reg = d + i * inc;
const auto new_reg = ir.VectorSetElement(ebytes * 8, ir.GetVector(ext_reg), index, element);
ir.SetVector(ext_reg, new_reg);
address = ir.Add(address, ir.Imm32(static_cast<u32>(ebytes)));
}
if (wback) {
if (register_index) {
ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.GetRegister(m)));
} else {
ir.SetRegister(n, ir.Add(ir.GetRegister(n), ir.Imm32(static_cast<u32>(nelem * ebytes))));
}
}
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,91 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2020 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/assert.h"
#include "common/bit_util.h"
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
static bool TableLookup(ArmTranslatorVisitor& v, bool is_vtbl, bool D, size_t Vn, size_t Vd, size_t len, bool N, bool M, size_t Vm) {
const size_t length = len + 1;
const auto d = ToVector(false, Vd, D);
const auto m = ToVector(false, Vm, M);
const auto n = ToVector(false, Vn, N);
if (RegNumber(n) + length > 32) {
return v.UnpredictableInstruction();
}
const IR::Table table = v.ir.VectorTable([&]{
std::vector<IR::U64> result;
for (size_t i = 0; i < length; ++i) {
result.emplace_back(v.ir.GetExtendedRegister(n + i));
}
return result;
}());
const IR::U64 indicies = v.ir.GetExtendedRegister(m);
const IR::U64 defaults = is_vtbl ? v.ir.Imm64(0) : IR::U64{v.ir.GetExtendedRegister(d)};
const IR::U64 result = v.ir.VectorTableLookup(defaults, table, indicies);
v.ir.SetExtendedRegister(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VEXT(bool D, size_t Vn, size_t Vd, Imm<4> imm4, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (!Q && imm4.Bit<3>()) {
return UndefinedInstruction();
}
const u8 position = 8 * imm4.ZeroExtend<u8>();
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_n = ir.GetVector(n);
const auto reg_m = ir.GetVector(m);
const auto result = Q ? ir.VectorExtract(reg_n, reg_m, position) : ir.VectorExtractLower(reg_n, reg_m, position);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VTBL(bool D, size_t Vn, size_t Vd, size_t len, bool N, bool M, size_t Vm) {
return TableLookup(*this, true, D, Vn, Vd, len, N, M, Vm);
}
bool ArmTranslatorVisitor::asimd_VTBX(bool D, size_t Vn, size_t Vd, size_t len, bool N, bool M, size_t Vm) {
return TableLookup(*this, false, D, Vn, Vd, len, N, M, Vm);
}
bool ArmTranslatorVisitor::asimd_VDUP_scalar(bool D, Imm<4> imm4, size_t Vd, bool Q, bool M, size_t Vm) {
if (Q && Common::Bit<0>(Vd)) {
return UndefinedInstruction();
}
if (imm4.Bits<0, 2>() == 0b000) {
return UndefinedInstruction();
}
const size_t imm4_lsb = Common::LowestSetBit(imm4.ZeroExtend());
const size_t esize = 8u << imm4_lsb;
const size_t index = imm4.ZeroExtend() >> (imm4_lsb + 1);
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(false, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto scalar = ir.VectorGetElement(esize, reg_m, index);
const auto result = ir.VectorBroadcast(esize, scalar);
ir.SetVector(d, result);
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,98 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2020 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/assert.h"
#include "common/bit_util.h"
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
bool ArmTranslatorVisitor::asimd_VMOV_imm(Imm<1> a, bool D, Imm<1> b, Imm<1> c, Imm<1> d, size_t Vd,
Imm<4> cmode, bool Q, bool op, Imm<1> e, Imm<1> f, Imm<1> g, Imm<1> h) {
if (Q && Common::Bit<0>(Vd)) {
return UndefinedInstruction();
}
const auto d_reg = ToVector(Q, Vd, D);
const auto imm = AdvSIMDExpandImm(op, cmode, concatenate(a, b, c, d, e, f, g, h));
// VMOV
const auto mov = [&] {
const auto imm64 = ir.Imm64(imm);
if (Q) {
ir.SetVector(d_reg, ir.VectorBroadcast(64, imm64));
} else {
ir.SetExtendedRegister(d_reg, imm64);
}
return true;
};
// VMVN
const auto mvn = [&] {
const auto imm64 = ir.Imm64(~imm);
if (Q) {
ir.SetVector(d_reg, ir.VectorBroadcast(64, imm64));
} else {
ir.SetExtendedRegister(d_reg, imm64);
}
return true;
};
// VORR
const auto orr = [&] {
const auto imm64 = ir.Imm64(imm);
if (Q) {
const auto reg_value = ir.GetVector(d_reg);
ir.SetVector(d_reg, ir.VectorOr(reg_value, ir.VectorBroadcast(64, imm64)));
} else {
const auto reg_value = ir.GetExtendedRegister(d_reg);
ir.SetExtendedRegister(d_reg, ir.Or(reg_value, imm64));
}
return true;
};
// VBIC
const auto bic = [&] {
const auto imm64 = ir.Imm64(~imm);
if (Q) {
const auto reg_value = ir.GetVector(d_reg);
ir.SetVector(d_reg, ir.VectorAnd(reg_value, ir.VectorBroadcast(64, imm64)));
} else {
const auto reg_value = ir.GetExtendedRegister(d_reg);
ir.SetExtendedRegister(d_reg, ir.And(reg_value, imm64));
}
return true;
};
switch (concatenate(cmode, Imm<1>{op}).ZeroExtend()) {
case 0b00000: case 0b00100:
case 0b01000: case 0b01100:
case 0b10000: case 0b10100:
case 0b11000: case 0b11010:
case 0b11100: case 0b11101:
case 0b11110:
return mov();
case 0b11111:
return UndefinedInstruction();
case 0b00001: case 0b00101:
case 0b01001: case 0b01101:
case 0b10001: case 0b10101:
case 0b11001: case 0b11011:
return mvn();
case 0b00010: case 0b00110:
case 0b01010: case 0b01110:
case 0b10010: case 0b10110:
return orr();
case 0b00011: case 0b00111:
case 0b01011: case 0b01111:
case 0b10011: case 0b10111:
return bic();
}
UNREACHABLE();
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,896 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2020 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/bit_util.h"
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
namespace {
enum class Comparison {
GE,
GT,
EQ,
AbsoluteGE,
AbsoluteGT,
};
enum class AccumulateBehavior {
None,
Accumulate,
};
enum class WidenBehaviour {
Second,
Both,
};
template <bool WithDst, typename Callable>
bool BitwiseInstruction(ArmTranslatorVisitor& v, bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm, Callable fn) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return v.UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
if constexpr (WithDst) {
const IR::U128 reg_d = v.ir.GetVector(d);
const IR::U128 reg_m = v.ir.GetVector(m);
const IR::U128 reg_n = v.ir.GetVector(n);
const IR::U128 result = fn(reg_d, reg_n, reg_m);
v.ir.SetVector(d, result);
} else {
const IR::U128 reg_m = v.ir.GetVector(m);
const IR::U128 reg_n = v.ir.GetVector(n);
const IR::U128 result = fn(reg_n, reg_m);
v.ir.SetVector(d, result);
}
return true;
}
template <typename Callable>
bool FloatingPointInstruction(ArmTranslatorVisitor& v, bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm, Callable fn) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return v.UndefinedInstruction();
}
if (sz == 0b1) {
return v.UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_d = v.ir.GetVector(d);
const auto reg_n = v.ir.GetVector(n);
const auto reg_m = v.ir.GetVector(m);
const auto result = fn(reg_d, reg_n, reg_m);
v.ir.SetVector(d, result);
return true;
}
bool IntegerComparison(ArmTranslatorVisitor& v, bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm,
Comparison comparison) {
if (sz == 0b11) {
return v.UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return v.UndefinedInstruction();
}
const size_t esize = 8 << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_n = v.ir.GetVector(n);
const auto reg_m = v.ir.GetVector(m);
const auto result = [&] {
switch (comparison) {
case Comparison::GT:
return U ? v.ir.VectorGreaterUnsigned(esize, reg_n, reg_m)
: v.ir.VectorGreaterSigned(esize, reg_n, reg_m);
case Comparison::GE:
return U ? v.ir.VectorGreaterEqualUnsigned(esize, reg_n, reg_m)
: v.ir.VectorGreaterEqualSigned(esize, reg_n, reg_m);
case Comparison::EQ:
return v.ir.VectorEqual(esize, reg_n, reg_m);
default:
return IR::U128{};
}
}();
v.ir.SetVector(d, result);
return true;
}
bool FloatComparison(ArmTranslatorVisitor& v, bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm,
Comparison comparison) {
if (sz) {
return v.UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return v.UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_n = v.ir.GetVector(n);
const auto reg_m = v.ir.GetVector(m);
const auto result = [&] {
switch (comparison) {
case Comparison::GE:
return v.ir.FPVectorGreaterEqual(32, reg_n, reg_m, false);
case Comparison::GT:
return v.ir.FPVectorGreater(32, reg_n, reg_m, false);
case Comparison::EQ:
return v.ir.FPVectorEqual(32, reg_n, reg_m, false);
case Comparison::AbsoluteGE:
return v.ir.FPVectorGreaterEqual(32, v.ir.FPVectorAbs(32, reg_n), v.ir.FPVectorAbs(32, reg_m), false);
case Comparison::AbsoluteGT:
return v.ir.FPVectorGreater(32, v.ir.FPVectorAbs(32, reg_n), v.ir.FPVectorAbs(32, reg_m), false);
default:
return IR::U128{};
}
}();
v.ir.SetVector(d, result);
return true;
}
bool AbsoluteDifference(ArmTranslatorVisitor& v, bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm,
AccumulateBehavior accumulate) {
if (sz == 0b11) {
return v.UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return v.UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_m = v.ir.GetVector(m);
const auto reg_n = v.ir.GetVector(n);
const auto result = [&] {
const auto absdiff = U ? v.ir.VectorUnsignedAbsoluteDifference(esize, reg_n, reg_m)
: v.ir.VectorSignedAbsoluteDifference(esize, reg_n, reg_m);
if (accumulate == AccumulateBehavior::Accumulate) {
const auto reg_d = v.ir.GetVector(d);
return v.ir.VectorAdd(esize, reg_d, absdiff);
}
return absdiff;
}();
v.ir.SetVector(d, result);
return true;
}
bool AbsoluteDifferenceLong(ArmTranslatorVisitor& v, bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm,
AccumulateBehavior accumulate) {
if (sz == 0b11) {
return v.DecodeError();
}
if (Common::Bit<0>(Vd)) {
return v.UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(true, Vd, D);
const auto m = ToVector(false, Vm, M);
const auto n = ToVector(false, Vn, N);
const auto reg_m = v.ir.GetVector(m);
const auto reg_n = v.ir.GetVector(n);
const auto operand_m = v.ir.VectorZeroExtend(esize, v.ir.ZeroExtendToQuad(v.ir.VectorGetElement(64, reg_m, 0)));
const auto operand_n = v.ir.VectorZeroExtend(esize, v.ir.ZeroExtendToQuad(v.ir.VectorGetElement(64, reg_n, 0)));
const auto result = [&] {
const auto absdiff = U ? v.ir.VectorUnsignedAbsoluteDifference(esize, operand_m, operand_n)
: v.ir.VectorSignedAbsoluteDifference(esize, operand_m, operand_n);
if (accumulate == AccumulateBehavior::Accumulate) {
const auto reg_d = v.ir.GetVector(d);
return v.ir.VectorAdd(2 * esize, reg_d, absdiff);
}
return absdiff;
}();
v.ir.SetVector(d, result);
return true;
}
template <typename Callable>
bool WideInstruction(ArmTranslatorVisitor& v, bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm, WidenBehaviour widen_behaviour, Callable fn) {
const size_t esize = 8U << sz;
const bool widen_first = widen_behaviour == WidenBehaviour::Both;
if (sz == 0b11) {
return v.DecodeError();
}
if (Common::Bit<0>(Vd) || (!widen_first && Common::Bit<0>(Vn))) {
return v.UndefinedInstruction();
}
const auto d = ToVector(true, Vd, D);
const auto m = ToVector(false, Vm, M);
const auto n = ToVector(!widen_first, Vn, N);
const auto reg_d = v.ir.GetVector(d);
const auto reg_m = v.ir.GetVector(m);
const auto reg_n = v.ir.GetVector(n);
const auto wide_n = U ? v.ir.VectorZeroExtend(esize, reg_n) : v.ir.VectorSignExtend(esize, reg_n);
const auto wide_m = U ? v.ir.VectorZeroExtend(esize, reg_m) : v.ir.VectorSignExtend(esize, reg_m);
const auto result = fn(esize * 2, reg_d, widen_first ? wide_n : reg_n, wide_m);
v.ir.SetVector(d, result);
return true;
}
} // Anonymous namespace
// ASIMD Three registers of the same length
bool ArmTranslatorVisitor::asimd_VHADD(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (sz == 0b11) {
return UndefinedInstruction();
}
const size_t esize = 8 << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const IR::U128 reg_n = ir.GetVector(n);
const IR::U128 reg_m = ir.GetVector(m);
const IR::U128 result = U ? ir.VectorHalvingAddUnsigned(esize, reg_n, reg_m) : ir.VectorHalvingAddSigned(esize, reg_n, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VQADD(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (sz == 0b11) {
return UndefinedInstruction();
}
const size_t esize = 8 << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const IR::U128 reg_n = ir.GetVector(n);
const IR::U128 reg_m = ir.GetVector(m);
const IR::U128 result = U ? ir.VectorUnsignedSaturatedAdd(esize, reg_n, reg_m) : ir.VectorSignedSaturatedAdd(esize, reg_n, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VRHADD(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (sz == 0b11) {
return UndefinedInstruction();
}
const size_t esize = 8 << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const IR::U128 reg_n = ir.GetVector(n);
const IR::U128 reg_m = ir.GetVector(m);
const IR::U128 result = U ? ir.VectorRoundingHalvingAddUnsigned(esize, reg_n, reg_m) : ir.VectorRoundingHalvingAddSigned(esize, reg_n, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VAND_reg(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return BitwiseInstruction<false>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_n, const auto& reg_m) {
return ir.VectorAnd(reg_n, reg_m);
});
}
bool ArmTranslatorVisitor::asimd_VBIC_reg(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return BitwiseInstruction<false>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_n, const auto& reg_m) {
return ir.VectorAnd(reg_n, ir.VectorNot(reg_m));
});
}
bool ArmTranslatorVisitor::asimd_VORR_reg(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return BitwiseInstruction<false>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_n, const auto& reg_m) {
return ir.VectorOr(reg_n, reg_m);
});
}
bool ArmTranslatorVisitor::asimd_VORN_reg(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return BitwiseInstruction<false>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_n, const auto& reg_m) {
return ir.VectorOr(reg_n, ir.VectorNot(reg_m));
});
}
bool ArmTranslatorVisitor::asimd_VEOR_reg(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return BitwiseInstruction<false>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_n, const auto& reg_m) {
return ir.VectorEor(reg_n, reg_m);
});
}
bool ArmTranslatorVisitor::asimd_VBSL(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return BitwiseInstruction<true>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) {
return ir.VectorOr(ir.VectorAnd(reg_n, reg_d), ir.VectorAnd(reg_m, ir.VectorNot(reg_d)));
});
}
bool ArmTranslatorVisitor::asimd_VBIT(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return BitwiseInstruction<true>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) {
return ir.VectorOr(ir.VectorAnd(reg_n, reg_m), ir.VectorAnd(reg_d, ir.VectorNot(reg_m)));
});
}
bool ArmTranslatorVisitor::asimd_VBIF(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return BitwiseInstruction<true>(*this, D, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) {
return ir.VectorOr(ir.VectorAnd(reg_d, reg_m), ir.VectorAnd(reg_n, ir.VectorNot(reg_m)));
});
}
bool ArmTranslatorVisitor::asimd_VHSUB(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (sz == 0b11) {
return UndefinedInstruction();
}
const size_t esize = 8 << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const IR::U128 reg_n = ir.GetVector(n);
const IR::U128 reg_m = ir.GetVector(m);
const IR::U128 result = U ? ir.VectorHalvingSubUnsigned(esize, reg_n, reg_m) : ir.VectorHalvingSubSigned(esize, reg_n, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VQSUB(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (sz == 0b11) {
return UndefinedInstruction();
}
const size_t esize = 8 << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const IR::U128 reg_n = ir.GetVector(n);
const IR::U128 reg_m = ir.GetVector(m);
const IR::U128 result = U ? ir.VectorUnsignedSaturatedSub(esize, reg_n, reg_m) : ir.VectorSignedSaturatedSub(esize, reg_n, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VCGT_reg(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return IntegerComparison(*this, U, D, sz, Vn, Vd, N, Q, M, Vm, Comparison::GT);
}
bool ArmTranslatorVisitor::asimd_VCGE_reg(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return IntegerComparison(*this, U, D, sz, Vn, Vd, N, Q, M, Vm, Comparison::GE);
}
bool ArmTranslatorVisitor::asimd_VABD(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return AbsoluteDifference(*this, U, D, sz, Vn, Vd, N, Q, M, Vm, AccumulateBehavior::None);
}
bool ArmTranslatorVisitor::asimd_VABA(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return AbsoluteDifference(*this, U, D, sz, Vn, Vd, N, Q, M, Vm, AccumulateBehavior::Accumulate);
}
bool ArmTranslatorVisitor::asimd_VADD_int(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_m = ir.GetVector(m);
const auto reg_n = ir.GetVector(n);
const auto result = ir.VectorAdd(esize, reg_n, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VSUB_int(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_m = ir.GetVector(m);
const auto reg_n = ir.GetVector(n);
const auto result = ir.VectorSub(esize, reg_n, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VSHL_reg(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_m = ir.GetVector(m);
const auto reg_n = ir.GetVector(n);
const auto result = U ? ir.VectorLogicalVShift(esize, reg_m, reg_n)
: ir.VectorArithmeticVShift(esize, reg_m, reg_n);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VQSHL_reg(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_m = ir.GetVector(m);
const auto reg_n = ir.GetVector(n);
const auto result = U ? ir.VectorUnsignedSaturatedShiftLeft(esize, reg_m, reg_n)
: ir.VectorSignedSaturatedShiftLeft(esize, reg_m, reg_n);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VRSHL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_m = ir.GetVector(m);
const auto reg_n = ir.GetVector(n);
const auto result = U ? ir.VectorRoundingShiftLeftUnsigned(esize, reg_m, reg_n)
: ir.VectorRoundingShiftLeftSigned(esize, reg_m, reg_n);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VMAX(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, bool op, size_t Vm) {
if (sz == 0b11) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_m = ir.GetVector(m);
const auto reg_n = ir.GetVector(n);
const auto result = [&] {
if (op) {
return U ? ir.VectorMinUnsigned(esize, reg_n, reg_m)
: ir.VectorMinSigned(esize, reg_n, reg_m);
} else {
return U ? ir.VectorMaxUnsigned(esize, reg_n, reg_m)
: ir.VectorMaxSigned(esize, reg_n, reg_m);
}
}();
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VTST(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (sz == 0b11) {
return UndefinedInstruction();
}
const size_t esize = 8 << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_n = ir.GetVector(n);
const auto reg_m = ir.GetVector(m);
const auto anded = ir.VectorAnd(reg_n, reg_m);
const auto result = ir.VectorNot(ir.VectorEqual(esize, anded, ir.ZeroVector()));
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VCEQ_reg(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return IntegerComparison(*this, false, D, sz, Vn, Vd, N, Q, M, Vm, Comparison::EQ);
}
bool ArmTranslatorVisitor::asimd_VMLA(bool op, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (sz == 0b11) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_n = ir.GetVector(n);
const auto reg_m = ir.GetVector(m);
const auto reg_d = ir.GetVector(d);
const auto multiply = ir.VectorMultiply(esize, reg_n, reg_m);
const auto result = op ? ir.VectorSub(esize, reg_d, multiply)
: ir.VectorAdd(esize, reg_d, multiply);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VMUL(bool P, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (sz == 0b11 || (P && sz != 0b00)) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_n = ir.GetVector(n);
const auto reg_m = ir.GetVector(m);
const auto result = P ? ir.VectorPolynomialMultiply(reg_n, reg_m)
: ir.VectorMultiply(esize, reg_n, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VPMAX_int(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, bool op, size_t Vm) {
if (sz == 0b11 || Q) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(false, Vd, D);
const auto m = ToVector(false, Vm, M);
const auto n = ToVector(false, Vn, N);
const auto reg_m = ir.GetVector(m);
const auto reg_n = ir.GetVector(n);
const auto bottom = ir.VectorDeinterleaveEvenLower(esize, reg_n, reg_m);
const auto top = ir.VectorDeinterleaveOddLower(esize, reg_n, reg_m);
const auto result = [&] {
if (op) {
return U ? ir.VectorMinUnsigned(esize, bottom, top)
: ir.VectorMinSigned(esize, bottom, top);
} else {
return U ? ir.VectorMaxUnsigned(esize, bottom, top)
: ir.VectorMaxSigned(esize, bottom, top);
}
}();
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VQDMULH(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (sz == 0b00 || sz == 0b11) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_n = ir.GetVector(n);
const auto reg_m = ir.GetVector(m);
const auto result = ir.VectorSignedSaturatedDoublingMultiply(esize, reg_n, reg_m);
ir.SetVector(d, result.upper);
return true;
}
bool ArmTranslatorVisitor::asimd_VQRDMULH(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (sz == 0b00 || sz == 0b11) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_n = ir.GetVector(n);
const auto reg_m = ir.GetVector(m);
const auto multiply = ir.VectorSignedSaturatedDoublingMultiply(esize, reg_n, reg_m);
const auto result = ir.VectorAdd(esize, multiply.upper, ir.VectorLogicalShiftRight(esize, multiply.lower, static_cast<u8>(esize - 1)));
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VPADD(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q || sz == 0b11) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto n = ToVector(Q, Vn, N);
const auto reg_n = ir.GetVector(n);
const auto reg_m = ir.GetVector(m);
const auto result = ir.VectorPairedAddLower(esize, reg_n, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VFMA(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) {
return ir.FPVectorMulAdd(32, reg_d, reg_n, reg_m, false);
});
}
bool ArmTranslatorVisitor::asimd_VFMS(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) {
return ir.FPVectorMulAdd(32, reg_d, ir.FPVectorNeg(32, reg_n), reg_m, false);
});
}
bool ArmTranslatorVisitor::asimd_VADD_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto&, const auto& reg_n, const auto& reg_m) {
return ir.FPVectorAdd(32, reg_n, reg_m, false);
});
}
bool ArmTranslatorVisitor::asimd_VSUB_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto&, const auto& reg_n, const auto& reg_m) {
return ir.FPVectorSub(32, reg_n, reg_m, false);
});
}
bool ArmTranslatorVisitor::asimd_VPADD_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q) {
return UndefinedInstruction();
}
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto&, const auto& reg_n, const auto& reg_m) {
return ir.FPVectorPairedAddLower(32, reg_n, reg_m, false);
});
}
bool ArmTranslatorVisitor::asimd_VABD_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto&, const auto& reg_n, const auto& reg_m) {
return ir.FPVectorAbs(32, ir.FPVectorSub(32, reg_n, reg_m, false));
});
}
bool ArmTranslatorVisitor::asimd_VMLA_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) {
const auto product = ir.FPVectorMul(32, reg_n, reg_m, false);
return ir.FPVectorAdd(32, reg_d, product, false);
});
}
bool ArmTranslatorVisitor::asimd_VMLS_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto& reg_d, const auto& reg_n, const auto& reg_m) {
const auto product = ir.FPVectorMul(32, reg_n, reg_m, false);
return ir.FPVectorAdd(32, reg_d, ir.FPVectorNeg(32, product), false);
});
}
bool ArmTranslatorVisitor::asimd_VMUL_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto&, const auto& reg_n, const auto& reg_m) {
return ir.FPVectorMul(32, reg_n, reg_m, false);
});
}
bool ArmTranslatorVisitor::asimd_VCEQ_reg_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatComparison(*this, D, sz, Vn, Vd, N, Q, M, Vm, Comparison::EQ);
}
bool ArmTranslatorVisitor::asimd_VCGE_reg_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatComparison(*this, D, sz, Vn, Vd, N, Q, M, Vm, Comparison::GE);
}
bool ArmTranslatorVisitor::asimd_VCGT_reg_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatComparison(*this, D, sz, Vn, Vd, N, Q, M, Vm, Comparison::GT);
}
bool ArmTranslatorVisitor::asimd_VACGE(bool D, bool op, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
const auto comparison = op ? Comparison::AbsoluteGT : Comparison::AbsoluteGE;
return FloatComparison(*this, D, sz, Vn, Vd, N, Q, M, Vm, comparison);
}
bool ArmTranslatorVisitor::asimd_VMAX_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto&, const auto& reg_n, const auto& reg_m) {
return ir.FPVectorMax(32, reg_n, reg_m, false);
});
}
bool ArmTranslatorVisitor::asimd_VMIN_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto&, const auto& reg_n, const auto& reg_m) {
return ir.FPVectorMin(32, reg_n, reg_m, false);
});
}
bool ArmTranslatorVisitor::asimd_VPMAX_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q) {
return UndefinedInstruction();
}
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto&, const auto& reg_n, const auto& reg_m) {
const auto bottom = ir.VectorDeinterleaveEvenLower(32, reg_n, reg_m);
const auto top = ir.VectorDeinterleaveOddLower(32, reg_n, reg_m);
return ir.FPVectorMax(32, bottom, top, false);
});
}
bool ArmTranslatorVisitor::asimd_VPMIN_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
if (Q) {
return UndefinedInstruction();
}
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto&, const auto& reg_n, const auto& reg_m) {
const auto bottom = ir.VectorDeinterleaveEvenLower(32, reg_n, reg_m);
const auto top = ir.VectorDeinterleaveOddLower(32, reg_n, reg_m);
return ir.FPVectorMin(32, bottom, top, false);
});
}
bool ArmTranslatorVisitor::asimd_VRECPS(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto&, const auto& reg_n, const auto& reg_m) {
return ir.FPVectorRecipStepFused(32, reg_n, reg_m, false);
});
}
bool ArmTranslatorVisitor::asimd_VRSQRTS(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm) {
return FloatingPointInstruction(*this, D, sz, Vn, Vd, N, Q, M, Vm, [this](const auto&, const auto& reg_n, const auto& reg_m) {
return ir.FPVectorRSqrtStepFused(32, reg_n, reg_m, false);
});
}
// ASIMD Three registers of different length
bool ArmTranslatorVisitor::asimd_VADDL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool op, bool N, bool M, size_t Vm) {
return WideInstruction(*this, U, D, sz, Vn, Vd, N, M, Vm, op ? WidenBehaviour::Second : WidenBehaviour::Both, [this](size_t esize, const auto&, const auto& reg_n, const auto& reg_m) {
return ir.VectorAdd(esize, reg_n, reg_m);
});
}
bool ArmTranslatorVisitor::asimd_VSUBL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool op, bool N, bool M, size_t Vm) {
return WideInstruction(*this, U, D, sz, Vn, Vd, N, M, Vm, op ? WidenBehaviour::Second : WidenBehaviour::Both, [this](size_t esize, const auto&, const auto& reg_n, const auto& reg_m) {
return ir.VectorSub(esize, reg_n, reg_m);
});
}
bool ArmTranslatorVisitor::asimd_VABAL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm) {
return AbsoluteDifferenceLong(*this, U, D, sz, Vn, Vd, N, M, Vm, AccumulateBehavior::Accumulate);
}
bool ArmTranslatorVisitor::asimd_VABDL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm) {
return AbsoluteDifferenceLong(*this, U, D, sz, Vn, Vd, N, M, Vm, AccumulateBehavior::None);
}
bool ArmTranslatorVisitor::asimd_VMLAL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool op, bool N, bool M, size_t Vm) {
return WideInstruction(*this, U, D, sz, Vn, Vd, N, M, Vm, WidenBehaviour::Both, [this, op](size_t esize, const auto& reg_d, const auto& reg_n, const auto& reg_m) {
const auto multiply = ir.VectorMultiply(esize, reg_n, reg_m);
return op ? ir.VectorSub(esize, reg_d, multiply)
: ir.VectorAdd(esize, reg_d, multiply);
});
}
bool ArmTranslatorVisitor::asimd_VMULL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool P, bool N, bool M, size_t Vm) {
if (sz == 0b11) {
return DecodeError();
}
if ((P & (U || sz == 0b10)) || Common::Bit<0>(Vd)) {
return UndefinedInstruction();
}
const size_t esize = P ? (sz == 0b00 ? 8 : 64) : 8U << sz;
const auto d = ToVector(true, Vd, D);
const auto m = ToVector(false, Vm, M);
const auto n = ToVector(false, Vn, N);
const auto extend_reg = [&](const auto& reg) {
return U ? ir.VectorZeroExtend(esize, reg) : ir.VectorSignExtend(esize, reg);
};
const auto reg_n = ir.GetVector(n);
const auto reg_m = ir.GetVector(m);
const auto result = P ? ir.VectorPolynomialMultiplyLong(esize, reg_n, reg_m)
: ir.VectorMultiply(2 * esize, extend_reg(reg_n), extend_reg(reg_m));
ir.SetVector(d, result);
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,687 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2020 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/bit_util.h"
#include <array>
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
namespace {
enum class Comparison {
EQ,
GE,
GT,
LE,
LT,
};
bool CompareWithZero(ArmTranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm, Comparison type) {
if (sz == 0b11 || (F && sz != 0b10)) {
return v.UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return v.UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto result = [&] {
const auto reg_m = v.ir.GetVector(m);
const auto zero = v.ir.ZeroVector();
if (F) {
switch (type) {
case Comparison::EQ:
return v.ir.FPVectorEqual(32, reg_m, zero, false);
case Comparison::GE:
return v.ir.FPVectorGreaterEqual(32, reg_m, zero, false);
case Comparison::GT:
return v.ir.FPVectorGreater(32, reg_m, zero, false);
case Comparison::LE:
return v.ir.FPVectorGreaterEqual(32, zero, reg_m, false);
case Comparison::LT:
return v.ir.FPVectorGreater(32, zero, reg_m, false);
}
return IR::U128{};
} else {
static constexpr std::array fns{
&IREmitter::VectorEqual,
&IREmitter::VectorGreaterEqualSigned,
&IREmitter::VectorGreaterSigned,
&IREmitter::VectorLessEqualSigned,
&IREmitter::VectorLessSigned,
};
const size_t esize = 8U << sz;
return (v.ir.*fns[static_cast<size_t>(type)])(esize, reg_m, zero);
}
}();
v.ir.SetVector(d, result);
return true;
}
enum class AccumulateBehavior {
None,
Accumulate,
};
bool PairedAddOperation(ArmTranslatorVisitor& v, bool D, size_t sz, size_t Vd, bool op, bool Q, bool M, size_t Vm,
AccumulateBehavior accumulate) {
if (sz == 0b11) {
return v.UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return v.UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = v.ir.GetVector(m);
const auto result = [&] {
const auto tmp = op ? v.ir.VectorPairedAddUnsignedWiden(esize, reg_m)
: v.ir.VectorPairedAddSignedWiden(esize, reg_m);
if (accumulate == AccumulateBehavior::Accumulate) {
const auto reg_d = v.ir.GetVector(d);
return v.ir.VectorAdd(esize * 2, reg_d, tmp);
}
return tmp;
}();
v.ir.SetVector(d, result);
return true;
}
} // Anonymous namespace
bool ArmTranslatorVisitor::asimd_VREV(bool D, size_t sz, size_t Vd, size_t op, bool Q, bool M, size_t Vm) {
if (op + sz >= 3) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto result = [this, m, op, sz] {
const auto reg_m = ir.GetVector(m);
const size_t esize = 16U << sz;
const auto shift = static_cast<u8>(8U << sz);
// 64-bit regions
if (op == 0b00) {
IR::U128 result = ir.VectorOr(ir.VectorLogicalShiftRight(esize, reg_m, shift),
ir.VectorLogicalShiftLeft(esize, reg_m, shift));
switch (sz) {
case 0: // 8-bit elements
result = ir.VectorShuffleLowHalfwords(result, 0b00011011);
result = ir.VectorShuffleHighHalfwords(result, 0b00011011);
break;
case 1: // 16-bit elements
result = ir.VectorShuffleLowHalfwords(result, 0b01001110);
result = ir.VectorShuffleHighHalfwords(result, 0b01001110);
break;
}
return result;
}
// 32-bit regions
if (op == 0b01) {
IR::U128 result = ir.VectorOr(ir.VectorLogicalShiftRight(esize, reg_m, shift),
ir.VectorLogicalShiftLeft(esize, reg_m, shift));
// If dealing with 8-bit elements we'll need to shuffle the bytes in each halfword
// e.g. Assume the following numbers point out bytes in a 32-bit word, we're essentially
// changing [3, 2, 1, 0] to [2, 3, 0, 1]
if (sz == 0) {
result = ir.VectorShuffleLowHalfwords(result, 0b10110001);
result = ir.VectorShuffleHighHalfwords(result, 0b10110001);
}
return result;
}
// 16-bit regions
return ir.VectorOr(ir.VectorLogicalShiftRight(esize, reg_m, 8),
ir.VectorLogicalShiftLeft(esize, reg_m, 8));
}();
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VPADDL(bool D, size_t sz, size_t Vd, bool op, bool Q, bool M, size_t Vm) {
return PairedAddOperation(*this, D, sz, Vd, op, Q, M, Vm, AccumulateBehavior::None);
}
bool ArmTranslatorVisitor::v8_AESD(bool D, size_t sz, size_t Vd, bool M, size_t Vm) {
if (sz != 0b00 || Common::Bit<0>(Vd) || Common::Bit<0>(Vm)) {
return UndefinedInstruction();
}
const auto d = ToVector(true, Vd, D);
const auto m = ToVector(true, Vm, M);
const auto reg_d = ir.GetVector(d);
const auto reg_m = ir.GetVector(m);
const auto result = ir.AESDecryptSingleRound(ir.VectorEor(reg_d, reg_m));
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::v8_AESE(bool D, size_t sz, size_t Vd, bool M, size_t Vm) {
if (sz != 0b00 || Common::Bit<0>(Vd) || Common::Bit<0>(Vm)) {
return UndefinedInstruction();
}
const auto d = ToVector(true, Vd, D);
const auto m = ToVector(true, Vm, M);
const auto reg_d = ir.GetVector(d);
const auto reg_m = ir.GetVector(m);
const auto result = ir.AESEncryptSingleRound(ir.VectorEor(reg_d, reg_m));
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::v8_AESIMC(bool D, size_t sz, size_t Vd, bool M, size_t Vm) {
if (sz != 0b00 || Common::Bit<0>(Vd) || Common::Bit<0>(Vm)) {
return UndefinedInstruction();
}
const auto d = ToVector(true, Vd, D);
const auto m = ToVector(true, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = ir.AESInverseMixColumns(reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::v8_AESMC(bool D, size_t sz, size_t Vd, bool M, size_t Vm) {
if (sz != 0b00 || Common::Bit<0>(Vd) || Common::Bit<0>(Vm)) {
return UndefinedInstruction();
}
const auto d = ToVector(true, Vd, D);
const auto m = ToVector(true, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = ir.AESMixColumns(reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VCLS(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm) {
if (sz == 0b11) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto result = [this, m, sz] {
const auto reg_m = ir.GetVector(m);
const size_t esize = 8U << sz;
const auto shifted = ir.VectorArithmeticShiftRight(esize, reg_m, static_cast<u8>(esize));
const auto xored = ir.VectorEor(reg_m, shifted);
const auto clz = ir.VectorCountLeadingZeros(esize, xored);
return ir.VectorSub(esize, clz, ir.VectorBroadcast(esize, I(esize, 1)));
}();
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VCLZ(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm) {
if (sz == 0b11) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto result = [this, m, sz] {
const auto reg_m = ir.GetVector(m);
const size_t esize = 8U << sz;
return ir.VectorCountLeadingZeros(esize, reg_m);
}();
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VCNT(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm) {
if (sz != 0b00) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = ir.VectorPopulationCount(reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VMVN_reg(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm) {
if (sz != 0b00) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = ir.VectorNot(reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VPADAL(bool D, size_t sz, size_t Vd, bool op, bool Q, bool M, size_t Vm) {
return PairedAddOperation(*this, D, sz, Vd, op, Q, M, Vm, AccumulateBehavior::Accumulate);
}
bool ArmTranslatorVisitor::asimd_VQABS(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm) {
if (sz == 0b11) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = ir.VectorSignedSaturatedAbs(esize, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VQNEG(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm) {
if (sz == 0b11) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = ir.VectorSignedSaturatedNeg(esize, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VCGT_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::GT);
}
bool ArmTranslatorVisitor::asimd_VCGE_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::GE);
}
bool ArmTranslatorVisitor::asimd_VCEQ_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::EQ);
}
bool ArmTranslatorVisitor::asimd_VCLE_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::LE);
}
bool ArmTranslatorVisitor::asimd_VCLT_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
return CompareWithZero(*this, D, sz, Vd, F, Q, M, Vm, Comparison::LT);
}
bool ArmTranslatorVisitor::asimd_VABS(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
if (sz == 0b11 || (F && sz != 0b10)) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto result = [this, F, m, sz] {
const auto reg_m = ir.GetVector(m);
if (F) {
return ir.FPVectorAbs(32, reg_m);
}
const size_t esize = 8U << sz;
return ir.VectorAbs(esize, reg_m);
}();
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VNEG(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
if (sz == 0b11 || (F && sz != 0b10)) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto result = [this, F, m, sz] {
const auto reg_m = ir.GetVector(m);
if (F) {
return ir.FPVectorNeg(32, reg_m);
}
const size_t esize = 8U << sz;
return ir.VectorSub(esize, ir.ZeroVector(), reg_m);
}();
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VSWP(bool D, size_t Vd, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
// Swapping the same register results in the same contents.
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
if (d == m) {
return true;
}
if (Q) {
const auto reg_d = ir.GetVector(d);
const auto reg_m = ir.GetVector(m);
ir.SetVector(m, reg_d);
ir.SetVector(d, reg_m);
} else {
const auto reg_d = ir.GetExtendedRegister(d);
const auto reg_m = ir.GetExtendedRegister(m);
ir.SetExtendedRegister(m, reg_d);
ir.SetExtendedRegister(d, reg_m);
}
return true;
}
bool ArmTranslatorVisitor::asimd_VTRN(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm) {
if (sz == 0b11) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
if (d == m) {
return UnpredictableInstruction();
}
const auto reg_d = ir.GetVector(d);
const auto reg_m = ir.GetVector(m);
const auto result_d = ir.VectorTranspose(esize, reg_d, reg_m, false);
const auto result_m = ir.VectorTranspose(esize, reg_d, reg_m, true);
ir.SetVector(d, result_d);
ir.SetVector(m, result_m);
return true;
}
bool ArmTranslatorVisitor::asimd_VUZP(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm) {
if (sz == 0b11 || (!Q && sz == 0b10)) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
if (d == m) {
return UnpredictableInstruction();
}
const auto reg_d = ir.GetVector(d);
const auto reg_m = ir.GetVector(m);
auto result_d = ir.VectorDeinterleaveEven(esize, reg_d, reg_m);
auto result_m = ir.VectorDeinterleaveOdd(esize, reg_d, reg_m);
if (!Q) {
result_d = ir.VectorShuffleWords(result_d, 0b11011000);
result_m = ir.VectorShuffleWords(result_m, 0b11011000);
}
ir.SetVector(d, result_d);
ir.SetVector(m, result_m);
return true;
}
bool ArmTranslatorVisitor::asimd_VZIP(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm) {
if (sz == 0b11 || (!Q && sz == 0b10)) {
return UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
if (d == m) {
return UnpredictableInstruction();
}
const auto reg_d = ir.GetVector(d);
const auto reg_m = ir.GetVector(m);
if (Q){
const auto result_d = ir.VectorInterleaveLower(esize, reg_d, reg_m);
const auto result_m = ir.VectorInterleaveUpper(esize, reg_d, reg_m);
ir.SetVector(d, result_d);
ir.SetVector(m, result_m);
} else {
const auto result = ir.VectorInterleaveLower(esize, reg_d, reg_m);
ir.SetExtendedRegister(d, ir.VectorGetElement(64, result, 0));
ir.SetExtendedRegister(m, ir.VectorGetElement(64, result, 1));
}
return true;
}
bool ArmTranslatorVisitor::asimd_VMOVN(bool D, size_t sz, size_t Vd, bool M, size_t Vm) {
if (sz == 0b11 || Common::Bit<0>(Vm)) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(false, Vd, D);
const auto m = ToVector(true, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = ir.VectorNarrow(2 * esize, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VQMOVUN(bool D, size_t sz, size_t Vd, bool M, size_t Vm) {
if (sz == 0b11 || Common::Bit<0>(Vm)) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(false, Vd, D);
const auto m = ToVector(true, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = ir.VectorSignedSaturatedNarrowToUnsigned(2 * esize, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VQMOVN(bool D, size_t sz, size_t Vd, bool op, bool M, size_t Vm) {
if (sz == 0b11 || Common::Bit<0>(Vm)) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(false, Vd, D);
const auto m = ToVector(true, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = op ? ir.VectorUnsignedSaturatedNarrow(2 * esize, reg_m)
: ir.VectorSignedSaturatedNarrowToSigned(2 * esize, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VSHLL_max(bool D, size_t sz, size_t Vd, bool M, size_t Vm) {
if (sz == 0b11 || Common::Bit<0>(Vd)) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(true, Vd, D);
const auto m = ToVector(false, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = ir.VectorLogicalShiftLeft(2 * esize, ir.VectorZeroExtend(esize, reg_m), static_cast<u8>(esize));
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VRECPE(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (sz == 0b00 || sz == 0b11) {
return UndefinedInstruction();
}
if (!F && sz == 0b01) {
// TODO: Implement 16-bit VectorUnsignedRecipEstimate
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = F ? ir.FPVectorRecipEstimate(esize, reg_m, false)
: ir.VectorUnsignedRecipEstimate(reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VRSQRTE(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (sz == 0b00 || sz == 0b11) {
return UndefinedInstruction();
}
if (!F && sz == 0b01) {
// TODO: Implement 16-bit VectorUnsignedRecipEstimate
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = F ? ir.FPVectorRSqrtEstimate(esize, reg_m, false)
: ir.VectorUnsignedRecipSqrtEstimate(reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VCVT_integer(bool D, size_t sz, size_t Vd, bool op, bool U, bool Q, bool M, size_t Vm) {
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (sz != 0b10) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = op ? (U ? ir.FPVectorToUnsignedFixed(esize, reg_m, 0, FP::RoundingMode::TowardsZero, false)
: ir.FPVectorToSignedFixed(esize, reg_m, 0, FP::RoundingMode::TowardsZero, false))
: (U ? ir.FPVectorFromUnsignedFixed(esize, reg_m, 0, FP::RoundingMode::ToNearest_TieEven, false)
: ir.FPVectorFromSignedFixed(esize, reg_m, 0, FP::RoundingMode::ToNearest_TieEven, false));
ir.SetVector(d, result);
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,200 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2020 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <utility>
#include "common/assert.h"
#include "common/bit_util.h"
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
namespace {
std::pair<ExtReg, size_t> GetScalarLocation(size_t esize, bool M, size_t Vm) {
const ExtReg m = ExtReg::Q0 + ((Vm >> 1) & (esize == 16 ? 0b11 : 0b111));
const size_t index = concatenate(Imm<1>{Common::Bit<0>(Vm)}, Imm<1>{M}, Imm<1>{Common::Bit<3>(Vm)}).ZeroExtend() >> (esize == 16 ? 0 : 1);
return std::make_pair(m, index);
}
enum class MultiplyBehavior {
Multiply,
MultiplyAccumulate,
MultiplySubtract,
};
enum class Rounding {
None,
Round,
};
bool ScalarMultiply(ArmTranslatorVisitor& v, bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool F, bool N, bool M, size_t Vm,
MultiplyBehavior multiply) {
if (sz == 0b11) {
return v.DecodeError();
}
if (sz == 0b00 || (F && sz == 0b01)) {
return v.UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn))) {
return v.UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto n = ToVector(Q, Vn, N);
const auto [m, index] = GetScalarLocation(esize, M, Vm);
const auto scalar = v.ir.VectorGetElement(esize, v.ir.GetVector(m), index);
const auto reg_n = v.ir.GetVector(n);
const auto reg_m = v.ir.VectorBroadcast(esize, scalar);
const auto addend = F ? v.ir.FPVectorMul(esize, reg_n, reg_m, false)
: v.ir.VectorMultiply(esize, reg_n, reg_m);
const auto result = [&] {
switch (multiply) {
case MultiplyBehavior::Multiply:
return addend;
case MultiplyBehavior::MultiplyAccumulate:
return F ? v.ir.FPVectorAdd(esize, v.ir.GetVector(d), addend, false)
: v.ir.VectorAdd(esize, v.ir.GetVector(d), addend);
case MultiplyBehavior::MultiplySubtract:
return F ? v.ir.FPVectorSub(esize, v.ir.GetVector(d), addend, false)
: v.ir.VectorSub(esize, v.ir.GetVector(d), addend);
default:
return IR::U128{};
}
}();
v.ir.SetVector(d, result);
return true;
}
bool ScalarMultiplyLong(ArmTranslatorVisitor& v, bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm, MultiplyBehavior multiply) {
if (sz == 0b11) {
return v.DecodeError();
}
if (sz == 0b00 || Common::Bit<0>(Vd)) {
return v.UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(true, Vd, D);
const auto n = ToVector(false, Vn, N);
const auto [m, index] = GetScalarLocation(esize, M, Vm);
const auto scalar = v.ir.VectorGetElement(esize, v.ir.GetVector(m), index);
const auto ext_scalar = U ? (esize == 16 ? IR::U32U64{v.ir.ZeroExtendToWord(scalar)} : IR::U32U64{v.ir.ZeroExtendToLong(scalar)})
: (esize == 16 ? IR::U32U64{v.ir.SignExtendToWord(scalar)} : IR::U32U64{v.ir.SignExtendToLong(scalar)});
const auto reg_n = U ? v.ir.VectorZeroExtend(esize, v.ir.GetVector(n)) : v.ir.VectorSignExtend(esize, v.ir.GetVector(n));
const auto reg_m = v.ir.VectorBroadcast(esize * 2, ext_scalar);
const auto addend = v.ir.VectorMultiply(esize * 2, reg_n, reg_m);
const auto result = [&] {
switch (multiply) {
case MultiplyBehavior::Multiply:
return addend;
case MultiplyBehavior::MultiplyAccumulate:
return v.ir.VectorAdd(esize * 2, v.ir.GetVector(d), addend);
case MultiplyBehavior::MultiplySubtract:
return v.ir.VectorSub(esize * 2, v.ir.GetVector(d), addend);
default:
return IR::U128{};
}
}();
v.ir.SetVector(d, result);
return true;
}
bool ScalarMultiplyReturnHigh(ArmTranslatorVisitor& v, bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm,
Rounding round) {
if (sz == 0b11) {
return v.DecodeError();
}
if (sz == 0b00) {
return v.UndefinedInstruction();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vn))) {
return v.UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(Q, Vd, D);
const auto n = ToVector(Q, Vn, N);
const auto [m, index] = GetScalarLocation(esize, M, Vm);
const auto scalar = v.ir.VectorGetElement(esize, v.ir.GetVector(m), index);
const auto reg_n = v.ir.GetVector(n);
const auto reg_m = v.ir.VectorBroadcast(esize, scalar);
const auto result = [&] {
const auto tmp = v.ir.VectorSignedSaturatedDoublingMultiply(esize, reg_n, reg_m);
if (round == Rounding::Round) {
return v.ir.VectorAdd(esize, tmp.upper, v.ir.VectorLogicalShiftRight(esize, tmp.lower, static_cast<u8>(esize - 1)));
}
return tmp.upper;
}();
v.ir.SetVector(d, result);
return true;
}
} // Anonymous namespace
bool ArmTranslatorVisitor::asimd_VMLA_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool op, bool F, bool N, bool M, size_t Vm) {
const auto behavior = op ? MultiplyBehavior::MultiplySubtract
: MultiplyBehavior::MultiplyAccumulate;
return ScalarMultiply(*this, Q, D, sz, Vn, Vd, F, N, M, Vm, behavior);
}
bool ArmTranslatorVisitor::asimd_VMLAL_scalar(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool op, bool N, bool M, size_t Vm) {
const auto behavior = op ? MultiplyBehavior::MultiplySubtract
: MultiplyBehavior::MultiplyAccumulate;
return ScalarMultiplyLong(*this, U, D, sz, Vn, Vd, N, M, Vm, behavior);
}
bool ArmTranslatorVisitor::asimd_VMUL_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool F, bool N, bool M, size_t Vm) {
return ScalarMultiply(*this, Q, D, sz, Vn, Vd, F, N, M, Vm, MultiplyBehavior::Multiply);
}
bool ArmTranslatorVisitor::asimd_VMULL_scalar(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm) {
return ScalarMultiplyLong(*this, U, D, sz, Vn, Vd, N, M, Vm, MultiplyBehavior::Multiply);
}
bool ArmTranslatorVisitor::asimd_VQDMULL_scalar(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm) {
if (sz == 0b11) {
return DecodeError();
}
if (sz == 0b00 || Common::Bit<0>(Vd)) {
return UndefinedInstruction();
}
const size_t esize = 8U << sz;
const auto d = ToVector(true, Vd, D);
const auto n = ToVector(false, Vn, N);
const auto [m, index] = GetScalarLocation(esize, M, Vm);
const auto scalar = ir.VectorGetElement(esize, ir.GetVector(m), index);
const auto reg_n = ir.GetVector(n);
const auto reg_m = ir.VectorBroadcast(esize, scalar);
const auto result = ir.VectorSignedSaturatedDoublingMultiplyLong(esize, reg_n, reg_m);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VQDMULH_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm) {
return ScalarMultiplyReturnHigh(*this, Q, D, sz, Vn, Vd, N, M, Vm, Rounding::None);
}
bool ArmTranslatorVisitor::asimd_VQRDMULH_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm) {
return ScalarMultiplyReturnHigh(*this, Q, D, sz, Vn, Vd, N, M, Vm, Rounding::Round);
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,350 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2020 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/assert.h"
#include "common/bit_util.h"
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
namespace {
enum class Accumulating {
None,
Accumulate
};
enum class Rounding {
None,
Round,
};
enum class Narrowing {
Truncation,
SaturateToUnsigned,
SaturateToSigned,
};
enum class Signedness {
Signed,
Unsigned
};
IR::U128 PerformRoundingCorrection(ArmTranslatorVisitor& v, size_t esize, u64 round_value, IR::U128 original, IR::U128 shifted) {
const auto round_const = v.ir.VectorBroadcast(esize, v.I(esize, round_value));
const auto round_correction = v.ir.VectorEqual(esize, v.ir.VectorAnd(original, round_const), round_const);
return v.ir.VectorSub(esize, shifted, round_correction);
}
std::pair<size_t, size_t> ElementSizeAndShiftAmount(bool right_shift, bool L, size_t imm6) {
if (right_shift) {
if (L) {
return {64, 64 - imm6};
}
const size_t esize = 8U << Common::HighestSetBit(imm6 >> 3);
const size_t shift_amount = (esize * 2) - imm6;
return {esize, shift_amount};
} else {
if (L) {
return {64, imm6};
}
const size_t esize = 8U << Common::HighestSetBit(imm6 >> 3);
const size_t shift_amount = imm6 - esize;
return {esize, shift_amount};
}
}
bool ShiftRight(ArmTranslatorVisitor& v, bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm,
Accumulating accumulate, Rounding rounding) {
if (!L && Common::Bits<3, 5>(imm6) == 0) {
return v.DecodeError();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return v.UndefinedInstruction();
}
const auto [esize, shift_amount] = ElementSizeAndShiftAmount(true, L, imm6);
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = v.ir.GetVector(m);
auto result = U ? v.ir.VectorLogicalShiftRight(esize, reg_m, static_cast<u8>(shift_amount))
: v.ir.VectorArithmeticShiftRight(esize, reg_m, static_cast<u8>(shift_amount));
if (rounding == Rounding::Round) {
const u64 round_value = 1ULL << (shift_amount - 1);
result = PerformRoundingCorrection(v, esize, round_value, reg_m, result);
}
if (accumulate == Accumulating::Accumulate) {
const auto reg_d = v.ir.GetVector(d);
result = v.ir.VectorAdd(esize, result, reg_d);
}
v.ir.SetVector(d, result);
return true;
}
bool ShiftRightNarrowing(ArmTranslatorVisitor& v, bool D, size_t imm6, size_t Vd, bool M, size_t Vm,
Rounding rounding, Narrowing narrowing, Signedness signedness) {
if (Common::Bits<3, 5>(imm6) == 0) {
return v.DecodeError();
}
if (Common::Bit<0>(Vm)) {
return v.UndefinedInstruction();
}
const auto [esize, shift_amount_] = ElementSizeAndShiftAmount(true, false, imm6);
const auto source_esize = 2 * esize;
const auto shift_amount = static_cast<u8>(shift_amount_);
const auto d = ToVector(false, Vd, D);
const auto m = ToVector(true, Vm, M);
const auto reg_m = v.ir.GetVector(m);
auto wide_result = [&] {
if (signedness == Signedness::Signed) {
return v.ir.VectorArithmeticShiftRight(source_esize, reg_m, shift_amount);
}
return v.ir.VectorLogicalShiftRight(source_esize, reg_m, shift_amount);
}();
if (rounding == Rounding::Round) {
const u64 round_value = 1ULL << (shift_amount - 1);
wide_result = PerformRoundingCorrection(v, source_esize, round_value, reg_m, wide_result);
}
const auto result = [&] {
switch (narrowing) {
case Narrowing::Truncation:
return v.ir.VectorNarrow(source_esize, wide_result);
case Narrowing::SaturateToUnsigned:
if (signedness == Signedness::Signed) {
return v.ir.VectorSignedSaturatedNarrowToUnsigned(source_esize, wide_result);
}
return v.ir.VectorUnsignedSaturatedNarrow(source_esize, wide_result);
case Narrowing::SaturateToSigned:
ASSERT(signedness == Signedness::Signed);
return v.ir.VectorSignedSaturatedNarrowToSigned(source_esize, wide_result);
}
UNREACHABLE();
}();
v.ir.SetVector(d, result);
return true;
}
} // Anonymous namespace
bool ArmTranslatorVisitor::asimd_SHR(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) {
return ShiftRight(*this, U, D, imm6, Vd, L, Q, M, Vm,
Accumulating::None, Rounding::None);
}
bool ArmTranslatorVisitor::asimd_SRA(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) {
return ShiftRight(*this, U, D, imm6, Vd, L, Q, M, Vm,
Accumulating::Accumulate, Rounding::None);
}
bool ArmTranslatorVisitor::asimd_VRSHR(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) {
return ShiftRight(*this, U, D, imm6, Vd, L, Q, M, Vm,
Accumulating::None, Rounding::Round);
}
bool ArmTranslatorVisitor::asimd_VRSRA(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) {
return ShiftRight(*this, U, D, imm6, Vd, L, Q, M, Vm,
Accumulating::Accumulate, Rounding::Round);
}
bool ArmTranslatorVisitor::asimd_VSRI(bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) {
if (!L && Common::Bits<3, 5>(imm6) == 0) {
return DecodeError();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const auto [esize, shift_amount] = ElementSizeAndShiftAmount(true, L, imm6);
const u64 mask = shift_amount == esize ? 0 : Common::Ones<u64>(esize) >> shift_amount;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto reg_d = ir.GetVector(d);
const auto shifted = ir.VectorLogicalShiftRight(esize, reg_m, static_cast<u8>(shift_amount));
const auto mask_vec = ir.VectorBroadcast(esize, I(esize, mask));
const auto result = ir.VectorOr(ir.VectorAnd(reg_d, ir.VectorNot(mask_vec)), shifted);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VSLI(bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) {
if (!L && Common::Bits<3, 5>(imm6) == 0) {
return DecodeError();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const auto [esize, shift_amount] = ElementSizeAndShiftAmount(false, L, imm6);
const u64 mask = Common::Ones<u64>(esize) << shift_amount;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto reg_d = ir.GetVector(d);
const auto shifted = ir.VectorLogicalShiftLeft(esize, reg_m, static_cast<u8>(shift_amount));
const auto mask_vec = ir.VectorBroadcast(esize, I(esize, mask));
const auto result = ir.VectorOr(ir.VectorAnd(reg_d, ir.VectorNot(mask_vec)), shifted);
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VQSHL(bool U, bool D, size_t imm6, size_t Vd, bool op, bool L, bool Q, bool M, size_t Vm) {
if (!L && Common::Bits<3, 5>(imm6) == 0) {
return DecodeError();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (!U && !op) {
return UndefinedInstruction();
}
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto result = [&] {
const auto reg_m = ir.GetVector(m);
const auto [esize, shift_amount] = ElementSizeAndShiftAmount(false, L, imm6);
const IR::U128 shift_vec = ir.VectorBroadcast(esize, I(esize, shift_amount));
if (U) {
if (op) {
return ir.VectorUnsignedSaturatedShiftLeft(esize, reg_m, shift_vec);
}
return ir.VectorSignedSaturatedShiftLeftUnsigned(esize, reg_m, shift_vec);
}
if (op) {
return ir.VectorSignedSaturatedShiftLeft(esize, reg_m, shift_vec);
}
return IR::U128{};
}();
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VSHL(bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm) {
if (!L && Common::Bits<3, 5>(imm6) == 0) {
return DecodeError();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
const auto [esize, shift_amount] = ElementSizeAndShiftAmount(false, L, imm6);
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = ir.VectorLogicalShiftLeft(esize, reg_m, static_cast<u8>(shift_amount));
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VSHRN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm) {
return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm,
Rounding::None, Narrowing::Truncation, Signedness::Unsigned);
}
bool ArmTranslatorVisitor::asimd_VRSHRN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm) {
return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm,
Rounding::Round, Narrowing::Truncation, Signedness::Unsigned);
}
bool ArmTranslatorVisitor::asimd_VQRSHRUN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm) {
return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm,
Rounding::Round, Narrowing::SaturateToUnsigned, Signedness::Signed);
}
bool ArmTranslatorVisitor::asimd_VQSHRUN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm) {
return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm,
Rounding::None, Narrowing::SaturateToUnsigned, Signedness::Signed);
}
bool ArmTranslatorVisitor::asimd_VQSHRN(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm) {
return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm,
Rounding::None, U ? Narrowing::SaturateToUnsigned : Narrowing::SaturateToSigned, U ? Signedness::Unsigned : Signedness::Signed);
}
bool ArmTranslatorVisitor::asimd_VQRSHRN(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm) {
return ShiftRightNarrowing(*this, D, imm6, Vd, M, Vm,
Rounding::Round, U ? Narrowing::SaturateToUnsigned : Narrowing::SaturateToSigned, U ? Signedness::Unsigned : Signedness::Signed);
}
bool ArmTranslatorVisitor::asimd_VSHLL(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm) {
if (Common::Bits<3, 5>(imm6) == 0) {
return DecodeError();
}
if (Common::Bit<0>(Vd)) {
return UndefinedInstruction();
}
const auto [esize, shift_amount] = ElementSizeAndShiftAmount(false, false, imm6);
const auto d = ToVector(true, Vd, D);
const auto m = ToVector(false, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto ext_vec = U ? ir.VectorZeroExtend(esize, reg_m) : ir.VectorSignExtend(esize, reg_m);
const auto result = ir.VectorLogicalShiftLeft(esize * 2, ext_vec, static_cast<u8>(shift_amount));
ir.SetVector(d, result);
return true;
}
bool ArmTranslatorVisitor::asimd_VCVT_fixed(bool U, bool D, size_t imm6, size_t Vd, bool to_fixed, bool Q, bool M, size_t Vm) {
if (Common::Bits<3, 5>(imm6) == 0) {
return DecodeError();
}
if (Q && (Common::Bit<0>(Vd) || Common::Bit<0>(Vm))) {
return UndefinedInstruction();
}
if (!Common::Bit<5>(imm6)) {
return UndefinedInstruction();
}
const size_t fbits = 64 - imm6;
const auto d = ToVector(Q, Vd, D);
const auto m = ToVector(Q, Vm, M);
const auto reg_m = ir.GetVector(m);
const auto result = to_fixed ? (U ? ir.FPVectorToUnsignedFixed(32, reg_m, fbits, FP::RoundingMode::TowardsZero, false) : ir.FPVectorToSignedFixed(32, reg_m, fbits, FP::RoundingMode::TowardsZero, false))
: (U ? ir.FPVectorFromUnsignedFixed(32, reg_m, fbits, FP::RoundingMode::ToNearest_TieEven, false) : ir.FPVectorFromSignedFixed(32, reg_m, fbits, FP::RoundingMode::ToNearest_TieEven, false));
ir.SetVector(d, result);
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,27 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
bool ArmTranslatorVisitor::arm_DMB([[maybe_unused]] Imm<4> option) {
ir.DataMemoryBarrier();
return true;
}
bool ArmTranslatorVisitor::arm_DSB([[maybe_unused]] Imm<4> option) {
ir.DataSynchronizationBarrier();
return true;
}
bool ArmTranslatorVisitor::arm_ISB([[maybe_unused]] Imm<4> option) {
ir.InstructionSynchronizationBarrier();
ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 4));
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,88 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/bit_util.h"
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
// B <label>
bool ArmTranslatorVisitor::arm_B(Cond cond, Imm<24> imm24) {
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = Common::SignExtend<26, u32>(imm24.ZeroExtend() << 2) + 8;
const auto new_location = ir.current_location.AdvancePC(imm32);
ir.SetTerm(IR::Term::LinkBlock{new_location});
return false;
}
// BL <label>
bool ArmTranslatorVisitor::arm_BL(Cond cond, Imm<24> imm24) {
if (!ConditionPassed(cond)) {
return true;
}
ir.PushRSB(ir.current_location.AdvancePC(4));
ir.SetRegister(Reg::LR, ir.Imm32(ir.current_location.PC() + 4));
const u32 imm32 = Common::SignExtend<26, u32>(imm24.ZeroExtend() << 2) + 8;
const auto new_location = ir.current_location.AdvancePC(imm32);
ir.SetTerm(IR::Term::LinkBlock{new_location});
return false;
}
// BLX <label>
bool ArmTranslatorVisitor::arm_BLX_imm(bool H, Imm<24> imm24) {
ir.PushRSB(ir.current_location.AdvancePC(4));
ir.SetRegister(Reg::LR, ir.Imm32(ir.current_location.PC() + 4));
const u32 imm32 = Common::SignExtend<26, u32>((imm24.ZeroExtend() << 2)) + (H ? 2 : 0) + 8;
const auto new_location = ir.current_location.AdvancePC(imm32).SetTFlag(true);
ir.SetTerm(IR::Term::LinkBlock{new_location});
return false;
}
// BLX <Rm>
bool ArmTranslatorVisitor::arm_BLX_reg(Cond cond, Reg m) {
if (m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
ir.PushRSB(ir.current_location.AdvancePC(4));
ir.BXWritePC(ir.GetRegister(m));
ir.SetRegister(Reg::LR, ir.Imm32(ir.current_location.PC() + 4));
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
}
// BX <Rm>
bool ArmTranslatorVisitor::arm_BX(Cond cond, Reg m) {
if (!ConditionPassed(cond)) {
return true;
}
ir.BXWritePC(ir.GetRegister(m));
if (m == Reg::R14) {
ir.SetTerm(IR::Term::PopRSBHint{});
} else {
ir.SetTerm(IR::Term::FastDispatchHint{});
}
return false;
}
bool ArmTranslatorVisitor::arm_BXJ(Cond cond, Reg m) {
// Jazelle not supported
return arm_BX(cond, m);
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,166 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
// CDP{2} <coproc_no>, #<opc1>, <CRd>, <CRn>, <CRm>, #<opc2>
bool ArmTranslatorVisitor::arm_CDP(Cond cond, size_t opc1, CoprocReg CRn, CoprocReg CRd, size_t coproc_no, size_t opc2, CoprocReg CRm) {
if ((coproc_no & 0b1110) == 0b1010) {
return arm_UDF();
}
const bool two = cond == Cond::NV;
if (two || ConditionPassed(cond)) {
ir.CoprocInternalOperation(coproc_no, two, opc1, CRd, CRn, CRm, opc2);
}
return true;
}
// LDC{2}{L}<c> <coproc_no>, <CRd>, [<Rn>, #+/-<imm32>]{!}
// LDC{2}{L}<c> <coproc_no>, <CRd>, [<Rn>], #+/-<imm32>
// LDC{2}{L}<c> <coproc_no>, <CRd>, [<Rn>], <imm8>
bool ArmTranslatorVisitor::arm_LDC(Cond cond, bool p, bool u, bool d, bool w, Reg n, CoprocReg CRd, size_t coproc_no, Imm<8> imm8) {
if (!p && !u && !d && !w) {
return arm_UDF();
}
if ((coproc_no & 0b1110) == 0b1010) {
return arm_UDF();
}
const bool two = cond == Cond::NV;
if (two || ConditionPassed(cond)) {
const u32 imm32 = imm8.ZeroExtend() << 2;
const bool index = p;
const bool add = u;
const bool wback = w;
const bool has_option = !p && !w && u;
const IR::U32 reg_n = ir.GetRegister(n);
const IR::U32 offset_address = add ? ir.Add(reg_n, ir.Imm32(imm32)) : ir.Sub(reg_n, ir.Imm32(imm32));
const IR::U32 address = index ? offset_address : reg_n;
ir.CoprocLoadWords(coproc_no, two, d, CRd, address, has_option, imm8.ZeroExtend<u8>());
if (wback) {
ir.SetRegister(n, offset_address);
}
}
return true;
}
// MCR{2}<c> <coproc_no>, #<opc1>, <Rt>, <CRn>, <CRm>, #<opc2>
bool ArmTranslatorVisitor::arm_MCR(Cond cond, size_t opc1, CoprocReg CRn, Reg t, size_t coproc_no, size_t opc2, CoprocReg CRm) {
if ((coproc_no & 0b1110) == 0b1010) {
return arm_UDF();
}
if (t == Reg::PC) {
return UnpredictableInstruction();
}
const bool two = cond == Cond::NV;
if (two || ConditionPassed(cond)) {
ir.CoprocSendOneWord(coproc_no, two, opc1, CRn, CRm, opc2, ir.GetRegister(t));
}
return true;
}
// MCRR{2}<c> <coproc_no>, #<opc>, <Rt>, <Rt2>, <CRm>
bool ArmTranslatorVisitor::arm_MCRR(Cond cond, Reg t2, Reg t, size_t coproc_no, size_t opc, CoprocReg CRm) {
if ((coproc_no & 0b1110) == 0b1010) {
return arm_UDF();
}
if (t == Reg::PC || t2 == Reg::PC) {
return UnpredictableInstruction();
}
const bool two = cond == Cond::NV;
if (two || ConditionPassed(cond)) {
ir.CoprocSendTwoWords(coproc_no, two, opc, CRm, ir.GetRegister(t), ir.GetRegister(t2));
}
return true;
}
// MRC{2}<c> <coproc_no>, #<opc1>, <Rt>, <CRn>, <CRm>, #<opc2>
bool ArmTranslatorVisitor::arm_MRC(Cond cond, size_t opc1, CoprocReg CRn, Reg t, size_t coproc_no, size_t opc2, CoprocReg CRm) {
if ((coproc_no & 0b1110) == 0b1010) {
return arm_UDF();
}
const bool two = cond == Cond::NV;
if (two || ConditionPassed(cond)) {
const auto word = ir.CoprocGetOneWord(coproc_no, two, opc1, CRn, CRm, opc2);
if (t != Reg::PC) {
ir.SetRegister(t, word);
} else {
const auto new_cpsr_nzcv = ir.And(word, ir.Imm32(0xF0000000));
ir.SetCpsrNZCV(new_cpsr_nzcv);
}
}
return true;
}
// MRRC{2}<c> <coproc_no>, #<opc>, <Rt>, <Rt2>, <CRm>
bool ArmTranslatorVisitor::arm_MRRC(Cond cond, Reg t2, Reg t, size_t coproc_no, size_t opc, CoprocReg CRm) {
if ((coproc_no & 0b1110) == 0b1010) {
return arm_UDF();
}
if (t == Reg::PC || t2 == Reg::PC || t == t2) {
return UnpredictableInstruction();
}
const bool two = cond == Cond::NV;
if (two || ConditionPassed(cond)) {
const auto two_words = ir.CoprocGetTwoWords(coproc_no, two, opc, CRm);
ir.SetRegister(t, ir.LeastSignificantWord(two_words));
ir.SetRegister(t2, ir.MostSignificantWord(two_words).result);
}
return true;
}
// STC{2}{L}<c> <coproc>, <CRd>, [<Rn>, #+/-<imm32>]{!}
// STC{2}{L}<c> <coproc>, <CRd>, [<Rn>], #+/-<imm32>
// STC{2}{L}<c> <coproc>, <CRd>, [<Rn>], <imm8>
bool ArmTranslatorVisitor::arm_STC(Cond cond, bool p, bool u, bool d, bool w, Reg n, CoprocReg CRd, size_t coproc_no, Imm<8> imm8) {
if ((coproc_no & 0b1110) == 0b1010) {
return arm_UDF();
}
if (!p && !u && !d && !w) {
return arm_UDF();
}
if (n == Reg::PC && w) {
return UnpredictableInstruction();
}
const bool two = cond == Cond::NV;
if (two || ConditionPassed(cond)) {
const u32 imm32 = imm8.ZeroExtend() << 2;
const bool index = p;
const bool add = u;
const bool wback = w;
const bool has_option = !p && !w && u;
const IR::U32 reg_n = ir.GetRegister(n);
const IR::U32 offset_address = add ? ir.Add(reg_n, ir.Imm32(imm32)) : ir.Sub(reg_n, ir.Imm32(imm32));
const IR::U32 address = index ? offset_address : reg_n;
ir.CoprocStoreWords(coproc_no, two, d, CRd, address, has_option, imm8.ZeroExtend<u8>());
if (wback) {
ir.SetRegister(n, offset_address);
}
}
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,95 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
// It's considered constrained UNPREDICTABLE behavior if either
// CRC32 instruction variant is executed with a condition code
// that is *not* 0xE (Always execute). ARM defines one of the following
// as being a requirement in this case. Either:
//
// 1. The instruction is undefined.
// 2. The instruction executes as a NOP.
// 3. The instruction executes unconditionally.
// 4. The instruction executes conditionally.
//
// It's also considered constrained UNPREDICTABLE behavior if
// either CRC32 instruction variant is executed with a size specifier
// of 64-bit (sz -> 0b11)
//
// In this case, either:
//
// 1. The instruction is undefined
// 2. The instruction executes as a NOP.
// 3. The instruction executes with the additional decode: size = 32.
//
// In both cases, we treat as unpredictable, to allow
// library users to provide their own intended behavior
// in the unpredictable exception handler.
namespace {
enum class CRCType {
Castagnoli,
ISO,
};
bool CRC32Variant(ArmTranslatorVisitor& v, Cond cond, Imm<2> sz, Reg n, Reg d, Reg m, CRCType type) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return v.UnpredictableInstruction();
}
if (sz == 0b11) {
return v.UnpredictableInstruction();
}
if (cond != Cond::AL) {
return v.UnpredictableInstruction();
}
const IR::U32 result = [m, n, sz, type, &v] {
const IR::U32 accumulator = v.ir.GetRegister(n);
const IR::U32 data = v.ir.GetRegister(m);
if (type == CRCType::ISO) {
switch (sz.ZeroExtend()) {
case 0b00:
return v.ir.CRC32ISO8(accumulator, data);
case 0b01:
return v.ir.CRC32ISO16(accumulator, data);
case 0b10:
return v.ir.CRC32ISO32(accumulator, data);
}
} else {
switch (sz.ZeroExtend()) {
case 0b00:
return v.ir.CRC32Castagnoli8(accumulator, data);
case 0b01:
return v.ir.CRC32Castagnoli16(accumulator, data);
case 0b10:
return v.ir.CRC32Castagnoli32(accumulator, data);
}
}
UNREACHABLE();
}();
v.ir.SetRegister(d, result);
return true;
}
} // Anonymous namespace
// CRC32{B,H,W}{<q>} <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_CRC32(Cond cond, Imm<2> sz, Reg n, Reg d, Reg m) {
return CRC32Variant(*this, cond, sz, n, d, m, CRCType::ISO);
}
// CRC32C{B,H,W}{<q>} <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_CRC32C(Cond cond, Imm<2> sz, Reg n, Reg d, Reg m) {
return CRC32Variant(*this, cond, sz, n, d, m, CRCType::Castagnoli);
}
} // namespace Dynarmic::A32

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
namespace {
using DivideFunction = IR::U32U64 (IREmitter::*)(const IR::U32U64&, const IR::U32U64&);
bool DivideOperation(ArmTranslatorVisitor& v, Cond cond, Reg d, Reg m, Reg n,
DivideFunction fn) {
if (d == Reg::PC || m == Reg::PC || n == Reg::PC) {
return v.UnpredictableInstruction();
}
if (!v.ConditionPassed(cond)) {
return true;
}
const IR::U32 operand1 = v.ir.GetRegister(n);
const IR::U32 operand2 = v.ir.GetRegister(m);
const IR::U32 result = (v.ir.*fn)(operand1, operand2);
v.ir.SetRegister(d, result);
return true;
}
} // Anonymous namespace
// SDIV<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SDIV(Cond cond, Reg d, Reg m, Reg n) {
return DivideOperation(*this, cond, d, m, n, &IREmitter::SignedDiv);
}
// UDIV<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UDIV(Cond cond, Reg d, Reg m, Reg n) {
return DivideOperation(*this, cond, d, m, n, &IREmitter::UnsignedDiv);
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,47 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <dynarmic/A32/config.h>
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
// BKPT #<imm16>
bool ArmTranslatorVisitor::arm_BKPT(Cond cond, Imm<12> /*imm12*/, Imm<4> /*imm4*/) {
if (cond != Cond::AL && !options.define_unpredictable_behaviour) {
return UnpredictableInstruction();
}
// UNPREDICTABLE: The instruction executes conditionally.
if (!ConditionPassed(cond)) {
return true;
}
ir.ExceptionRaised(Exception::Breakpoint);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
// SVC<c> #<imm24>
bool ArmTranslatorVisitor::arm_SVC(Cond cond, Imm<24> imm24) {
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = imm24.ZeroExtend();
ir.PushRSB(ir.current_location.AdvancePC(4));
ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 4));
ir.CallSupervisor(ir.Imm32(imm32));
ir.SetTerm(IR::Term::CheckHalt{IR::Term::PopRSBHint{}});
return false;
}
// UDF<c> #<imm16>
bool ArmTranslatorVisitor::arm_UDF() {
return UndefinedInstruction();
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,229 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
static IR::U32 Rotate(A32::IREmitter& ir, Reg m, SignExtendRotation rotate) {
const u8 rotate_by = static_cast<u8>(static_cast<size_t>(rotate) * 8);
return ir.RotateRight(ir.GetRegister(m), ir.Imm8(rotate_by), ir.Imm1(0)).result;
}
// SXTAB<c> <Rd>, <Rn>, <Rm>{, <rotation>}
bool ArmTranslatorVisitor::arm_SXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rotated = Rotate(ir, m, rotate);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.Add(reg_n, ir.SignExtendByteToWord(ir.LeastSignificantByte(rotated)));
ir.SetRegister(d, result);
return true;
}
// SXTAB16<c> <Rd>, <Rn>, <Rm>{, <rotation>}
bool ArmTranslatorVisitor::arm_SXTAB16(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rotated = Rotate(ir, m, rotate);
const auto low_byte = ir.And(rotated, ir.Imm32(0x00FF00FF));
const auto sign_bit = ir.And(rotated, ir.Imm32(0x00800080));
const auto addend = ir.Or(low_byte, ir.Mul(sign_bit, ir.Imm32(0x1FE)));
const auto result = ir.PackedAddU16(addend, ir.GetRegister(n)).result;
ir.SetRegister(d, result);
return true;
}
// SXTAH<c> <Rd>, <Rn>, <Rm>{, <rotation>}
bool ArmTranslatorVisitor::arm_SXTAH(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rotated = Rotate(ir, m, rotate);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.Add(reg_n, ir.SignExtendHalfToWord(ir.LeastSignificantHalf(rotated)));
ir.SetRegister(d, result);
return true;
}
// SXTB<c> <Rd>, <Rm>{, <rotation>}
bool ArmTranslatorVisitor::arm_SXTB(Cond cond, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rotated = Rotate(ir, m, rotate);
const auto result = ir.SignExtendByteToWord(ir.LeastSignificantByte(rotated));
ir.SetRegister(d, result);
return true;
}
// SXTB16<c> <Rd>, <Rm>{, <rotation>}
bool ArmTranslatorVisitor::arm_SXTB16(Cond cond, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rotated = Rotate(ir, m, rotate);
const auto low_byte = ir.And(rotated, ir.Imm32(0x00FF00FF));
const auto sign_bit = ir.And(rotated, ir.Imm32(0x00800080));
const auto result = ir.Or(low_byte, ir.Mul(sign_bit, ir.Imm32(0x1FE)));
ir.SetRegister(d, result);
return true;
}
// SXTH<c> <Rd>, <Rm>{, <rotation>}
bool ArmTranslatorVisitor::arm_SXTH(Cond cond, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rotated = Rotate(ir, m, rotate);
const auto result = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(rotated));
ir.SetRegister(d, result);
return true;
}
// UXTAB<c> <Rd>, <Rn>, <Rm>{, <rotation>}
bool ArmTranslatorVisitor::arm_UXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rotated = Rotate(ir, m, rotate);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.Add(reg_n, ir.ZeroExtendByteToWord(ir.LeastSignificantByte(rotated)));
ir.SetRegister(d, result);
return true;
}
// UXTAB16<c> <Rd>, <Rn>, <Rm>{, <rotation>}
bool ArmTranslatorVisitor::arm_UXTAB16(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rotated = Rotate(ir, m, rotate);
auto result = ir.And(rotated, ir.Imm32(0x00FF00FF));
const auto reg_n = ir.GetRegister(n);
result = ir.PackedAddU16(reg_n, result).result;
ir.SetRegister(d, result);
return true;
}
// UXTAH<c> <Rd>, <Rn>, <Rm>{, <rotation>}
bool ArmTranslatorVisitor::arm_UXTAH(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rotated = Rotate(ir, m, rotate);
const auto reg_n = ir.GetRegister(n);
const auto result = ir.Add(reg_n, ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(rotated)));
ir.SetRegister(d, result);
return true;
}
// UXTB<c> <Rd>, <Rm>{, <rotation>}
bool ArmTranslatorVisitor::arm_UXTB(Cond cond, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rotated = Rotate(ir, m, rotate);
const auto result = ir.ZeroExtendByteToWord(ir.LeastSignificantByte(rotated));
ir.SetRegister(d, result);
return true;
}
// UXTB16<c> <Rd>, <Rm>{, <rotation>}
bool ArmTranslatorVisitor::arm_UXTB16(Cond cond, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rotated = Rotate(ir, m, rotate);
const auto result = ir.And(rotated, ir.Imm32(0x00FF00FF));
ir.SetRegister(d, result);
return true;
}
// UXTH<c> <Rd>, <Rm>{, <rotation>}
bool ArmTranslatorVisitor::arm_UXTH(Cond cond, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rotated = Rotate(ir, m, rotate);
const auto result = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(rotated));
ir.SetRegister(d, result);
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,79 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <dynarmic/A32/config.h>
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
bool ArmTranslatorVisitor::arm_PLD_imm([[maybe_unused]] bool add,
bool R,
[[maybe_unused]] Reg n,
[[maybe_unused]] Imm<12> imm12) {
if (!options.hook_hint_instructions) {
return true;
}
const auto exception = R ? Exception::PreloadData
: Exception::PreloadDataWithIntentToWrite;
return RaiseException(exception);
}
bool ArmTranslatorVisitor::arm_PLD_reg([[maybe_unused]] bool add,
bool R,
[[maybe_unused]] Reg n,
[[maybe_unused]] Imm<5> imm5,
[[maybe_unused]] ShiftType shift,
[[maybe_unused]] Reg m) {
if (!options.hook_hint_instructions) {
return true;
}
const auto exception = R ? Exception::PreloadData
: Exception::PreloadDataWithIntentToWrite;
return RaiseException(exception);
}
bool ArmTranslatorVisitor::arm_SEV() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::SendEvent);
}
bool ArmTranslatorVisitor::arm_SEVL() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::SendEventLocal);
}
bool ArmTranslatorVisitor::arm_WFE() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::WaitForEvent);
}
bool ArmTranslatorVisitor::arm_WFI() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::WaitForInterrupt);
}
bool ArmTranslatorVisitor::arm_YIELD() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::Yield);
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,934 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
bool ArmTranslatorVisitor::arm_LDRBT() {
// System instructions unimplemented
return UndefinedInstruction();
}
bool ArmTranslatorVisitor::arm_LDRHT() {
// System instructions unimplemented
return UndefinedInstruction();
}
bool ArmTranslatorVisitor::arm_LDRSBT() {
// System instructions unimplemented
return UndefinedInstruction();
}
bool ArmTranslatorVisitor::arm_LDRSHT() {
// System instructions unimplemented
return UndefinedInstruction();
}
bool ArmTranslatorVisitor::arm_LDRT() {
// System instructions unimplemented
return UndefinedInstruction();
}
bool ArmTranslatorVisitor::arm_STRBT() {
// System instructions unimplemented
return UndefinedInstruction();
}
bool ArmTranslatorVisitor::arm_STRHT() {
// System instructions unimplemented
return UndefinedInstruction();
}
bool ArmTranslatorVisitor::arm_STRT() {
// System instructions unimplemented
return UndefinedInstruction();
}
static IR::U32 GetAddress(A32::IREmitter& ir, bool P, bool U, bool W, Reg n, IR::U32 offset) {
const bool index = P;
const bool add = U;
const bool wback = !P || W;
const IR::U32 offset_addr = add ? ir.Add(ir.GetRegister(n), offset) : ir.Sub(ir.GetRegister(n), offset);
const IR::U32 address = index ? offset_addr : ir.GetRegister(n);
if (wback) {
ir.SetRegister(n, offset_addr);
}
return address;
}
// LDR <Rt>, [PC, #+/-<imm>]
bool ArmTranslatorVisitor::arm_LDR_lit(Cond cond, bool U, Reg t, Imm<12> imm12) {
if (!ConditionPassed(cond)) {
return true;
}
const bool add = U;
const u32 base = ir.AlignPC(4);
const u32 address = add ? (base + imm12.ZeroExtend()) : (base - imm12.ZeroExtend());
const auto data = ir.ReadMemory32(ir.Imm32(address));
if (t == Reg::PC) {
ir.LoadWritePC(data);
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
}
ir.SetRegister(t, data);
return true;
}
// LDR <Rt>, [<Rn>, #+/-<imm>]{!}
// LDR <Rt>, [<Rn>], #+/-<imm>
bool ArmTranslatorVisitor::arm_LDR_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<12> imm12) {
if (n == Reg::PC) {
return UnpredictableInstruction();
}
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if ((!P || W) && n == t) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = imm12.ZeroExtend();
const auto offset = ir.Imm32(imm32);
const auto address = GetAddress(ir, P, U, W, n, offset);
const auto data = ir.ReadMemory32(address);
if (t == Reg::PC) {
ir.LoadWritePC(data);
if (!P && W && n == Reg::R13) {
ir.SetTerm(IR::Term::PopRSBHint{});
} else {
ir.SetTerm(IR::Term::FastDispatchHint{});
}
return false;
}
ir.SetRegister(t, data);
return true;
}
// LDR <Rt>, [<Rn>, #+/-<Rm>]{!}
// LDR <Rt>, [<Rn>], #+/-<Rm>
bool ArmTranslatorVisitor::arm_LDR_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<5> imm5, ShiftType shift, Reg m) {
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if (m == Reg::PC) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == Reg::PC || n == t)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto offset = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag()).result;
const auto address = GetAddress(ir, P, U, W, n, offset);
const auto data = ir.ReadMemory32(address);
if (t == Reg::PC) {
ir.LoadWritePC(data);
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
}
ir.SetRegister(t, data);
return true;
}
// LDRB <Rt>, [PC, #+/-<imm>]
bool ArmTranslatorVisitor::arm_LDRB_lit(Cond cond, bool U, Reg t, Imm<12> imm12) {
if (t == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = imm12.ZeroExtend();
const bool add = U;
const u32 base = ir.AlignPC(4);
const u32 address = add ? (base + imm32) : (base - imm32);
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(ir.Imm32(address)));
ir.SetRegister(t, data);
return true;
}
// LDRB <Rt>, [<Rn>, #+/-<imm>]{!}
// LDRB <Rt>, [<Rn>], #+/-<imm>
bool ArmTranslatorVisitor::arm_LDRB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<12> imm12) {
if (n == Reg::PC) {
return UnpredictableInstruction();
}
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if ((!P || W) && n == t) {
return UnpredictableInstruction();
}
if (t == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = imm12.ZeroExtend();
const auto offset = ir.Imm32(imm32);
const auto address = GetAddress(ir, P, U, W, n, offset);
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address));
ir.SetRegister(t, data);
return true;
}
// LDRB <Rt>, [<Rn>, #+/-<Rm>]{!}
// LDRB <Rt>, [<Rn>], #+/-<Rm>
bool ArmTranslatorVisitor::arm_LDRB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<5> imm5, ShiftType shift, Reg m) {
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if (t == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == Reg::PC || n == t)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto offset = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag()).result;
const auto address = GetAddress(ir, P, U, W, n, offset);
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address));
ir.SetRegister(t, data);
return true;
}
// LDRD <Rt>, <Rt2>, [PC, #+/-<imm>]
bool ArmTranslatorVisitor::arm_LDRD_lit(Cond cond, bool U, Reg t, Imm<4> imm8a, Imm<4> imm8b) {
if (RegNumber(t) % 2 == 1) {
return UnpredictableInstruction();
}
if (t+1 == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const Reg t2 = t+1;
const u32 imm32 = concatenate(imm8a, imm8b).ZeroExtend();
const bool add = U;
const u32 base = ir.AlignPC(4);
const u32 address = add ? (base + imm32) : (base - imm32);
const auto data_a = ir.ReadMemory32(ir.Imm32(address));
const auto data_b = ir.ReadMemory32(ir.Imm32(address + 4));
ir.SetRegister(t, data_a);
ir.SetRegister(t2, data_b);
return true;
}
// LDRD <Rt>, [<Rn>, #+/-<imm>]{!}
// LDRD <Rt>, [<Rn>], #+/-<imm>
bool ArmTranslatorVisitor::arm_LDRD_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<4> imm8a, Imm<4> imm8b) {
if (n == Reg::PC) {
return UnpredictableInstruction();
}
if (RegNumber(t) % 2 == 1) {
return UnpredictableInstruction();
}
if (!P && W) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == t || n == t+1)) {
return UnpredictableInstruction();
}
if (t+1 == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const Reg t2 = t+1;
const u32 imm32 = concatenate(imm8a, imm8b).ZeroExtend();
const auto offset = ir.Imm32(imm32);
const auto address_a = GetAddress(ir, P, U, W, n, offset);
const auto address_b = ir.Add(address_a, ir.Imm32(4));
const auto data_a = ir.ReadMemory32(address_a);
const auto data_b = ir.ReadMemory32(address_b);
ir.SetRegister(t, data_a);
ir.SetRegister(t2, data_b);
return true;
}
// LDRD <Rt>, [<Rn>, #+/-<Rm>]{!}
// LDRD <Rt>, [<Rn>], #+/-<Rm>
bool ArmTranslatorVisitor::arm_LDRD_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Reg m) {
if (RegNumber(t) % 2 == 1) {
return UnpredictableInstruction();
}
if (!P && W) {
return UnpredictableInstruction();
}
if (t+1 == Reg::PC || m == Reg::PC || m == t || m == t+1) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == Reg::PC || n == t || n == t+1)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const Reg t2 = t+1;
const auto offset = ir.GetRegister(m);
const auto address_a = GetAddress(ir, P, U, W, n, offset);
const auto address_b = ir.Add(address_a, ir.Imm32(4));
const auto data_a = ir.ReadMemory32(address_a);
const auto data_b = ir.ReadMemory32(address_b);
ir.SetRegister(t, data_a);
ir.SetRegister(t2, data_b);
return true;
}
// LDRH <Rt>, [PC, #-/+<imm>]
bool ArmTranslatorVisitor::arm_LDRH_lit(Cond cond, bool P, bool U, bool W, Reg t, Imm<4> imm8a, Imm<4> imm8b) {
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if (P == W) {
return UnpredictableInstruction();
}
if (t == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = concatenate(imm8a, imm8b).ZeroExtend();
const bool add = U;
const u32 base = ir.AlignPC(4);
const u32 address = add ? (base + imm32) : (base - imm32);
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(ir.Imm32(address)));
ir.SetRegister(t, data);
return true;
}
// LDRH <Rt>, [<Rn>, #+/-<imm>]{!}
// LDRH <Rt>, [<Rn>], #+/-<imm>
bool ArmTranslatorVisitor::arm_LDRH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<4> imm8a, Imm<4> imm8b) {
if (n == Reg::PC) {
return UnpredictableInstruction();
}
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if ((!P || W) && n == t) {
return UnpredictableInstruction();
}
if (t == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = concatenate(imm8a, imm8b).ZeroExtend();
const auto offset = ir.Imm32(imm32);
const auto address = GetAddress(ir, P, U, W, n, offset);
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address));
ir.SetRegister(t, data);
return true;
}
// LDRH <Rt>, [<Rn>, #+/-<Rm>]{!}
// LDRH <Rt>, [<Rn>], #+/-<Rm>
bool ArmTranslatorVisitor::arm_LDRH_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Reg m) {
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if (t == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == Reg::PC || n == t)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto offset = ir.GetRegister(m);
const auto address = GetAddress(ir, P, U, W, n, offset);
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address));
ir.SetRegister(t, data);
return true;
}
// LDRSB <Rt>, [PC, #+/-<imm>]
bool ArmTranslatorVisitor::arm_LDRSB_lit(Cond cond, bool U, Reg t, Imm<4> imm8a, Imm<4> imm8b) {
if (t == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = concatenate(imm8a, imm8b).ZeroExtend();
const bool add = U;
const u32 base = ir.AlignPC(4);
const u32 address = add ? (base + imm32) : (base - imm32);
const auto data = ir.SignExtendByteToWord(ir.ReadMemory8(ir.Imm32(address)));
ir.SetRegister(t, data);
return true;
}
// LDRSB <Rt>, [<Rn>, #+/-<imm>]{!}
// LDRSB <Rt>, [<Rn>], #+/-<imm>
bool ArmTranslatorVisitor::arm_LDRSB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<4> imm8a, Imm<4> imm8b) {
if (n == Reg::PC) {
return UnpredictableInstruction();
}
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if ((!P || W) && n == t) {
return UnpredictableInstruction();
}
if (t == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = concatenate(imm8a, imm8b).ZeroExtend();
const auto offset = ir.Imm32(imm32);
const auto address = GetAddress(ir, P, U, W, n, offset);
const auto data = ir.SignExtendByteToWord(ir.ReadMemory8(address));
ir.SetRegister(t, data);
return true;
}
// LDRSB <Rt>, [<Rn>, #+/-<Rm>]{!}
// LDRSB <Rt>, [<Rn>], #+/-<Rm>
bool ArmTranslatorVisitor::arm_LDRSB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Reg m) {
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if (t == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == Reg::PC || n == t)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto offset = ir.GetRegister(m);
const auto address = GetAddress(ir, P, U, W, n, offset);
const auto data = ir.SignExtendByteToWord(ir.ReadMemory8(address));
ir.SetRegister(t, data);
return true;
}
// LDRSH <Rt>, [PC, #-/+<imm>]
bool ArmTranslatorVisitor::arm_LDRSH_lit(Cond cond, bool U, Reg t, Imm<4> imm8a, Imm<4> imm8b) {
if (t == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = concatenate(imm8a, imm8b).ZeroExtend();
const bool add = U;
const u32 base = ir.AlignPC(4);
const u32 address = add ? (base + imm32) : (base - imm32);
const auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(ir.Imm32(address)));
ir.SetRegister(t, data);
return true;
}
// LDRSH <Rt>, [<Rn>, #+/-<imm>]{!}
// LDRSH <Rt>, [<Rn>], #+/-<imm>
bool ArmTranslatorVisitor::arm_LDRSH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<4> imm8a, Imm<4> imm8b) {
if (n == Reg::PC) {
return UnpredictableInstruction();
}
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if ((!P || W) && n == t) {
return UnpredictableInstruction();
}
if (t == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = concatenate(imm8a, imm8b).ZeroExtend();
const auto offset = ir.Imm32(imm32);
const auto address = GetAddress(ir, P, U, W, n, offset);
const auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(address));
ir.SetRegister(t, data);
return true;
}
// LDRSH <Rt>, [<Rn>, #+/-<Rm>]{!}
// LDRSH <Rt>, [<Rn>], #+/-<Rm>
bool ArmTranslatorVisitor::arm_LDRSH_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Reg m) {
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if (t == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == Reg::PC || n == t)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto offset = ir.GetRegister(m);
const auto address = GetAddress(ir, P, U, W, n, offset);
const auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(address));
ir.SetRegister(t, data);
return true;
}
// STR <Rt>, [<Rn>, #+/-<imm>]{!}
// STR <Rt>, [<Rn>], #+/-<imm>
bool ArmTranslatorVisitor::arm_STR_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<12> imm12) {
if ((!P || W) && (n == Reg::PC || n == t)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto offset = ir.Imm32(imm12.ZeroExtend());
const auto address = GetAddress(ir, P, U, W, n, offset);
ir.WriteMemory32(address, ir.GetRegister(t));
return true;
}
// STR <Rt>, [<Rn>, #+/-<Rm>]{!}
// STR <Rt>, [<Rn>], #+/-<Rm>
bool ArmTranslatorVisitor::arm_STR_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<5> imm5, ShiftType shift, Reg m) {
if (m == Reg::PC) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == Reg::PC || n == t)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto offset = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag()).result;
const auto address = GetAddress(ir, P, U, W, n, offset);
ir.WriteMemory32(address, ir.GetRegister(t));
return true;
}
// STRB <Rt>, [<Rn>, #+/-<imm>]{!}
// STRB <Rt>, [<Rn>], #+/-<imm>
bool ArmTranslatorVisitor::arm_STRB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<12> imm12) {
if (t == Reg::PC) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == Reg::PC || n == t)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto offset = ir.Imm32(imm12.ZeroExtend());
const auto address = GetAddress(ir, P, U, W, n, offset);
ir.WriteMemory8(address, ir.LeastSignificantByte(ir.GetRegister(t)));
return true;
}
// STRB <Rt>, [<Rn>, #+/-<Rm>]{!}
// STRB <Rt>, [<Rn>], #+/-<Rm>
bool ArmTranslatorVisitor::arm_STRB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<5> imm5, ShiftType shift, Reg m) {
if (t == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == Reg::PC || n == t)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto offset = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag()).result;
const auto address = GetAddress(ir, P, U, W, n, offset);
ir.WriteMemory8(address, ir.LeastSignificantByte(ir.GetRegister(t)));
return true;
}
// STRD <Rt>, [<Rn>, #+/-<imm>]{!}
// STRD <Rt>, [<Rn>], #+/-<imm>
bool ArmTranslatorVisitor::arm_STRD_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<4> imm8a, Imm<4> imm8b) {
if (size_t(t) % 2 != 0) {
return UnpredictableInstruction();
}
if (!P && W) {
return UnpredictableInstruction();
}
const Reg t2 = t + 1;
if ((!P || W) && (n == Reg::PC || n == t || n == t2)) {
return UnpredictableInstruction();
}
if (t2 == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = concatenate(imm8a, imm8b).ZeroExtend();
const auto offset = ir.Imm32(imm32);
const auto address_a = GetAddress(ir, P, U, W, n, offset);
const auto address_b = ir.Add(address_a, ir.Imm32(4));
const auto value_a = ir.GetRegister(t);
const auto value_b = ir.GetRegister(t2);
ir.WriteMemory32(address_a, value_a);
ir.WriteMemory32(address_b, value_b);
return true;
}
// STRD <Rt>, [<Rn>, #+/-<Rm>]{!}
// STRD <Rt>, [<Rn>], #+/-<Rm>
bool ArmTranslatorVisitor::arm_STRD_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Reg m) {
if (size_t(t) % 2 != 0) {
return UnpredictableInstruction();
}
if (!P && W) {
return UnpredictableInstruction();
}
const Reg t2 = t + 1;
if (t2 == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == Reg::PC || n == t || n == t2)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto offset = ir.GetRegister(m);
const auto address_a = GetAddress(ir, P, U, W, n, offset);
const auto address_b = ir.Add(address_a, ir.Imm32(4));
const auto value_a = ir.GetRegister(t);
const auto value_b = ir.GetRegister(t2);
ir.WriteMemory32(address_a, value_a);
ir.WriteMemory32(address_b, value_b);
return true;
}
// STRH <Rt>, [<Rn>, #+/-<imm>]{!}
// STRH <Rt>, [<Rn>], #+/-<imm>
bool ArmTranslatorVisitor::arm_STRH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<4> imm8a, Imm<4> imm8b) {
if (t == Reg::PC) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == Reg::PC || n == t)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 imm32 = concatenate(imm8a, imm8b).ZeroExtend();
const auto offset = ir.Imm32(imm32);
const auto address = GetAddress(ir, P, U, W, n, offset);
ir.WriteMemory16(address, ir.LeastSignificantHalf(ir.GetRegister(t)));
return true;
}
// STRH <Rt>, [<Rn>, #+/-<Rm>]{!}
// STRH <Rt>, [<Rn>], #+/-<Rm>
bool ArmTranslatorVisitor::arm_STRH_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Reg m) {
if (t == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if ((!P || W) && (n == Reg::PC || n == t)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto offset = ir.GetRegister(m);
const auto address = GetAddress(ir, P, U, W, n, offset);
ir.WriteMemory16(address, ir.LeastSignificantHalf(ir.GetRegister(t)));
return true;
}
static bool LDMHelper(A32::IREmitter& ir, bool W, Reg n, RegList list, IR::U32 start_address, IR::U32 writeback_address) {
auto address = start_address;
for (size_t i = 0; i <= 14; i++) {
if (Common::Bit(i, list)) {
ir.SetRegister(static_cast<Reg>(i), ir.ReadMemory32(address));
address = ir.Add(address, ir.Imm32(4));
}
}
if (W && !Common::Bit(RegNumber(n), list)) {
ir.SetRegister(n, writeback_address);
}
if (Common::Bit<15>(list)) {
ir.LoadWritePC(ir.ReadMemory32(address));
if (n == Reg::R13)
ir.SetTerm(IR::Term::PopRSBHint{});
else
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
}
return true;
}
// LDM <Rn>{!}, <reg_list>
bool ArmTranslatorVisitor::arm_LDM(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1) {
return UnpredictableInstruction();
}
if (W && Common::Bit(static_cast<size_t>(n), list)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto start_address = ir.GetRegister(n);
const auto writeback_address = ir.Add(start_address, ir.Imm32(u32(Common::BitCount(list) * 4)));
return LDMHelper(ir, W, n, list, start_address, writeback_address);
}
// LDMDA <Rn>{!}, <reg_list>
bool ArmTranslatorVisitor::arm_LDMDA(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1) {
return UnpredictableInstruction();
}
if (W && Common::Bit(static_cast<size_t>(n), list)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * Common::BitCount(list) - 4)));
const auto writeback_address = ir.Sub(start_address, ir.Imm32(4));
return LDMHelper(ir, W, n, list, start_address, writeback_address);
}
// LDMDB <Rn>{!}, <reg_list>
bool ArmTranslatorVisitor::arm_LDMDB(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1) {
return UnpredictableInstruction();
}
if (W && Common::Bit(static_cast<size_t>(n), list)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * Common::BitCount(list))));
const auto writeback_address = start_address;
return LDMHelper(ir, W, n, list, start_address, writeback_address);
}
// LDMIB <Rn>{!}, <reg_list>
bool ArmTranslatorVisitor::arm_LDMIB(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1) {
return UnpredictableInstruction();
}
if (W && Common::Bit(static_cast<size_t>(n), list)) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto start_address = ir.Add(ir.GetRegister(n), ir.Imm32(4));
const auto writeback_address = ir.Add(ir.GetRegister(n), ir.Imm32(u32(4 * Common::BitCount(list))));
return LDMHelper(ir, W, n, list, start_address, writeback_address);
}
bool ArmTranslatorVisitor::arm_LDM_usr() {
return InterpretThisInstruction();
}
bool ArmTranslatorVisitor::arm_LDM_eret() {
return InterpretThisInstruction();
}
static bool STMHelper(A32::IREmitter& ir, bool W, Reg n, RegList list, IR::U32 start_address, IR::U32 writeback_address) {
auto address = start_address;
for (size_t i = 0; i <= 14; i++) {
if (Common::Bit(i, list)) {
ir.WriteMemory32(address, ir.GetRegister(static_cast<Reg>(i)));
address = ir.Add(address, ir.Imm32(4));
}
}
if (W) {
ir.SetRegister(n, writeback_address);
}
if (Common::Bit<15>(list)) {
ir.WriteMemory32(address, ir.Imm32(ir.PC()));
}
return true;
}
// STM <Rn>{!}, <reg_list>
bool ArmTranslatorVisitor::arm_STM(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto start_address = ir.GetRegister(n);
const auto writeback_address = ir.Add(start_address, ir.Imm32(u32(Common::BitCount(list) * 4)));
return STMHelper(ir, W, n, list, start_address, writeback_address);
}
// STMDA <Rn>{!}, <reg_list>
bool ArmTranslatorVisitor::arm_STMDA(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * Common::BitCount(list) - 4)));
const auto writeback_address = ir.Sub(start_address, ir.Imm32(4));
return STMHelper(ir, W, n, list, start_address, writeback_address);
}
// STMDB <Rn>{!}, <reg_list>
bool ArmTranslatorVisitor::arm_STMDB(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * Common::BitCount(list))));
const auto writeback_address = start_address;
return STMHelper(ir, W, n, list, start_address, writeback_address);
}
// STMIB <Rn>{!}, <reg_list>
bool ArmTranslatorVisitor::arm_STMIB(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto start_address = ir.Add(ir.GetRegister(n), ir.Imm32(4));
const auto writeback_address = ir.Add(ir.GetRegister(n), ir.Imm32(u32(4 * Common::BitCount(list))));
return STMHelper(ir, W, n, list, start_address, writeback_address);
}
bool ArmTranslatorVisitor::arm_STM_usr() {
return InterpretThisInstruction();
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,178 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/bit_util.h"
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
// BFC<c> <Rd>, #<lsb>, #<width>
bool ArmTranslatorVisitor::arm_BFC(Cond cond, Imm<5> msb, Reg d, Imm<5> lsb) {
if (d == Reg::PC) {
return UnpredictableInstruction();
}
if (msb < lsb) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 lsb_value = lsb.ZeroExtend();
const u32 msb_value = msb.ZeroExtend();
const u32 mask = ~(Common::Ones<u32>(msb_value - lsb_value + 1) << lsb_value);
const IR::U32 operand = ir.GetRegister(d);
const IR::U32 result = ir.And(operand, ir.Imm32(mask));
ir.SetRegister(d, result);
return true;
}
// BFI<c> <Rd>, <Rn>, #<lsb>, #<width>
bool ArmTranslatorVisitor::arm_BFI(Cond cond, Imm<5> msb, Reg d, Imm<5> lsb, Reg n) {
if (d == Reg::PC) {
return UnpredictableInstruction();
}
if (msb < lsb) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const u32 lsb_value = lsb.ZeroExtend();
const u32 msb_value = msb.ZeroExtend();
const u32 inclusion_mask = Common::Ones<u32>(msb_value - lsb_value + 1) << lsb_value;
const u32 exclusion_mask = ~inclusion_mask;
const IR::U32 operand1 = ir.And(ir.GetRegister(d), ir.Imm32(exclusion_mask));
const IR::U32 operand2 = ir.And(ir.LogicalShiftLeft(ir.GetRegister(n), ir.Imm8(u8(lsb_value))), ir.Imm32(inclusion_mask));
const IR::U32 result = ir.Or(operand1, operand2);
ir.SetRegister(d, result);
return true;
}
// CLZ<c> <Rd>, <Rm>
bool ArmTranslatorVisitor::arm_CLZ(Cond cond, Reg d, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
ir.SetRegister(d, ir.CountLeadingZeros(ir.GetRegister(m)));
return true;
}
// MOVT<c> <Rd>, #<imm16>
bool ArmTranslatorVisitor::arm_MOVT(Cond cond, Imm<4> imm4, Reg d, Imm<12> imm12) {
if (d == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 imm16 = ir.Imm32(concatenate(imm4, imm12).ZeroExtend() << 16);
const IR::U32 operand = ir.GetRegister(d);
const IR::U32 result = ir.Or(ir.And(operand, ir.Imm32(0x0000FFFFU)), imm16);
ir.SetRegister(d, result);
return true;
}
bool ArmTranslatorVisitor::arm_MOVW(Cond cond, Imm<4> imm4, Reg d, Imm<12> imm12) {
if (d == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 imm = ir.Imm32(concatenate(imm4, imm12).ZeroExtend());
ir.SetRegister(d, imm);
return true;
}
// SBFX<c> <Rd>, <Rn>, #<lsb>, #<width>
bool ArmTranslatorVisitor::arm_SBFX(Cond cond, Imm<5> widthm1, Reg d, Imm<5> lsb, Reg n) {
if (d == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
const u32 lsb_value = lsb.ZeroExtend();
const u32 widthm1_value = widthm1.ZeroExtend();
const u32 msb = lsb_value + widthm1_value;
if (msb >= Common::BitSize<u32>()) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
constexpr size_t max_width = Common::BitSize<u32>();
const u32 width = widthm1_value + 1;
const u8 left_shift_amount = static_cast<u8>(max_width - width - lsb_value);
const u8 right_shift_amount = static_cast<u8>(max_width - width);
const IR::U32 operand = ir.GetRegister(n);
const IR::U32 tmp = ir.LogicalShiftLeft(operand, ir.Imm8(left_shift_amount));
const IR::U32 result = ir.ArithmeticShiftRight(tmp, ir.Imm8(right_shift_amount));
ir.SetRegister(d, result);
return true;
}
// SEL<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SEL(Cond cond, Reg n, Reg d, Reg m) {
if (n == Reg::PC || d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto to = ir.GetRegister(m);
const auto from = ir.GetRegister(n);
const auto result = ir.PackedSelect(ir.GetGEFlags(), to, from);
ir.SetRegister(d, result);
return true;
}
// UBFX<c> <Rd>, <Rn>, #<lsb>, #<width>
bool ArmTranslatorVisitor::arm_UBFX(Cond cond, Imm<5> widthm1, Reg d, Imm<5> lsb, Reg n) {
if (d == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
const u32 lsb_value = lsb.ZeroExtend();
const u32 widthm1_value = widthm1.ZeroExtend();
const u32 msb = lsb_value + widthm1_value;
if (msb >= Common::BitSize<u32>()) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 operand = ir.GetRegister(n);
const IR::U32 mask = ir.Imm32(Common::Ones<u32>(widthm1_value + 1));
const IR::U32 result = ir.And(ir.LogicalShiftRight(operand, ir.Imm8(u8(lsb_value))), mask);
ir.SetRegister(d, result);
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,611 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
// MLA{S}<c> <Rd>, <Rn>, <Rm>, <Ra>
bool ArmTranslatorVisitor::arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.Add(ir.Mul(ir.GetRegister(n), ir.GetRegister(m)), ir.GetRegister(a));
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
// MLS<c> <Rd>, <Rn>, <Rm>, <Ra>
bool ArmTranslatorVisitor::arm_MLS(Cond cond, Reg d, Reg a, Reg m, Reg n) {
if (d == Reg::PC || a == Reg::PC || m == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 operand1 = ir.GetRegister(n);
const IR::U32 operand2 = ir.GetRegister(m);
const IR::U32 operand3 = ir.GetRegister(a);
const IR::U32 result = ir.Sub(operand3, ir.Mul(operand1, operand2));
ir.SetRegister(d, result);
return true;
}
// MUL{S}<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.Mul(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
// SMLAL{S}<c> <RdLo>, <RdHi>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (dLo == dHi) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
const auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
const auto product = ir.Mul(n64, m64);
const auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
const auto result = ir.Add(product, addend);
const auto lo = ir.LeastSignificantWord(result);
const auto hi = ir.MostSignificantWord(result).result;
ir.SetRegister(dLo, lo);
ir.SetRegister(dHi, hi);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
// SMULL{S}<c> <RdLo>, <RdHi>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (dLo == dHi) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
const auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
const auto result = ir.Mul(n64, m64);
const auto lo = ir.LeastSignificantWord(result);
const auto hi = ir.MostSignificantWord(result).result;
ir.SetRegister(dLo, lo);
ir.SetRegister(dHi, hi);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
// UMAAL<c> <RdLo>, <RdHi>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n) {
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (dLo == dHi) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto lo64 = ir.ZeroExtendWordToLong(ir.GetRegister(dLo));
const auto hi64 = ir.ZeroExtendWordToLong(ir.GetRegister(dHi));
const auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n));
const auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m));
const auto result = ir.Add(ir.Add(ir.Mul(n64, m64), hi64), lo64);
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
ir.SetRegister(dHi, ir.MostSignificantWord(result).result);
return true;
}
// UMLAL{S}<c> <RdLo>, <RdHi>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (dLo == dHi) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
const auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n));
const auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m));
const auto result = ir.Add(ir.Mul(n64, m64), addend);
const auto lo = ir.LeastSignificantWord(result);
const auto hi = ir.MostSignificantWord(result).result;
ir.SetRegister(dLo, lo);
ir.SetRegister(dHi, hi);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
// UMULL{S}<c> <RdLo>, <RdHi>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n) {
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (dLo == dHi) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n));
const auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m));
const auto result = ir.Mul(n64, m64);
const auto lo = ir.LeastSignificantWord(result);
const auto hi = ir.MostSignificantWord(result).result;
ir.SetRegister(dLo, lo);
ir.SetRegister(dHi, hi);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi));
ir.SetZFlag(ir.IsZero(result));
}
return true;
}
// SMLAL<x><y><c> <RdLo>, <RdHi>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SMLALxy(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, bool N, Reg n) {
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (dLo == dHi) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 n32 = ir.GetRegister(n);
const IR::U32 m32 = ir.GetRegister(m);
const IR::U32 n16 = N ? ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
const IR::U32 m16 = M ? ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
const IR::U64 product = ir.SignExtendWordToLong(ir.Mul(n16, m16));
const auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
const auto result = ir.Add(product, addend);
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
ir.SetRegister(dHi, ir.MostSignificantWord(result).result);
return true;
}
// SMLA<x><y><c> <Rd>, <Rn>, <Rm>, <Ra>
bool ArmTranslatorVisitor::arm_SMLAxy(Cond cond, Reg d, Reg a, Reg m, bool M, bool N, Reg n) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 n32 = ir.GetRegister(n);
const IR::U32 m32 = ir.GetRegister(m);
const IR::U32 n16 = N ? ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
const IR::U32 m16 = M ? ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
const IR::U32 product = ir.Mul(n16, m16);
const auto result_overflow = ir.AddWithCarry(product, ir.GetRegister(a), ir.Imm1(0));
ir.SetRegister(d, result_overflow.result);
ir.OrQFlag(result_overflow.overflow);
return true;
}
// SMUL<x><y><c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SMULxy(Cond cond, Reg d, Reg m, bool M, bool N, Reg n) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 n32 = ir.GetRegister(n);
const IR::U32 m32 = ir.GetRegister(m);
const IR::U32 n16 = N ? ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
const IR::U32 m16 = M ? ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
const IR::U32 result = ir.Mul(n16, m16);
ir.SetRegister(d, result);
return true;
}
// SMLAW<y><c> <Rd>, <Rn>, <Rm>, <Ra>
bool ArmTranslatorVisitor::arm_SMLAWy(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U64 n32 = ir.SignExtendWordToLong(ir.GetRegister(n));
IR::U32 m32 = ir.GetRegister(m);
if (M) {
m32 = ir.LogicalShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
}
const IR::U64 m16 = ir.SignExtendWordToLong(ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)));
const auto product = ir.LeastSignificantWord(ir.LogicalShiftRight(ir.Mul(n32, m16), ir.Imm8(16)));
const auto result_overflow = ir.AddWithCarry(product, ir.GetRegister(a), ir.Imm1(0));
ir.SetRegister(d, result_overflow.result);
ir.OrQFlag(result_overflow.overflow);
return true;
}
// SMULW<y><c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SMULWy(Cond cond, Reg d, Reg m, bool M, Reg n) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U64 n32 = ir.SignExtendWordToLong(ir.GetRegister(n));
IR::U32 m32 = ir.GetRegister(m);
if (M) {
m32 = ir.LogicalShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
}
const IR::U64 m16 = ir.SignExtendWordToLong(ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32)));
const auto result = ir.LogicalShiftRight(ir.Mul(n32, m16), ir.Imm8(16));
ir.SetRegister(d, ir.LeastSignificantWord(result));
return true;
}
// SMMLA{R}<c> <Rd>, <Rn>, <Rm>, <Ra>
bool ArmTranslatorVisitor::arm_SMMLA(Cond cond, Reg d, Reg a, Reg m, bool R, Reg n) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC /* no check for a */) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
const auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
const auto a64 = ir.Pack2x32To1x64(ir.Imm32(0), ir.GetRegister(a));
const auto temp = ir.Add(a64, ir.Mul(n64, m64));
const auto result_carry = ir.MostSignificantWord(temp);
auto result = result_carry.result;
if (R) {
result = ir.AddWithCarry(result, ir.Imm32(0), result_carry.carry).result;
}
ir.SetRegister(d, result);
return true;
}
// SMMLS{R}<c> <Rd>, <Rn>, <Rm>, <Ra>
bool ArmTranslatorVisitor::arm_SMMLS(Cond cond, Reg d, Reg a, Reg m, bool R, Reg n) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC || a == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
const auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
const auto a64 = ir.Pack2x32To1x64(ir.Imm32(0), ir.GetRegister(a));
const auto temp = ir.Sub(a64, ir.Mul(n64, m64));
const auto result_carry = ir.MostSignificantWord(temp);
auto result = result_carry.result;
if (R) {
result = ir.AddWithCarry(result, ir.Imm32(0), result_carry.carry).result;
}
ir.SetRegister(d, result);
return true;
}
// SMMUL{R}<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SMMUL(Cond cond, Reg d, Reg m, bool R, Reg n) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
const auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
const auto product = ir.Mul(n64, m64);
const auto result_carry = ir.MostSignificantWord(product);
auto result = result_carry.result;
if (R) {
result = ir.AddWithCarry(result, ir.Imm32(0), result_carry.carry).result;
}
ir.SetRegister(d, result);
return true;
}
// SMLAD{X}<c> <Rd>, <Rn>, <Rm>, <Ra>
bool ArmTranslatorVisitor::arm_SMLAD(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n) {
if (a == Reg::PC) {
return arm_SMUAD(cond, d, m, M, n);
}
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 n32 = ir.GetRegister(n);
const IR::U32 m32 = ir.GetRegister(m);
const IR::U32 n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
const IR::U32 n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result;
IR::U32 m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
IR::U32 m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
if (M) {
std::swap(m_lo, m_hi);
}
const IR::U32 product_lo = ir.Mul(n_lo, m_lo);
const IR::U32 product_hi = ir.Mul(n_hi, m_hi);
const IR::U32 addend = ir.GetRegister(a);
auto result_overflow = ir.AddWithCarry(product_lo, product_hi, ir.Imm1(0));
ir.OrQFlag(result_overflow.overflow);
result_overflow = ir.AddWithCarry(result_overflow.result, addend, ir.Imm1(0));
ir.SetRegister(d, result_overflow.result);
ir.OrQFlag(result_overflow.overflow);
return true;
}
// SMLALD{X}<c> <RdLo>, <RdHi>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SMLALD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, Reg n) {
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (dLo == dHi) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 n32 = ir.GetRegister(n);
const IR::U32 m32 = ir.GetRegister(m);
const IR::U32 n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
const IR::U32 n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result;
IR::U32 m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
IR::U32 m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
if (M) {
std::swap(m_lo, m_hi);
}
const IR::U64 product_lo = ir.SignExtendWordToLong(ir.Mul(n_lo, m_lo));
const IR::U64 product_hi = ir.SignExtendWordToLong(ir.Mul(n_hi, m_hi));
const auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
const auto result = ir.Add(ir.Add(product_lo, product_hi), addend);
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
ir.SetRegister(dHi, ir.MostSignificantWord(result).result);
return true;
}
// SMLSD{X}<c> <Rd>, <Rn>, <Rm>, <Ra>
bool ArmTranslatorVisitor::arm_SMLSD(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n) {
if (a == Reg::PC) {
return arm_SMUSD(cond, d, m, M, n);
}
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 n32 = ir.GetRegister(n);
const IR::U32 m32 = ir.GetRegister(m);
const IR::U32 n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
const IR::U32 n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result;
IR::U32 m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
IR::U32 m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
if (M) {
std::swap(m_lo, m_hi);
}
const IR::U32 product_lo = ir.Mul(n_lo, m_lo);
const IR::U32 product_hi = ir.Mul(n_hi, m_hi);
const IR::U32 addend = ir.GetRegister(a);
const IR::U32 product = ir.Sub(product_lo, product_hi);
auto result_overflow = ir.AddWithCarry(product, addend, ir.Imm1(0));
ir.SetRegister(d, result_overflow.result);
ir.OrQFlag(result_overflow.overflow);
return true;
}
// SMLSLD{X}<c> <RdLo>, <RdHi>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SMLSLD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, Reg n) {
if (dLo == Reg::PC || dHi == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (dLo == dHi) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 n32 = ir.GetRegister(n);
const IR::U32 m32 = ir.GetRegister(m);
const IR::U32 n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
const IR::U32 n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result;
IR::U32 m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
IR::U32 m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
if (M) {
std::swap(m_lo, m_hi);
}
const IR::U64 product_lo = ir.SignExtendWordToLong(ir.Mul(n_lo, m_lo));
const IR::U64 product_hi = ir.SignExtendWordToLong(ir.Mul(n_hi, m_hi));
const auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
const auto result = ir.Add(ir.Sub(product_lo, product_hi), addend);
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
ir.SetRegister(dHi, ir.MostSignificantWord(result).result);
return true;
}
// SMUAD{X}<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SMUAD(Cond cond, Reg d, Reg m, bool M, Reg n) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 n32 = ir.GetRegister(n);
const IR::U32 m32 = ir.GetRegister(m);
const IR::U32 n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
const IR::U32 n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result;
IR::U32 m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
IR::U32 m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
if (M) {
std::swap(m_lo, m_hi);
}
const IR::U32 product_lo = ir.Mul(n_lo, m_lo);
const IR::U32 product_hi = ir.Mul(n_hi, m_hi);
const auto result_overflow = ir.AddWithCarry(product_lo, product_hi, ir.Imm1(0));
ir.SetRegister(d, result_overflow.result);
ir.OrQFlag(result_overflow.overflow);
return true;
}
// SMUSD{X}<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SMUSD(Cond cond, Reg d, Reg m, bool M, Reg n) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 n32 = ir.GetRegister(n);
const IR::U32 m32 = ir.GetRegister(m);
const IR::U32 n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
const IR::U32 n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result;
IR::U32 m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
IR::U32 m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
if (M) {
std::swap(m_lo, m_hi);
}
const IR::U32 product_lo = ir.Mul(n_lo, m_lo);
const IR::U32 product_hi = ir.Mul(n_hi, m_hi);
auto result = ir.Sub(product_lo, product_hi);
ir.SetRegister(d, result);
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,46 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
// PKHBT<c> <Rd>, <Rn>, <Rm>{, LSL #<imm>}
bool ArmTranslatorVisitor::arm_PKHBT(Cond cond, Reg n, Reg d, Imm<5> imm5, Reg m) {
if (n == Reg::PC || d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto shifted = EmitImmShift(ir.GetRegister(m), ShiftType::LSL, imm5, ir.Imm1(false)).result;
const auto lower_half = ir.And(ir.GetRegister(n), ir.Imm32(0x0000FFFF));
const auto upper_half = ir.And(shifted, ir.Imm32(0xFFFF0000));
ir.SetRegister(d, ir.Or(lower_half, upper_half));
return true;
}
// PKHTB<c> <Rd>, <Rn>, <Rm>{, ASR #<imm>}
bool ArmTranslatorVisitor::arm_PKHTB(Cond cond, Reg n, Reg d, Imm<5> imm5, Reg m) {
if (n == Reg::PC || d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto shifted = EmitImmShift(ir.GetRegister(m), ShiftType::ASR, imm5, ir.Imm1(false)).result;
const auto lower_half = ir.And(shifted, ir.Imm32(0x0000FFFF));
const auto upper_half = ir.And(ir.GetRegister(n), ir.Imm32(0xFFFF0000));
ir.SetRegister(d, ir.Or(lower_half, upper_half));
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,537 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
// SADD8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SADD8(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedAddS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
// SADD16<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SADD16(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedAddS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
// SASX<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SASX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedAddSubS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
// SSAX<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SSAX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSubAddS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
// SSUB8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SSUB8(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSubS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
// SSUB16<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SSUB16(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSubS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
// UADD8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UADD8(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedAddU8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
// UADD16<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UADD16(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedAddU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
// UASX<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UASX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedAddSubU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
// USAX<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_USAX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSubAddU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
// USAD8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_USAD8(Cond cond, Reg d, Reg m, Reg n) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedAbsDiffSumS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// USADA8<c> <Rd>, <Rn>, <Rm>, <Ra>
bool ArmTranslatorVisitor::arm_USADA8(Cond cond, Reg d, Reg a, Reg m, Reg n){
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto tmp = ir.PackedAbsDiffSumS8(ir.GetRegister(n), ir.GetRegister(m));
const auto result = ir.AddWithCarry(ir.GetRegister(a), tmp, ir.Imm1(0));
ir.SetRegister(d, result.result);
return true;
}
// USUB8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_USUB8(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSubU8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
// USUB16<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_USUB16(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSubU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
return true;
}
// Parallel Add/Subtract (Saturating) instructions
// QADD8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_QADD8(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSaturatedAddS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// QADD16<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_QADD16(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSaturatedAddS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// QSUB8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_QSUB8(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSaturatedSubS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// QSUB16<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_QSUB16(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSaturatedSubS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// UQADD8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UQADD8(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSaturatedAddU8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// UQADD16<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UQADD16(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSaturatedAddU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// UQSUB8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UQSUB8(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSaturatedSubU8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// UQSUB16<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UQSUB16(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedSaturatedSubU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// Parallel Add/Subtract (Halving) instructions
// SHADD8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SHADD8(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedHalvingAddS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// SHADD16<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SHADD16(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedHalvingAddS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// SHASX<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SHASX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedHalvingAddSubS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// SHSAX<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SHSAX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedHalvingSubAddS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// SHSUB8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SHSUB8(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedHalvingSubS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// SHSUB16<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_SHSUB16(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedHalvingSubS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// UHADD8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UHADD8(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedHalvingAddU8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// UHADD16<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UHADD16(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedHalvingAddU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// UHASX<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UHASX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedHalvingAddSubU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// UHSAX<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UHSAX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedHalvingSubAddU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// UHSUB8<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UHSUB8(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedHalvingSubU8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// UHSUB16<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UHSUB16(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.PackedHalvingSubU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,89 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
// RBIT<c> <Rd>, <Rm>
bool ArmTranslatorVisitor::arm_RBIT(Cond cond, Reg d, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const IR::U32 swapped = ir.ByteReverseWord(ir.GetRegister(m));
// ((x & 0xF0F0F0F0) >> 4) | ((x & 0x0F0F0F0F) << 4)
const IR::U32 first_lsr = ir.LogicalShiftRight(ir.And(swapped, ir.Imm32(0xF0F0F0F0)), ir.Imm8(4));
const IR::U32 first_lsl = ir.LogicalShiftLeft(ir.And(swapped, ir.Imm32(0x0F0F0F0F)), ir.Imm8(4));
const IR::U32 corrected = ir.Or(first_lsl, first_lsr);
// ((x & 0x88888888) >> 3) | ((x & 0x44444444) >> 1) |
// ((x & 0x22222222) << 1) | ((x & 0x11111111) << 3)
const IR::U32 second_lsr = ir.LogicalShiftRight(ir.And(corrected, ir.Imm32(0x88888888)), ir.Imm8(3));
const IR::U32 third_lsr = ir.LogicalShiftRight(ir.And(corrected, ir.Imm32(0x44444444)), ir.Imm8(1));
const IR::U32 second_lsl = ir.LogicalShiftLeft(ir.And(corrected, ir.Imm32(0x22222222)), ir.Imm8(1));
const IR::U32 third_lsl = ir.LogicalShiftLeft(ir.And(corrected, ir.Imm32(0x11111111)), ir.Imm8(3));
const IR::U32 result = ir.Or(ir.Or(ir.Or(second_lsr, third_lsr), second_lsl), third_lsl);
ir.SetRegister(d, result);
return true;
}
// REV<c> <Rd>, <Rm>
bool ArmTranslatorVisitor::arm_REV(Cond cond, Reg d, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto result = ir.ByteReverseWord(ir.GetRegister(m));
ir.SetRegister(d, result);
return true;
}
// REV16<c> <Rd>, <Rm>
bool ArmTranslatorVisitor::arm_REV16(Cond cond, Reg d, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto reg_m = ir.GetRegister(m);
const auto lo = ir.And(ir.LogicalShiftRight(reg_m, ir.Imm8(8), ir.Imm1(0)).result, ir.Imm32(0x00FF00FF));
const auto hi = ir.And(ir.LogicalShiftLeft(reg_m, ir.Imm8(8), ir.Imm1(0)).result, ir.Imm32(0xFF00FF00));
const auto result = ir.Or(lo, hi);
ir.SetRegister(d, result);
return true;
}
// REVSH<c> <Rd>, <Rm>
bool ArmTranslatorVisitor::arm_REVSH(Cond cond, Reg d, Reg m) {
if (d == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto rev_half = ir.ByteReverseHalf(ir.LeastSignificantHalf(ir.GetRegister(m)));
ir.SetRegister(d, ir.SignExtendHalfToWord(rev_half));
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,285 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
static IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) {
return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result);
}
static IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) {
return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result);
}
// Saturation instructions
// SSAT<c> <Rd>, #<imm>, <Rn>{, <shift>}
bool ArmTranslatorVisitor::arm_SSAT(Cond cond, Imm<5> sat_imm, Reg d, Imm<5> imm5, bool sh, Reg n) {
if (d == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto saturate_to = static_cast<size_t>(sat_imm.ZeroExtend()) + 1;
const auto shift = !sh ? ShiftType::LSL : ShiftType::ASR;
const auto operand = EmitImmShift(ir.GetRegister(n), shift, imm5, ir.GetCFlag());
const auto result = ir.SignedSaturation(operand.result, saturate_to);
ir.SetRegister(d, result.result);
ir.OrQFlag(result.overflow);
return true;
}
// SSAT16<c> <Rd>, #<imm>, <Rn>
bool ArmTranslatorVisitor::arm_SSAT16(Cond cond, Imm<4> sat_imm, Reg d, Reg n) {
if (d == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto saturate_to = static_cast<size_t>(sat_imm.ZeroExtend()) + 1;
const auto lo_operand = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(ir.GetRegister(n)));
const auto hi_operand = ir.SignExtendHalfToWord(MostSignificantHalf(ir, ir.GetRegister(n)));
const auto lo_result = ir.SignedSaturation(lo_operand, saturate_to);
const auto hi_result = ir.SignedSaturation(hi_operand, saturate_to);
ir.SetRegister(d, Pack2x16To1x32(ir, lo_result.result, hi_result.result));
ir.OrQFlag(lo_result.overflow);
ir.OrQFlag(hi_result.overflow);
return true;
}
// USAT<c> <Rd>, #<imm5>, <Rn>{, <shift>}
bool ArmTranslatorVisitor::arm_USAT(Cond cond, Imm<5> sat_imm, Reg d, Imm<5> imm5, bool sh, Reg n) {
if (d == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto saturate_to = static_cast<size_t>(sat_imm.ZeroExtend());
const auto shift = !sh ? ShiftType::LSL : ShiftType::ASR;
const auto operand = EmitImmShift(ir.GetRegister(n), shift, imm5, ir.GetCFlag());
const auto result = ir.UnsignedSaturation(operand.result, saturate_to);
ir.SetRegister(d, result.result);
ir.OrQFlag(result.overflow);
return true;
}
// USAT16<c> <Rd>, #<imm4>, <Rn>
bool ArmTranslatorVisitor::arm_USAT16(Cond cond, Imm<4> sat_imm, Reg d, Reg n) {
if (d == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
// UnsignedSaturation takes a *signed* value as input, hence sign extension is required.
const auto saturate_to = static_cast<size_t>(sat_imm.ZeroExtend());
const auto lo_operand = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(ir.GetRegister(n)));
const auto hi_operand = ir.SignExtendHalfToWord(MostSignificantHalf(ir, ir.GetRegister(n)));
const auto lo_result = ir.UnsignedSaturation(lo_operand, saturate_to);
const auto hi_result = ir.UnsignedSaturation(hi_operand, saturate_to);
ir.SetRegister(d, Pack2x16To1x32(ir, lo_result.result, hi_result.result));
ir.OrQFlag(lo_result.overflow);
ir.OrQFlag(hi_result.overflow);
return true;
}
// Saturated Add/Subtract instructions
// QADD<c> <Rd>, <Rm>, <Rn>
bool ArmTranslatorVisitor::arm_QADD(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto a = ir.GetRegister(m);
const auto b = ir.GetRegister(n);
const auto result = ir.SignedSaturatedAdd(a, b);
ir.SetRegister(d, result.result);
ir.OrQFlag(result.overflow);
return true;
}
// QSUB<c> <Rd>, <Rm>, <Rn>
bool ArmTranslatorVisitor::arm_QSUB(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto a = ir.GetRegister(m);
const auto b = ir.GetRegister(n);
const auto result = ir.SignedSaturatedSub(a, b);
ir.SetRegister(d, result.result);
ir.OrQFlag(result.overflow);
return true;
}
// QDADD<c> <Rd>, <Rm>, <Rn>
bool ArmTranslatorVisitor::arm_QDADD(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto a = ir.GetRegister(m);
const auto b = ir.GetRegister(n);
const auto doubled = ir.SignedSaturatedAdd(b, b);
ir.OrQFlag(doubled.overflow);
const auto result = ir.SignedSaturatedAdd(a, doubled.result);
ir.SetRegister(d, result.result);
ir.OrQFlag(result.overflow);
return true;
}
// QDSUB<c> <Rd>, <Rm>, <Rn>
bool ArmTranslatorVisitor::arm_QDSUB(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto a = ir.GetRegister(m);
const auto b = ir.GetRegister(n);
const auto doubled = ir.SignedSaturatedAdd(b, b);
ir.OrQFlag(doubled.overflow);
const auto result = ir.SignedSaturatedSub(a, doubled.result);
ir.SetRegister(d, result.result);
ir.OrQFlag(result.overflow);
return true;
}
// Parallel saturated instructions
// QASX<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_QASX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto Rn = ir.GetRegister(n);
const auto Rm = ir.GetRegister(m);
const auto Rn_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rn));
const auto Rn_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rn));
const auto Rm_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rm));
const auto Rm_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rm));
const auto diff = ir.SignedSaturation(ir.Sub(Rn_lo, Rm_hi), 16).result;
const auto sum = ir.SignedSaturation(ir.Add(Rn_hi, Rm_lo), 16).result;
const auto result = Pack2x16To1x32(ir, diff, sum);
ir.SetRegister(d, result);
return true;
}
// QSAX<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_QSAX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto Rn = ir.GetRegister(n);
const auto Rm = ir.GetRegister(m);
const auto Rn_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rn));
const auto Rn_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rn));
const auto Rm_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rm));
const auto Rm_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rm));
const auto sum = ir.SignedSaturation(ir.Add(Rn_lo, Rm_hi), 16).result;
const auto diff = ir.SignedSaturation(ir.Sub(Rn_hi, Rm_lo), 16).result;
const auto result = Pack2x16To1x32(ir, sum, diff);
ir.SetRegister(d, result);
return true;
}
// UQASX<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UQASX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto Rn = ir.GetRegister(n);
const auto Rm = ir.GetRegister(m);
const auto Rn_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rn));
const auto Rn_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rn));
const auto Rm_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rm));
const auto Rm_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rm));
const auto diff = ir.UnsignedSaturation(ir.Sub(Rn_lo, Rm_hi), 16).result;
const auto sum = ir.UnsignedSaturation(ir.Add(Rn_hi, Rm_lo), 16).result;
const auto result = Pack2x16To1x32(ir, diff, sum);
ir.SetRegister(d, result);
return true;
}
// UQSAX<c> <Rd>, <Rn>, <Rm>
bool ArmTranslatorVisitor::arm_UQSAX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto Rn = ir.GetRegister(n);
const auto Rm = ir.GetRegister(m);
const auto Rn_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rn));
const auto Rn_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rn));
const auto Rm_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rm));
const auto Rm_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rm));
const auto sum = ir.UnsignedSaturation(ir.Add(Rn_lo, Rm_hi), 16).result;
const auto diff = ir.UnsignedSaturation(ir.Sub(Rn_hi, Rm_lo), 16).result;
const auto result = Pack2x16To1x32(ir, sum, diff);
ir.SetRegister(d, result);
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,120 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/bit_util.h"
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
// CPS<effect> <iflags>{, #<mode>}
// CPS #<mode>
bool ArmTranslatorVisitor::arm_CPS() {
return InterpretThisInstruction();
}
// MRS<c> <Rd>, <spec_reg>
bool ArmTranslatorVisitor::arm_MRS(Cond cond, Reg d) {
if (d == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
ir.SetRegister(d, ir.GetCpsr());
return true;
}
// MSR<c> <spec_reg>, #<const>
bool ArmTranslatorVisitor::arm_MSR_imm(Cond cond, int mask, int rotate, Imm<8> imm8) {
ASSERT_MSG(mask != 0, "Decode error");
if (!ConditionPassed(cond)) {
return true;
}
const bool write_nzcvq = Common::Bit<3>(mask);
const bool write_g = Common::Bit<2>(mask);
const bool write_e = Common::Bit<1>(mask);
const u32 imm32 = ArmExpandImm(rotate, imm8);
if (write_nzcvq) {
ir.SetCpsrNZCVQ(ir.Imm32(imm32 & 0xF8000000));
}
if (write_g) {
ir.SetGEFlagsCompressed(ir.Imm32(imm32 & 0x000F0000));
}
if (write_e) {
const bool E = (imm32 & 0x00000200) != 0;
if (E != ir.current_location.EFlag()) {
ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(4).SetEFlag(E)});
return false;
}
}
return true;
}
// MSR<c> <spec_reg>, <Rn>
bool ArmTranslatorVisitor::arm_MSR_reg(Cond cond, int mask, Reg n) {
if (mask == 0) {
return UnpredictableInstruction();
}
if (n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const bool write_nzcvq = Common::Bit<3>(mask);
const bool write_g = Common::Bit<2>(mask);
const bool write_e = Common::Bit<1>(mask);
const auto value = ir.GetRegister(n);
if (!write_e) {
if (write_nzcvq) {
ir.SetCpsrNZCVQ(ir.And(value, ir.Imm32(0xF8000000)));
}
if (write_g) {
ir.SetGEFlagsCompressed(ir.And(value, ir.Imm32(0x000F0000)));
}
} else {
const u32 cpsr_mask = (write_nzcvq ? 0xF8000000 : 0) | (write_g ? 0x000F0000 : 0) | 0x00000200;
const auto old_cpsr = ir.And(ir.GetCpsr(), ir.Imm32(~cpsr_mask));
const auto new_cpsr = ir.And(value, ir.Imm32(cpsr_mask));
ir.SetCpsr(ir.Or(old_cpsr, new_cpsr));
ir.PushRSB(ir.current_location.AdvancePC(4));
ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 4));
ir.SetTerm(IR::Term::CheckHalt{IR::Term::PopRSBHint{}});
return false;
}
return true;
}
// RFE{<amode>} <Rn>{!}
bool ArmTranslatorVisitor::arm_RFE() {
return InterpretThisInstruction();
}
// SETEND <endian_specifier>
bool ArmTranslatorVisitor::arm_SETEND(bool E) {
ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(4).SetEFlag(E)});
return false;
}
// SRS{<amode>} SP{!}, #<mode>
bool ArmTranslatorVisitor::arm_SRS() {
return InterpretThisInstruction();
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,437 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_arm.h"
namespace Dynarmic::A32 {
// CLREX
bool ArmTranslatorVisitor::arm_CLREX() {
ir.ClearExclusive();
return true;
}
// SWP<c> <Rt>, <Rt2>, [<Rn>]
// TODO: UNDEFINED if current mode is Hypervisor
bool ArmTranslatorVisitor::arm_SWP(Cond cond, Reg n, Reg t, Reg t2) {
if (t == Reg::PC || t2 == Reg::PC || n == Reg::PC || n == t || n == t2) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto data = ir.ReadMemory32(ir.GetRegister(n));
ir.WriteMemory32(ir.GetRegister(n), ir.GetRegister(t2));
// TODO: Alignment check
ir.SetRegister(t, data);
return true;
}
// SWPB<c> <Rt>, <Rt2>, [<Rn>]
// TODO: UNDEFINED if current mode is Hypervisor
bool ArmTranslatorVisitor::arm_SWPB(Cond cond, Reg n, Reg t, Reg t2) {
if (t == Reg::PC || t2 == Reg::PC || n == Reg::PC || n == t || n == t2) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto data = ir.ReadMemory8(ir.GetRegister(n));
ir.WriteMemory8(ir.GetRegister(n), ir.LeastSignificantByte(ir.GetRegister(t2)));
// TODO: Alignment check
ir.SetRegister(t, ir.ZeroExtendByteToWord(data));
return true;
}
// LDA<c> <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_LDA(Cond cond, Reg n, Reg t) {
if (t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
ir.SetRegister(t, ir.ReadMemory32(address)); // AccType::Ordered
return true;
}
// LDAB<c> <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_LDAB(Cond cond, Reg n, Reg t) {
if (t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
ir.SetRegister(t, ir.ZeroExtendToWord(ir.ReadMemory8(address))); // AccType::Ordered
return true;
}
// LDAH<c> <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_LDAH(Cond cond, Reg n, Reg t) {
if (t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
ir.SetRegister(t, ir.ZeroExtendToWord(ir.ReadMemory16(address))); // AccType::Ordered
return true;
}
// LDAEX<c> <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_LDAEX(Cond cond, Reg n, Reg t) {
if (t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
ir.SetRegister(t, ir.ExclusiveReadMemory32(address)); // AccType::Ordered
return true;
}
// LDAEXB<c> <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_LDAEXB(Cond cond, Reg n, Reg t) {
if (t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
ir.SetRegister(t, ir.ZeroExtendByteToWord(ir.ExclusiveReadMemory8(address))); // AccType::Ordered
return true;
}
// LDAEXD<c> <Rt>, <Rt2>, [<Rn>]
bool ArmTranslatorVisitor::arm_LDAEXD(Cond cond, Reg n, Reg t) {
if (t == Reg::LR || t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
const auto [lo, hi] = ir.ExclusiveReadMemory64(address); // AccType::Ordered
// DO NOT SWAP hi AND lo IN BIG ENDIAN MODE, THIS IS CORRECT BEHAVIOUR
ir.SetRegister(t, lo);
ir.SetRegister(t+1, hi);
return true;
}
// LDAEXH<c> <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_LDAEXH(Cond cond, Reg n, Reg t) {
if (t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
ir.SetRegister(t, ir.ZeroExtendHalfToWord(ir.ExclusiveReadMemory16(address))); // AccType::Ordered
return true;
}
// STL<c> <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_STL(Cond cond, Reg n, Reg t) {
if (t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
ir.WriteMemory32(address, ir.GetRegister(t)); // AccType::Ordered
return true;
}
// STLB<c> <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_STLB(Cond cond, Reg n, Reg t) {
if (t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
ir.WriteMemory8(address, ir.LeastSignificantByte(ir.GetRegister(t))); // AccType::Ordered
return true;
}
// STLH<c> <Rd>, <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_STLH(Cond cond, Reg n, Reg t) {
if (t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
ir.WriteMemory16(address, ir.LeastSignificantHalf(ir.GetRegister(t))); // AccType::Ordered
return true;
}
// STLEXB<c> <Rd>, <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_STLEXB(Cond cond, Reg n, Reg d, Reg t) {
if (n == Reg::PC || d == Reg::PC || t == Reg::PC) {
return UnpredictableInstruction();
}
if (d == n || d == t) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
const auto value = ir.LeastSignificantByte(ir.GetRegister(t));
const auto passed = ir.ExclusiveWriteMemory8(address, value); // AccType::Ordered
ir.SetRegister(d, passed);
return true;
}
// STLEXD<c> <Rd>, <Rt>, <Rt2>, [<Rn>]
bool ArmTranslatorVisitor::arm_STLEXD(Cond cond, Reg n, Reg d, Reg t) {
if (n == Reg::PC || d == Reg::PC || t == Reg::LR || static_cast<size_t>(t) % 2 == 1) {
return UnpredictableInstruction();
}
if (d == n || d == t || d == t+1) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const Reg t2 = t + 1;
const auto address = ir.GetRegister(n);
const auto value_lo = ir.GetRegister(t);
const auto value_hi = ir.GetRegister(t2);
const auto passed = ir.ExclusiveWriteMemory64(address, value_lo, value_hi); // AccType::Ordered
ir.SetRegister(d, passed);
return true;
}
// STLEXH<c> <Rd>, <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_STLEXH(Cond cond, Reg n, Reg d, Reg t) {
if (n == Reg::PC || d == Reg::PC || t == Reg::PC) {
return UnpredictableInstruction();
}
if (d == n || d == t) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
const auto value = ir.LeastSignificantHalf(ir.GetRegister(t));
const auto passed = ir.ExclusiveWriteMemory16(address, value); // AccType::Ordered
ir.SetRegister(d, passed);
return true;
}
// STLEX<c> <Rd>, <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_STLEX(Cond cond, Reg n, Reg d, Reg t) {
if (n == Reg::PC || d == Reg::PC || t == Reg::PC) {
return UnpredictableInstruction();
}
if (d == n || d == t) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
const auto value = ir.GetRegister(t);
const auto passed = ir.ExclusiveWriteMemory32(address, value);
ir.SetRegister(d, passed);
return true;
}
// LDREX<c> <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_LDREX(Cond cond, Reg n, Reg t) {
if (t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
ir.SetRegister(t, ir.ExclusiveReadMemory32(address));
return true;
}
// LDREXB<c> <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_LDREXB(Cond cond, Reg n, Reg t) {
if (t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
ir.SetRegister(t, ir.ZeroExtendByteToWord(ir.ExclusiveReadMemory8(address)));
return true;
}
// LDREXD<c> <Rt>, <Rt2>, [<Rn>]
bool ArmTranslatorVisitor::arm_LDREXD(Cond cond, Reg n, Reg t) {
if (t == Reg::LR || t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
const auto [lo, hi] = ir.ExclusiveReadMemory64(address);
// DO NOT SWAP hi AND lo IN BIG ENDIAN MODE, THIS IS CORRECT BEHAVIOUR
ir.SetRegister(t, lo);
ir.SetRegister(t+1, hi);
return true;
}
// LDREXH<c> <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_LDREXH(Cond cond, Reg n, Reg t) {
if (t == Reg::PC || n == Reg::PC) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
ir.SetRegister(t, ir.ZeroExtendHalfToWord(ir.ExclusiveReadMemory16(address)));
return true;
}
// STREX<c> <Rd>, <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_STREX(Cond cond, Reg n, Reg d, Reg t) {
if (n == Reg::PC || d == Reg::PC || t == Reg::PC) {
return UnpredictableInstruction();
}
if (d == n || d == t) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
const auto value = ir.GetRegister(t);
const auto passed = ir.ExclusiveWriteMemory32(address, value);
ir.SetRegister(d, passed);
return true;
}
// STREXB<c> <Rd>, <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_STREXB(Cond cond, Reg n, Reg d, Reg t) {
if (n == Reg::PC || d == Reg::PC || t == Reg::PC) {
return UnpredictableInstruction();
}
if (d == n || d == t) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
const auto value = ir.LeastSignificantByte(ir.GetRegister(t));
const auto passed = ir.ExclusiveWriteMemory8(address, value);
ir.SetRegister(d, passed);
return true;
}
// STREXD<c> <Rd>, <Rt>, <Rt2>, [<Rn>]
bool ArmTranslatorVisitor::arm_STREXD(Cond cond, Reg n, Reg d, Reg t) {
if (n == Reg::PC || d == Reg::PC || t == Reg::LR || static_cast<size_t>(t) % 2 == 1) {
return UnpredictableInstruction();
}
if (d == n || d == t || d == t+1) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const Reg t2 = t + 1;
const auto address = ir.GetRegister(n);
const auto value_lo = ir.GetRegister(t);
const auto value_hi = ir.GetRegister(t2);
const auto passed = ir.ExclusiveWriteMemory64(address, value_lo, value_hi);
ir.SetRegister(d, passed);
return true;
}
// STREXH<c> <Rd>, <Rt>, [<Rn>]
bool ArmTranslatorVisitor::arm_STREXH(Cond cond, Reg n, Reg d, Reg t) {
if (n == Reg::PC || d == Reg::PC || t == Reg::PC) {
return UnpredictableInstruction();
}
if (d == n || d == t) {
return UnpredictableInstruction();
}
if (!ConditionPassed(cond)) {
return true;
}
const auto address = ir.GetRegister(n);
const auto value = ir.LeastSignificantHalf(ir.GetRegister(t));
const auto passed = ir.ExclusiveWriteMemory16(address, value);
ir.SetRegister(d, passed);
return true;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,953 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <dynarmic/A32/config.h>
#include "frontend/A32/translate/impl/translate_thumb.h"
namespace Dynarmic::A32 {
// LSLS <Rd>, <Rm>, #<imm5>
bool ThumbTranslatorVisitor::thumb16_LSL_imm(Imm<5> imm5, Reg m, Reg d) {
const u8 shift_n = imm5.ZeroExtend<u8>();
const auto cpsr_c = ir.GetCFlag();
const auto result = ir.LogicalShiftLeft(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
return true;
}
// LSRS <Rd>, <Rm>, #<imm5>
bool ThumbTranslatorVisitor::thumb16_LSR_imm(Imm<5> imm5, Reg m, Reg d) {
const u8 shift_n = imm5 != 0 ? imm5.ZeroExtend<u8>() : u8(32);
const auto cpsr_c = ir.GetCFlag();
const auto result = ir.LogicalShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
return true;
}
// ASRS <Rd>, <Rm>, #<imm5>
bool ThumbTranslatorVisitor::thumb16_ASR_imm(Imm<5> imm5, Reg m, Reg d) {
const u8 shift_n = imm5 != 0 ? imm5.ZeroExtend<u8>() : u8(32);
const auto cpsr_c = ir.GetCFlag();
const auto result = ir.ArithmeticShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
return true;
}
// ADDS <Rd>, <Rn>, <Rm>
// Note that it is not possible to encode Rd == R15.
bool ThumbTranslatorVisitor::thumb16_ADD_reg_t1(Reg m, Reg n, Reg d) {
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0));
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// SUBS <Rd>, <Rn>, <Rm>
// Note that it is not possible to encode Rd == R15.
bool ThumbTranslatorVisitor::thumb16_SUB_reg(Reg m, Reg n, Reg d) {
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(1));
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// ADDS <Rd>, <Rn>, #<imm3>
// Rd can never encode R15.
bool ThumbTranslatorVisitor::thumb16_ADD_imm_t1(Imm<3> imm3, Reg n, Reg d) {
const u32 imm32 = imm3.ZeroExtend();
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(0));
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// SUBS <Rd>, <Rn>, #<imm3>
// Rd can never encode R15.
bool ThumbTranslatorVisitor::thumb16_SUB_imm_t1(Imm<3> imm3, Reg n, Reg d) {
const u32 imm32 = imm3.ZeroExtend();
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1));
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// MOVS <Rd>, #<imm8>
// Rd can never encode R15.
bool ThumbTranslatorVisitor::thumb16_MOV_imm(Reg d, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend();
const auto result = ir.Imm32(imm32);
ir.SetRegister(d, result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
return true;
}
// CMP <Rn>, #<imm8>
bool ThumbTranslatorVisitor::thumb16_CMP_imm(Reg n, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend();
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1));
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// ADDS <Rdn>, #<imm8>
// Rd can never encode R15.
bool ThumbTranslatorVisitor::thumb16_ADD_imm_t2(Reg d_n, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend();
const Reg d = d_n;
const Reg n = d_n;
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(0));
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// SUBS <Rd>, <Rn>, #<imm3>
// Rd can never encode R15.
bool ThumbTranslatorVisitor::thumb16_SUB_imm_t2(Reg d_n, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend();
const Reg d = d_n;
const Reg n = d_n;
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1));
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// ANDS <Rdn>, <Rm>
// Note that it is not possible to encode Rdn == R15.
bool ThumbTranslatorVisitor::thumb16_AND_reg(Reg m, Reg d_n) {
const Reg d = d_n;
const Reg n = d_n;
const auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
return true;
}
// EORS <Rdn>, <Rm>
// Note that it is not possible to encode Rdn == R15.
bool ThumbTranslatorVisitor::thumb16_EOR_reg(Reg m, Reg d_n) {
const Reg d = d_n;
const Reg n = d_n;
const auto result = ir.Eor(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
return true;
}
// LSLS <Rdn>, <Rm>
bool ThumbTranslatorVisitor::thumb16_LSL_reg(Reg m, Reg d_n) {
const Reg d = d_n;
const Reg n = d_n;
const auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
const auto apsr_c = ir.GetCFlag();
const auto result_carry = ir.LogicalShiftLeft(ir.GetRegister(n), shift_n, apsr_c);
ir.SetRegister(d, result_carry.result);
ir.SetNFlag(ir.MostSignificantBit(result_carry.result));
ir.SetZFlag(ir.IsZero(result_carry.result));
ir.SetCFlag(result_carry.carry);
return true;
}
// LSRS <Rdn>, <Rm>
bool ThumbTranslatorVisitor::thumb16_LSR_reg(Reg m, Reg d_n) {
const Reg d = d_n;
const Reg n = d_n;
const auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
const auto cpsr_c = ir.GetCFlag();
const auto result = ir.LogicalShiftRight(ir.GetRegister(n), shift_n, cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
return true;
}
// ASRS <Rdn>, <Rm>
bool ThumbTranslatorVisitor::thumb16_ASR_reg(Reg m, Reg d_n) {
const Reg d = d_n;
const Reg n = d_n;
const auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
const auto cpsr_c = ir.GetCFlag();
const auto result = ir.ArithmeticShiftRight(ir.GetRegister(n), shift_n, cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
return true;
}
// ADCS <Rdn>, <Rm>
// Note that it is not possible to encode Rd == R15.
bool ThumbTranslatorVisitor::thumb16_ADC_reg(Reg m, Reg d_n) {
const Reg d = d_n;
const Reg n = d_n;
const auto aspr_c = ir.GetCFlag();
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), aspr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// SBCS <Rdn>, <Rm>
// Note that it is not possible to encode Rd == R15.
bool ThumbTranslatorVisitor::thumb16_SBC_reg(Reg m, Reg d_n) {
const Reg d = d_n;
const Reg n = d_n;
const auto aspr_c = ir.GetCFlag();
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), aspr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// RORS <Rdn>, <Rm>
bool ThumbTranslatorVisitor::thumb16_ROR_reg(Reg m, Reg d_n) {
const Reg d = d_n;
const Reg n = d_n;
const auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
const auto cpsr_c = ir.GetCFlag();
const auto result = ir.RotateRight(ir.GetRegister(n), shift_n, cpsr_c);
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
return true;
}
// TST <Rn>, <Rm>
bool ThumbTranslatorVisitor::thumb16_TST_reg(Reg m, Reg n) {
const auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m));
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
return true;
}
// RSBS <Rd>, <Rn>, #0
// Rd can never encode R15.
bool ThumbTranslatorVisitor::thumb16_RSB_imm(Reg n, Reg d) {
const auto result = ir.SubWithCarry(ir.Imm32(0), ir.GetRegister(n), ir.Imm1(1));
ir.SetRegister(d, result.result);
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// CMP <Rn>, <Rm>
bool ThumbTranslatorVisitor::thumb16_CMP_reg_t1(Reg m, Reg n) {
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(1));
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// CMN <Rn>, <Rm>
bool ThumbTranslatorVisitor::thumb16_CMN_reg(Reg m, Reg n) {
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0));
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// ORRS <Rdn>, <Rm>
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_ORR_reg(Reg m, Reg d_n) {
const Reg d = d_n;
const Reg n = d_n;
const auto result = ir.Or(ir.GetRegister(m), ir.GetRegister(n));
ir.SetRegister(d, result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
return true;
}
// MULS <Rdn>, <Rm>, <Rdn>
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_MUL_reg(Reg n, Reg d_m) {
const Reg d = d_m;
const Reg m = d_m;
const auto result = ir.Mul(ir.GetRegister(m), ir.GetRegister(n));
ir.SetRegister(d, result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
return true;
}
// BICS <Rdn>, <Rm>
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_BIC_reg(Reg m, Reg d_n) {
const Reg d = d_n;
const Reg n = d_n;
const auto result = ir.And(ir.GetRegister(n), ir.Not(ir.GetRegister(m)));
ir.SetRegister(d, result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
return true;
}
// MVNS <Rd>, <Rm>
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_MVN_reg(Reg m, Reg d) {
const auto result = ir.Not(ir.GetRegister(m));
ir.SetRegister(d, result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
return true;
}
// ADD <Rdn>, <Rm>
bool ThumbTranslatorVisitor::thumb16_ADD_reg_t2(bool d_n_hi, Reg m, Reg d_n_lo) {
const Reg d_n = d_n_hi ? (d_n_lo + 8) : d_n_lo;
const Reg n = d_n;
if (n == Reg::PC && m == Reg::PC) {
return UnpredictableInstruction();
}
const Reg d = d_n;
const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0));
if (d == Reg::PC) {
ir.ALUWritePC(result.result);
// Return to dispatch as we can't predict what PC is going to be. Stop compilation.
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
} else {
ir.SetRegister(d, result.result);
return true;
}
}
// CMP <Rn>, <Rm>
bool ThumbTranslatorVisitor::thumb16_CMP_reg_t2(bool n_hi, Reg m, Reg n_lo) {
const Reg n = n_hi ? (n_lo + 8) : n_lo;
if (n < Reg::R8 && m < Reg::R8) {
return UnpredictableInstruction();
}
if (n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(1));
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
return true;
}
// MOV <Rd>, <Rm>
bool ThumbTranslatorVisitor::thumb16_MOV_reg(bool d_hi, Reg m, Reg d_lo) {
const Reg d = d_hi ? (d_lo + 8) : d_lo;
const auto result = ir.GetRegister(m);
if (d == Reg::PC) {
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
} else {
ir.SetRegister(d, result);
return true;
}
}
// LDR <Rt>, <label>
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_LDR_literal(Reg t, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend() << 2;
const u32 address = ir.AlignPC(4) + imm32;
const auto data = ir.ReadMemory32(ir.Imm32(address));
ir.SetRegister(t, data);
return true;
}
// STR <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_STR_reg(Reg m, Reg n, Reg t) {
const auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
const auto data = ir.GetRegister(t);
ir.WriteMemory32(address, data);
return true;
}
// STRH <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_STRH_reg(Reg m, Reg n, Reg t) {
const auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
const auto data = ir.LeastSignificantHalf(ir.GetRegister(t));
ir.WriteMemory16(address, data);
return true;
}
// STRB <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_STRB_reg(Reg m, Reg n, Reg t) {
const auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
const auto data = ir.LeastSignificantByte(ir.GetRegister(t));
ir.WriteMemory8(address, data);
return true;
}
// LDRSB <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_LDRSB_reg(Reg m, Reg n, Reg t) {
const auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
const auto data = ir.SignExtendByteToWord(ir.ReadMemory8(address));
ir.SetRegister(t, data);
return true;
}
// LDR <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_LDR_reg(Reg m, Reg n, Reg t) {
const auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
const auto data = ir.ReadMemory32(address);
ir.SetRegister(t, data);
return true;
}
// LDRH <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_LDRH_reg(Reg m, Reg n, Reg t) {
const auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address));
ir.SetRegister(t, data);
return true;
}
// LDRB <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_LDRB_reg(Reg m, Reg n, Reg t) {
const auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address));
ir.SetRegister(t, data);
return true;
}
// LDRH <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_LDRSH_reg(Reg m, Reg n, Reg t) {
const auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
const auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(address));
ir.SetRegister(t, data);
return true;
}
// STR <Rt>, [<Rn>, #<imm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_STR_imm_t1(Imm<5> imm5, Reg n, Reg t) {
const u32 imm32 = imm5.ZeroExtend() << 2;
const auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
const auto data = ir.GetRegister(t);
ir.WriteMemory32(address, data);
return true;
}
// LDR <Rt>, [<Rn>, #<imm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_LDR_imm_t1(Imm<5> imm5, Reg n, Reg t) {
const u32 imm32 = imm5.ZeroExtend() << 2;
const auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
const auto data = ir.ReadMemory32(address);
ir.SetRegister(t, data);
return true;
}
// STRB <Rt>, [<Rn>, #<imm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_STRB_imm(Imm<5> imm5, Reg n, Reg t) {
const u32 imm32 = imm5.ZeroExtend();
const auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
const auto data = ir.LeastSignificantByte(ir.GetRegister(t));
ir.WriteMemory8(address, data);
return true;
}
// LDRB <Rt>, [<Rn>, #<imm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_LDRB_imm(Imm<5> imm5, Reg n, Reg t) {
const u32 imm32 = imm5.ZeroExtend();
const auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
const auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address));
ir.SetRegister(t, data);
return true;
}
// STRH <Rt>, [<Rn>, #<imm5>]
bool ThumbTranslatorVisitor::thumb16_STRH_imm(Imm<5> imm5, Reg n, Reg t) {
const u32 imm32 = imm5.ZeroExtend() << 1;
const auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
const auto data = ir.LeastSignificantHalf(ir.GetRegister(t));
ir.WriteMemory16(address, data);
return true;
}
// LDRH <Rt>, [<Rn>, #<imm5>]
bool ThumbTranslatorVisitor::thumb16_LDRH_imm(Imm<5> imm5, Reg n, Reg t) {
const u32 imm32 = imm5.ZeroExtend() << 1;
const auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
const auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address));
ir.SetRegister(t, data);
return true;
}
// STR <Rt>, [<Rn>, #<imm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_STR_imm_t2(Reg t, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend() << 2;
const Reg n = Reg::SP;
const auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
const auto data = ir.GetRegister(t);
ir.WriteMemory32(address, data);
return true;
}
// LDR <Rt>, [<Rn>, #<imm>]
// Rt cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_LDR_imm_t2(Reg t, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend() << 2;
const Reg n = Reg::SP;
const auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
const auto data = ir.ReadMemory32(address);
ir.SetRegister(t, data);
return true;
}
// ADR <Rd>, <label>
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_ADR(Reg d, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend() << 2;
const auto result = ir.Imm32(ir.AlignPC(4) + imm32);
ir.SetRegister(d, result);
return true;
}
// ADD <Rd>, SP, #<imm>
bool ThumbTranslatorVisitor::thumb16_ADD_sp_t1(Reg d, Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend() << 2;
const auto result = ir.AddWithCarry(ir.GetRegister(Reg::SP), ir.Imm32(imm32), ir.Imm1(0));
ir.SetRegister(d, result.result);
return true;
}
// ADD SP, SP, #<imm>
bool ThumbTranslatorVisitor::thumb16_ADD_sp_t2(Imm<7> imm7) {
const u32 imm32 = imm7.ZeroExtend() << 2;
const Reg d = Reg::SP;
const auto result = ir.AddWithCarry(ir.GetRegister(Reg::SP), ir.Imm32(imm32), ir.Imm1(0));
ir.SetRegister(d, result.result);
return true;
}
// SUB SP, SP, #<imm>
bool ThumbTranslatorVisitor::thumb16_SUB_sp(Imm<7> imm7) {
const u32 imm32 = imm7.ZeroExtend() << 2;
const Reg d = Reg::SP;
const auto result = ir.SubWithCarry(ir.GetRegister(Reg::SP), ir.Imm32(imm32), ir.Imm1(1));
ir.SetRegister(d, result.result);
return true;
}
// NOP<c>
bool ThumbTranslatorVisitor::thumb16_NOP() {
return true;
}
// SEV<c>
bool ThumbTranslatorVisitor::thumb16_SEV() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::SendEvent);
}
// SEVL<c>
bool ThumbTranslatorVisitor::thumb16_SEVL() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::SendEventLocal);
}
// WFE<c>
bool ThumbTranslatorVisitor::thumb16_WFE() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::WaitForEvent);
}
// WFI<c>
bool ThumbTranslatorVisitor::thumb16_WFI() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::WaitForInterrupt);
}
// YIELD<c>
bool ThumbTranslatorVisitor::thumb16_YIELD() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::Yield);
}
// SXTH <Rd>, <Rm>
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_SXTH(Reg m, Reg d) {
const auto half = ir.LeastSignificantHalf(ir.GetRegister(m));
ir.SetRegister(d, ir.SignExtendHalfToWord(half));
return true;
}
// SXTB <Rd>, <Rm>
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_SXTB(Reg m, Reg d) {
const auto byte = ir.LeastSignificantByte(ir.GetRegister(m));
ir.SetRegister(d, ir.SignExtendByteToWord(byte));
return true;
}
// UXTH <Rd>, <Rm>
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_UXTH(Reg m, Reg d) {
const auto half = ir.LeastSignificantHalf(ir.GetRegister(m));
ir.SetRegister(d, ir.ZeroExtendHalfToWord(half));
return true;
}
// UXTB <Rd>, <Rm>
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_UXTB(Reg m, Reg d) {
const auto byte = ir.LeastSignificantByte(ir.GetRegister(m));
ir.SetRegister(d, ir.ZeroExtendByteToWord(byte));
return true;
}
// PUSH <reg_list>
// reg_list cannot encode for R15.
bool ThumbTranslatorVisitor::thumb16_PUSH(bool M, RegList reg_list) {
if (M) {
reg_list |= 1 << 14;
}
if (Common::BitCount(reg_list) < 1) {
return UnpredictableInstruction();
}
const u32 num_bytes_to_push = static_cast<u32>(4 * Common::BitCount(reg_list));
const auto final_address = ir.Sub(ir.GetRegister(Reg::SP), ir.Imm32(num_bytes_to_push));
auto address = final_address;
for (size_t i = 0; i < 16; i++) {
if (Common::Bit(i, reg_list)) {
// TODO: Deal with alignment
const auto Ri = ir.GetRegister(static_cast<Reg>(i));
ir.WriteMemory32(address, Ri);
address = ir.Add(address, ir.Imm32(4));
}
}
ir.SetRegister(Reg::SP, final_address);
// TODO(optimization): Possible location for an RSB push.
return true;
}
// POP <reg_list>
bool ThumbTranslatorVisitor::thumb16_POP(bool P, RegList reg_list) {
if (P) {
reg_list |= 1 << 15;
}
if (Common::BitCount(reg_list) < 1) {
return UnpredictableInstruction();
}
auto address = ir.GetRegister(Reg::SP);
for (size_t i = 0; i < 15; i++) {
if (Common::Bit(i, reg_list)) {
// TODO: Deal with alignment
const auto data = ir.ReadMemory32(address);
ir.SetRegister(static_cast<Reg>(i), data);
address = ir.Add(address, ir.Imm32(4));
}
}
if (Common::Bit<15>(reg_list)) {
// TODO(optimization): Possible location for an RSB pop.
const auto data = ir.ReadMemory32(address);
ir.LoadWritePC(data);
address = ir.Add(address, ir.Imm32(4));
ir.SetRegister(Reg::SP, address);
ir.SetTerm(IR::Term::PopRSBHint{});
return false;
} else {
ir.SetRegister(Reg::SP, address);
return true;
}
}
// SETEND <endianness>
bool ThumbTranslatorVisitor::thumb16_SETEND(bool E) {
if (E == ir.current_location.EFlag()) {
return true;
}
ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(2).SetEFlag(E)});
return false;
}
// CPS{IE,ID} <a,i,f>
// A CPS is treated as a NOP in User mode.
bool ThumbTranslatorVisitor::thumb16_CPS(bool, bool, bool, bool) {
return true;
}
// REV <Rd>, <Rm>
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_REV(Reg m, Reg d) {
ir.SetRegister(d, ir.ByteReverseWord(ir.GetRegister(m)));
return true;
}
// REV16 <Rd>, <Rm>
// Rd cannot encode R15.
// TODO: Consider optimizing
bool ThumbTranslatorVisitor::thumb16_REV16(Reg m, Reg d) {
const auto Rm = ir.GetRegister(m);
const auto upper_half = ir.LeastSignificantHalf(ir.LogicalShiftRight(Rm, ir.Imm8(16), ir.Imm1(0)).result);
const auto lower_half = ir.LeastSignificantHalf(Rm);
const auto rev_upper_half = ir.ZeroExtendHalfToWord(ir.ByteReverseHalf(upper_half));
const auto rev_lower_half = ir.ZeroExtendHalfToWord(ir.ByteReverseHalf(lower_half));
const auto result = ir.Or(ir.LogicalShiftLeft(rev_upper_half, ir.Imm8(16), ir.Imm1(0)).result,
rev_lower_half);
ir.SetRegister(d, result);
return true;
}
// REVSH <Rd>, <Rm>
// Rd cannot encode R15.
bool ThumbTranslatorVisitor::thumb16_REVSH(Reg m, Reg d) {
const auto rev_half = ir.ByteReverseHalf(ir.LeastSignificantHalf(ir.GetRegister(m)));
ir.SetRegister(d, ir.SignExtendHalfToWord(rev_half));
return true;
}
// BKPT #<imm8>
bool ThumbTranslatorVisitor::thumb16_BKPT([[maybe_unused]] Imm<8> imm8) {
ir.ExceptionRaised(Exception::Breakpoint);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
// STM <Rn>!, <reg_list>
bool ThumbTranslatorVisitor::thumb16_STMIA(Reg n, RegList reg_list) {
if (Common::BitCount(reg_list) == 0) {
return UnpredictableInstruction();
}
if (Common::Bit(static_cast<size_t>(n), reg_list) && n != static_cast<Reg>(Common::LowestSetBit(reg_list))) {
return UnpredictableInstruction();
}
auto address = ir.GetRegister(n);
for (size_t i = 0; i < 8; i++) {
if (Common::Bit(i, reg_list)) {
const auto Ri = ir.GetRegister(static_cast<Reg>(i));
ir.WriteMemory32(address, Ri);
address = ir.Add(address, ir.Imm32(4));
}
}
ir.SetRegister(n, address);
return true;
}
// LDM <Rn>!, <reg_list>
bool ThumbTranslatorVisitor::thumb16_LDMIA(Reg n, RegList reg_list) {
if (Common::BitCount(reg_list) == 0) {
return UnpredictableInstruction();
}
const bool write_back = !Common::Bit(static_cast<size_t>(n), reg_list);
auto address = ir.GetRegister(n);
for (size_t i = 0; i < 8; i++) {
if (Common::Bit(i, reg_list)) {
const auto data = ir.ReadMemory32(address);
ir.SetRegister(static_cast<Reg>(i), data);
address = ir.Add(address, ir.Imm32(4));
}
}
if (write_back) {
ir.SetRegister(n, address);
}
return true;
}
// CB{N}Z <Rn>, <label>
bool ThumbTranslatorVisitor::thumb16_CBZ_CBNZ(bool nonzero, Imm<1> i, Imm<5> imm5, Reg n) {
const u32 imm = concatenate(i, imm5, Imm<1>{0}).ZeroExtend();
const IR::U32 rn = ir.GetRegister(n);
ir.SetCheckBit(ir.IsZero(rn));
const auto [cond_pass, cond_fail] = [this, imm, nonzero] {
const u32 target = ir.PC() + imm;
const auto skip = IR::Term::LinkBlock{ir.current_location.AdvancePC(2)};
const auto branch = IR::Term::LinkBlock{ir.current_location.AdvancePC(target)};
if (nonzero) {
return std::make_pair(skip, branch);
} else {
return std::make_pair(branch, skip);
}
}();
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
return false;
}
bool ThumbTranslatorVisitor::thumb16_UDF() {
return UndefinedInstruction();
}
// BX <Rm>
bool ThumbTranslatorVisitor::thumb16_BX(Reg m) {
ir.BXWritePC(ir.GetRegister(m));
if (m == Reg::R14)
ir.SetTerm(IR::Term::PopRSBHint{});
else
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
}
// BLX <Rm>
bool ThumbTranslatorVisitor::thumb16_BLX_reg(Reg m) {
ir.PushRSB(ir.current_location.AdvancePC(2));
ir.BXWritePC(ir.GetRegister(m));
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 2) | 1));
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
}
// SVC #<imm8>
bool ThumbTranslatorVisitor::thumb16_SVC(Imm<8> imm8) {
const u32 imm32 = imm8.ZeroExtend();
ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 2));
ir.PushRSB(ir.current_location.AdvancePC(2));
ir.CallSupervisor(ir.Imm32(imm32));
ir.SetTerm(IR::Term::CheckHalt{IR::Term::PopRSBHint{}});
return false;
}
// B<cond> <label>
bool ThumbTranslatorVisitor::thumb16_B_t1(Cond cond, Imm<8> imm8) {
if (cond == Cond::AL) {
return thumb16_UDF();
}
const s32 imm32 = static_cast<s32>((imm8.SignExtend<u32>() << 1) + 4);
const auto then_location = ir.current_location.AdvancePC(imm32);
const auto else_location = ir.current_location.AdvancePC(2);
ir.SetTerm(IR::Term::If{cond, IR::Term::LinkBlock{then_location}, IR::Term::LinkBlock{else_location}});
return false;
}
// B <label>
bool ThumbTranslatorVisitor::thumb16_B_t2(Imm<11> imm11) {
const s32 imm32 = static_cast<s32>((imm11.SignExtend<u32>() << 1) + 4);
const auto next_location = ir.current_location.AdvancePC(imm32);
ir.SetTerm(IR::Term::LinkBlock{next_location});
return false;
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,42 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/translate/impl/translate_thumb.h"
namespace Dynarmic::A32 {
// BL <label>
bool ThumbTranslatorVisitor::thumb32_BL_imm(Imm<11> hi, Imm<11> lo) {
ir.PushRSB(ir.current_location.AdvancePC(4));
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1));
const s32 imm32 = static_cast<s32>((concatenate(hi, lo).SignExtend<u32>() << 1) + 4);
const auto new_location = ir.current_location.AdvancePC(imm32);
ir.SetTerm(IR::Term::LinkBlock{new_location});
return false;
}
// BLX <label>
bool ThumbTranslatorVisitor::thumb32_BLX_imm(Imm<11> hi, Imm<11> lo) {
if (lo.Bit<0>()) {
return UnpredictableInstruction();
}
ir.PushRSB(ir.current_location.AdvancePC(4));
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1));
const s32 imm32 = static_cast<s32>(concatenate(hi, lo).SignExtend<u32>() << 1);
const auto new_location = ir.current_location
.SetPC(ir.AlignPC(4) + imm32)
.SetTFlag(false);
ir.SetTerm(IR::Term::LinkBlock{new_location});
return false;
}
bool ThumbTranslatorVisitor::thumb32_UDF() {
return thumb16_UDF();
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,591 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include "common/assert.h"
#include "common/bit_util.h"
#include "frontend/imm.h"
#include "frontend/A32/ir_emitter.h"
#include "frontend/A32/location_descriptor.h"
#include "frontend/A32/translate/translate.h"
#include "frontend/A32/types.h"
namespace Dynarmic::A32 {
enum class Exception;
enum class ConditionalState {
/// We haven't met any conditional instructions yet.
None,
/// Current instruction is a conditional. This marks the end of this basic block.
Break,
/// This basic block is made up solely of conditional instructions.
Translating,
/// This basic block is made up of conditional instructions followed by unconditional instructions.
Trailing,
};
struct ArmTranslatorVisitor final {
using instruction_return_type = bool;
explicit ArmTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor), options(options) {
ASSERT_MSG(!descriptor.TFlag(), "The processor must be in Arm mode");
}
A32::IREmitter ir;
ConditionalState cond_state = ConditionalState::None;
TranslationOptions options;
bool ConditionPassed(Cond cond);
bool InterpretThisInstruction();
bool UnpredictableInstruction();
bool UndefinedInstruction();
bool DecodeError();
bool RaiseException(Exception exception);
static u32 ArmExpandImm(int rotate, Imm<8> imm8) {
return Common::RotateRight<u32>(imm8.ZeroExtend(), rotate * 2);
}
struct ImmAndCarry {
u32 imm32;
IR::U1 carry;
};
ImmAndCarry ArmExpandImm_C(int rotate, Imm<8> imm8, IR::U1 carry_in) {
u32 imm32 = imm8.ZeroExtend();
auto carry_out = carry_in;
if (rotate) {
imm32 = ArmExpandImm(rotate, imm8);
carry_out = ir.Imm1(Common::Bit<31>(imm32));
}
return {imm32, carry_out};
}
// Creates an immediate of the given value
IR::UAny I(size_t bitsize, u64 value);
IR::ResultAndCarry<IR::U32> EmitImmShift(IR::U32 value, ShiftType type, Imm<5> imm5, IR::U1 carry_in);
IR::ResultAndCarry<IR::U32> EmitRegShift(IR::U32 value, ShiftType type, IR::U8 amount, IR::U1 carry_in);
template <typename FnT> bool EmitVfpVectorOperation(bool sz, ExtReg d, ExtReg n, ExtReg m, const FnT& fn);
template <typename FnT> bool EmitVfpVectorOperation(bool sz, ExtReg d, ExtReg m, const FnT& fn);
// Barrier instructions
bool arm_DMB(Imm<4> option);
bool arm_DSB(Imm<4> option);
bool arm_ISB(Imm<4> option);
// Branch instructions
bool arm_B(Cond cond, Imm<24> imm24);
bool arm_BL(Cond cond, Imm<24> imm24);
bool arm_BLX_imm(bool H, Imm<24> imm24);
bool arm_BLX_reg(Cond cond, Reg m);
bool arm_BX(Cond cond, Reg m);
bool arm_BXJ(Cond cond, Reg m);
// Coprocessor instructions
bool arm_CDP(Cond cond, size_t opc1, CoprocReg CRn, CoprocReg CRd, size_t coproc_no, size_t opc2, CoprocReg CRm);
bool arm_LDC(Cond cond, bool p, bool u, bool d, bool w, Reg n, CoprocReg CRd, size_t coproc_no, Imm<8> imm8);
bool arm_MCR(Cond cond, size_t opc1, CoprocReg CRn, Reg t, size_t coproc_no, size_t opc2, CoprocReg CRm);
bool arm_MCRR(Cond cond, Reg t2, Reg t, size_t coproc_no, size_t opc, CoprocReg CRm);
bool arm_MRC(Cond cond, size_t opc1, CoprocReg CRn, Reg t, size_t coproc_no, size_t opc2, CoprocReg CRm);
bool arm_MRRC(Cond cond, Reg t2, Reg t, size_t coproc_no, size_t opc, CoprocReg CRm);
bool arm_STC(Cond cond, bool p, bool u, bool d, bool w, Reg n, CoprocReg CRd, size_t coproc_no, Imm<8> imm8);
// CRC32 instructions
bool arm_CRC32(Cond cond, Imm<2> sz, Reg n, Reg d, Reg m);
bool arm_CRC32C(Cond cond, Imm<2> sz, Reg n, Reg d, Reg m);
// Data processing instructions
bool arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8);
bool arm_ADC_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_ADC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m);
bool arm_ADD_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8);
bool arm_ADD_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_ADD_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m);
bool arm_AND_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8);
bool arm_AND_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_AND_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m);
bool arm_BIC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8);
bool arm_BIC_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_BIC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m);
bool arm_CMN_imm(Cond cond, Reg n, int rotate, Imm<8> imm8);
bool arm_CMN_reg(Cond cond, Reg n, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_CMN_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m);
bool arm_CMP_imm(Cond cond, Reg n, int rotate, Imm<8> imm8);
bool arm_CMP_reg(Cond cond, Reg n, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_CMP_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m);
bool arm_EOR_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8);
bool arm_EOR_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_EOR_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m);
bool arm_MOV_imm(Cond cond, bool S, Reg d, int rotate, Imm<8> imm8);
bool arm_MOV_reg(Cond cond, bool S, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_MOV_rsr(Cond cond, bool S, Reg d, Reg s, ShiftType shift, Reg m);
bool arm_MVN_imm(Cond cond, bool S, Reg d, int rotate, Imm<8> imm8);
bool arm_MVN_reg(Cond cond, bool S, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_MVN_rsr(Cond cond, bool S, Reg d, Reg s, ShiftType shift, Reg m);
bool arm_ORR_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8);
bool arm_ORR_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_ORR_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m);
bool arm_RSB_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8);
bool arm_RSB_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_RSB_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m);
bool arm_RSC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8);
bool arm_RSC_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_RSC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m);
bool arm_SBC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8);
bool arm_SBC_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_SBC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m);
bool arm_SUB_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8);
bool arm_SUB_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_SUB_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m);
bool arm_TEQ_imm(Cond cond, Reg n, int rotate, Imm<8> imm8);
bool arm_TEQ_reg(Cond cond, Reg n, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_TEQ_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m);
bool arm_TST_imm(Cond cond, Reg n, int rotate, Imm<8> imm8);
bool arm_TST_reg(Cond cond, Reg n, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_TST_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m);
// Exception generating instructions
bool arm_BKPT(Cond cond, Imm<12> imm12, Imm<4> imm4);
bool arm_SVC(Cond cond, Imm<24> imm24);
bool arm_UDF();
// Extension instructions
bool arm_SXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m);
bool arm_SXTAB16(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m);
bool arm_SXTAH(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m);
bool arm_SXTB(Cond cond, Reg d, SignExtendRotation rotate, Reg m);
bool arm_SXTB16(Cond cond, Reg d, SignExtendRotation rotate, Reg m);
bool arm_SXTH(Cond cond, Reg d, SignExtendRotation rotate, Reg m);
bool arm_UXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m);
bool arm_UXTAB16(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m);
bool arm_UXTAH(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m);
bool arm_UXTB(Cond cond, Reg d, SignExtendRotation rotate, Reg m);
bool arm_UXTB16(Cond cond, Reg d, SignExtendRotation rotate, Reg m);
bool arm_UXTH(Cond cond, Reg d, SignExtendRotation rotate, Reg m);
// Hint instructions
bool arm_PLD_imm(bool add, bool R, Reg n, Imm<12> imm12);
bool arm_PLD_reg(bool add, bool R, Reg n, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_SEV();
bool arm_SEVL();
bool arm_WFE();
bool arm_WFI();
bool arm_YIELD();
// Load/Store
bool arm_LDRBT();
bool arm_LDRHT();
bool arm_LDRSBT();
bool arm_LDRSHT();
bool arm_LDRT();
bool arm_STRBT();
bool arm_STRHT();
bool arm_STRT();
bool arm_LDR_lit(Cond cond, bool U, Reg t, Imm<12> imm12);
bool arm_LDR_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm<12> imm12);
bool arm_LDR_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_LDRB_lit(Cond cond, bool U, Reg t, Imm<12> imm12);
bool arm_LDRB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<12> imm12);
bool arm_LDRB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_LDRD_lit(Cond cond, bool U, Reg t, Imm<4> imm8a, Imm<4> imm8b);
bool arm_LDRD_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<4> imm8a, Imm<4> imm8b);
bool arm_LDRD_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m);
bool arm_LDRH_lit(Cond cond, bool P, bool U, bool W, Reg t, Imm<4> imm8a, Imm<4> imm8b);
bool arm_LDRH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<4> imm8a, Imm<4> imm8b);
bool arm_LDRH_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Reg m);
bool arm_LDRSB_lit(Cond cond, bool U, Reg t, Imm<4> imm8a, Imm<4> imm8b);
bool arm_LDRSB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<4> imm8a, Imm<4> imm8b);
bool arm_LDRSB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Reg m);
bool arm_LDRSH_lit(Cond cond, bool U, Reg t, Imm<4> imm8a, Imm<4> imm8b);
bool arm_LDRSH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<4> imm8a, Imm<4> imm8b);
bool arm_LDRSH_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Reg m);
bool arm_STR_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<12> imm12);
bool arm_STR_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_STRB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<12> imm12);
bool arm_STRB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<5> imm5, ShiftType shift, Reg m);
bool arm_STRD_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<4> imm8a, Imm<4> imm8b);
bool arm_STRD_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Reg m);
bool arm_STRH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm<4> imm8a, Imm<4> imm8b);
bool arm_STRH_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Reg m);
// Load/Store multiple instructions
bool arm_LDM(Cond cond, bool W, Reg n, RegList list);
bool arm_LDMDA(Cond cond, bool W, Reg n, RegList list);
bool arm_LDMDB(Cond cond, bool W, Reg n, RegList list);
bool arm_LDMIB(Cond cond, bool W, Reg n, RegList list);
bool arm_LDM_usr();
bool arm_LDM_eret();
bool arm_STM(Cond cond, bool W, Reg n, RegList list);
bool arm_STMDA(Cond cond, bool W, Reg n, RegList list);
bool arm_STMDB(Cond cond, bool W, Reg n, RegList list);
bool arm_STMIB(Cond cond, bool W, Reg n, RegList list);
bool arm_STM_usr();
// Miscellaneous instructions
bool arm_BFC(Cond cond, Imm<5> msb, Reg d, Imm<5> lsb);
bool arm_BFI(Cond cond, Imm<5> msb, Reg d, Imm<5> lsb, Reg n);
bool arm_CLZ(Cond cond, Reg d, Reg m);
bool arm_MOVT(Cond cond, Imm<4> imm4, Reg d, Imm<12> imm12);
bool arm_MOVW(Cond cond, Imm<4> imm4, Reg d, Imm<12> imm12);
bool arm_NOP() { return true; }
bool arm_RBIT(Cond cond, Reg d, Reg m);
bool arm_SBFX(Cond cond, Imm<5> widthm1, Reg d, Imm<5> lsb, Reg n);
bool arm_SEL(Cond cond, Reg n, Reg d, Reg m);
bool arm_UBFX(Cond cond, Imm<5> widthm1, Reg d, Imm<5> lsb, Reg n);
// Unsigned sum of absolute difference functions
bool arm_USAD8(Cond cond, Reg d, Reg m, Reg n);
bool arm_USADA8(Cond cond, Reg d, Reg a, Reg m, Reg n);
// Packing instructions
bool arm_PKHBT(Cond cond, Reg n, Reg d, Imm<5> imm5, Reg m);
bool arm_PKHTB(Cond cond, Reg n, Reg d, Imm<5> imm5, Reg m);
// Reversal instructions
bool arm_REV(Cond cond, Reg d, Reg m);
bool arm_REV16(Cond cond, Reg d, Reg m);
bool arm_REVSH(Cond cond, Reg d, Reg m);
// Saturation instructions
bool arm_SSAT(Cond cond, Imm<5> sat_imm, Reg d, Imm<5> imm5, bool sh, Reg n);
bool arm_SSAT16(Cond cond, Imm<4> sat_imm, Reg d, Reg n);
bool arm_USAT(Cond cond, Imm<5> sat_imm, Reg d, Imm<5> imm5, bool sh, Reg n);
bool arm_USAT16(Cond cond, Imm<4> sat_imm, Reg d, Reg n);
// Divide instructions
bool arm_SDIV(Cond cond, Reg d, Reg m, Reg n);
bool arm_UDIV(Cond cond, Reg d, Reg m, Reg n);
// Multiply (Normal) instructions
bool arm_MLA(Cond cond, bool S, Reg d, Reg a, Reg m, Reg n);
bool arm_MLS(Cond cond, Reg d, Reg a, Reg m, Reg n);
bool arm_MUL(Cond cond, bool S, Reg d, Reg m, Reg n);
// Multiply (Long) instructions
bool arm_SMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n);
bool arm_SMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n);
bool arm_UMAAL(Cond cond, Reg dHi, Reg dLo, Reg m, Reg n);
bool arm_UMLAL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n);
bool arm_UMULL(Cond cond, bool S, Reg dHi, Reg dLo, Reg m, Reg n);
// Multiply (Halfword) instructions
bool arm_SMLALxy(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, bool N, Reg n);
bool arm_SMLAxy(Cond cond, Reg d, Reg a, Reg m, bool M, bool N, Reg n);
bool arm_SMULxy(Cond cond, Reg d, Reg m, bool M, bool N, Reg n);
// Multiply (word by halfword) instructions
bool arm_SMLAWy(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n);
bool arm_SMULWy(Cond cond, Reg d, Reg m, bool M, Reg n);
// Multiply (Most significant word) instructions
bool arm_SMMLA(Cond cond, Reg d, Reg a, Reg m, bool R, Reg n);
bool arm_SMMLS(Cond cond, Reg d, Reg a, Reg m, bool R, Reg n);
bool arm_SMMUL(Cond cond, Reg d, Reg m, bool R, Reg n);
// Multiply (Dual) instructions
bool arm_SMLAD(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n);
bool arm_SMLALD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, Reg n);
bool arm_SMLSD(Cond cond, Reg d, Reg a, Reg m, bool M, Reg n);
bool arm_SMLSLD(Cond cond, Reg dHi, Reg dLo, Reg m, bool M, Reg n);
bool arm_SMUAD(Cond cond, Reg d, Reg m, bool M, Reg n);
bool arm_SMUSD(Cond cond, Reg d, Reg m, bool M, Reg n);
// Parallel Add/Subtract (Modulo arithmetic) instructions
bool arm_SADD8(Cond cond, Reg n, Reg d, Reg m);
bool arm_SADD16(Cond cond, Reg n, Reg d, Reg m);
bool arm_SASX(Cond cond, Reg n, Reg d, Reg m);
bool arm_SSAX(Cond cond, Reg n, Reg d, Reg m);
bool arm_SSUB8(Cond cond, Reg n, Reg d, Reg m);
bool arm_SSUB16(Cond cond, Reg n, Reg d, Reg m);
bool arm_UADD8(Cond cond, Reg n, Reg d, Reg m);
bool arm_UADD16(Cond cond, Reg n, Reg d, Reg m);
bool arm_UASX(Cond cond, Reg n, Reg d, Reg m);
bool arm_USAX(Cond cond, Reg n, Reg d, Reg m);
bool arm_USUB8(Cond cond, Reg n, Reg d, Reg m);
bool arm_USUB16(Cond cond, Reg n, Reg d, Reg m);
// Parallel Add/Subtract (Saturating) instructions
bool arm_QADD8(Cond cond, Reg n, Reg d, Reg m);
bool arm_QADD16(Cond cond, Reg n, Reg d, Reg m);
bool arm_QASX(Cond cond, Reg n, Reg d, Reg m);
bool arm_QSAX(Cond cond, Reg n, Reg d, Reg m);
bool arm_QSUB8(Cond cond, Reg n, Reg d, Reg m);
bool arm_QSUB16(Cond cond, Reg n, Reg d, Reg m);
bool arm_UQADD8(Cond cond, Reg n, Reg d, Reg m);
bool arm_UQADD16(Cond cond, Reg n, Reg d, Reg m);
bool arm_UQASX(Cond cond, Reg n, Reg d, Reg m);
bool arm_UQSAX(Cond cond, Reg n, Reg d, Reg m);
bool arm_UQSUB8(Cond cond, Reg n, Reg d, Reg m);
bool arm_UQSUB16(Cond cond, Reg n, Reg d, Reg m);
// Parallel Add/Subtract (Halving) instructions
bool arm_SHADD8(Cond cond, Reg n, Reg d, Reg m);
bool arm_SHADD16(Cond cond, Reg n, Reg d, Reg m);
bool arm_SHASX(Cond cond, Reg n, Reg d, Reg m);
bool arm_SHSAX(Cond cond, Reg n, Reg d, Reg m);
bool arm_SHSUB8(Cond cond, Reg n, Reg d, Reg m);
bool arm_SHSUB16(Cond cond, Reg n, Reg d, Reg m);
bool arm_UHADD8(Cond cond, Reg n, Reg d, Reg m);
bool arm_UHADD16(Cond cond, Reg n, Reg d, Reg m);
bool arm_UHASX(Cond cond, Reg n, Reg d, Reg m);
bool arm_UHSAX(Cond cond, Reg n, Reg d, Reg m);
bool arm_UHSUB8(Cond cond, Reg n, Reg d, Reg m);
bool arm_UHSUB16(Cond cond, Reg n, Reg d, Reg m);
// Saturated Add/Subtract instructions
bool arm_QADD(Cond cond, Reg n, Reg d, Reg m);
bool arm_QSUB(Cond cond, Reg n, Reg d, Reg m);
bool arm_QDADD(Cond cond, Reg n, Reg d, Reg m);
bool arm_QDSUB(Cond cond, Reg n, Reg d, Reg m);
// Synchronization Primitive instructions
bool arm_CLREX();
bool arm_SWP(Cond cond, Reg n, Reg t, Reg t2);
bool arm_SWPB(Cond cond, Reg n, Reg t, Reg t2);
bool arm_STL(Cond cond, Reg n, Reg t);
bool arm_STLEX(Cond cond, Reg n, Reg d, Reg t);
bool arm_STREX(Cond cond, Reg n, Reg d, Reg t);
bool arm_LDA(Cond cond, Reg n, Reg t);
bool arm_LDAEX(Cond cond, Reg n, Reg t);
bool arm_LDREX(Cond cond, Reg n, Reg t);
bool arm_STLEXD(Cond cond, Reg n, Reg d, Reg t);
bool arm_STREXD(Cond cond, Reg n, Reg d, Reg t);
bool arm_LDAEXD(Cond cond, Reg n, Reg t);
bool arm_LDREXD(Cond cond, Reg n, Reg t);
bool arm_STLB(Cond cond, Reg n, Reg t);
bool arm_STLEXB(Cond cond, Reg n, Reg d, Reg t);
bool arm_STREXB(Cond cond, Reg n, Reg d, Reg t);
bool arm_LDAB(Cond cond, Reg n, Reg t);
bool arm_LDAEXB(Cond cond, Reg n, Reg t);
bool arm_LDREXB(Cond cond, Reg n, Reg t);
bool arm_STLH(Cond cond, Reg n, Reg t);
bool arm_STLEXH(Cond cond, Reg n, Reg d, Reg t);
bool arm_STREXH(Cond cond, Reg n, Reg d, Reg t);
bool arm_LDAH(Cond cond, Reg n, Reg t);
bool arm_LDAEXH(Cond cond, Reg n, Reg t);
bool arm_LDREXH(Cond cond, Reg n, Reg t);
// Status register access instructions
bool arm_CPS();
bool arm_MRS(Cond cond, Reg d);
bool arm_MSR_imm(Cond cond, int mask, int rotate, Imm<8> imm8);
bool arm_MSR_reg(Cond cond, int mask, Reg n);
bool arm_RFE();
bool arm_SETEND(bool E);
bool arm_SRS();
// Floating-point three-register data processing instructions
bool vfp_VADD(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VSUB(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VMLA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VMLS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VNMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VNMLA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VNMLS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VDIV(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VFNMS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VFNMA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VFMA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VFMS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VSEL(bool D, Imm<2> cc, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VMAXNM(bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp_VMINNM(bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
// Floating-point move instructions
bool vfp_VMOV_u32_f64(Cond cond, size_t Vd, Reg t, bool D);
bool vfp_VMOV_f64_u32(Cond cond, size_t Vn, Reg t, bool N);
bool vfp_VMOV_u32_f32(Cond cond, size_t Vn, Reg t, bool N);
bool vfp_VMOV_f32_u32(Cond cond, size_t Vn, Reg t, bool N);
bool vfp_VMOV_2u32_2f32(Cond cond, Reg t2, Reg t, bool M, size_t Vm);
bool vfp_VMOV_2f32_2u32(Cond cond, Reg t2, Reg t, bool M, size_t Vm);
bool vfp_VMOV_2u32_f64(Cond cond, Reg t2, Reg t, bool M, size_t Vm);
bool vfp_VMOV_f64_2u32(Cond cond, Reg t2, Reg t, bool M, size_t Vm);
bool vfp_VMOV_from_i32(Cond cond, Imm<1> i, size_t Vd, Reg t, bool D);
bool vfp_VMOV_from_i16(Cond cond, Imm<1> i1, size_t Vd, Reg t, bool D, Imm<1> i2);
bool vfp_VMOV_from_i8(Cond cond, Imm<1> i1, size_t Vd, Reg t, bool D, Imm<2> i2);
bool vfp_VMOV_to_i32(Cond cond, Imm<1> i, size_t Vn, Reg t, bool N);
bool vfp_VMOV_to_i16(Cond cond, bool U, Imm<1> i1, size_t Vn, Reg t, bool N, Imm<1> i2);
bool vfp_VMOV_to_i8(Cond cond, bool U, Imm<1> i1, size_t Vn, Reg t, bool N, Imm<2> i2);
bool vfp_VDUP(Cond cond, Imm<1> B, bool Q, size_t Vd, Reg t, bool D, Imm<1> E);
bool vfp_VMOV_imm(Cond cond, bool D, Imm<4> imm4H, size_t Vd, bool sz, Imm<4> imm4L);
bool vfp_VMOV_reg(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
// Floating-point misc instructions
bool vfp_VABS(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp_VNEG(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp_VSQRT(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp_VCVTB(Cond cond, bool D, bool op, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp_VCVTT(Cond cond, bool D, bool op, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp_VCMP(Cond cond, bool D, size_t Vd, bool sz, bool E, bool M, size_t Vm);
bool vfp_VCMP_zero(Cond cond, bool D, size_t Vd, bool sz, bool E);
bool vfp_VRINTR(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp_VRINTZ(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp_VRINTX(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp_VCVT_f_to_f(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp_VCVT_from_int(Cond cond, bool D, size_t Vd, bool sz, bool is_signed, bool M, size_t Vm);
bool vfp_VCVT_from_fixed(Cond cond, bool D, bool U, size_t Vd, bool sz, bool sx, Imm<1> i, Imm<4> imm4);
bool vfp_VCVT_to_u32(Cond cond, bool D, size_t Vd, bool sz, bool round_towards_zero, bool M, size_t Vm);
bool vfp_VCVT_to_s32(Cond cond, bool D, size_t Vd, bool sz, bool round_towards_zero, bool M, size_t Vm);
bool vfp_VCVT_to_fixed(Cond cond, bool D, bool U, size_t Vd, bool sz, bool sx, Imm<1> i, Imm<4> imm4);
bool vfp_VRINT_rm(bool D, size_t rm, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp_VCVT_rm(bool D, size_t rm, size_t Vd, bool sz, bool U, bool M, size_t Vm);
// Floating-point system register access
bool vfp_VMSR(Cond cond, Reg t);
bool vfp_VMRS(Cond cond, Reg t);
// Floating-point load-store instructions
bool vfp_VLDR(Cond cond, bool U, bool D, Reg n, size_t Vd, bool sz, Imm<8> imm8);
bool vfp_VSTR(Cond cond, bool U, bool D, Reg n, size_t Vd, bool sz, Imm<8> imm8);
bool vfp_VPOP(Cond cond, bool D, size_t Vd, bool sz, Imm<8> imm8);
bool vfp_VPUSH(Cond cond, bool D, size_t Vd, bool sz, Imm<8> imm8);
bool vfp_VSTM_a1(Cond cond, bool p, bool u, bool D, bool w, Reg n, size_t Vd, Imm<8> imm8);
bool vfp_VSTM_a2(Cond cond, bool p, bool u, bool D, bool w, Reg n, size_t Vd, Imm<8> imm8);
bool vfp_VLDM_a1(Cond cond, bool p, bool u, bool D, bool w, Reg n, size_t Vd, Imm<8> imm8);
bool vfp_VLDM_a2(Cond cond, bool p, bool u, bool D, bool w, Reg n, size_t Vd, Imm<8> imm8);
// Advanced SIMD one register, modified immediate
bool asimd_VMOV_imm(Imm<1> a, bool D, Imm<1> b, Imm<1> c, Imm<1> d, size_t Vd,
Imm<4> cmode, bool Q, bool op, Imm<1> e, Imm<1> f, Imm<1> g, Imm<1> h);
// Advanced SIMD three register with same length
bool asimd_VHADD(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VQADD(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VRHADD(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VAND_reg(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VBIC_reg(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VORR_reg(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VORN_reg(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VEOR_reg(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VBSL(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VBIT(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VBIF(bool D, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VHSUB(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VQSUB(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VCGT_reg(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VCGE_reg(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VABD(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VABA(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VADD_int(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VSUB_int(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VSHL_reg(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VQSHL_reg(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VRSHL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VMAX(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, bool op, size_t Vm);
bool asimd_VTST(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VCEQ_reg(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VMLA(bool op, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VMUL(bool P, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VPMAX_int(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, bool op, size_t Vm);
bool asimd_VQDMULH(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VQRDMULH(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VPADD(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VFMA(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VFMS(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VADD_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VSUB_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VPADD_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VABD_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VMLA_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VMLS_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VMUL_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VCEQ_reg_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VCGE_reg_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VCGT_reg_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VACGE(bool D, bool op, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VMAX_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VMIN_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VPMAX_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VPMIN_float(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VRECPS(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
bool asimd_VRSQRTS(bool D, bool sz, size_t Vn, size_t Vd, bool N, bool Q, bool M, size_t Vm);
// Advanced SIMD three registers with different lengths
bool asimd_VADDL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool op, bool N, bool M, size_t Vm);
bool asimd_VSUBL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool op, bool N, bool M, size_t Vm);
bool asimd_VABAL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm);
bool asimd_VABDL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm);
bool asimd_VMLAL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool op, bool N, bool M, size_t Vm);
bool asimd_VMULL(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool P, bool N, bool M, size_t Vm);
// Advanced SIMD two registers and a scalar
bool asimd_VMLA_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool op, bool F, bool N, bool M, size_t Vm);
bool asimd_VMLAL_scalar(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool op, bool N, bool M, size_t Vm);
bool asimd_VMUL_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool F, bool N, bool M, size_t Vm);
bool asimd_VMULL_scalar(bool U, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm);
bool asimd_VQDMULL_scalar(bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm);
bool asimd_VQDMULH_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm);
bool asimd_VQRDMULH_scalar(bool Q, bool D, size_t sz, size_t Vn, size_t Vd, bool N, bool M, size_t Vm);
// Two registers and a shift amount
bool asimd_SHR(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm);
bool asimd_SRA(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm);
bool asimd_VRSHR(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm);
bool asimd_VRSRA(bool U, bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm);
bool asimd_VSRI(bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm);
bool asimd_VSHL(bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm);
bool asimd_VSLI(bool D, size_t imm6, size_t Vd, bool L, bool Q, bool M, size_t Vm);
bool asimd_VQSHL(bool U, bool D, size_t imm6, size_t Vd, bool op, bool L, bool Q, bool M, size_t Vm);
bool asimd_VSHRN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm);
bool asimd_VRSHRN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm);
bool asimd_VQSHRUN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm);
bool asimd_VQRSHRUN(bool D, size_t imm6, size_t Vd, bool M, size_t Vm);
bool asimd_VQSHRN(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm);
bool asimd_VQRSHRN(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm);
bool asimd_VSHLL(bool U, bool D, size_t imm6, size_t Vd, bool M, size_t Vm);
bool asimd_VCVT_fixed(bool U, bool D, size_t imm6, size_t Vd, bool to_fixed, bool Q, bool M, size_t Vm);
// Advanced SIMD two register, miscellaneous
bool asimd_VREV(bool D, size_t sz, size_t Vd, size_t op, bool Q, bool M, size_t Vm);
bool asimd_VPADDL(bool D, size_t sz, size_t Vd, bool op, bool Q, bool M, size_t Vm);
bool v8_AESD(bool D, size_t sz, size_t Vd, bool M, size_t Vm);
bool v8_AESE(bool D, size_t sz, size_t Vd, bool M, size_t Vm);
bool v8_AESIMC(bool D, size_t sz, size_t Vd, bool M, size_t Vm);
bool v8_AESMC(bool D, size_t sz, size_t Vd, bool M, size_t Vm);
bool asimd_VCLS(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VCLZ(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VCNT(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VMVN_reg(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VPADAL(bool D, size_t sz, size_t Vd, bool op, bool Q, bool M, size_t Vm);
bool asimd_VQABS(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VQNEG(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VCGT_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VCGE_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VCEQ_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VCLE_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VCLT_zero(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VABS(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VNEG(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VSWP(bool D, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VTRN(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VUZP(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VZIP(bool D, size_t sz, size_t Vd, bool Q, bool M, size_t Vm);
bool asimd_VMOVN(bool D, size_t sz, size_t Vd, bool M, size_t Vm);
bool asimd_VQMOVUN(bool D, size_t sz, size_t Vd, bool M, size_t Vm);
bool asimd_VQMOVN(bool D, size_t sz, size_t Vd, bool op, bool M, size_t Vm);
bool asimd_VSHLL_max(bool D, size_t sz, size_t Vd, bool M, size_t Vm);
bool asimd_VRECPE(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VRSQRTE(bool D, size_t sz, size_t Vd, bool F, bool Q, bool M, size_t Vm);
bool asimd_VCVT_integer(bool D, size_t sz, size_t Vd, bool op, bool U, bool Q, bool M, size_t Vm);
// Advanced SIMD miscellaneous
bool asimd_VEXT(bool D, size_t Vn, size_t Vd, Imm<4> imm4, bool N, bool Q, bool M, size_t Vm);
bool asimd_VTBL(bool D, size_t Vn, size_t Vd, size_t len, bool N, bool M, size_t Vm);
bool asimd_VTBX(bool D, size_t Vn, size_t Vd, size_t len, bool N, bool M, size_t Vm);
bool asimd_VDUP_scalar(bool D, Imm<4> imm4, size_t Vd, bool Q, bool M, size_t Vm);
// Advanced SIMD load/store structures
bool v8_VST_multiple(bool D, Reg n, size_t Vd, Imm<4> type, size_t sz, size_t align, Reg m);
bool v8_VLD_multiple(bool D, Reg n, size_t Vd, Imm<4> type, size_t sz, size_t align, Reg m);
bool v8_VLD_all_lanes(bool D, Reg n, size_t Vd, size_t nn, size_t sz, bool T, bool a, Reg m);
bool v8_VST_single(bool D, Reg n, size_t Vd, size_t sz, size_t nn, size_t index_align, Reg m);
bool v8_VLD_single(bool D, Reg n, size_t Vd, size_t sz, size_t nn, size_t index_align, Reg m);
};
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,120 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include "common/assert.h"
#include "frontend/imm.h"
#include "frontend/A32/ir_emitter.h"
#include "frontend/A32/location_descriptor.h"
#include "frontend/A32/translate/translate.h"
#include "frontend/A32/types.h"
namespace Dynarmic::A32 {
enum class Exception;
struct ThumbTranslatorVisitor final {
using instruction_return_type = bool;
explicit ThumbTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor), options(options) {
ASSERT_MSG(descriptor.TFlag(), "The processor must be in Thumb mode");
}
A32::IREmitter ir;
TranslationOptions options;
bool InterpretThisInstruction();
bool UnpredictableInstruction();
bool UndefinedInstruction();
bool RaiseException(Exception exception);
// thumb16
bool thumb16_LSL_imm(Imm<5> imm5, Reg m, Reg d);
bool thumb16_LSR_imm(Imm<5> imm5, Reg m, Reg d);
bool thumb16_ASR_imm(Imm<5> imm5, Reg m, Reg d);
bool thumb16_ADD_reg_t1(Reg m, Reg n, Reg d);
bool thumb16_SUB_reg(Reg m, Reg n, Reg d);
bool thumb16_ADD_imm_t1(Imm<3> imm3, Reg n, Reg d);
bool thumb16_SUB_imm_t1(Imm<3> imm3, Reg n, Reg d);
bool thumb16_MOV_imm(Reg d, Imm<8> imm8);
bool thumb16_CMP_imm(Reg n, Imm<8> imm8);
bool thumb16_ADD_imm_t2(Reg d_n, Imm<8> imm8);
bool thumb16_SUB_imm_t2(Reg d_n, Imm<8> imm8);
bool thumb16_AND_reg(Reg m, Reg d_n);
bool thumb16_EOR_reg(Reg m, Reg d_n);
bool thumb16_LSL_reg(Reg m, Reg d_n);
bool thumb16_LSR_reg(Reg m, Reg d_n);
bool thumb16_ASR_reg(Reg m, Reg d_n);
bool thumb16_ADC_reg(Reg m, Reg d_n);
bool thumb16_SBC_reg(Reg m, Reg d_n);
bool thumb16_ROR_reg(Reg m, Reg d_n);
bool thumb16_TST_reg(Reg m, Reg n);
bool thumb16_RSB_imm(Reg n, Reg d);
bool thumb16_CMP_reg_t1(Reg m, Reg n);
bool thumb16_CMN_reg(Reg m, Reg n);
bool thumb16_ORR_reg(Reg m, Reg d_n);
bool thumb16_MUL_reg(Reg n, Reg d_m);
bool thumb16_BIC_reg(Reg m, Reg d_n);
bool thumb16_MVN_reg(Reg m, Reg d);
bool thumb16_ADD_reg_t2(bool d_n_hi, Reg m, Reg d_n_lo);
bool thumb16_CMP_reg_t2(bool n_hi, Reg m, Reg n_lo);
bool thumb16_MOV_reg(bool d_hi, Reg m, Reg d_lo);
bool thumb16_LDR_literal(Reg t, Imm<8> imm8);
bool thumb16_STR_reg(Reg m, Reg n, Reg t);
bool thumb16_STRH_reg(Reg m, Reg n, Reg t);
bool thumb16_STRB_reg(Reg m, Reg n, Reg t);
bool thumb16_LDRSB_reg(Reg m, Reg n, Reg t);
bool thumb16_LDR_reg(Reg m, Reg n, Reg t);
bool thumb16_LDRH_reg(Reg m, Reg n, Reg t);
bool thumb16_LDRB_reg(Reg m, Reg n, Reg t);
bool thumb16_LDRSH_reg(Reg m, Reg n, Reg t);
bool thumb16_STR_imm_t1(Imm<5> imm5, Reg n, Reg t);
bool thumb16_LDR_imm_t1(Imm<5> imm5, Reg n, Reg t);
bool thumb16_STRB_imm(Imm<5> imm5, Reg n, Reg t);
bool thumb16_LDRB_imm(Imm<5> imm5, Reg n, Reg t);
bool thumb16_STRH_imm(Imm<5> imm5, Reg n, Reg t);
bool thumb16_LDRH_imm(Imm<5> imm5, Reg n, Reg t);
bool thumb16_STR_imm_t2(Reg t, Imm<8> imm8);
bool thumb16_LDR_imm_t2(Reg t, Imm<8> imm8);
bool thumb16_ADR(Reg d, Imm<8> imm8);
bool thumb16_ADD_sp_t1(Reg d, Imm<8> imm8);
bool thumb16_ADD_sp_t2(Imm<7> imm7);
bool thumb16_SUB_sp(Imm<7> imm7);
bool thumb16_NOP();
bool thumb16_SEV();
bool thumb16_SEVL();
bool thumb16_WFE();
bool thumb16_WFI();
bool thumb16_YIELD();
bool thumb16_SXTH(Reg m, Reg d);
bool thumb16_SXTB(Reg m, Reg d);
bool thumb16_UXTH(Reg m, Reg d);
bool thumb16_UXTB(Reg m, Reg d);
bool thumb16_PUSH(bool M, RegList reg_list);
bool thumb16_POP(bool P, RegList reg_list);
bool thumb16_SETEND(bool E);
bool thumb16_CPS(bool, bool, bool, bool);
bool thumb16_REV(Reg m, Reg d);
bool thumb16_REV16(Reg m, Reg d);
bool thumb16_REVSH(Reg m, Reg d);
bool thumb16_BKPT([[maybe_unused]] Imm<8> imm8);
bool thumb16_STMIA(Reg n, RegList reg_list);
bool thumb16_LDMIA(Reg n, RegList reg_list);
bool thumb16_CBZ_CBNZ(bool nonzero, Imm<1> i, Imm<5> imm5, Reg n);
bool thumb16_UDF();
bool thumb16_BX(Reg m);
bool thumb16_BLX_reg(Reg m);
bool thumb16_SVC(Imm<8> imm8);
bool thumb16_B_t1(Cond cond, Imm<8> imm8);
bool thumb16_B_t2(Imm<11> imm11);
// thumb32
bool thumb32_BL_imm(Imm<11> hi, Imm<11> lo);
bool thumb32_BLX_imm(Imm<11> hi, Imm<11> lo);
bool thumb32_UDF();
};
} // namespace Dynarmic::A32

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A32/location_descriptor.h"
#include "frontend/A32/translate/translate.h"
#include "frontend/ir/basic_block.h"
namespace Dynarmic::A32 {
IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options);
IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options);
IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) {
return (descriptor.TFlag() ? TranslateThumb : TranslateArm)(descriptor, memory_read_code, options);
}
bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction);
bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction);
bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction) {
return (descriptor.TFlag() ? TranslateSingleThumbInstruction : TranslateSingleArmInstruction)(block, descriptor, instruction);
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,49 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include "common/common_types.h"
namespace Dynarmic::IR {
class Block;
} // namespace Dynarmic::IR
namespace Dynarmic::A32 {
class LocationDescriptor;
using MemoryReadCodeFuncType = std::function<u32(u32 vaddr)>;
struct TranslationOptions {
/// This changes what IR we emit when we translate an unpredictable instruction.
/// If this is false, the ExceptionRaised IR instruction is emitted.
/// If this is true, we define some behaviour for some instructions.
bool define_unpredictable_behaviour = false;
/// This changes what IR we emit when we translate a hint instruction.
/// If this is false, we treat the instruction as a NOP.
/// If this is true, we emit an ExceptionRaised instruction.
bool hook_hint_instructions = true;
};
/**
* This function translates instructions in memory into our intermediate representation.
* @param descriptor The starting location of the basic block. Includes information like PC, Thumb state, &c.
* @param memory_read_code The function we should use to read emulated memory.
* @param options Configures how certain instructions are translated.
* @return A translated basic block in the intermediate representation.
*/
IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options);
/**
* This function translates a single provided instruction into our intermediate representation.
* @param block The block to append the IR for the instruction to.
* @param descriptor The location of the instruction. Includes information like PC, Thumb state, &c.
* @param instruction The instruction to translate.
* @return The translated instruction translated to the intermediate representation.
*/
bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction);
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,234 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <algorithm>
#include <dynarmic/A32/config.h>
#include "common/assert.h"
#include "frontend/A32/decoder/arm.h"
#include "frontend/A32/decoder/asimd.h"
#include "frontend/A32/decoder/vfp.h"
#include "frontend/A32/location_descriptor.h"
#include "frontend/A32/translate/impl/translate_arm.h"
#include "frontend/A32/translate/translate.h"
#include "frontend/A32/types.h"
#include "frontend/ir/basic_block.h"
namespace Dynarmic::A32 {
static bool CondCanContinue(ConditionalState cond_state, const A32::IREmitter& ir) {
ASSERT_MSG(cond_state != ConditionalState::Break, "Should never happen.");
if (cond_state == ConditionalState::None)
return true;
// TODO: This is more conservative than necessary.
return std::all_of(ir.block.begin(), ir.block.end(), [](const IR::Inst& inst) { return !inst.WritesToCPSR(); });
}
IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) {
const bool single_step = descriptor.SingleStepping();
IR::Block block{descriptor};
ArmTranslatorVisitor visitor{block, descriptor, options};
bool should_continue = true;
do {
const u32 arm_pc = visitor.ir.current_location.PC();
const u32 arm_instruction = memory_read_code(arm_pc);
if (const auto vfp_decoder = DecodeVFP<ArmTranslatorVisitor>(arm_instruction)) {
should_continue = vfp_decoder->get().call(visitor, arm_instruction);
} else if (const auto asimd_decoder = DecodeASIMD<ArmTranslatorVisitor>(arm_instruction)) {
should_continue = asimd_decoder->get().call(visitor, arm_instruction);
} else if (const auto decoder = DecodeArm<ArmTranslatorVisitor>(arm_instruction)) {
should_continue = decoder->get().call(visitor, arm_instruction);
} else {
should_continue = visitor.arm_UDF();
}
if (visitor.cond_state == ConditionalState::Break) {
break;
}
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(4);
block.CycleCount()++;
} while (should_continue && CondCanContinue(visitor.cond_state, visitor.ir) && !single_step);
if (visitor.cond_state == ConditionalState::Translating || visitor.cond_state == ConditionalState::Trailing || single_step) {
if (should_continue) {
if (single_step) {
visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location});
} else {
visitor.ir.SetTerm(IR::Term::LinkBlockFast{visitor.ir.current_location});
}
}
}
ASSERT_MSG(block.HasTerminal(), "Terminal has not been set");
block.SetEndLocation(visitor.ir.current_location);
return block;
}
bool TranslateSingleArmInstruction(IR::Block& block, LocationDescriptor descriptor, u32 arm_instruction) {
ArmTranslatorVisitor visitor{block, descriptor, {}};
// TODO: Proper cond handling
bool should_continue = true;
if (const auto vfp_decoder = DecodeVFP<ArmTranslatorVisitor>(arm_instruction)) {
should_continue = vfp_decoder->get().call(visitor, arm_instruction);
} else if (const auto asimd_decoder = DecodeASIMD<ArmTranslatorVisitor>(arm_instruction)) {
should_continue = asimd_decoder->get().call(visitor, arm_instruction);
} else if (const auto decoder = DecodeArm<ArmTranslatorVisitor>(arm_instruction)) {
should_continue = decoder->get().call(visitor, arm_instruction);
} else {
should_continue = visitor.arm_UDF();
}
// TODO: Feedback resulting cond status to caller somehow.
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(4);
block.CycleCount()++;
block.SetEndLocation(visitor.ir.current_location);
return should_continue;
}
bool ArmTranslatorVisitor::ConditionPassed(Cond cond) {
ASSERT_MSG(cond_state != ConditionalState::Break,
"This should never happen. We requested a break but that wasn't honored.");
if (cond == Cond::NV) {
// NV conditional is obsolete
ir.ExceptionRaised(Exception::UnpredictableInstruction);
return false;
}
if (cond_state == ConditionalState::Translating) {
if (ir.block.ConditionFailedLocation() != ir.current_location || cond == Cond::AL) {
cond_state = ConditionalState::Trailing;
} else {
if (cond == ir.block.GetCondition()) {
ir.block.SetConditionFailedLocation(ir.current_location.AdvancePC(4));
ir.block.ConditionFailedCycleCount()++;
return true;
}
// cond has changed, abort
cond_state = ConditionalState::Break;
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location});
return false;
}
}
if (cond == Cond::AL) {
// Everything is fine with the world
return true;
}
// non-AL cond
if (!ir.block.empty()) {
// We've already emitted instructions. Quit for now, we'll make a new block here later.
cond_state = ConditionalState::Break;
ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location});
return false;
}
// We've not emitted instructions yet.
// We'll emit one instruction, and set the block-entry conditional appropriately.
cond_state = ConditionalState::Translating;
ir.block.SetCondition(cond);
ir.block.SetConditionFailedLocation(ir.current_location.AdvancePC(4));
ir.block.ConditionFailedCycleCount() = ir.block.CycleCount() + 1;
return true;
}
bool ArmTranslatorVisitor::InterpretThisInstruction() {
ir.SetTerm(IR::Term::Interpret(ir.current_location));
return false;
}
bool ArmTranslatorVisitor::UnpredictableInstruction() {
ir.ExceptionRaised(Exception::UnpredictableInstruction);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
bool ArmTranslatorVisitor::UndefinedInstruction() {
ir.ExceptionRaised(Exception::UndefinedInstruction);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
bool ArmTranslatorVisitor::DecodeError() {
ir.ExceptionRaised(Exception::DecodeError);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
bool ArmTranslatorVisitor::RaiseException(Exception exception) {
ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 4));
ir.ExceptionRaised(exception);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
IR::UAny ArmTranslatorVisitor::I(size_t bitsize, u64 value) {
switch (bitsize) {
case 8:
return ir.Imm8(static_cast<u8>(value));
case 16:
return ir.Imm16(static_cast<u16>(value));
case 32:
return ir.Imm32(static_cast<u32>(value));
case 64:
return ir.Imm64(value);
default:
ASSERT_FALSE("Imm - get: Invalid bitsize");
}
}
IR::ResultAndCarry<IR::U32> ArmTranslatorVisitor::EmitImmShift(IR::U32 value, ShiftType type, Imm<5> imm5, IR::U1 carry_in) {
u8 imm5_value = imm5.ZeroExtend<u8>();
switch (type) {
case ShiftType::LSL:
return ir.LogicalShiftLeft(value, ir.Imm8(imm5_value), carry_in);
case ShiftType::LSR:
imm5_value = imm5_value ? imm5_value : 32;
return ir.LogicalShiftRight(value, ir.Imm8(imm5_value), carry_in);
case ShiftType::ASR:
imm5_value = imm5_value ? imm5_value : 32;
return ir.ArithmeticShiftRight(value, ir.Imm8(imm5_value), carry_in);
case ShiftType::ROR:
if (imm5_value) {
return ir.RotateRight(value, ir.Imm8(imm5_value), carry_in);
} else {
return ir.RotateRightExtended(value, carry_in);
}
}
UNREACHABLE();
}
IR::ResultAndCarry<IR::U32> ArmTranslatorVisitor::EmitRegShift(IR::U32 value, ShiftType type, IR::U8 amount, IR::U1 carry_in) {
switch (type) {
case ShiftType::LSL:
return ir.LogicalShiftLeft(value, amount, carry_in);
case ShiftType::LSR:
return ir.LogicalShiftRight(value, amount, carry_in);
case ShiftType::ASR:
return ir.ArithmeticShiftRight(value, amount, carry_in);
case ShiftType::ROR:
return ir.RotateRight(value, amount, carry_in);
}
UNREACHABLE();
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,148 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <tuple>
#include <dynarmic/A32/config.h>
#include "common/assert.h"
#include "common/bit_util.h"
#include "frontend/imm.h"
#include "frontend/A32/decoder/thumb16.h"
#include "frontend/A32/decoder/thumb32.h"
#include "frontend/A32/ir_emitter.h"
#include "frontend/A32/location_descriptor.h"
#include "frontend/A32/translate/impl/translate_thumb.h"
#include "frontend/A32/translate/translate.h"
namespace Dynarmic::A32 {
namespace {
enum class ThumbInstSize {
Thumb16, Thumb32
};
bool IsThumb16(u16 first_part) {
return (first_part & 0xF800) <= 0xE800;
}
std::tuple<u32, ThumbInstSize> ReadThumbInstruction(u32 arm_pc, MemoryReadCodeFuncType memory_read_code) {
u32 first_part = memory_read_code(arm_pc & 0xFFFFFFFC);
if ((arm_pc & 0x2) != 0) {
first_part >>= 16;
}
first_part &= 0xFFFF;
if (IsThumb16(static_cast<u16>(first_part))) {
// 16-bit thumb instruction
return std::make_tuple(first_part, ThumbInstSize::Thumb16);
}
// 32-bit thumb instruction
// These always start with 0b11101, 0b11110 or 0b11111.
u32 second_part = memory_read_code((arm_pc + 2) & 0xFFFFFFFC);
if (((arm_pc + 2) & 0x2) != 0) {
second_part >>= 16;
}
second_part &= 0xFFFF;
return std::make_tuple(static_cast<u32>((first_part << 16) | second_part), ThumbInstSize::Thumb32);
}
} // local namespace
IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, const TranslationOptions& options) {
const bool single_step = descriptor.SingleStepping();
IR::Block block{descriptor};
ThumbTranslatorVisitor visitor{block, descriptor, options};
bool should_continue = true;
do {
const u32 arm_pc = visitor.ir.current_location.PC();
const auto [thumb_instruction, inst_size] = ReadThumbInstruction(arm_pc, memory_read_code);
if (inst_size == ThumbInstSize::Thumb16) {
if (const auto decoder = DecodeThumb16<ThumbTranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
} else {
should_continue = visitor.thumb16_UDF();
}
} else {
if (const auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction)) {
should_continue = decoder->get().call(visitor, thumb_instruction);
} else {
should_continue = visitor.thumb32_UDF();
}
}
const s32 advance_pc = (inst_size == ThumbInstSize::Thumb16) ? 2 : 4;
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(advance_pc);
block.CycleCount()++;
} while (should_continue && !single_step);
if (single_step && should_continue) {
visitor.ir.SetTerm(IR::Term::LinkBlock{visitor.ir.current_location});
}
block.SetEndLocation(visitor.ir.current_location);
return block;
}
bool TranslateSingleThumbInstruction(IR::Block& block, LocationDescriptor descriptor, u32 thumb_instruction) {
ThumbTranslatorVisitor visitor{block, descriptor, {}};
const bool is_thumb_16 = IsThumb16(static_cast<u16>(thumb_instruction));
bool should_continue = true;
if (is_thumb_16) {
if (const auto decoder = DecodeThumb16<ThumbTranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
} else {
should_continue = visitor.thumb16_UDF();
}
} else {
if (const auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction)) {
should_continue = decoder->get().call(visitor, thumb_instruction);
} else {
should_continue = visitor.thumb32_UDF();
}
}
const s32 advance_pc = is_thumb_16 ? 2 : 4;
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(advance_pc);
block.CycleCount()++;
block.SetEndLocation(visitor.ir.current_location);
return should_continue;
}
bool ThumbTranslatorVisitor::InterpretThisInstruction() {
ir.SetTerm(IR::Term::Interpret(ir.current_location));
return false;
}
bool ThumbTranslatorVisitor::UnpredictableInstruction() {
ir.ExceptionRaised(Exception::UnpredictableInstruction);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
bool ThumbTranslatorVisitor::UndefinedInstruction() {
ir.ExceptionRaised(Exception::UndefinedInstruction);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
bool ThumbTranslatorVisitor::RaiseException(Exception exception) {
ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 2)); // TODO: T32
ir.ExceptionRaised(exception);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
} // namespace Dynarmic::A32

91
externals/dynarmic/src/frontend/A32/types.cpp vendored Executable file
View File

@@ -0,0 +1,91 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <array>
#include <ostream>
#include "common/bit_util.h"
#include "frontend/A32/types.h"
namespace Dynarmic::A32 {
const char* CondToString(Cond cond, bool explicit_al) {
static constexpr std::array cond_strs = {
"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
"hi", "ls", "ge", "lt", "gt", "le", "al", "nv",
};
return (!explicit_al && cond == Cond::AL) ? "" : cond_strs.at(static_cast<size_t>(cond));
}
const char* RegToString(Reg reg) {
static constexpr std::array reg_strs = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc"
};
return reg_strs.at(static_cast<size_t>(reg));
}
const char* ExtRegToString(ExtReg reg) {
static constexpr std::array reg_strs = {
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8",
"s9", "s10", "s11", "s12", "s13", "s14", "s15", "s16",
"s17", "s18", "s19", "s20", "s21", "s22", "s23", "s24",
"s25", "s26", "s27", "s28", "s29", "s30", "s31",
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8",
"d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16",
"d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24",
"d25", "d26", "d27", "d28", "d29", "d30", "d31",
"q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8",
"q9", "q10", "q11", "q12", "q13", "q14", "q15", "q16",
};
return reg_strs.at(static_cast<size_t>(reg));
}
const char* CoprocRegToString(CoprocReg reg) {
static constexpr std::array reg_strs = {
"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8",
"c9", "c10", "c11", "c12", "c13", "c14", "c15"
};
return reg_strs.at(static_cast<size_t>(reg));
}
std::string RegListToString(RegList reg_list) {
std::string ret;
bool first_reg = true;
for (size_t i = 0; i < 16; i++) {
if (Common::Bit(i, reg_list)) {
if (!first_reg) {
ret += ", ";
}
ret += RegToString(static_cast<Reg>(i));
first_reg = false;
}
}
return ret;
}
std::ostream& operator<<(std::ostream& o, Reg reg) {
o << RegToString(reg);
return o;
}
std::ostream& operator<<(std::ostream& o, ExtReg reg) {
o << ExtRegToString(reg);
return o;
}
std::ostream& operator<<(std::ostream& o, CoprocReg reg) {
o << CoprocRegToString(reg);
return o;
}
std::ostream& operator<<(std::ostream& o, RegList reg_list) {
o << RegListToString(reg_list);
return o;
}
} // namespace Dynarmic::A32

140
externals/dynarmic/src/frontend/A32/types.h vendored Executable file
View File

@@ -0,0 +1,140 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <iosfwd>
#include <string>
#include <utility>
#include <dynarmic/A32/coprocessor_util.h>
#include "common/assert.h"
#include "common/common_types.h"
#include "frontend/ir/cond.h"
namespace Dynarmic::A32 {
using Cond = IR::Cond;
enum class Reg {
R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15,
SP = R13,
LR = R14,
PC = R15,
INVALID_REG = 99
};
enum class ExtReg {
S0, S1, S2, S3, S4, S5, S6, S7,
S8, S9, S10, S11, S12, S13, S14, S15,
S16, S17, S18, S19, S20, S21, S22, S23,
S24, S25, S26, S27, S28, S29, S30, S31,
D0, D1, D2, D3, D4, D5, D6, D7,
D8, D9, D10, D11, D12, D13, D14, D15,
D16, D17, D18, D19, D20, D21, D22, D23,
D24, D25, D26, D27, D28, D29, D30, D31,
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7,
Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15,
};
using RegList = u16;
enum class ShiftType {
LSL,
LSR,
ASR,
ROR ///< RRX falls under this too
};
enum class SignExtendRotation {
ROR_0, ///< ROR #0 or omitted
ROR_8, ///< ROR #8
ROR_16, ///< ROR #16
ROR_24 ///< ROR #24
};
const char* CondToString(Cond cond, bool explicit_al = false);
const char* RegToString(Reg reg);
const char* ExtRegToString(ExtReg reg);
const char* CoprocRegToString(CoprocReg reg);
std::string RegListToString(RegList reg_list);
std::ostream& operator<<(std::ostream& o, Reg reg);
std::ostream& operator<<(std::ostream& o, ExtReg reg);
std::ostream& operator<<(std::ostream& o, CoprocReg reg);
std::ostream& operator<<(std::ostream& o, RegList reg_list);
constexpr bool IsSingleExtReg(ExtReg reg) {
return reg >= ExtReg::S0 && reg <= ExtReg::S31;
}
constexpr bool IsDoubleExtReg(ExtReg reg) {
return reg >= ExtReg::D0 && reg <= ExtReg::D31;
}
constexpr bool IsQuadExtReg(ExtReg reg) {
return reg >= ExtReg::Q0 && reg <= ExtReg::Q15;
}
inline size_t RegNumber(Reg reg) {
ASSERT(reg != Reg::INVALID_REG);
return static_cast<size_t>(reg);
}
inline size_t RegNumber(ExtReg reg) {
if (IsSingleExtReg(reg)) {
return static_cast<size_t>(reg) - static_cast<size_t>(ExtReg::S0);
}
if (IsDoubleExtReg(reg)) {
return static_cast<size_t>(reg) - static_cast<size_t>(ExtReg::D0);
}
if (IsQuadExtReg(reg)) {
return static_cast<size_t>(reg) - static_cast<size_t>(ExtReg::Q0);
}
ASSERT_FALSE("Invalid extended register");
}
inline Reg operator+(Reg reg, size_t number) {
const size_t new_reg = RegNumber(reg) + number;
ASSERT(new_reg <= 15);
return static_cast<Reg>(new_reg);
}
inline ExtReg operator+(ExtReg reg, size_t number) {
const auto new_reg = static_cast<ExtReg>(static_cast<size_t>(reg) + number);
ASSERT((IsSingleExtReg(reg) && IsSingleExtReg(new_reg)) ||
(IsDoubleExtReg(reg) && IsDoubleExtReg(new_reg)) ||
(IsQuadExtReg(reg) && IsQuadExtReg(new_reg)));
return new_reg;
}
inline ExtReg ToExtRegQ(size_t base, bool bit) {
return ExtReg::Q0 + ((base >> 1) + (bit ? 8 : 0));
}
inline ExtReg ToExtRegD(size_t base, bool bit) {
return ExtReg::D0 + (base + (bit ? 16 : 0));
}
inline ExtReg ToExtRegS(size_t base, bool bit) {
return ExtReg::S0 + ((base << 1) + (bit ? 1 : 0));
}
inline ExtReg ToExtReg(bool sz, size_t base, bool bit) {
return sz ? ToExtRegD(base, bit) : ToExtRegS(base, bit);
}
inline ExtReg ToVector(bool Q, size_t base, bool bit) {
return Q ? ToExtRegQ(base, bit) : ToExtRegD(base, bit);
}
} // namespace Dynarmic::A32

View File

@@ -0,0 +1,82 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <algorithm>
#include <functional>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include "common/bit_util.h"
#include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic::A64 {
template <typename Visitor>
using Matcher = Decoder::Matcher<Visitor, u32>;
template <typename Visitor>
using DecodeTable = std::array<std::vector<Matcher<Visitor>>, 0x1000>;
namespace detail {
inline size_t ToFastLookupIndex(u32 instruction) {
return ((instruction >> 10) & 0x00F) | ((instruction >> 18) & 0xFF0);
}
} // namespace detail
template <typename Visitor>
DecodeTable<Visitor> GetDecodeTable() {
std::vector<Matcher<Visitor>> list = {
#define INST(fn, name, bitstring) Decoder::detail::detail<Matcher<Visitor>>::GetMatcher(&Visitor::fn, name, bitstring),
#include "a64.inc"
#undef INST
};
std::stable_sort(list.begin(), list.end(), [](const auto& matcher1, const auto& matcher2) {
// If a matcher has more bits in its mask it is more specific, so it should come first.
return Common::BitCount(matcher1.GetMask()) > Common::BitCount(matcher2.GetMask());
});
// Exceptions to the above rule of thumb.
const std::set<std::string> comes_first {
"MOVI, MVNI, ORR, BIC (vector, immediate)",
"FMOV (vector, immediate)",
"Unallocated SIMD modified immediate",
};
std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) {
return comes_first.count(matcher.GetName()) > 0;
});
DecodeTable<Visitor> table{};
for (size_t i = 0; i < table.size(); ++i) {
for (auto matcher : list) {
const auto expect = detail::ToFastLookupIndex(matcher.GetExpected());
const auto mask = detail::ToFastLookupIndex(matcher.GetMask());
if ((i & mask) == expect) {
table[i].push_back(matcher);
}
}
}
return table;
}
template<typename Visitor>
std::optional<std::reference_wrapper<const Matcher<Visitor>>> Decode(u32 instruction) {
static const auto table = GetDecodeTable<Visitor>();
const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); };
const auto& subtable = table[detail::ToFastLookupIndex(instruction)];
auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction);
return iter != subtable.end() ? std::optional<std::reference_wrapper<const Matcher<Visitor>>>(*iter) : std::nullopt;
}
} // namespace Dynarmic::A64

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,259 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/assert.h"
#include "frontend/A64/ir_emitter.h"
#include "frontend/ir/opcodes.h"
namespace Dynarmic::A64 {
using Opcode = IR::Opcode;
u64 IREmitter::PC() const {
return current_location->PC();
}
u64 IREmitter::AlignPC(size_t alignment) const {
const u64 pc = PC();
return static_cast<u64>(pc - pc % alignment);
}
void IREmitter::SetCheckBit(const IR::U1& value) {
Inst(Opcode::A64SetCheckBit, value);
}
IR::U1 IREmitter::GetCFlag() {
return Inst<IR::U1>(Opcode::A64GetCFlag);
}
IR::U32 IREmitter::GetNZCVRaw() {
return Inst<IR::U32>(Opcode::A64GetNZCVRaw);
}
void IREmitter::SetNZCVRaw(IR::U32 value) {
Inst(Opcode::A64SetNZCVRaw, value);
}
void IREmitter::SetNZCV(const IR::NZCV& nzcv) {
Inst(Opcode::A64SetNZCV, nzcv);
}
void IREmitter::OrQC(const IR::U1& value) {
Inst(Opcode::A64OrQC, value);
}
void IREmitter::CallSupervisor(u32 imm) {
Inst(Opcode::A64CallSupervisor, Imm32(imm));
}
void IREmitter::ExceptionRaised(Exception exception) {
Inst(Opcode::A64ExceptionRaised, Imm64(PC()), Imm64(static_cast<u64>(exception)));
}
void IREmitter::DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value) {
Inst(Opcode::A64DataCacheOperationRaised, Imm64(static_cast<u64>(op)), value);
}
void IREmitter::DataSynchronizationBarrier() {
Inst(Opcode::A64DataSynchronizationBarrier);
}
void IREmitter::DataMemoryBarrier() {
Inst(Opcode::A64DataMemoryBarrier);
}
void IREmitter::InstructionSynchronizationBarrier() {
Inst(Opcode::A64InstructionSynchronizationBarrier);
}
IR::U32 IREmitter::GetCNTFRQ() {
return Inst<IR::U32>(Opcode::A64GetCNTFRQ);
}
IR::U64 IREmitter::GetCNTPCT() {
return Inst<IR::U64>(Opcode::A64GetCNTPCT);
}
IR::U32 IREmitter::GetCTR() {
return Inst<IR::U32>(Opcode::A64GetCTR);
}
IR::U32 IREmitter::GetDCZID() {
return Inst<IR::U32>(Opcode::A64GetDCZID);
}
IR::U64 IREmitter::GetTPIDR() {
return Inst<IR::U64>(Opcode::A64GetTPIDR);
}
void IREmitter::SetTPIDR(const IR::U64& value) {
Inst(Opcode::A64SetTPIDR, value);
}
IR::U64 IREmitter::GetTPIDRRO() {
return Inst<IR::U64>(Opcode::A64GetTPIDRRO);
}
void IREmitter::ClearExclusive() {
Inst(Opcode::A64ClearExclusive);
}
IR::U8 IREmitter::ReadMemory8(const IR::U64& vaddr) {
return Inst<IR::U8>(Opcode::A64ReadMemory8, vaddr);
}
IR::U16 IREmitter::ReadMemory16(const IR::U64& vaddr) {
return Inst<IR::U16>(Opcode::A64ReadMemory16, vaddr);
}
IR::U32 IREmitter::ReadMemory32(const IR::U64& vaddr) {
return Inst<IR::U32>(Opcode::A64ReadMemory32, vaddr);
}
IR::U64 IREmitter::ReadMemory64(const IR::U64& vaddr) {
return Inst<IR::U64>(Opcode::A64ReadMemory64, vaddr);
}
IR::U128 IREmitter::ReadMemory128(const IR::U64& vaddr) {
return Inst<IR::U128>(Opcode::A64ReadMemory128, vaddr);
}
IR::U8 IREmitter::ExclusiveReadMemory8(const IR::U64& vaddr) {
return Inst<IR::U8>(Opcode::A64ExclusiveReadMemory8, vaddr);
}
IR::U16 IREmitter::ExclusiveReadMemory16(const IR::U64& vaddr) {
return Inst<IR::U16>(Opcode::A64ExclusiveReadMemory16, vaddr);
}
IR::U32 IREmitter::ExclusiveReadMemory32(const IR::U64& vaddr) {
return Inst<IR::U32>(Opcode::A64ExclusiveReadMemory32, vaddr);
}
IR::U64 IREmitter::ExclusiveReadMemory64(const IR::U64& vaddr) {
return Inst<IR::U64>(Opcode::A64ExclusiveReadMemory64, vaddr);
}
IR::U128 IREmitter::ExclusiveReadMemory128(const IR::U64& vaddr) {
return Inst<IR::U128>(Opcode::A64ExclusiveReadMemory128, vaddr);
}
void IREmitter::WriteMemory8(const IR::U64& vaddr, const IR::U8& value) {
Inst(Opcode::A64WriteMemory8, vaddr, value);
}
void IREmitter::WriteMemory16(const IR::U64& vaddr, const IR::U16& value) {
Inst(Opcode::A64WriteMemory16, vaddr, value);
}
void IREmitter::WriteMemory32(const IR::U64& vaddr, const IR::U32& value) {
Inst(Opcode::A64WriteMemory32, vaddr, value);
}
void IREmitter::WriteMemory64(const IR::U64& vaddr, const IR::U64& value) {
Inst(Opcode::A64WriteMemory64, vaddr, value);
}
void IREmitter::WriteMemory128(const IR::U64& vaddr, const IR::U128& value) {
Inst(Opcode::A64WriteMemory128, vaddr, value);
}
IR::U32 IREmitter::ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value) {
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory8, vaddr, value);
}
IR::U32 IREmitter::ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value) {
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory16, vaddr, value);
}
IR::U32 IREmitter::ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value) {
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory32, vaddr, value);
}
IR::U32 IREmitter::ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value) {
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory64, vaddr, value);
}
IR::U32 IREmitter::ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value) {
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory128, vaddr, value);
}
IR::U32 IREmitter::GetW(Reg reg) {
if (reg == Reg::ZR)
return Imm32(0);
return Inst<IR::U32>(Opcode::A64GetW, IR::Value(reg));
}
IR::U64 IREmitter::GetX(Reg reg) {
if (reg == Reg::ZR)
return Imm64(0);
return Inst<IR::U64>(Opcode::A64GetX, IR::Value(reg));
}
IR::U128 IREmitter::GetS(Vec vec) {
return Inst<IR::U128>(Opcode::A64GetS, IR::Value(vec));
}
IR::U128 IREmitter::GetD(Vec vec) {
return Inst<IR::U128>(Opcode::A64GetD, IR::Value(vec));
}
IR::U128 IREmitter::GetQ(Vec vec) {
return Inst<IR::U128>(Opcode::A64GetQ, IR::Value(vec));
}
IR::U64 IREmitter::GetSP() {
return Inst<IR::U64>(Opcode::A64GetSP);
}
IR::U32 IREmitter::GetFPCR() {
return Inst<IR::U32>(Opcode::A64GetFPCR);
}
IR::U32 IREmitter::GetFPSR() {
return Inst<IR::U32>(Opcode::A64GetFPSR);
}
void IREmitter::SetW(const Reg reg, const IR::U32& value) {
if (reg == Reg::ZR)
return;
Inst(Opcode::A64SetW, IR::Value(reg), value);
}
void IREmitter::SetX(const Reg reg, const IR::U64& value) {
if (reg == Reg::ZR)
return;
Inst(Opcode::A64SetX, IR::Value(reg), value);
}
void IREmitter::SetS(const Vec vec, const IR::U128& value) {
Inst(Opcode::A64SetS, IR::Value(vec), value);
}
void IREmitter::SetD(const Vec vec, const IR::U128& value) {
Inst(Opcode::A64SetD, IR::Value(vec), value);
}
void IREmitter::SetQ(const Vec vec, const IR::U128& value) {
Inst(Opcode::A64SetQ, IR::Value(vec), value);
}
void IREmitter::SetSP(const IR::U64& value) {
Inst(Opcode::A64SetSP, value);
}
void IREmitter::SetFPCR(const IR::U32& value) {
Inst(Opcode::A64SetFPCR, value);
}
void IREmitter::SetFPSR(const IR::U32& value) {
Inst(Opcode::A64SetFPSR, value);
}
void IREmitter::SetPC(const IR::U64& value) {
Inst(Opcode::A64SetPC, value);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,97 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <optional>
#include <dynarmic/A64/config.h>
#include "common/common_types.h"
#include "frontend/A64/location_descriptor.h"
#include "frontend/A64/types.h"
#include "frontend/ir/ir_emitter.h"
#include "frontend/ir/value.h"
namespace Dynarmic::A64 {
/**
* Convenience class to construct a basic block of the intermediate representation.
* `block` is the resulting block.
* The user of this class updates `current_location` as appropriate.
*/
class IREmitter : public IR::IREmitter {
public:
explicit IREmitter(IR::Block& block) : IR::IREmitter(block) {}
explicit IREmitter(IR::Block& block, LocationDescriptor descriptor) : IR::IREmitter(block), current_location(descriptor) {}
std::optional<LocationDescriptor> current_location;
u64 PC() const;
u64 AlignPC(size_t alignment) const;
void SetCheckBit(const IR::U1& value);
IR::U1 GetCFlag();
IR::U32 GetNZCVRaw();
void SetNZCVRaw(IR::U32 value);
void SetNZCV(const IR::NZCV& nzcv);
void OrQC(const IR::U1& value);
void CallSupervisor(u32 imm);
void ExceptionRaised(Exception exception);
void DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value);
void DataSynchronizationBarrier();
void DataMemoryBarrier();
void InstructionSynchronizationBarrier();
IR::U32 GetCNTFRQ();
IR::U64 GetCNTPCT(); // TODO: Ensure sub-basic-block cycle counts are updated before this.
IR::U32 GetCTR();
IR::U32 GetDCZID();
IR::U64 GetTPIDR();
IR::U64 GetTPIDRRO();
void SetTPIDR(const IR::U64& value);
void ClearExclusive();
IR::U8 ReadMemory8(const IR::U64& vaddr);
IR::U16 ReadMemory16(const IR::U64& vaddr);
IR::U32 ReadMemory32(const IR::U64& vaddr);
IR::U64 ReadMemory64(const IR::U64& vaddr);
IR::U128 ReadMemory128(const IR::U64& vaddr);
IR::U8 ExclusiveReadMemory8(const IR::U64& vaddr);
IR::U16 ExclusiveReadMemory16(const IR::U64& vaddr);
IR::U32 ExclusiveReadMemory32(const IR::U64& vaddr);
IR::U64 ExclusiveReadMemory64(const IR::U64& vaddr);
IR::U128 ExclusiveReadMemory128(const IR::U64& vaddr);
void WriteMemory8(const IR::U64& vaddr, const IR::U8& value);
void WriteMemory16(const IR::U64& vaddr, const IR::U16& value);
void WriteMemory32(const IR::U64& vaddr, const IR::U32& value);
void WriteMemory64(const IR::U64& vaddr, const IR::U64& value);
void WriteMemory128(const IR::U64& vaddr, const IR::U128& value);
IR::U32 ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value);
IR::U32 ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value);
IR::U32 ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value);
IR::U32 ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value);
IR::U32 ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value);
IR::U32 GetW(Reg source_reg);
IR::U64 GetX(Reg source_reg);
IR::U128 GetS(Vec source_vec);
IR::U128 GetD(Vec source_vec);
IR::U128 GetQ(Vec source_vec);
IR::U64 GetSP();
IR::U32 GetFPCR();
IR::U32 GetFPSR();
void SetW(Reg dest_reg, const IR::U32& value);
void SetX(Reg dest_reg, const IR::U64& value);
void SetS(Vec dest_vec, const IR::U128& value);
void SetD(Vec dest_vec, const IR::U128& value);
void SetQ(Vec dest_vec, const IR::U128& value);
void SetSP(const IR::U64& value);
void SetFPCR(const IR::U32& value);
void SetFPSR(const IR::U32& value);
void SetPC(const IR::U64& value);
};
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,18 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <ostream>
#include <fmt/format.h>
#include "frontend/A64/location_descriptor.h"
namespace Dynarmic::A64 {
std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) {
o << fmt::format("{{{}, {}{}}}", descriptor.PC(), descriptor.FPCR().Value(), descriptor.SingleStepping() ? ", step" : "");
return o;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,108 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <functional>
#include <iosfwd>
#include <tuple>
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/fp/fpcr.h"
#include "frontend/ir/location_descriptor.h"
namespace Dynarmic::A64 {
/**
* LocationDescriptor describes the location of a basic block.
* The location is not solely based on the PC because other flags influence the way
* instructions should be translated.
*/
class LocationDescriptor {
public:
static constexpr size_t pc_bit_count = 56;
static constexpr u64 pc_mask = Common::Ones<u64>(pc_bit_count);
static constexpr u32 fpcr_mask = 0x07C8'0000;
static constexpr size_t fpcr_shift = 37;
static constexpr size_t single_stepping_bit = 57;
static_assert((pc_mask & (u64(fpcr_mask) << fpcr_shift) & (u64(1) << single_stepping_bit)) == 0);
LocationDescriptor(u64 pc, FP::FPCR fpcr, bool single_stepping = false)
: pc(pc & pc_mask), fpcr(fpcr.Value() & fpcr_mask), single_stepping(single_stepping)
{}
explicit LocationDescriptor(const IR::LocationDescriptor& o)
: pc(o.Value() & pc_mask)
, fpcr((o.Value() >> fpcr_shift) & fpcr_mask)
, single_stepping(Common::Bit<single_stepping_bit>(o.Value()))
{}
u64 PC() const { return Common::SignExtend<pc_bit_count>(pc); }
FP::FPCR FPCR() const { return fpcr; }
bool SingleStepping() const { return single_stepping; }
bool operator == (const LocationDescriptor& o) const {
return std::tie(pc, fpcr, single_stepping) == std::tie(o.pc, o.fpcr, single_stepping);
}
bool operator != (const LocationDescriptor& o) const {
return !operator==(o);
}
LocationDescriptor SetPC(u64 new_pc) const {
return LocationDescriptor(new_pc, fpcr, single_stepping);
}
LocationDescriptor AdvancePC(int amount) const {
return LocationDescriptor(static_cast<u64>(pc + amount), fpcr, single_stepping);
}
LocationDescriptor SetSingleStepping(bool new_single_stepping) const {
return LocationDescriptor(pc, fpcr, new_single_stepping);
}
u64 UniqueHash() const noexcept {
// This value MUST BE UNIQUE.
// This calculation has to match up with EmitTerminalPopRSBHint
const u64 fpcr_u64 = static_cast<u64>(fpcr.Value()) << fpcr_shift;
const u64 single_stepping_u64 = static_cast<u64>(single_stepping) << single_stepping_bit;
return pc | fpcr_u64 | single_stepping_u64;
}
operator IR::LocationDescriptor() const {
return IR::LocationDescriptor{UniqueHash()};
}
private:
u64 pc; ///< Current program counter value.
FP::FPCR fpcr; ///< Floating point control register.
bool single_stepping;
};
/**
* Provides a string representation of a LocationDescriptor.
*
* @param o Output stream
* @param descriptor The descriptor to get a string representation of
*/
std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor);
} // namespace Dynarmic::A64
namespace std {
template <>
struct less<Dynarmic::A64::LocationDescriptor> {
bool operator()(const Dynarmic::A64::LocationDescriptor& x, const Dynarmic::A64::LocationDescriptor& y) const noexcept {
return x.UniqueHash() < y.UniqueHash();
}
};
template <>
struct hash<Dynarmic::A64::LocationDescriptor> {
size_t operator()(const Dynarmic::A64::LocationDescriptor& x) const noexcept {
return std::hash<u64>()(x.UniqueHash());
}
};
} // namespace std

View File

@@ -0,0 +1,128 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::B_cond(Imm<19> imm19, Cond cond) {
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
const u64 target = ir.PC() + offset;
const auto cond_pass = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
const auto cond_fail = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
ir.SetTerm(IR::Term::If{cond, cond_pass, cond_fail});
return false;
}
bool TranslatorVisitor::B_uncond(Imm<26> imm26) {
const s64 offset = concatenate(imm26, Imm<2>{0}).SignExtend<s64>();
const u64 target = ir.PC() + offset;
ir.SetTerm(IR::Term::LinkBlock{ir.current_location->SetPC(target)});
return false;
}
bool TranslatorVisitor::BL(Imm<26> imm26) {
const s64 offset = concatenate(imm26, Imm<2>{0}).SignExtend<s64>();
X(64, Reg::R30, ir.Imm64(ir.PC() + 4));
ir.PushRSB(ir.current_location->AdvancePC(4));
const u64 target = ir.PC() + offset;
ir.SetTerm(IR::Term::LinkBlock{ir.current_location->SetPC(target)});
return false;
}
bool TranslatorVisitor::BLR(Reg Rn) {
const auto target = X(64, Rn);
X(64, Reg::R30, ir.Imm64(ir.PC() + 4));
ir.PushRSB(ir.current_location->AdvancePC(4));
ir.SetPC(target);
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
}
bool TranslatorVisitor::BR(Reg Rn) {
const auto target = X(64, Rn);
ir.SetPC(target);
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
}
bool TranslatorVisitor::RET(Reg Rn) {
const auto target = X(64, Rn);
ir.SetPC(target);
ir.SetTerm(IR::Term::PopRSBHint{});
return false;
}
bool TranslatorVisitor::CBZ(bool sf, Imm<19> imm19, Reg Rt) {
const size_t datasize = sf ? 64 : 32;
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
const IR::U32U64 operand1 = X(datasize, Rt);
ir.SetCheckBit(ir.IsZero(operand1));
const u64 target = ir.PC() + offset;
const auto cond_pass = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
const auto cond_fail = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
return false;
}
bool TranslatorVisitor::CBNZ(bool sf, Imm<19> imm19, Reg Rt) {
const size_t datasize = sf ? 64 : 32;
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
const IR::U32U64 operand1 = X(datasize, Rt);
ir.SetCheckBit(ir.IsZero(operand1));
const u64 target = ir.PC() + offset;
const auto cond_pass = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
const auto cond_fail = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
return false;
}
bool TranslatorVisitor::TBZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) {
const size_t datasize = b5 == 1 ? 64 : 32;
const u8 bit_pos = concatenate(b5, b40).ZeroExtend<u8>();
const s64 offset = concatenate(imm14, Imm<2>{0}).SignExtend<s64>();
const auto operand = X(datasize, Rt);
ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos)));
const u64 target = ir.PC() + offset;
const auto cond_1 = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
const auto cond_0 = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0});
return false;
}
bool TranslatorVisitor::TBNZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) {
const size_t datasize = b5 == 1 ? 64 : 32;
const u8 bit_pos = concatenate(b5, b40).ZeroExtend<u8>();
const s64 offset = concatenate(imm14, Imm<2>{0}).SignExtend<s64>();
const auto operand = X(datasize, Rt);
ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos)));
const u64 target = ir.PC() + offset;
const auto cond_1 = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
const auto cond_0 = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0});
return false;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,330 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::ADD_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
u64 imm;
switch (shift.ZeroExtend()) {
case 0b00:
imm = imm12.ZeroExtend<u64>();
break;
case 0b01:
imm = imm12.ZeroExtend<u64>() << 12;
break;
default:
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto result = ir.Add(operand1, I(datasize, imm));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::ADDS_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
u64 imm;
switch (shift.ZeroExtend()) {
case 0b00:
imm = imm12.ZeroExtend<u64>();
break;
case 0b01:
imm = imm12.ZeroExtend<u64>() << 12;
break;
default:
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto result = ir.Add(operand1, I(datasize, imm));
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SUB_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
u64 imm;
switch (shift.ZeroExtend()) {
case 0b00:
imm = imm12.ZeroExtend<u64>();
break;
case 0b01:
imm = imm12.ZeroExtend<u64>() << 12;
break;
default:
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto result = ir.Sub(operand1, I(datasize, imm));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::SUBS_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
u64 imm;
switch (shift.ZeroExtend()) {
case 0b00:
imm = imm12.ZeroExtend<u64>();
break;
case 0b01:
imm = imm12.ZeroExtend<u64>() << 12;
break;
default:
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto result = ir.Sub(operand1, I(datasize, imm));
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADD_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (shift == 0b11) {
return ReservedValue();
}
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.Add(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADDS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (shift == 0b11) {
return ReservedValue();
}
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.Add(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SUB_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (shift == 0b11) {
return ReservedValue();
}
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.Sub(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SUBS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (shift == 0b11) {
return ReservedValue();
}
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.Sub(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADD_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
const u8 shift = imm3.ZeroExtend<u8>();
if (shift > 4) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto operand2 = ExtendReg(datasize, Rm, option, shift);
const auto result = ir.Add(operand1, operand2);
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::ADDS_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
const u8 shift = imm3.ZeroExtend<u8>();
if (shift > 4) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto operand2 = ExtendReg(datasize, Rm, option, shift);
const auto result = ir.Add(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SUB_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
const u8 shift = imm3.ZeroExtend<u8>();
if (shift > 4) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto operand2 = ExtendReg(datasize, Rm, option, shift);
const auto result = ir.Sub(operand1, operand2);
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::SUBS_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
const u8 shift = imm3.ZeroExtend<u8>();
if (shift > 4) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto operand2 = ExtendReg(datasize, Rm, option, shift);
const auto result = ir.Sub(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADC(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const auto result = ir.AddWithCarry(operand1, operand2, ir.GetCFlag());
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADCS(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const auto result = ir.AddWithCarry(operand1, operand2, ir.GetCFlag());
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SBC(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const auto result = ir.SubWithCarry(operand1, operand2, ir.GetCFlag());
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SBCS(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const auto result = ir.SubWithCarry(operand1, operand2, ir.GetCFlag());
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,155 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::SBFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (sf && !N) {
return ReservedValue();
}
if (!sf && (N || immr.Bit<5>() || imms.Bit<5>())) {
return ReservedValue();
}
const u8 R = immr.ZeroExtend<u8>();
const u8 S = imms.ZeroExtend<u8>();
const auto masks = DecodeBitMasks(N, imms, immr, false);
if (!masks) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto src = X(datasize, Rn);
auto bot = ir.And(ir.RotateRight(src, ir.Imm8(R)), I(datasize, masks->wmask));
auto top = ir.ReplicateBit(src, S);
top = ir.And(top, I(datasize, ~masks->tmask));
bot = ir.And(bot, I(datasize, masks->tmask));
X(datasize, Rd, ir.Or(top, bot));
return true;
}
bool TranslatorVisitor::BFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (sf && !N) {
return ReservedValue();
}
if (!sf && (N || immr.Bit<5>() || imms.Bit<5>())) {
return ReservedValue();
}
const u8 R = immr.ZeroExtend<u8>();
const auto masks = DecodeBitMasks(N, imms, immr, false);
if (!masks) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto dst = X(datasize, Rd);
const auto src = X(datasize, Rn);
const auto bot = ir.Or(ir.And(dst, I(datasize, ~masks->wmask)), ir.And(ir.RotateRight(src, ir.Imm8(R)), I(datasize, masks->wmask)));
X(datasize, Rd, ir.Or(ir.And(dst, I(datasize, ~masks->tmask)), ir.And(bot, I(datasize, masks->tmask))));
return true;
}
bool TranslatorVisitor::UBFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (sf && !N) {
return ReservedValue();
}
if (!sf && (N || immr.Bit<5>() || imms.Bit<5>())) {
return ReservedValue();
}
const u8 R = immr.ZeroExtend<u8>();
const auto masks = DecodeBitMasks(N, imms, immr, false);
if (!masks) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto src = X(datasize, Rn);
const auto bot = ir.And(ir.RotateRight(src, ir.Imm8(R)), I(datasize, masks->wmask));
X(datasize, Rd, ir.And(bot, I(datasize, masks->tmask)));
return true;
}
bool TranslatorVisitor::ASR_1(Imm<5> immr, Reg Rn, Reg Rd) {
const auto src = X(32, Rn);
const auto result = ir.ArithmeticShiftRightMasked(src, ir.Imm32(immr.ZeroExtend<u32>()));
X(32, Rd, result);
return true;
}
bool TranslatorVisitor::ASR_2(Imm<6> immr, Reg Rn, Reg Rd) {
const auto src = X(64, Rn);
const auto result = ir.ArithmeticShiftRightMasked(src, ir.Imm64(immr.ZeroExtend<u64>()));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::SXTB_1(Reg Rn, Reg Rd) {
const auto src = X(32, Rn);
const auto result = ir.SignExtendToWord(ir.LeastSignificantByte(src));
X(32, Rd, result);
return true;
}
bool TranslatorVisitor::SXTB_2(Reg Rn, Reg Rd) {
const auto src = X(64, Rn);
const auto result = ir.SignExtendToLong(ir.LeastSignificantByte(src));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::SXTH_1(Reg Rn, Reg Rd) {
const auto src = X(32, Rn);
const auto result = ir.SignExtendToWord(ir.LeastSignificantHalf(src));
X(32, Rd, result);
return true;
}
bool TranslatorVisitor::SXTH_2(Reg Rn, Reg Rd) {
const auto src = X(64, Rn);
const auto result = ir.SignExtendToLong(ir.LeastSignificantHalf(src));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::SXTW(Reg Rn, Reg Rd) {
const auto src = X(64, Rn);
const auto result = ir.SignExtendToLong(ir.LeastSignificantWord(src));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::EXTR(bool sf, bool N, Reg Rm, Imm<6> imms, Reg Rn, Reg Rd) {
if (N != sf) {
return UnallocatedEncoding();
}
if (!sf && imms.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 m = X(datasize, Rm);
const IR::U32U64 n = X(datasize, Rn);
const IR::U32U64 result = ir.ExtractRegister(m, n, ir.Imm8(imms.ZeroExtend<u8>()));
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,62 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::CCMN_reg(bool sf, Reg Rm, Cond cond, Reg Rn, Imm<4> nzcv) {
const size_t datasize = sf ? 64 : 32;
const u32 flags = nzcv.ZeroExtend<u32>() << 28;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const IR::NZCV then_flags = ir.NZCVFrom(ir.AddWithCarry(operand1, operand2, ir.Imm1(0)));
const IR::NZCV else_flags = ir.NZCVFromPackedFlags(ir.Imm32(flags));
ir.SetNZCV(ir.ConditionalSelect(cond, then_flags, else_flags));
return true;
}
bool TranslatorVisitor::CCMP_reg(bool sf, Reg Rm, Cond cond, Reg Rn, Imm<4> nzcv) {
const size_t datasize = sf ? 64 : 32;
const u32 flags = nzcv.ZeroExtend<u32>() << 28;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const IR::NZCV then_flags = ir.NZCVFrom(ir.AddWithCarry(operand1, ir.Not(operand2), ir.Imm1(1)));
const IR::NZCV else_flags = ir.NZCVFromPackedFlags(ir.Imm32(flags));
ir.SetNZCV(ir.ConditionalSelect(cond, then_flags, else_flags));
return true;
}
bool TranslatorVisitor::CCMN_imm(bool sf, Imm<5> imm5, Cond cond, Reg Rn, Imm<4> nzcv) {
const size_t datasize = sf ? 64 : 32;
const u32 flags = nzcv.ZeroExtend<u32>() << 28;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = I(datasize, imm5.ZeroExtend<u32>());
const IR::NZCV then_flags = ir.NZCVFrom(ir.AddWithCarry(operand1, operand2, ir.Imm1(0)));
const IR::NZCV else_flags = ir.NZCVFromPackedFlags(ir.Imm32(flags));
ir.SetNZCV(ir.ConditionalSelect(cond, then_flags, else_flags));
return true;
}
bool TranslatorVisitor::CCMP_imm(bool sf, Imm<5> imm5, Cond cond, Reg Rn, Imm<4> nzcv) {
const size_t datasize = sf ? 64 : 32;
const u32 flags = nzcv.ZeroExtend<u32>() << 28;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = I(datasize, imm5.ZeroExtend<u32>());
const IR::NZCV then_flags = ir.NZCVFrom(ir.AddWithCarry(operand1, ir.Not(operand2), ir.Imm1(1)));
const IR::NZCV else_flags = ir.NZCVFromPackedFlags(ir.Imm32(flags));
ir.SetNZCV(ir.ConditionalSelect(cond, then_flags, else_flags));
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,58 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::CSEL(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const IR::U32U64 result = ir.ConditionalSelect(cond, operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::CSINC(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const IR::U32U64 result = ir.ConditionalSelect(cond, operand1, ir.Add(operand2, I(datasize, 1)));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::CSINV(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const IR::U32U64 result = ir.ConditionalSelect(cond, operand1, ir.Not(operand2));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::CSNEG(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const IR::U32U64 result = ir.ConditionalSelect(cond, operand1, ir.Add(ir.Not(operand2), I(datasize, 1)));
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,76 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::CRC32(bool sf, Reg Rm, Imm<2> sz, Reg Rn, Reg Rd) {
const u32 integral_size = sz.ZeroExtend();
if (sf && integral_size != 0b11) {
return UnallocatedEncoding();
}
if (!sf && integral_size == 0b11) {
return UnallocatedEncoding();
}
const IR::U32 result = [&] {
const size_t datasize = sf ? 64 : 32;
const IR::U32 accumulator = ir.GetW(Rn);
const IR::U32U64 data = X(datasize, Rm);
switch (integral_size) {
case 0b00:
return ir.CRC32ISO8(accumulator, data);
case 0b01:
return ir.CRC32ISO16(accumulator, data);
case 0b10:
return ir.CRC32ISO32(accumulator, data);
case 0b11:
default:
return ir.CRC32ISO64(accumulator, data);
}
}();
X(32, Rd, result);
return true;
}
bool TranslatorVisitor::CRC32C(bool sf, Reg Rm, Imm<2> sz, Reg Rn, Reg Rd) {
const u32 integral_size = sz.ZeroExtend();
if (sf && integral_size != 0b11) {
return UnallocatedEncoding();
}
if (!sf && integral_size == 0b11) {
return UnallocatedEncoding();
}
const IR::U32 result = [&] {
const size_t datasize = sf ? 64 : 32;
const IR::U32 accumulator = ir.GetW(Rn);
const IR::U32U64 data = X(datasize, Rm);
switch (integral_size) {
case 0b00:
return ir.CRC32Castagnoli8(accumulator, data);
case 0b01:
return ir.CRC32Castagnoli16(accumulator, data);
case 0b10:
return ir.CRC32Castagnoli32(accumulator, data);
case 0b11:
default:
return ir.CRC32Castagnoli64(accumulator, data);
}
}();
X(32, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,236 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::AND_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (!sf && N) {
return ReservedValue();
}
u64 imm;
if (auto masks = DecodeBitMasks(N, imms, immr, true)) {
imm = masks->wmask;
} else {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = X(datasize, Rn);
const auto result = ir.And(operand1, I(datasize, imm));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::ORR_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (!sf && N) {
return ReservedValue();
}
u64 imm;
if (auto masks = DecodeBitMasks(N, imms, immr, true)) {
imm = masks->wmask;
} else {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = X(datasize, Rn);
const auto result = ir.Or(operand1, I(datasize, imm));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::EOR_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (!sf && N) {
return ReservedValue();
}
u64 imm;
if (auto masks = DecodeBitMasks(N, imms, immr, true)) {
imm = masks->wmask;
} else {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = X(datasize, Rn);
const auto result = ir.Eor(operand1, I(datasize, imm));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::ANDS_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (!sf && N) {
return ReservedValue();
}
u64 imm;
if (auto masks = DecodeBitMasks(N, imms, immr, true)) {
imm = masks->wmask;
} else {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = X(datasize, Rn);
const auto result = ir.And(operand1, I(datasize, imm));
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::AND_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.And(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::BIC_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ir.Not(ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)));
const auto result = ir.And(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ORR_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.Or(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ORN_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ir.Not(ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)));
const auto result = ir.Or(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::EOR_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.Eor(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::EON(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ir.Not(ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)));
const auto result = ir.Eor(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ANDS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.And(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::BICS(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ir.Not(ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)));
const auto result = ir.And(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,100 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::MADD(bool sf, Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 a = X(datasize, Ra);
const IR::U32U64 m = X(datasize, Rm);
const IR::U32U64 n = X(datasize, Rn);
const IR::U32U64 result = ir.Add(a, ir.Mul(n, m));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::MSUB(bool sf, Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 a = X(datasize, Ra);
const IR::U32U64 m = X(datasize, Rm);
const IR::U32U64 n = X(datasize, Rn);
const IR::U32U64 result = ir.Sub(a, ir.Mul(n, m));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SMADDL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
const IR::U64 a = X(64, Ra);
const IR::U64 m = ir.SignExtendToLong(X(32, Rm));
const IR::U64 n = ir.SignExtendToLong(X(32, Rn));
const IR::U64 result = ir.Add(a, ir.Mul(n, m));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::SMSUBL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
const IR::U64 a = X(64, Ra);
const IR::U64 m = ir.SignExtendToLong(X(32, Rm));
const IR::U64 n = ir.SignExtendToLong(X(32, Rn));
const IR::U64 result = ir.Sub(a, ir.Mul(n, m));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::SMULH(Reg Rm, Reg Rn, Reg Rd) {
const IR::U64 m = X(64, Rm);
const IR::U64 n = X(64, Rn);
const IR::U64 result = ir.SignedMultiplyHigh(n, m);
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::UMADDL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
const IR::U64 a = X(64, Ra);
const IR::U64 m = ir.ZeroExtendToLong(X(32, Rm));
const IR::U64 n = ir.ZeroExtendToLong(X(32, Rn));
const IR::U64 result = ir.Add(a, ir.Mul(n, m));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::UMSUBL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
const IR::U64 a = X(64, Ra);
const IR::U64 m = ir.ZeroExtendToLong(X(32, Rm));
const IR::U64 n = ir.ZeroExtendToLong(X(32, Rn));
const IR::U64 result = ir.Sub(a, ir.Mul(n, m));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::UMULH(Reg Rm, Reg Rn, Reg Rd) {
const IR::U64 m = X(64, Rm);
const IR::U64 n = X(64, Rn);
const IR::U64 result = ir.UnsignedMultiplyHigh(n, m);
X(64, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,24 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::ADR(Imm<2> immlo, Imm<19> immhi, Reg Rd) {
const u64 imm = concatenate(immhi, immlo).SignExtend<u64>();
const u64 base = ir.PC();
X(64, Rd, ir.Imm64(base + imm));
return true;
}
bool TranslatorVisitor::ADRP(Imm<2> immlo, Imm<19> immhi, Reg Rd) {
const u64 imm = concatenate(immhi, immlo).SignExtend<u64>() << 12;
const u64 base = ir.PC() & ~u64(0xFFF);
X(64, Rd, ir.Imm64(base + imm));
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,138 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::CLZ_int(bool sf, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
const IR::U32U64 result = ir.CountLeadingZeros(operand);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::CLS_int(bool sf, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
const IR::U32U64 result = ir.Sub(ir.CountLeadingZeros(ir.Eor(operand, ir.ArithmeticShiftRight(operand, ir.Imm8(u8(datasize))))), I(datasize, 1));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::RBIT_int(bool sf, Reg Rn, Reg Rd) {
const auto rbit32 = [this](const IR::U32& operand) {
// x = (x & 0x55555555) << 1 | ((x >> 1) & 0x55555555);
const IR::U32 first_lsl = ir.LogicalShiftLeft(ir.And(operand, ir.Imm32(0x55555555)), ir.Imm8(1));
const IR::U32 first_lsr = ir.And(ir.LogicalShiftRight(operand, ir.Imm8(1)), ir.Imm32(0x55555555));
const IR::U32 first = ir.Or(first_lsl, first_lsr);
// x = (x & 0x33333333) << 2 | ((x >> 2) & 0x33333333);
const IR::U32 second_lsl = ir.LogicalShiftLeft(ir.And(first, ir.Imm32(0x33333333)), ir.Imm8(2));
const IR::U32 second_lsr = ir.And(ir.LogicalShiftRight(first, ir.Imm8(2)), ir.Imm32(0x33333333));
const IR::U32 second = ir.Or(second_lsl, second_lsr);
// x = (x & 0x0F0F0F0F) << 4 | ((x >> 4) & 0x0F0F0F0F);
const IR::U32 third_lsl = ir.LogicalShiftLeft(ir.And(second, ir.Imm32(0x0F0F0F0F)), ir.Imm8(4));
const IR::U32 third_lsr = ir.And(ir.LogicalShiftRight(second, ir.Imm8(4)), ir.Imm32(0x0F0F0F0F));
const IR::U32 third = ir.Or(third_lsl, third_lsr);
// x = (x << 24) | ((x & 0xFF00) << 8) | ((x >> 8) & 0xFF00) | (x >> 24);
const IR::U32 fourth_lsl = ir.Or(ir.LogicalShiftLeft(third, ir.Imm8(24)),
ir.LogicalShiftLeft(ir.And(third, ir.Imm32(0xFF00)), ir.Imm8(8)));
const IR::U32 fourth_lsr = ir.Or(ir.And(ir.LogicalShiftRight(third, ir.Imm8(8)), ir.Imm32(0xFF00)),
ir.LogicalShiftRight(third, ir.Imm8(24)));
return ir.Or(fourth_lsl, fourth_lsr);
};
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
if (sf) {
const IR::U32 lsw = rbit32(ir.LeastSignificantWord(operand));
const IR::U32 msw = rbit32(ir.MostSignificantWord(operand).result);
const IR::U64 result = ir.Pack2x32To1x64(msw, lsw);
X(datasize, Rd, result);
} else {
X(datasize, Rd, rbit32(operand));
}
return true;
}
bool TranslatorVisitor::REV(bool sf, bool opc_0, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
if (!sf && opc_0) return UnallocatedEncoding();
const IR::U32U64 operand = X(datasize, Rn);
if (sf) {
X(datasize, Rd, ir.ByteReverseDual(operand));
} else {
X(datasize, Rd, ir.ByteReverseWord(operand));
}
return true;
}
bool TranslatorVisitor::REV32_int(Reg Rn, Reg Rd) {
const IR::U64 operand = ir.GetX(Rn);
const IR::U32 lo = ir.ByteReverseWord(ir.LeastSignificantWord(operand));
const IR::U32 hi = ir.ByteReverseWord(ir.MostSignificantWord(operand).result);
const IR::U64 result = ir.Pack2x32To1x64(lo, hi);
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::REV16_int(bool sf, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
if (sf) {
const IR::U64 operand = X(datasize, Rn);
const IR::U64 hihalf = ir.And(ir.LogicalShiftRight(operand, ir.Imm8(8)), ir.Imm64(0x00FF00FF00FF00FF));
const IR::U64 lohalf = ir.And(ir.LogicalShiftLeft(operand, ir.Imm8(8)), ir.Imm64(0xFF00FF00FF00FF00));
const IR::U64 result = ir.Or(hihalf, lohalf);
X(datasize, Rd, result);
} else {
const IR::U32 operand = X(datasize, Rn);
const IR::U32 hihalf = ir.And(ir.LogicalShiftRight(operand, ir.Imm8(8)), ir.Imm32(0x00FF00FF));
const IR::U32 lohalf = ir.And(ir.LogicalShiftLeft(operand, ir.Imm8(8)), ir.Imm32(0xFF00FF00));
const IR::U32 result = ir.Or(hihalf, lohalf);
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::UDIV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 m = X(datasize, Rm);
const IR::U32U64 n = X(datasize, Rn);
const IR::U32U64 result = ir.UnsignedDiv(n,m);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SDIV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 m = X(datasize, Rm);
const IR::U32U64 n = X(datasize, Rn);
const IR::U32U64 result = ir.SignedDiv(n,m);
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,58 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::LSLV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
const IR::U32U64 shift_amount = X(datasize, Rm);
const IR::U32U64 result = ir.LogicalShiftLeftMasked(operand, shift_amount);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::LSRV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
const IR::U32U64 shift_amount = X(datasize, Rm);
const IR::U32U64 result = ir.LogicalShiftRightMasked(operand, shift_amount);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ASRV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
const IR::U32U64 shift_amount = X(datasize, Rm);
const IR::U32U64 result = ir.ArithmeticShiftRightMasked(operand, shift_amount);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::RORV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
const IR::U32U64 shift_amount = X(datasize, Rm);
const IR::U32U64 result = ir.RotateRightMasked(operand, shift_amount);
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,24 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::BRK([[maybe_unused]] Imm<16> imm16) {
ir.ExceptionRaised(Exception::Breakpoint);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
bool TranslatorVisitor::SVC(Imm<16> imm16) {
ir.PushRSB(ir.current_location->AdvancePC(4));
ir.SetPC(ir.Imm64(ir.current_location->PC() + 4));
ir.CallSupervisor(imm16.ZeroExtend());
ir.SetTerm(IR::Term::CheckHalt{IR::Term::PopRSBHint{}});
return false;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,38 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
bool FPCompare(TranslatorVisitor& v, Imm<2> type, Vec Vm, Vec Vn, bool exc_on_qnan, bool cmp_with_zero) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return v.UnallocatedEncoding();
}
const IR::U32U64 operand1 = v.V_scalar(*datasize, Vn);
IR::U32U64 operand2;
if (cmp_with_zero) {
operand2 = v.I(*datasize, 0);
} else {
operand2 = v.V_scalar(*datasize, Vm);
}
const auto nzcv = v.ir.FPCompare(operand1, operand2, exc_on_qnan);
v.ir.SetNZCV(nzcv);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::FCMP_float(Imm<2> type, Vec Vm, Vec Vn, bool cmp_with_zero) {
return FPCompare(*this, type, Vm, Vn, false, cmp_with_zero);
}
bool TranslatorVisitor::FCMPE_float(Imm<2> type, Vec Vm, Vec Vn, bool cmp_with_zero) {
return FPCompare(*this, type, Vm, Vn, true, cmp_with_zero);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,35 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
bool FPCompare(TranslatorVisitor& v, Imm<2> type, Vec Vm, Cond cond, Vec Vn, Imm<4> nzcv, bool exc_on_qnan) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return v.UnallocatedEncoding();
}
const u32 flags = nzcv.ZeroExtend<u32>() << 28;
const IR::U32U64 operand1 = v.V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = v.V_scalar(*datasize, Vm);
const IR::NZCV then_flags = v.ir.FPCompare(operand1, operand2, exc_on_qnan);
const IR::NZCV else_flags = v.ir.NZCVFromPackedFlags(v.ir.Imm32(flags));
v.ir.SetNZCV(v.ir.ConditionalSelect(cond, then_flags, else_flags));
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::FCCMP_float(Imm<2> type, Vec Vm, Cond cond, Vec Vn, Imm<4> nzcv) {
return FPCompare(*this, type, Vm, cond, Vn, nzcv, false);
}
bool TranslatorVisitor::FCCMPE_float(Imm<2> type, Vec Vm, Cond cond, Vec Vn, Imm<4> nzcv) {
return FPCompare(*this, type, Vm, cond, Vn, nzcv, true);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,24 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::FCSEL_float(Imm<2> type, Vec Vm, Cond cond, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.ConditionalSelect(cond, operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,114 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::SCVTF_float_fix(bool sf, Imm<2> type, Imm<6> scale, Reg Rn, Vec Vd) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize || *fltsize == 16) {
return UnallocatedEncoding();
}
if (!sf && !scale.Bit<5>()) {
return UnallocatedEncoding();
}
const u8 fracbits = 64 - scale.ZeroExtend<u8>();
const FP::RoundingMode rounding_mode = ir.current_location->FPCR().RMode();
const IR::U32U64 intval = X(intsize, Rn);
const IR::U32U64 fltval = [&]() -> IR::U32U64 {
switch (*fltsize) {
case 32:
return ir.FPSignedFixedToSingle(intval, fracbits, rounding_mode);
case 64:
return ir.FPSignedFixedToDouble(intval, fracbits, rounding_mode);
}
UNREACHABLE();
}();
V_scalar(*fltsize, Vd, fltval);
return true;
}
bool TranslatorVisitor::UCVTF_float_fix(bool sf, Imm<2> type, Imm<6> scale, Reg Rn, Vec Vd) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize || *fltsize == 16) {
return UnallocatedEncoding();
}
if (!sf && !scale.Bit<5>()) {
return UnallocatedEncoding();
}
const u8 fracbits = 64 - scale.ZeroExtend<u8>();
const FP::RoundingMode rounding_mode = ir.current_location->FPCR().RMode();
const IR::U32U64 intval = X(intsize, Rn);
const IR::U32U64 fltval = [&]() -> IR::U32U64 {
switch (*fltsize) {
case 32:
return ir.FPUnsignedFixedToSingle(intval, fracbits, rounding_mode);
case 64:
return ir.FPUnsignedFixedToDouble(intval, fracbits, rounding_mode);
}
UNREACHABLE();
}();
V_scalar(*fltsize, Vd, fltval);
return true;
}
bool TranslatorVisitor::FCVTZS_float_fix(bool sf, Imm<2> type, Imm<6> scale, Vec Vn, Reg Rd) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize) {
return UnallocatedEncoding();
}
if (!sf && !scale.Bit<5>()) {
return UnallocatedEncoding();
}
const u8 fracbits = 64 - scale.ZeroExtend<u8>();
const IR::U16U32U64 fltval = V_scalar(*fltsize, Vn);
IR::U32U64 intval;
if (intsize == 32) {
intval = ir.FPToFixedS32(fltval, fracbits, FP::RoundingMode::TowardsZero);
} else if (intsize == 64) {
intval = ir.FPToFixedS64(fltval, fracbits, FP::RoundingMode::TowardsZero);
} else {
UNREACHABLE();
}
X(intsize, Rd, intval);
return true;
}
bool TranslatorVisitor::FCVTZU_float_fix(bool sf, Imm<2> type, Imm<6> scale, Vec Vn, Reg Rd) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize) {
return UnallocatedEncoding();
}
if (!sf && !scale.Bit<5>()) {
return UnallocatedEncoding();
}
const u8 fracbits = 64 - scale.ZeroExtend<u8>();
const IR::U16U32U64 fltval = V_scalar(*fltsize, Vn);
IR::U32U64 intval;
if (intsize == 32) {
intval = ir.FPToFixedU32(fltval, fracbits, FP::RoundingMode::TowardsZero);
} else if (intsize == 64) {
intval = ir.FPToFixedU64(fltval, fracbits, FP::RoundingMode::TowardsZero);
} else {
UNREACHABLE();
}
X(intsize, Rd, intval);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,199 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/fp/rounding_mode.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::SCVTF_float_int(bool sf, Imm<2> type, Reg Rn, Vec Vd) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize || *fltsize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 intval = X(intsize, Rn);
IR::U32U64 fltval;
if (*fltsize == 32) {
fltval = ir.FPSignedFixedToSingle(intval, 0, ir.current_location->FPCR().RMode());
} else if (*fltsize == 64) {
fltval = ir.FPSignedFixedToDouble(intval, 0, ir.current_location->FPCR().RMode());
} else {
UNREACHABLE();
}
V_scalar(*fltsize, Vd, fltval);
return true;
}
bool TranslatorVisitor::UCVTF_float_int(bool sf, Imm<2> type, Reg Rn, Vec Vd) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize || *fltsize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 intval = X(intsize, Rn);
IR::U32U64 fltval;
if (*fltsize == 32) {
fltval = ir.FPUnsignedFixedToSingle(intval, 0, ir.current_location->FPCR().RMode());
} else if (*fltsize == 64) {
fltval = ir.FPUnsignedFixedToDouble(intval, 0, ir.current_location->FPCR().RMode());
} else {
UNREACHABLE();
}
V_scalar(*fltsize, Vd, fltval);
return true;
}
bool TranslatorVisitor::FMOV_float_gen(bool sf, Imm<2> type, Imm<1> rmode_0, Imm<1> opc_0, size_t n, size_t d) {
// NOTE:
// opcode<2:1> == 0b11
// rmode<1> == 0b0
if (type == 0b10 && rmode_0 != 1) {
return UnallocatedEncoding();
}
const size_t intsize = sf ? 64 : 32;
size_t fltsize = [type] {
switch (type.ZeroExtend()) {
case 0b00:
return 32;
case 0b01:
return 64;
case 0b10:
return 128;
case 0b11:
return 16;
default:
UNREACHABLE();
}
}();
bool integer_to_float;
size_t part;
switch (rmode_0.ZeroExtend()) {
case 0b0:
if (fltsize != 16 && fltsize != intsize) {
return UnallocatedEncoding();
}
integer_to_float = opc_0 == 0b1;
part = 0;
break;
default:
case 0b1:
if (intsize != 64 || fltsize != 128) {
return UnallocatedEncoding();
}
integer_to_float = opc_0 == 0b1;
part = 1;
fltsize = 64;
break;
}
if (integer_to_float) {
const IR::U16U32U64 intval = X(fltsize, static_cast<Reg>(n));
Vpart_scalar(fltsize, static_cast<Vec>(d), part, intval);
} else {
const IR::UAny fltval = Vpart_scalar(fltsize, static_cast<Vec>(n), part);
const IR::U32U64 intval = ZeroExtend(fltval, intsize);
X(intsize, static_cast<Reg>(d), intval);
}
return true;
}
static bool FloaingPointConvertSignedInteger(TranslatorVisitor& v, bool sf, Imm<2> type, Vec Vn, Reg Rd, FP::RoundingMode rounding_mode) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize) {
return v.UnallocatedEncoding();
}
const IR::U16U32U64 fltval = v.V_scalar(*fltsize, Vn);
IR::U32U64 intval;
if (intsize == 32) {
intval = v.ir.FPToFixedS32(fltval, 0, rounding_mode);
} else if (intsize == 64) {
intval = v.ir.FPToFixedS64(fltval, 0, rounding_mode);
} else {
UNREACHABLE();
}
v.X(intsize, Rd, intval);
return true;
}
static bool FloaingPointConvertUnsignedInteger(TranslatorVisitor& v, bool sf, Imm<2> type, Vec Vn, Reg Rd, FP::RoundingMode rounding_mode) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize) {
return v.UnallocatedEncoding();
}
const IR::U16U32U64 fltval = v.V_scalar(*fltsize, Vn);
IR::U32U64 intval;
if (intsize == 32) {
intval = v.ir.FPToFixedU32(fltval, 0, rounding_mode);
} else if (intsize == 64) {
intval = v.ir.FPToFixedU64(fltval, 0, rounding_mode);
} else {
UNREACHABLE();
}
v.X(intsize, Rd, intval);
return true;
}
bool TranslatorVisitor::FCVTNS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertSignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::ToNearest_TieEven);
}
bool TranslatorVisitor::FCVTNU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertUnsignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::ToNearest_TieEven);
}
bool TranslatorVisitor::FCVTZS_float_int(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertSignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::TowardsZero);
}
bool TranslatorVisitor::FCVTZU_float_int(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertUnsignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::TowardsZero);
}
bool TranslatorVisitor::FCVTAS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertSignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::ToNearest_TieAwayFromZero);
}
bool TranslatorVisitor::FCVTAU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertUnsignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::ToNearest_TieAwayFromZero);
}
bool TranslatorVisitor::FCVTPS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertSignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::TowardsPlusInfinity);
}
bool TranslatorVisitor::FCVTPU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertUnsignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::TowardsPlusInfinity);
}
bool TranslatorVisitor::FCVTMS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertSignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::TowardsMinusInfinity);
}
bool TranslatorVisitor::FCVTMU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertUnsignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::TowardsMinusInfinity);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,187 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::FMOV_float(Imm<2> type, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operand = V_scalar(*datasize, Vn);
V_scalar(*datasize, Vd, operand);
return true;
}
bool TranslatorVisitor::FABS_float(Imm<2> type, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operand = V_scalar(*datasize, Vn);
const IR::U16U32U64 result = ir.FPAbs(operand);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FNEG_float(Imm<2> type, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operand = V_scalar(*datasize, Vn);
const IR::U16U32U64 result = ir.FPNeg(operand);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FSQRT_float(Imm<2> type, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand = V_scalar(*datasize, Vn);
const IR::U32U64 result = ir.FPSqrt(operand);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FMOV_float_imm(Imm<2> type, Imm<8> imm8, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
IR::UAny result = [&]() -> IR::UAny {
switch (*datasize) {
case 16: {
const u16 sign = imm8.Bit<7>() ? 1 : 0;
const u16 exp = (imm8.Bit<6>() ? 0b0'1100 : 0b1'0000) | imm8.Bits<4, 5, u16>();
const u16 fract = imm8.Bits<0, 3, u16>() << 6;
return ir.Imm16((sign << 15) | (exp << 10) | fract);
}
case 32: {
const u32 sign = imm8.Bit<7>() ? 1 : 0;
const u32 exp = (imm8.Bit<6>() ? 0b0111'1100 : 0b1000'0000) | imm8.Bits<4, 5, u32>();
const u32 fract = imm8.Bits<0, 3, u32>() << 19;
return ir.Imm32((sign << 31) | (exp << 23) | fract);
}
case 64:
default: {
const u64 sign = imm8.Bit<7>() ? 1 : 0;
const u64 exp = (imm8.Bit<6>() ? 0b011'1111'1100 : 0b100'0000'0000) | imm8.Bits<4, 5, u64>();
const u64 fract = imm8.Bits<0, 3, u64>() << 48;
return ir.Imm64((sign << 63) | (exp << 52) | fract);
}
}
}();
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FCVT_float(Imm<2> type, Imm<2> opc, Vec Vn, Vec Vd) {
if (type == opc) {
return UnallocatedEncoding();
}
const auto srcsize = FPGetDataSize(type);
const auto dstsize = FPGetDataSize(opc);
if (!srcsize || !dstsize) {
return UnallocatedEncoding();
}
const IR::UAny operand = V_scalar(*srcsize, Vn);
const auto rounding_mode = ir.current_location->FPCR().RMode();
IR::UAny result;
switch (*srcsize) {
case 16:
switch (*dstsize) {
case 32:
result = ir.FPHalfToSingle(operand, rounding_mode);
break;
case 64:
result = ir.FPHalfToDouble(operand, rounding_mode);
break;
}
break;
case 32:
switch (*dstsize) {
case 16:
result = ir.FPSingleToHalf(operand, rounding_mode);
break;
case 64:
result = ir.FPSingleToDouble(operand, rounding_mode);
break;
}
break;
case 64:
switch (*dstsize) {
case 16:
result = ir.FPDoubleToHalf(operand, rounding_mode);
break;
case 32:
result = ir.FPDoubleToSingle(operand, rounding_mode);
break;
}
break;
}
V_scalar(*dstsize, Vd, result);
return true;
}
static bool FloatingPointRoundToIntegral(TranslatorVisitor& v, Imm<2> type, Vec Vn, Vec Vd,
FP::RoundingMode rounding_mode, bool exact) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return v.UnallocatedEncoding();
}
const IR::U16U32U64 operand = v.V_scalar(*datasize, Vn);
const IR::U16U32U64 result = v.ir.FPRoundInt(operand, rounding_mode, exact);
v.V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FRINTN_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, false);
}
bool TranslatorVisitor::FRINTP_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, false);
}
bool TranslatorVisitor::FRINTM_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, false);
}
bool TranslatorVisitor::FRINTZ_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, FP::RoundingMode::TowardsZero, false);
}
bool TranslatorVisitor::FRINTA_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, false);
}
bool TranslatorVisitor::FRINTX_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, ir.current_location->FPCR().RMode(), true);
}
bool TranslatorVisitor::FRINTI_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, ir.current_location->FPCR().RMode(), false);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,66 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::FMADD_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operanda = V_scalar(*datasize, Va);
const IR::U16U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U16U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U16U32U64 result = ir.FPMulAdd(operanda, operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FMSUB_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operanda = V_scalar(*datasize, Va);
const IR::U16U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U16U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U16U32U64 result = ir.FPMulAdd(operanda, ir.FPNeg(operand1), operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FNMADD_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operanda = V_scalar(*datasize, Va);
const IR::U16U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U16U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U16U32U64 result = ir.FPMulAdd(ir.FPNeg(operanda), ir.FPNeg(operand1), operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FNMSUB_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operanda = V_scalar(*datasize, Va);
const IR::U16U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U16U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U16U32U64 result = ir.FPMulAdd(ir.FPNeg(operanda), operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,145 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::FMUL_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPMul(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FDIV_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPDiv(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FADD_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPAdd(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FSUB_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPSub(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FMAX_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPMax(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FMIN_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPMin(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FMAXNM_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPMaxNumeric(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FMINNM_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPMinNumeric(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FNMUL_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPNeg(ir.FPMul(operand1, operand2));
V_scalar(*datasize, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,405 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/bit_util.h"
#include "frontend/ir/terminal.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::InterpretThisInstruction() {
ir.SetTerm(IR::Term::Interpret(*ir.current_location));
return false;
}
bool TranslatorVisitor::UnpredictableInstruction() {
return RaiseException(Exception::UnpredictableInstruction);
}
bool TranslatorVisitor::DecodeError() {
UNREACHABLE();
}
bool TranslatorVisitor::ReservedValue() {
return RaiseException(Exception::ReservedValue);
}
bool TranslatorVisitor::UnallocatedEncoding() {
return RaiseException(Exception::UnallocatedEncoding);
}
bool TranslatorVisitor::RaiseException(Exception exception) {
ir.SetPC(ir.Imm64(ir.current_location->PC() + 4));
ir.ExceptionRaised(exception);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
std::optional<TranslatorVisitor::BitMasks> TranslatorVisitor::DecodeBitMasks(bool immN, Imm<6> imms, Imm<6> immr, bool immediate) {
const int len = Common::HighestSetBit((immN ? 1 << 6 : 0) | (imms.ZeroExtend() ^ 0b111111));
if (len < 1) {
return std::nullopt;
}
const size_t levels = Common::Ones<size_t>(len);
if (immediate && (imms.ZeroExtend() & levels) == levels) {
return std::nullopt;
}
const s32 S = s32(imms.ZeroExtend() & levels);
const s32 R = s32(immr.ZeroExtend() & levels);
const u64 d = u64(S - R) & levels;
const size_t esize = size_t{1} << len;
const u64 welem = Common::Ones<u64>(S + 1);
const u64 telem = Common::Ones<u64>(d + 1);
const u64 wmask = Common::RotateRight(Common::Replicate(welem, esize), R);
const u64 tmask = Common::Replicate(telem, esize);
return BitMasks{wmask, tmask};
}
IR::UAny TranslatorVisitor::I(size_t bitsize, u64 value) {
switch (bitsize) {
case 8:
return ir.Imm8(static_cast<u8>(value));
case 16:
return ir.Imm16(static_cast<u16>(value));
case 32:
return ir.Imm32(static_cast<u32>(value));
case 64:
return ir.Imm64(value);
default:
ASSERT_FALSE("Imm - get: Invalid bitsize");
}
}
IR::UAny TranslatorVisitor::X(size_t bitsize, Reg reg) {
switch (bitsize) {
case 8:
return ir.LeastSignificantByte(ir.GetW(reg));
case 16:
return ir.LeastSignificantHalf(ir.GetW(reg));
case 32:
return ir.GetW(reg);
case 64:
return ir.GetX(reg);
default:
ASSERT_FALSE("X - get: Invalid bitsize");
}
}
void TranslatorVisitor::X(size_t bitsize, Reg reg, IR::U32U64 value) {
switch (bitsize) {
case 32:
ir.SetW(reg, value);
return;
case 64:
ir.SetX(reg, value);
return;
default:
ASSERT_FALSE("X - set: Invalid bitsize");
}
}
IR::U32U64 TranslatorVisitor::SP(size_t bitsize) {
switch (bitsize) {
case 32:
return ir.LeastSignificantWord(ir.GetSP());
case 64:
return ir.GetSP();
default:
ASSERT_FALSE("SP - get : Invalid bitsize");
}
}
void TranslatorVisitor::SP(size_t bitsize, IR::U32U64 value) {
switch (bitsize) {
case 32:
ir.SetSP(ir.ZeroExtendWordToLong(value));
break;
case 64:
ir.SetSP(value);
break;
default:
ASSERT_FALSE("SP - set : Invalid bitsize");
}
}
IR::U128 TranslatorVisitor::V(size_t bitsize, Vec vec) {
switch (bitsize) {
case 32:
return ir.GetS(vec);
case 64:
return ir.GetD(vec);
case 128:
return ir.GetQ(vec);
default:
ASSERT_FALSE("V - get : Invalid bitsize");
}
}
void TranslatorVisitor::V(size_t bitsize, Vec vec, IR::U128 value) {
switch (bitsize) {
case 32:
ir.SetS(vec, value);
return;
case 64:
// TODO: Remove VectorZeroUpper when possible.
ir.SetD(vec, ir.VectorZeroUpper(value));
return;
case 128:
ir.SetQ(vec, value);
return;
default:
ASSERT_FALSE("V - Set : Invalid bitsize");
}
}
IR::UAnyU128 TranslatorVisitor::V_scalar(size_t bitsize, Vec vec) {
if (bitsize == 128) {
return V(128, vec);
}
// TODO: Optimize
return ir.VectorGetElement(bitsize, ir.GetQ(vec), 0);
}
void TranslatorVisitor::V_scalar(size_t bitsize, Vec vec, IR::UAnyU128 value) {
if (bitsize == 128) {
V(128, vec, value);
return;
}
// TODO: Optimize
ir.SetQ(vec, ir.ZeroExtendToQuad(value));
}
IR::U128 TranslatorVisitor::Vpart(size_t bitsize, Vec vec, size_t part) {
ASSERT(part == 0 || part == 1);
ASSERT(bitsize == 64);
if (part == 0) {
return V(64, vec);
}
return ir.ZeroExtendToQuad(ir.VectorGetElement(bitsize, V(128, vec), part));
}
void TranslatorVisitor::Vpart(size_t bitsize, Vec vec, size_t part, IR::U128 value) {
ASSERT(part == 0 || part == 1);
if (part == 0) {
ASSERT(bitsize == 64);
V(128, vec, ir.VectorZeroExtend(bitsize, value));
} else {
ASSERT(bitsize == 64);
V(128, vec, ir.VectorInterleaveLower(64, V(128, vec), value));
}
}
IR::UAny TranslatorVisitor::Vpart_scalar(size_t bitsize, Vec vec, size_t part) {
ASSERT(part == 0 || part == 1);
if (part == 0) {
ASSERT(bitsize == 8 || bitsize == 16 || bitsize == 32 || bitsize == 64);
} else {
ASSERT(bitsize == 64);
}
return ir.VectorGetElement(bitsize, V(128, vec), part);
}
void TranslatorVisitor::Vpart_scalar(size_t bitsize, Vec vec, size_t part, IR::UAny value) {
ASSERT(part == 0 || part == 1);
if (part == 0) {
ASSERT(bitsize == 8 || bitsize == 16 || bitsize == 32 || bitsize == 64);
V(128, vec, ir.ZeroExtendToQuad(value));
} else {
ASSERT(bitsize == 64);
V(128, vec, ir.VectorSetElement(64, V(128, vec), 1, value));
}
}
IR::UAnyU128 TranslatorVisitor::Mem(IR::U64 address, size_t bytesize, IR::AccType /*acc_type*/) {
switch (bytesize) {
case 1:
return ir.ReadMemory8(address);
case 2:
return ir.ReadMemory16(address);
case 4:
return ir.ReadMemory32(address);
case 8:
return ir.ReadMemory64(address);
case 16:
return ir.ReadMemory128(address);
default:
ASSERT_FALSE("Invalid bytesize parameter {}", bytesize);
}
}
void TranslatorVisitor::Mem(IR::U64 address, size_t bytesize, IR::AccType /*acc_type*/, IR::UAnyU128 value) {
switch (bytesize) {
case 1:
ir.WriteMemory8(address, value);
return;
case 2:
ir.WriteMemory16(address, value);
return;
case 4:
ir.WriteMemory32(address, value);
return;
case 8:
ir.WriteMemory64(address, value);
return;
case 16:
ir.WriteMemory128(address, value);
return;
default:
ASSERT_FALSE("Invalid bytesize parameter {}", bytesize);
}
}
IR::UAnyU128 TranslatorVisitor::ExclusiveMem(IR::U64 address, size_t bytesize, IR::AccType /*acctype*/) {
switch (bytesize) {
case 1:
return ir.ExclusiveReadMemory8(address);
case 2:
return ir.ExclusiveReadMemory16(address);
case 4:
return ir.ExclusiveReadMemory32(address);
case 8:
return ir.ExclusiveReadMemory64(address);
case 16:
return ir.ExclusiveReadMemory128(address);
default:
ASSERT_FALSE("Invalid bytesize parameter {}", bytesize);
}
}
IR::U32 TranslatorVisitor::ExclusiveMem(IR::U64 address, size_t bytesize, IR::AccType /*acctype*/, IR::UAnyU128 value) {
switch (bytesize) {
case 1:
return ir.ExclusiveWriteMemory8(address, value);
case 2:
return ir.ExclusiveWriteMemory16(address, value);
case 4:
return ir.ExclusiveWriteMemory32(address, value);
case 8:
return ir.ExclusiveWriteMemory64(address, value);
case 16:
return ir.ExclusiveWriteMemory128(address, value);
default:
ASSERT_FALSE("Invalid bytesize parameter {}", bytesize);
}
}
IR::U32U64 TranslatorVisitor::SignExtend(IR::UAny value, size_t to_size) {
switch (to_size) {
case 32:
return ir.SignExtendToWord(value);
case 64:
return ir.SignExtendToLong(value);
default:
ASSERT_FALSE("Invalid size parameter {}", to_size);
}
}
IR::U32U64 TranslatorVisitor::ZeroExtend(IR::UAny value, size_t to_size) {
switch (to_size) {
case 32:
return ir.ZeroExtendToWord(value);
case 64:
return ir.ZeroExtendToLong(value);
default:
ASSERT_FALSE("Invalid size parameter {}", to_size);
}
}
IR::U32U64 TranslatorVisitor::ShiftReg(size_t bitsize, Reg reg, Imm<2> shift, IR::U8 amount) {
IR::U32U64 result = X(bitsize, reg);
switch (shift.ZeroExtend()) {
case 0b00:
return ir.LogicalShiftLeft(result, amount);
case 0b01:
return ir.LogicalShiftRight(result, amount);
case 0b10:
return ir.ArithmeticShiftRight(result, amount);
case 0b11:
return ir.RotateRight(result, amount);
}
UNREACHABLE();
}
IR::U32U64 TranslatorVisitor::ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift) {
ASSERT(shift <= 4);
ASSERT(bitsize == 32 || bitsize == 64);
IR::UAny val = X(bitsize, reg);
size_t len;
IR::U32U64 extended;
bool signed_extend;
switch (option.ZeroExtend()) {
case 0b000: { // UXTB
val = ir.LeastSignificantByte(val);
len = 8;
signed_extend = false;
break;
}
case 0b001: { // UXTH
val = ir.LeastSignificantHalf(val);
len = 16;
signed_extend = false;
break;
}
case 0b010: { // UXTW
if (bitsize != 32) {
val = ir.LeastSignificantWord(val);
}
len = 32;
signed_extend = false;
break;
}
case 0b011: { // UXTX
len = 64;
signed_extend = false;
break;
}
case 0b100: { // SXTB
val = ir.LeastSignificantByte(val);
len = 8;
signed_extend = true;
break;
}
case 0b101: { // SXTH
val = ir.LeastSignificantHalf(val);
len = 16;
signed_extend = true;
break;
}
case 0b110: { // SXTW
if (bitsize != 32) {
val = ir.LeastSignificantWord(val);
}
len = 32;
signed_extend = true;
break;
}
case 0b111: { // SXTX
len = 64;
signed_extend = true;
break;
}
default:
UNREACHABLE();
}
if (len < bitsize) {
if (bitsize == 32) {
extended = signed_extend ? ir.SignExtendToWord(val) : ir.ZeroExtendToWord(val);
} else {
extended = signed_extend ? ir.SignExtendToLong(val) : ir.ZeroExtendToLong(val);
}
} else {
extended = val;
}
return ir.LogicalShiftLeft(extended, ir.Imm8(shift));
}
} // namespace Dynarmic::A64

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,209 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <optional>
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool ExclusiveSharedDecodeAndOperation(TranslatorVisitor& v, bool pair, size_t size, bool L, bool o0, std::optional<Reg> Rs, std::optional<Reg> Rt2, Reg Rn, Reg Rt) {
// Shared Decode
const auto acctype = o0 ? IR::AccType::ORDERED : IR::AccType::ATOMIC;
const auto memop = L ? IR::MemOp::LOAD : IR::MemOp::STORE;
const size_t elsize = 8 << size;
const size_t regsize = elsize == 64 ? 64 : 32;
const size_t datasize = pair ? elsize * 2 : elsize;
// Operation
const size_t dbytes = datasize / 8;
if (memop == IR::MemOp::LOAD && pair && Rt == *Rt2) {
return v.UnpredictableInstruction();
} else if (memop == IR::MemOp::STORE && (*Rs == Rt || (pair && *Rs == *Rt2))) {
if (!v.options.define_unpredictable_behaviour) {
return v.UnpredictableInstruction();
}
// UNPREDICTABLE: The Constraint_NONE case is executed.
} else if (memop == IR::MemOp::STORE && *Rs == Rn && Rn != Reg::R31) {
return v.UnpredictableInstruction();
}
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
switch (memop) {
case IR::MemOp::STORE: {
IR::UAnyU128 data;
if (pair && elsize == 64) {
data = v.ir.Pack2x64To1x128(v.X(64, Rt), v.X(64, *Rt2));
} else if (pair && elsize == 32) {
data = v.ir.Pack2x32To1x64(v.X(32, Rt), v.X(32, *Rt2));
} else {
data = v.X(elsize, Rt);
}
const IR::U32 status = v.ExclusiveMem(address, dbytes, acctype, data);
v.X(32, *Rs, status);
break;
}
case IR::MemOp::LOAD: {
const IR::UAnyU128 data = v.ExclusiveMem(address, dbytes, acctype);
if (pair && elsize == 64) {
v.X(64, Rt, v.ir.VectorGetElement(64, data, 0));
v.X(64, *Rt2, v.ir.VectorGetElement(64, data, 1));
} else if (pair && elsize == 32) {
v.X(32, Rt, v.ir.LeastSignificantWord(data));
v.X(32, *Rt2, v.ir.MostSignificantWord(data).result);
} else {
v.X(regsize, Rt, v.ZeroExtend(data, regsize));
}
break;
}
default:
UNREACHABLE();
}
return true;
}
bool TranslatorVisitor::STXR(Imm<2> sz, Reg Rs, Reg Rn, Reg Rt) {
const bool pair = false;
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 0;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, Rs, {}, Rn, Rt);
}
bool TranslatorVisitor::STLXR(Imm<2> sz, Reg Rs, Reg Rn, Reg Rt) {
const bool pair = false;
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 1;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, Rs, {}, Rn, Rt);
}
bool TranslatorVisitor::STXP(Imm<1> sz, Reg Rs, Reg Rt2, Reg Rn, Reg Rt) {
const bool pair = true;
const size_t size = concatenate(Imm<1>{1}, sz).ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 0;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, Rs, Rt2, Rn, Rt);
}
bool TranslatorVisitor::STLXP(Imm<1> sz, Reg Rs, Reg Rt2, Reg Rn, Reg Rt) {
const bool pair = true;
const size_t size = concatenate(Imm<1>{1}, sz).ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 1;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, Rs, Rt2, Rn, Rt);
}
bool TranslatorVisitor::LDXR(Imm<2> sz, Reg Rn, Reg Rt) {
const bool pair = false;
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 0;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, {}, {}, Rn, Rt);
}
bool TranslatorVisitor::LDAXR(Imm<2> sz, Reg Rn, Reg Rt) {
const bool pair = false;
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 1;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, {}, {}, Rn, Rt);
}
bool TranslatorVisitor::LDXP(Imm<1> sz, Reg Rt2, Reg Rn, Reg Rt) {
const bool pair = true;
const size_t size = concatenate(Imm<1>{1}, sz).ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 0;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, {}, Rt2, Rn, Rt);
}
bool TranslatorVisitor::LDAXP(Imm<1> sz, Reg Rt2, Reg Rn, Reg Rt) {
const bool pair = true;
const size_t size = concatenate(Imm<1>{1}, sz).ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 1;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, {}, Rt2, Rn, Rt);
}
static bool OrderedSharedDecodeAndOperation(TranslatorVisitor& v, size_t size, bool L, bool o0, Reg Rn, Reg Rt) {
// Shared Decode
const auto acctype = !o0 ? IR::AccType::LIMITEDORDERED : IR::AccType::ORDERED;
const auto memop = L ? IR::MemOp::LOAD : IR::MemOp::STORE;
const size_t elsize = 8 << size;
const size_t regsize = elsize == 64 ? 64 : 32;
const size_t datasize = elsize;
// Operation
const size_t dbytes = datasize / 8;
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
switch (memop) {
case IR::MemOp::STORE: {
const IR::UAny data = v.X(datasize, Rt);
v.Mem(address, dbytes, acctype, data);
break;
}
case IR::MemOp::LOAD: {
const IR::UAny data = v.Mem(address, dbytes, acctype);
v.X(regsize, Rt, v.ZeroExtend(data, regsize));
break;
}
default:
UNREACHABLE();
}
return true;
}
bool TranslatorVisitor::STLLR(Imm<2> sz, Reg Rn, Reg Rt) {
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 0;
return OrderedSharedDecodeAndOperation(*this, size, L, o0, Rn, Rt);
}
bool TranslatorVisitor::STLR(Imm<2> sz, Reg Rn, Reg Rt) {
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 1;
return OrderedSharedDecodeAndOperation(*this, size, L, o0, Rn, Rt);
}
bool TranslatorVisitor::LDLAR(Imm<2> sz, Reg Rn, Reg Rt) {
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 0;
return OrderedSharedDecodeAndOperation(*this, size, L, o0, Rn, Rt);
}
bool TranslatorVisitor::LDAR(Imm<2> sz, Reg Rn, Reg Rt) {
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 1;
return OrderedSharedDecodeAndOperation(*this, size, L, o0, Rn, Rt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,55 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) {
const size_t size = opc_0 == 0 ? 4 : 8;
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
const u64 address = ir.PC() + offset;
const auto data = Mem(ir.Imm64(address), size, IR::AccType::NORMAL);
X(8 * size, Rt, data);
return true;
}
bool TranslatorVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
if (opc == 0b11) {
return UnallocatedEncoding();
}
const u64 size = 4 << opc.ZeroExtend();
const u64 offset = imm19.SignExtend<u64>() << 2;
const IR::U64 address = ir.Imm64(ir.PC() + offset);
const IR::UAnyU128 data = Mem(address, size, IR::AccType::VEC);
if (size == 16) {
V(128, Vt, data);
} else {
V(128, Vt, ir.ZeroExtendToQuad(data));
}
return true;
}
bool TranslatorVisitor::LDRSW_lit(Imm<19> imm19, Reg Rt) {
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
const u64 address = ir.PC() + offset;
const auto data = Mem(ir.Imm64(address), 4, IR::AccType::NORMAL);
X(64, Rt, ir.SignExtendWordToLong(data));
return true;
}
bool TranslatorVisitor::PRFM_lit(Imm<19> /*imm19*/, Imm<5> /*prfop*/) {
// s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
// u64 address = ir.PC() + offset;
// Prefetch(address, prfop);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,134 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <optional>
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool SharedDecodeAndOperation(TranslatorVisitor& v, bool wback, IR::MemOp memop, bool Q, std::optional<Reg> Rm, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
const size_t datasize = Q ? 128 : 64;
const size_t esize = 8 << size.ZeroExtend<size_t>();
const size_t elements = datasize / esize;
const size_t ebytes = esize / 8;
size_t rpt, selem;
switch (opcode.ZeroExtend()) {
case 0b0000:
rpt = 1;
selem = 4;
break;
case 0b0010:
rpt = 4;
selem = 1;
break;
case 0b0100:
rpt = 1;
selem = 3;
break;
case 0b0110:
rpt = 3;
selem = 1;
break;
case 0b0111:
rpt = 1;
selem = 1;
break;
case 0b1000:
rpt = 1;
selem = 2;
break;
case 0b1010:
rpt = 2;
selem = 1;
break;
default:
return v.UnallocatedEncoding();
}
ASSERT(rpt == 1 || selem == 1);
if ((size == 0b11 && !Q) && selem != 1) {
return v.ReservedValue();
}
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
IR::U64 offs = v.ir.Imm64(0);
if (selem == 1) {
for (size_t r = 0; r < rpt; r++) {
const Vec tt = static_cast<Vec>((VecNumber(Vt) + r) % 32);
if (memop == IR::MemOp::LOAD) {
const IR::UAnyU128 vec = v.Mem(v.ir.Add(address, offs), ebytes * elements, IR::AccType::VEC);
v.V_scalar(datasize, tt, vec);
} else {
const IR::UAnyU128 vec = v.V_scalar(datasize, tt);
v.Mem(v.ir.Add(address, offs), ebytes * elements, IR::AccType::VEC, vec);
}
offs = v.ir.Add(offs, v.ir.Imm64(ebytes * elements));
}
} else {
for (size_t e = 0; e < elements; e++) {
for (size_t s = 0; s < selem; s++) {
const Vec tt = static_cast<Vec>((VecNumber(Vt) + s) % 32);
if (memop == IR::MemOp::LOAD) {
const IR::UAny elem = v.Mem(v.ir.Add(address, offs), ebytes, IR::AccType::VEC);
const IR::U128 vec = v.ir.VectorSetElement(esize, v.V(datasize, tt), e, elem);
v.V(datasize, tt, vec);
} else {
const IR::UAny elem = v.ir.VectorGetElement(esize, v.V(datasize, tt), e);
v.Mem(v.ir.Add(address, offs), ebytes, IR::AccType::VEC, elem);
}
offs = v.ir.Add(offs, v.ir.Imm64(ebytes));
}
}
}
if (wback) {
if (*Rm != Reg::SP) {
offs = v.X(64, *Rm);
}
if (Rn == Reg::SP) {
v.SP(64, v.ir.Add(address, offs));
} else {
v.X(64, Rn, v.ir.Add(address, offs));
}
}
return true;
}
bool TranslatorVisitor::STx_mult_1(bool Q, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
const bool wback = false;
const auto memop = IR::MemOp::STORE;
return SharedDecodeAndOperation(*this, wback, memop, Q, {}, opcode, size, Rn, Vt);
}
bool TranslatorVisitor::STx_mult_2(bool Q, Reg Rm, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
const bool wback = true;
const auto memop = IR::MemOp::STORE;
return SharedDecodeAndOperation(*this, wback, memop, Q, Rm, opcode, size, Rn, Vt);
}
bool TranslatorVisitor::LDx_mult_1(bool Q, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
const bool wback = false;
const auto memop = IR::MemOp::LOAD;
return SharedDecodeAndOperation(*this, wback, memop, Q, {}, opcode, size, Rn, Vt);
}
bool TranslatorVisitor::LDx_mult_2(bool Q, Reg Rm, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
const bool wback = true;
const auto memop = IR::MemOp::LOAD;
return SharedDecodeAndOperation(*this, wback, memop, Q, Rm, opcode, size, Rn, Vt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,22 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
// Given the LDNP and STNP instructions are simply hinting at non-temporal
// accesses, we can treat them as regular LDP and STP instructions for the
// time being since we don't perform data caching.
bool TranslatorVisitor::STNP_LDNP_gen(Imm<1> upper_opc, Imm<1> L, Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) {
return STP_LDP_gen(Imm<2>{upper_opc.ZeroExtend() << 1}, true, false, L, imm7, Rt2, Rn, Rt);
}
bool TranslatorVisitor::STNP_LDNP_fpsimd(Imm<2> opc, Imm<1> L, Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) {
return STP_LDP_fpsimd(opc, true, false, L, imm7, Vt2, Rn, Vt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,249 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool LoadStoreRegisterImmediate(TranslatorVisitor& v, bool wback, bool postindex, size_t scale, u64 offset, Imm<2> size, Imm<2> opc, Reg Rn, Reg Rt) {
IR::MemOp memop;
bool signed_ = false;
size_t regsize = 0;
if (opc.Bit<1>() == 0) {
memop = opc.Bit<0>() ? IR::MemOp::LOAD : IR::MemOp::STORE;
regsize = size == 0b11 ? 64 : 32;
signed_ = false;
} else if (size == 0b11) {
memop = IR::MemOp::PREFETCH;
ASSERT(!opc.Bit<0>());
} else {
memop = IR::MemOp::LOAD;
ASSERT(!(size == 0b10 && opc.Bit<0>() == 1));
regsize = opc.Bit<0>() ? 32 : 64;
signed_ = true;
}
if (memop == IR::MemOp::LOAD && wback && Rn == Rt && Rn != Reg::R31) {
return v.UnpredictableInstruction();
}
if (memop == IR::MemOp::STORE && wback && Rn == Rt && Rn != Reg::R31) {
return v.UnpredictableInstruction();
}
// TODO: Check SP alignment
IR::U64 address = Rn == Reg::SP ? IR::U64(v.SP(64)) : IR::U64(v.X(64, Rn));
if (!postindex) {
address = v.ir.Add(address, v.ir.Imm64(offset));
}
const size_t datasize = 8 << scale;
switch (memop) {
case IR::MemOp::STORE: {
const auto data = v.X(datasize, Rt);
v.Mem(address, datasize / 8, IR::AccType::NORMAL, data);
break;
}
case IR::MemOp::LOAD: {
const auto data = v.Mem(address, datasize / 8, IR::AccType::NORMAL);
if (signed_) {
v.X(regsize, Rt, v.SignExtend(data, regsize));
} else {
v.X(regsize, Rt, v.ZeroExtend(data, regsize));
}
break;
}
case IR::MemOp::PREFETCH:
// Prefetch(address, Rt)
break;
}
if (wback) {
if (postindex) {
address = v.ir.Add(address, v.ir.Imm64(offset));
}
if (Rn == Reg::SP) {
v.SP(64, address);
} else {
v.X(64, Rn, address);
}
}
return true;
}
bool TranslatorVisitor::STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn, Reg Rt) {
const bool wback = true;
const bool postindex = !not_postindex;
const size_t scale = size.ZeroExtend<size_t>();
const u64 offset = imm9.SignExtend<u64>();
return LoadStoreRegisterImmediate(*this, wback, postindex, scale, offset, size, opc, Rn, Rt);
}
bool TranslatorVisitor::STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) {
const bool wback = false;
const bool postindex = false;
const size_t scale = size.ZeroExtend<size_t>();
const u64 offset = imm12.ZeroExtend<u64>() << scale;
return LoadStoreRegisterImmediate(*this, wback, postindex, scale, offset, size, opc, Rn, Rt);
}
bool TranslatorVisitor::STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
const bool wback = false;
const bool postindex = false;
const size_t scale = size.ZeroExtend<size_t>();
const u64 offset = imm9.SignExtend<u64>();
return LoadStoreRegisterImmediate(*this, wback, postindex, scale, offset, size, opc, Rn, Rt);
}
bool TranslatorVisitor::PRFM_imm([[maybe_unused]] Imm<12> imm12, [[maybe_unused]] Reg Rn, [[maybe_unused]] Reg Rt) {
// Currently a NOP (which is valid behavior, as indicated by
// the ARMv8 architecture reference manual)
return true;
}
bool TranslatorVisitor::PRFM_unscaled_imm([[maybe_unused]] Imm<9> imm9, [[maybe_unused]] Reg Rn, [[maybe_unused]] Reg Rt) {
// Currently a NOP (which is valid behavior, as indicated by
// the ARMv8 architecture reference manual)
return true;
}
static bool LoadStoreSIMD(TranslatorVisitor& v, bool wback, bool postindex, size_t scale, u64 offset, IR::MemOp memop, Reg Rn, Vec Vt) {
const auto acctype = IR::AccType::VEC;
const size_t datasize = 8 << scale;
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
if (!postindex) {
address = v.ir.Add(address, v.ir.Imm64(offset));
}
switch (memop) {
case IR::MemOp::STORE:
if (datasize == 128) {
const IR::U128 data = v.V(128, Vt);
v.Mem(address, 16, acctype, data);
} else {
const IR::UAny data = v.ir.VectorGetElement(datasize, v.V(128, Vt), 0);
v.Mem(address, datasize / 8, acctype, data);
}
break;
case IR::MemOp::LOAD:
if (datasize == 128) {
const IR::U128 data = v.Mem(address, 16, acctype);
v.V(128, Vt, data);
} else {
const IR::UAny data = v.Mem(address, datasize / 8, acctype);
v.V(128, Vt, v.ir.ZeroExtendToQuad(data));
}
break;
default:
UNREACHABLE();
}
if (wback) {
if (postindex) {
address = v.ir.Add(address, v.ir.Imm64(offset));
}
if (Rn == Reg::SP) {
v.SP(64, address);
} else {
v.X(64, Rn, address);
}
}
return true;
}
bool TranslatorVisitor::STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn, Vec Vt) {
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const bool wback = true;
const bool postindex = !not_postindex;
const u64 offset = imm9.SignExtend<u64>();
return LoadStoreSIMD(*this, wback, postindex, scale, offset, IR::MemOp::STORE, Rn, Vt);
}
bool TranslatorVisitor::STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) {
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const bool wback = false;
const bool postindex = false;
const u64 offset = imm12.ZeroExtend<u64>() << scale;
return LoadStoreSIMD(*this, wback, postindex, scale, offset, IR::MemOp::STORE, Rn, Vt);
}
bool TranslatorVisitor::LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn, Vec Vt) {
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const bool wback = true;
const bool postindex = !not_postindex;
const u64 offset = imm9.SignExtend<u64>();
return LoadStoreSIMD(*this, wback, postindex, scale, offset, IR::MemOp::LOAD, Rn, Vt);
}
bool TranslatorVisitor::LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) {
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const bool wback = false;
const bool postindex = false;
const u64 offset = imm12.ZeroExtend<u64>() << scale;
return LoadStoreSIMD(*this, wback, postindex, scale, offset, IR::MemOp::LOAD, Rn, Vt);
}
bool TranslatorVisitor::STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const bool wback = false;
const bool postindex = false;
const u64 offset = imm9.SignExtend<u64>();
return LoadStoreSIMD(*this, wback, postindex, scale, offset, IR::MemOp::STORE, Rn, Vt);
}
bool TranslatorVisitor::LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const bool wback = false;
const bool postindex = false;
const u64 offset = imm9.SignExtend<u64>();
return LoadStoreSIMD(*this, wback, postindex, scale, offset, IR::MemOp::LOAD, Rn, Vt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,154 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) {
if ((L == 0 && opc.Bit<0>() == 1) || opc == 0b11) {
return UnallocatedEncoding();
}
const auto memop = L == 1 ? IR::MemOp::LOAD : IR::MemOp::STORE;
if (memop == IR::MemOp::LOAD && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) {
return UnpredictableInstruction();
}
if (memop == IR::MemOp::STORE && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) {
return UnpredictableInstruction();
}
if (memop == IR::MemOp::LOAD && Rt == Rt2) {
return UnpredictableInstruction();
}
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = SP(64);
} else {
address = X(64, Rn);
}
const bool postindex = !not_postindex;
const bool signed_ = opc.Bit<0>() != 0;
const size_t scale = 2 + opc.Bit<1>();
const size_t datasize = 8 << scale;
const u64 offset = imm7.SignExtend<u64>() << scale;
if (!postindex) {
address = ir.Add(address, ir.Imm64(offset));
}
const size_t dbytes = datasize / 8;
switch (memop) {
case IR::MemOp::STORE: {
const IR::U32U64 data1 = X(datasize, Rt);
const IR::U32U64 data2 = X(datasize, Rt2);
Mem(address, dbytes, IR::AccType::NORMAL, data1);
Mem(ir.Add(address, ir.Imm64(dbytes)), dbytes, IR::AccType::NORMAL, data2);
break;
}
case IR::MemOp::LOAD: {
const IR::U32U64 data1 = Mem(address, dbytes, IR::AccType::NORMAL);
const IR::U32U64 data2 = Mem(ir.Add(address, ir.Imm64(dbytes)), dbytes, IR::AccType::NORMAL);
if (signed_) {
X(64, Rt, SignExtend(data1, 64));
X(64, Rt2, SignExtend(data2, 64));
} else {
X(datasize, Rt, data1);
X(datasize, Rt2, data2);
}
break;
}
case IR::MemOp::PREFETCH:
UNREACHABLE();
}
if (wback) {
if (postindex) {
address = ir.Add(address, ir.Imm64(offset));
}
if (Rn == Reg::SP) {
SP(64, address);
} else {
X(64, Rn, address);
}
}
return true;
}
bool TranslatorVisitor::STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) {
if (opc == 0b11) {
return UnallocatedEncoding();
}
const auto memop = L == 1 ? IR::MemOp::LOAD : IR::MemOp::STORE;
if (memop == IR::MemOp::LOAD && Vt == Vt2) {
return UnpredictableInstruction();
}
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = SP(64);
} else {
address = X(64, Rn);
}
const bool postindex = !not_postindex;
const size_t scale = 2 + opc.ZeroExtend<size_t>();
const size_t datasize = 8 << scale;
const u64 offset = imm7.SignExtend<u64>() << scale;
const size_t dbytes = datasize / 8;
if (!postindex) {
address = ir.Add(address, ir.Imm64(offset));
}
switch (memop) {
case IR::MemOp::STORE: {
IR::UAnyU128 data1 = V(datasize, Vt);
IR::UAnyU128 data2 = V(datasize, Vt2);
if (datasize != 128) {
data1 = ir.VectorGetElement(datasize, data1, 0);
data2 = ir.VectorGetElement(datasize, data2, 0);
}
Mem(address, dbytes, IR::AccType::VEC, data1);
Mem(ir.Add(address, ir.Imm64(dbytes)), dbytes, IR::AccType::VEC, data2);
break;
}
case IR::MemOp::LOAD: {
IR::UAnyU128 data1 = Mem(address, dbytes, IR::AccType::VEC);
IR::UAnyU128 data2 = Mem(ir.Add(address, ir.Imm64(dbytes)), dbytes, IR::AccType::VEC);
if (datasize != 128) {
data1 = ir.ZeroExtendToQuad(data1);
data2 = ir.ZeroExtendToQuad(data2);
}
V(datasize, Vt, data1);
V(datasize, Vt2, data2);
break;
}
case IR::MemOp::PREFETCH:
UNREACHABLE();
}
if (wback) {
if (postindex) {
address = ir.Add(address, ir.Imm64(offset));
}
if (Rn == Reg::SP) {
SP(64, address);
} else {
X(64, Rn, address);
}
}
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,160 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool RegSharedDecodeAndOperation(TranslatorVisitor& v, size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1, Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Reg Rt) {
// Shared Decode
const auto acctype = IR::AccType::NORMAL;
IR::MemOp memop;
size_t regsize = 64;
bool signed_ = false;
if (opc_1 == 0) {
memop = opc_0 == 1 ? IR::MemOp::LOAD : IR::MemOp::STORE;
regsize = size == 0b11 ? 64 : 32;
signed_ = false;
} else if (size == 0b11) {
memop = IR::MemOp::PREFETCH;
if (opc_0 == 1) {
return v.UnallocatedEncoding();
}
} else {
memop = IR::MemOp::LOAD;
if (size == 0b10 && opc_0 == 1) {
return v.UnallocatedEncoding();
}
regsize = opc_0 == 1 ? 32 : 64;
signed_ = true;
}
const size_t datasize = 8 << scale;
// Operation
const IR::U64 offset = v.ExtendReg(64, Rm, option, shift);
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
address = v.ir.Add(address, offset);
switch (memop) {
case IR::MemOp::STORE: {
const IR::UAny data = v.X(datasize, Rt);
v.Mem(address, datasize / 8, acctype, data);
break;
}
case IR::MemOp::LOAD: {
const IR::UAny data = v.Mem(address, datasize / 8, acctype);
if (signed_) {
v.X(regsize, Rt, v.SignExtend(data, regsize));
} else {
v.X(regsize, Rt, v.ZeroExtend(data, regsize));
}
break;
}
case IR::MemOp::PREFETCH:
// TODO: Prefetch
break;
default:
UNREACHABLE();
}
return true;
}
bool TranslatorVisitor::STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Reg Rt) {
const Imm<1> opc_0{0};
const size_t scale = size.ZeroExtend<size_t>();
const u8 shift = S ? static_cast<u8>(scale) : 0;
if (!option.Bit<1>()) {
return UnallocatedEncoding();
}
return RegSharedDecodeAndOperation(*this, scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt);
}
bool TranslatorVisitor::LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Reg Rt) {
const Imm<1> opc_0{1};
const size_t scale = size.ZeroExtend<size_t>();
const u8 shift = S ? static_cast<u8>(scale) : 0;
if (!option.Bit<1>()) {
return UnallocatedEncoding();
}
return RegSharedDecodeAndOperation(*this, scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt);
}
static bool VecSharedDecodeAndOperation(TranslatorVisitor& v, size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Vec Vt) {
// Shared Decode
const auto acctype = IR::AccType::VEC;
const auto memop = opc_0 == 1 ? IR::MemOp::LOAD : IR::MemOp::STORE;
const size_t datasize = 8 << scale;
// Operation
const IR::U64 offset = v.ExtendReg(64, Rm, option, shift);
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
address = v.ir.Add(address, offset);
switch (memop) {
case IR::MemOp::STORE: {
const IR::UAnyU128 data = v.V_scalar(datasize, Vt);
v.Mem(address, datasize / 8, acctype, data);
break;
}
case IR::MemOp::LOAD: {
const IR::UAnyU128 data = v.Mem(address, datasize / 8, acctype);
v.V_scalar(datasize, Vt, data);
break;
}
default:
UNREACHABLE();
}
return true;
}
bool TranslatorVisitor::STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Vec Vt) {
const Imm<1> opc_0{0};
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const u8 shift = S ? static_cast<u8>(scale) : 0;
if (!option.Bit<1>()) {
return UnallocatedEncoding();
}
return VecSharedDecodeAndOperation(*this, scale, shift, opc_0, Rm, option, Rn, Vt);
}
bool TranslatorVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Vec Vt) {
const Imm<1> opc_0{1};
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const u8 shift = S ? static_cast<u8>(scale) : 0;
if (!option.Bit<1>()) {
return UnallocatedEncoding();
}
return VecSharedDecodeAndOperation(*this, scale, shift, opc_0, Rm, option, Rn, Vt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,152 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool StoreRegister(TranslatorVisitor& v, const size_t datasize,
const Imm<9> imm9, const Reg Rn, const Reg Rt) {
const u64 offset = imm9.SignExtend<u64>();
const auto acctype = IR::AccType::UNPRIV;
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check Stack Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
address = v.ir.Add(address, v.ir.Imm64(offset));
const IR::UAny data = v.X(datasize, Rt);
v.Mem(address, datasize / 8, acctype, data);
return true;
}
static bool LoadRegister(TranslatorVisitor& v, const size_t datasize,
const Imm<9> imm9, const Reg Rn, const Reg Rt) {
const u64 offset = imm9.SignExtend<u64>();
const auto acctype = IR::AccType::UNPRIV;
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check Stack Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
address = v.ir.Add(address, v.ir.Imm64(offset));
const IR::UAny data = v.Mem(address, datasize / 8, acctype);
// max is used to zeroextend < 32 to 32, and > 32 to 64
const size_t extended_size = std::max<size_t>(32, datasize);
v.X(extended_size, Rt, v.ZeroExtend(data, extended_size));
return true;
}
static bool LoadRegisterSigned(TranslatorVisitor& v, const size_t datasize,
const Imm<2> opc, const Imm<9> imm9, const Reg Rn, const Reg Rt) {
const u64 offset = imm9.SignExtend<u64>();
const auto acctype = IR::AccType::UNPRIV;
IR::MemOp memop;
bool is_signed;
size_t regsize;
if (opc.Bit<1>() == 0) {
// store or zero-extending load
memop = opc.Bit<0>() ? IR::MemOp::LOAD : IR::MemOp::STORE;
regsize = 32;
is_signed = false;
} else {
// sign-extending load
memop = IR::MemOp::LOAD;
regsize = opc.Bit<0>() ? 32 : 64;
is_signed = true;
}
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check Stack Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
address = v.ir.Add(address, v.ir.Imm64(offset));
switch (memop) {
case IR::MemOp::STORE:
v.Mem(address, datasize / 8, acctype, v.X(datasize, Rt));
break;
case IR::MemOp::LOAD: {
const IR::UAny data = v.Mem(address, datasize / 8, acctype);
if (is_signed) {
v.X(regsize, Rt, v.SignExtend(data, regsize));
} else {
v.X(regsize, Rt, v.ZeroExtend(data, regsize));
}
break;
}
case IR::MemOp::PREFETCH:
// Prefetch(address, Rt);
break;
}
return true;
}
bool TranslatorVisitor::STTRB(Imm<9> imm9, Reg Rn, Reg Rt) {
return StoreRegister(*this, 8, imm9, Rn, Rt);
}
bool TranslatorVisitor::STTRH(Imm<9> imm9, Reg Rn, Reg Rt) {
return StoreRegister(*this, 16, imm9, Rn, Rt);
}
bool TranslatorVisitor::STTR(Imm<2> size, Imm<9> imm9, Reg Rn, Reg Rt) {
const size_t scale = size.ZeroExtend<size_t>();
const size_t datasize = 8 << scale;
return StoreRegister(*this, datasize, imm9, Rn, Rt);
}
bool TranslatorVisitor::LDTRB(Imm<9> imm9, Reg Rn, Reg Rt) {
return LoadRegister(*this, 8, imm9, Rn, Rt);
}
bool TranslatorVisitor::LDTRH(Imm<9> imm9, Reg Rn, Reg Rt) {
return LoadRegister(*this, 16, imm9, Rn, Rt);
}
bool TranslatorVisitor::LDTR(Imm<2> size, Imm<9> imm9, Reg Rn, Reg Rt) {
const size_t scale = size.ZeroExtend<size_t>();
const size_t datasize = 8 << scale;
return LoadRegister(*this, datasize, imm9, Rn, Rt);
}
bool TranslatorVisitor::LDTRSB(Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
return LoadRegisterSigned(*this, 8, opc, imm9, Rn, Rt);
}
bool TranslatorVisitor::LDTRSH(Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
return LoadRegisterSigned(*this, 16, opc, imm9, Rn, Rt);
}
bool TranslatorVisitor::LDTRSW(Imm<9> imm9, Reg Rn, Reg Rt) {
const u64 offset = imm9.SignExtend<u64>();
const auto acctype = IR::AccType::UNPRIV;
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check Stack Alignment
address = SP(64);
} else {
address = X(64, Rn);
}
address = ir.Add(address, ir.Imm64(offset));
const IR::UAny data = Mem(address, 4, acctype);
X(64, Rt, SignExtend(data, 64));
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,226 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <optional>
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool SharedDecodeAndOperation(TranslatorVisitor& v, bool wback, IR::MemOp memop,
bool Q, bool S, bool R, bool replicate, std::optional<Reg> Rm,
Imm<3> opcode, Imm<2> size, Reg Rn, Vec Vt) {
const size_t selem = (opcode.Bit<0>() << 1 | u32{R}) + 1;
size_t scale = opcode.Bits<1, 2>();
size_t index = 0;
switch (scale) {
case 0:
index = Q << 3 | S << 2 | size.ZeroExtend();
break;
case 1:
if (size.Bit<0>()) {
return v.UnallocatedEncoding();
}
index = Q << 2 | S << 1 | u32{size.Bit<1>()};
break;
case 2:
if (size.Bit<1>()) {
return v.UnallocatedEncoding();
}
if (size.Bit<0>()) {
if (S) {
return v.UnallocatedEncoding();
}
index = Q;
scale = 3;
} else {
index = Q << 1 | u32{S};
}
break;
case 3:
if (memop == IR::MemOp::STORE || S) {
return v.UnallocatedEncoding();
}
scale = size.ZeroExtend();
break;
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = 8 << scale;
const size_t ebytes = esize / 8;
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
IR::U64 offs = v.ir.Imm64(0);
if (replicate) {
for (size_t s = 0; s < selem; s++) {
const Vec tt = static_cast<Vec>((VecNumber(Vt) + s) % 32);
const IR::UAnyU128 element = v.Mem(v.ir.Add(address, offs), ebytes, IR::AccType::VEC);
const IR::U128 broadcasted_element = v.ir.VectorBroadcast(esize, element);
v.V(datasize, tt, broadcasted_element);
offs = v.ir.Add(offs, v.ir.Imm64(ebytes));
}
} else {
for (size_t s = 0; s < selem; s++) {
const Vec tt = static_cast<Vec>((VecNumber(Vt) + s) % 32);
const IR::U128 rval = v.V(128, tt);
if (memop == IR::MemOp::LOAD) {
const IR::UAny elem = v.Mem(v.ir.Add(address, offs), ebytes, IR::AccType::VEC);
const IR::U128 vec = v.ir.VectorSetElement(esize, rval, index, elem);
v.V(128, tt, vec);
} else {
const IR::UAny elem = v.ir.VectorGetElement(esize, rval, index);
v.Mem(v.ir.Add(address, offs), ebytes, IR::AccType::VEC, elem);
}
offs = v.ir.Add(offs, v.ir.Imm64(ebytes));
}
}
if (wback) {
if (*Rm != Reg::SP) {
offs = v.X(64, *Rm);
}
if (Rn == Reg::SP) {
v.SP(64, v.ir.Add(address, offs));
} else {
v.X(64, Rn, v.ir.Add(address, offs));
}
}
return true;
}
bool TranslatorVisitor::LD1_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, S, false, false, {},
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD1_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, S, false, false, Rm,
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD1R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, false, false, true,
{}, Imm<3>{0b110}, size, Rn, Vt);
}
bool TranslatorVisitor::LD1R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, false, false, true,
Rm, Imm<3>{0b110}, size, Rn, Vt);
}
bool TranslatorVisitor::LD2_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, S, true, false, {},
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD2_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, S, true, false, Rm,
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD2R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, false, true, true,
{}, Imm<3>{0b110}, size, Rn, Vt);
}
bool TranslatorVisitor::LD2R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, false, true, true,
Rm, Imm<3>{0b110}, size, Rn, Vt);
}
bool TranslatorVisitor::LD3_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, S, false, false, {},
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD3_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, S, false, false, Rm,
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD3R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, false, false, true,
{}, Imm<3>{0b111}, size, Rn, Vt);
}
bool TranslatorVisitor::LD3R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, false, false, true,
Rm, Imm<3>{0b111}, size, Rn, Vt);
}
bool TranslatorVisitor::LD4_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, S, true, false, {},
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD4_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, S, true, false, Rm,
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD4R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, false, true, true,
{}, Imm<3>{0b111}, size, Rn, Vt);
}
bool TranslatorVisitor::LD4R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, false, true, true,
Rm, Imm<3>{0b111}, size, Rn, Vt);
}
bool TranslatorVisitor::ST1_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::STORE, Q, S, false, false, {},
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST1_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::STORE, Q, S, false, false, Rm,
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST2_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::STORE, Q, S, true, false, {},
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST2_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::STORE, Q, S, true, false, Rm,
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST3_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::STORE, Q, S, false, false, {},
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST3_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::STORE, Q, S, false, false, Rm,
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST4_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::STORE, Q, S, true, false, {},
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST4_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::STORE, Q, S, true, false, Rm,
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,60 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::MOVN(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) {
if (!sf && hw.Bit<1>()) {
return UnallocatedEncoding();
}
const size_t datasize = sf ? 64 : 32;
const size_t pos = hw.ZeroExtend<size_t>() << 4;
u64 value = imm16.ZeroExtend<u64>() << pos;
value = ~value;
const auto result = I(datasize, value);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::MOVZ(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) {
if (!sf && hw.Bit<1>()) {
return UnallocatedEncoding();
}
const size_t datasize = sf ? 64 : 32;
const size_t pos = hw.ZeroExtend<size_t>() << 4;
const u64 value = imm16.ZeroExtend<u64>() << pos;
const auto result = I(datasize, value);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::MOVK(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) {
if (!sf && hw.Bit<1>()) {
return UnallocatedEncoding();
}
const size_t datasize = sf ? 64 : 32;
const size_t pos = hw.ZeroExtend<size_t>() << 4;
const u64 mask = u64(0xFFFF) << pos;
const u64 value = imm16.ZeroExtend<u64>() << pos;
auto result = X(datasize, Rd);
result = ir.And(result, I(datasize, ~mask));
result = ir.Or(result, I(datasize, value));
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,238 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class Signedness {
Signed,
Unsigned
};
bool LongAdd(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, Signedness sign) {
if ((size == 0b10 && !Q) || size == 0b11) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const size_t elements = datasize / esize;
const IR::U128 operand = v.V(datasize, Vn);
const auto get_element = [&](IR::U128 vec, size_t element) {
const auto vec_element = v.ir.VectorGetElement(esize, vec, element);
if (sign == Signedness::Signed) {
return v.ir.SignExtendToLong(vec_element);
}
return v.ir.ZeroExtendToLong(vec_element);
};
IR::U64 sum = get_element(operand, 0);
for (size_t i = 1; i < elements; i++) {
sum = v.ir.Add(sum, get_element(operand, i));
}
if (size == 0b00) {
v.V(datasize, Vd, v.ir.ZeroExtendToQuad(v.ir.LeastSignificantHalf(sum)));
} else if (size == 0b01) {
v.V(datasize, Vd, v.ir.ZeroExtendToQuad(v.ir.LeastSignificantWord(sum)));
} else {
v.V(datasize, Vd, v.ir.ZeroExtendToQuad(sum));
}
return true;
}
enum class MinMaxOperation {
Max,
MaxNumeric,
Min,
MinNumeric,
};
bool FPMinMax(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, MinMaxOperation operation) {
if (!Q || sz) {
return v.ReservedValue();
}
const size_t esize = 32;
const size_t datasize = 128;
const size_t elements = datasize / esize;
const IR::U128 operand = v.V(datasize, Vn);
const auto op = [&](const IR::U32U64& lhs, const IR::U32U64& rhs) {
switch (operation) {
case MinMaxOperation::Max:
return v.ir.FPMax(lhs, rhs);
case MinMaxOperation::MaxNumeric:
return v.ir.FPMaxNumeric(lhs, rhs);
case MinMaxOperation::Min:
return v.ir.FPMin(lhs, rhs);
case MinMaxOperation::MinNumeric:
return v.ir.FPMinNumeric(lhs, rhs);
default:
UNREACHABLE();
}
};
const auto reduce = [&](size_t start, size_t end) {
IR::U32U64 result = v.ir.VectorGetElement(esize, operand, start);
for (size_t i = start + 1; i < end; i++) {
const IR::U32U64 element = v.ir.VectorGetElement(esize, operand, i);
result = op(result, element);
}
return result;
};
const IR::U32U64 hi = reduce(elements / 2, elements);
const IR::U32U64 lo = reduce(0, elements / 2);
const IR::U32U64 result = op(lo, hi);
v.V_scalar(esize, Vd, result);
return true;
}
enum class ScalarMinMaxOperation {
Max,
Min,
};
bool ScalarMinMax(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd,
ScalarMinMaxOperation operation, Signedness sign) {
if ((size == 0b10 && !Q) || size == 0b11) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const size_t elements = datasize / esize;
const auto get_element = [&](IR::U128 vec, size_t element) {
const auto vec_element = v.ir.VectorGetElement(esize, vec, element);
if (sign == Signedness::Signed) {
return v.ir.SignExtendToWord(vec_element);
}
return v.ir.ZeroExtendToWord(vec_element);
};
const auto op_func = [&](const auto& a, const auto& b) {
switch (operation) {
case ScalarMinMaxOperation::Max:
if (sign == Signedness::Signed) {
return v.ir.MaxSigned(a, b);
}
return v.ir.MaxUnsigned(a, b);
case ScalarMinMaxOperation::Min:
if (sign == Signedness::Signed) {
return v.ir.MinSigned(a, b);
}
return v.ir.MinUnsigned(a, b);
default:
UNREACHABLE();
}
};
const IR::U128 operand = v.V(datasize, Vn);
IR::U32 value = get_element(operand, 0);
for (size_t i = 1; i < elements; i++) {
value = op_func(value, get_element(operand, i));
}
if (size == 0b00) {
v.V(datasize, Vd, v.ir.ZeroExtendToQuad(v.ir.LeastSignificantByte(value)));
} else if (size == 0b01) {
v.V(datasize, Vd, v.ir.ZeroExtendToQuad(v.ir.LeastSignificantHalf(value)));
} else {
v.V(datasize, Vd, v.ir.ZeroExtendToQuad(value));
}
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::ADDV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if ((size == 0b10 && !Q) || size == 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const size_t elements = datasize / esize;
const IR::U128 operand = V(datasize, Vn);
const auto get_element = [&](IR::U128 vec, size_t element) {
return ir.ZeroExtendToWord(ir.VectorGetElement(esize, vec, element));
};
IR::U32 sum = get_element(operand, 0);
for (size_t i = 1; i < elements; i++) {
sum = ir.Add(sum, get_element(operand, i));
}
if (size == 0b00) {
V(datasize, Vd, ir.ZeroExtendToQuad(ir.LeastSignificantByte(sum)));
} else if (size == 0b01) {
V(datasize, Vd, ir.ZeroExtendToQuad(ir.LeastSignificantHalf(sum)));
} else {
V(datasize, Vd, ir.ZeroExtendToQuad(sum));
}
return true;
}
bool TranslatorVisitor::FMAXNMV_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPMinMax(*this, Q, sz, Vn, Vd, MinMaxOperation::MaxNumeric);
}
bool TranslatorVisitor::FMAXV_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPMinMax(*this, Q, sz, Vn, Vd, MinMaxOperation::Max);
}
bool TranslatorVisitor::FMINNMV_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPMinMax(*this, Q, sz, Vn, Vd, MinMaxOperation::MinNumeric);
}
bool TranslatorVisitor::FMINV_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPMinMax(*this, Q, sz, Vn, Vd, MinMaxOperation::Min);
}
bool TranslatorVisitor::SADDLV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return LongAdd(*this, Q, size, Vn, Vd, Signedness::Signed);
}
bool TranslatorVisitor::SMAXV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return ScalarMinMax(*this, Q, size, Vn, Vd, ScalarMinMaxOperation::Max, Signedness::Signed);
}
bool TranslatorVisitor::SMINV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return ScalarMinMax(*this, Q, size, Vn, Vd, ScalarMinMaxOperation::Min, Signedness::Signed);
}
bool TranslatorVisitor::UADDLV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return LongAdd(*this, Q, size, Vn, Vd, Signedness::Unsigned);
}
bool TranslatorVisitor::UMAXV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return ScalarMinMax(*this, Q, size, Vn, Vd, ScalarMinMaxOperation::Max, Signedness::Unsigned);
}
bool TranslatorVisitor::UMINV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return ScalarMinMax(*this, Q, size, Vn, Vd, ScalarMinMaxOperation::Min, Signedness::Unsigned);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,46 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::AESD(Vec Vn, Vec Vd) {
const IR::U128 operand1 = ir.GetQ(Vd);
const IR::U128 operand2 = ir.GetQ(Vn);
const IR::U128 result = ir.AESDecryptSingleRound(ir.VectorEor(operand1, operand2));
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::AESE(Vec Vn, Vec Vd) {
const IR::U128 operand1 = ir.GetQ(Vd);
const IR::U128 operand2 = ir.GetQ(Vn);
const IR::U128 result = ir.AESEncryptSingleRound(ir.VectorEor(operand1, operand2));
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::AESIMC(Vec Vn, Vec Vd) {
const IR::U128 operand = ir.GetQ(Vn);
const IR::U128 result = ir.AESInverseMixColumns(operand);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::AESMC(Vec Vn, Vec Vd) {
const IR::U128 operand = ir.GetQ(Vn);
const IR::U128 result = ir.AESMixColumns(operand);
ir.SetQ(Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,158 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/bit_util.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::DUP_elt_1(Imm<5> imm5, Vec Vn, Vec Vd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size > 3) {
return ReservedValue();
}
const size_t index = imm5.ZeroExtend<size_t>() >> (size + 1);
const size_t idxdsize = imm5.Bit<4>() ? 128 : 64;
const size_t esize = 8 << size;
const IR::U128 operand = V(idxdsize, Vn);
const IR::UAny element = ir.VectorGetElement(esize, operand, index);
const IR::U128 result = ir.ZeroExtendToQuad(element);
V(128, Vd, result);
return true;
}
bool TranslatorVisitor::DUP_elt_2(bool Q, Imm<5> imm5, Vec Vn, Vec Vd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size > 3) {
return ReservedValue();
}
if (size == 3 && !Q) {
return ReservedValue();
}
const size_t index = imm5.ZeroExtend<size_t>() >> (size + 1);
const size_t idxdsize = imm5.Bit<4>() ? 128 : 64;
const size_t esize = 8 << size;
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(idxdsize, Vn);
const IR::UAny element = ir.VectorGetElement(esize, operand, index);
const IR::U128 result = Q ? ir.VectorBroadcast(esize, element) : ir.VectorBroadcastLower(esize, element);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::DUP_gen(bool Q, Imm<5> imm5, Reg Rn, Vec Vd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size > 3) {
return ReservedValue();
}
if (size == 3 && !Q) {
return ReservedValue();
}
const size_t esize = 8 << size;
const size_t datasize = Q ? 128 : 64;
const IR::UAny element = X(esize, Rn);
const IR::U128 result = Q ? ir.VectorBroadcast(esize, element) : ir.VectorBroadcastLower(esize, element);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size == 2 && !Q) {
return UnallocatedEncoding();
}
if (size > 2) {
return ReservedValue();
}
const size_t idxdsize = imm5.Bit<4>() ? 128 : 64;
const size_t index = imm5.ZeroExtend<size_t>() >> (size + 1);
const size_t esize = 8 << size;
const size_t datasize = Q ? 64 : 32;
const IR::U128 operand = V(idxdsize, Vn);
const IR::UAny elem = ir.VectorGetElement(esize, operand, index);
X(datasize, Rd, SignExtend(elem, datasize));
return true;
}
bool TranslatorVisitor::UMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size < 3 && Q) {
return UnallocatedEncoding();
}
if (size == 3 && !Q) {
return UnallocatedEncoding();
}
if (size > 3) {
return ReservedValue();
}
const size_t idxdsize = imm5.Bit<4>() ? 128 : 64;
const size_t index = imm5.ZeroExtend<size_t>() >> (size + 1);
const size_t esize = 8 << size;
const size_t datasize = Q ? 64 : 32;
const IR::U128 operand = V(idxdsize, Vn);
const IR::UAny elem = ir.VectorGetElement(esize, operand, index);
X(datasize, Rd, ZeroExtend(elem, datasize));
return true;
}
bool TranslatorVisitor::INS_gen(Imm<5> imm5, Reg Rn, Vec Vd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size > 3) {
return ReservedValue();
}
const size_t index = imm5.ZeroExtend<size_t>() >> (size + 1);
const size_t esize = 8 << size;
const size_t datasize = 128;
const IR::UAny element = X(esize, Rn);
const IR::U128 result = ir.VectorSetElement(esize, V(datasize, Vd), index, element);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::INS_elt(Imm<5> imm5, Imm<4> imm4, Vec Vn, Vec Vd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size > 3) {
return ReservedValue();
}
const size_t dst_index = imm5.ZeroExtend<size_t>() >> (size + 1);
const size_t src_index = imm4.ZeroExtend<size_t>() >> size;
const size_t idxdsize = imm4.Bit<3>() ? 128 : 64;
const size_t esize = 8 << size;
const IR::U128 operand = V(idxdsize, Vn);
const IR::UAny elem = ir.VectorGetElement(esize, operand, src_index);
const IR::U128 result = ir.VectorSetElement(esize, V(128, Vd), dst_index, elem);
V(128, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,52 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::EOR3(Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const IR::U128 a = ir.GetQ(Va);
const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn);
const IR::U128 result = ir.VectorEor(ir.VectorEor(n, m), a);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::BCAX(Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const IR::U128 a = ir.GetQ(Va);
const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn);
const IR::U128 result = ir.VectorEor(n, ir.VectorAnd(m, ir.VectorNot(a)));
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SM3SS1(Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const IR::U128 a = ir.GetQ(Va);
const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn);
const IR::U32 top_a = ir.VectorGetElement(32, a, 3);
const IR::U32 top_m = ir.VectorGetElement(32, m, 3);
const IR::U32 top_n = ir.VectorGetElement(32, n, 3);
const IR::U32 rotated_n = ir.RotateRight(top_n, ir.Imm8(20));
const IR::U32 sum = ir.Add(ir.Add(rotated_n, top_m), top_a);
const IR::U32 result = ir.RotateRight(sum, ir.Imm8(25));
const IR::U128 zero_vector = ir.ZeroVector();
const IR::U128 vector_result = ir.VectorSetElement(32, zero_vector, 3, result);
ir.SetQ(Vd, vector_result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,102 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class SM3TTVariant {
A,
B,
};
bool SM3TT1(TranslatorVisitor& v, Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd, SM3TTVariant behavior) {
const IR::U128 d = v.ir.GetQ(Vd);
const IR::U128 m = v.ir.GetQ(Vm);
const IR::U128 n = v.ir.GetQ(Vn);
const u32 index = imm2.ZeroExtend();
const IR::U32 top_d = v.ir.VectorGetElement(32, d, 3);
const IR::U32 before_top_d = v.ir.VectorGetElement(32, d, 2);
const IR::U32 after_low_d = v.ir.VectorGetElement(32, d, 1);
const IR::U32 low_d = v.ir.VectorGetElement(32, d, 0);
const IR::U32 top_n = v.ir.VectorGetElement(32, n, 3);
const IR::U32 wj_prime = v.ir.VectorGetElement(32, m, index);
const IR::U32 ss2 = v.ir.Eor(top_n, v.ir.RotateRight(top_d, v.ir.Imm8(20)));
const IR::U32 tt1 = [&] {
if (behavior == SM3TTVariant::A) {
return v.ir.Eor(after_low_d, v.ir.Eor(top_d, before_top_d));
}
const IR::U32 tmp1 = v.ir.And(top_d, after_low_d);
const IR::U32 tmp2 = v.ir.And(top_d, before_top_d);
const IR::U32 tmp3 = v.ir.And(after_low_d, before_top_d);
return v.ir.Or(v.ir.Or(tmp1, tmp2), tmp3);
}();
const IR::U32 final_tt1 = v.ir.Add(tt1, v.ir.Add(low_d, v.ir.Add(ss2, wj_prime)));
const IR::U128 zero_vector = v.ir.ZeroVector();
const IR::U128 tmp1 = v.ir.VectorSetElement(32, zero_vector, 0, after_low_d);
const IR::U128 tmp2 = v.ir.VectorSetElement(32, tmp1, 1, v.ir.RotateRight(before_top_d, v.ir.Imm8(23)));
const IR::U128 tmp3 = v.ir.VectorSetElement(32, tmp2, 2, top_d);
const IR::U128 result = v.ir.VectorSetElement(32, tmp3, 3, final_tt1);
v.ir.SetQ(Vd, result);
return true;
}
bool SM3TT2(TranslatorVisitor& v, Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd, SM3TTVariant behavior) {
const IR::U128 d = v.ir.GetQ(Vd);
const IR::U128 m = v.ir.GetQ(Vm);
const IR::U128 n = v.ir.GetQ(Vn);
const u32 index = imm2.ZeroExtend();
const IR::U32 top_d = v.ir.VectorGetElement(32, d, 3);
const IR::U32 before_top_d = v.ir.VectorGetElement(32, d, 2);
const IR::U32 after_low_d = v.ir.VectorGetElement(32, d, 1);
const IR::U32 low_d = v.ir.VectorGetElement(32, d, 0);
const IR::U32 top_n = v.ir.VectorGetElement(32, n, 3);
const IR::U32 wj = v.ir.VectorGetElement(32, m, index);
const IR::U32 tt2 = [&] {
if (behavior == SM3TTVariant::A) {
return v.ir.Eor(after_low_d, v.ir.Eor(top_d, before_top_d));
}
const IR::U32 tmp1 = v.ir.And(top_d, before_top_d);
const IR::U32 tmp2 = v.ir.And(v.ir.Not(top_d), after_low_d);
return v.ir.Or(tmp1, tmp2);
}();
const IR::U32 final_tt2 = v.ir.Add(tt2, v.ir.Add(low_d, v.ir.Add(top_n, wj)));
const IR::U32 top_result = v.ir.Eor(final_tt2, v.ir.Eor(v.ir.RotateRight(final_tt2, v.ir.Imm8(23)),
v.ir.RotateRight(final_tt2, v.ir.Imm8(15))));
const IR::U128 zero_vector = v.ir.ZeroVector();
const IR::U128 tmp1 = v.ir.VectorSetElement(32, zero_vector, 0, after_low_d);
const IR::U128 tmp2 = v.ir.VectorSetElement(32, tmp1, 1, v.ir.RotateRight(before_top_d, v.ir.Imm8(13)));
const IR::U128 tmp3 = v.ir.VectorSetElement(32, tmp2, 2, top_d);
const IR::U128 result = v.ir.VectorSetElement(32, tmp3, 3, top_result);
v.ir.SetQ(Vd, result);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::SM3TT1A(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) {
return SM3TT1(*this, Vm, imm2, Vn, Vd, SM3TTVariant::A);
}
bool TranslatorVisitor::SM3TT1B(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) {
return SM3TT1(*this, Vm, imm2, Vn, Vd, SM3TTVariant::B);
}
bool TranslatorVisitor::SM3TT2A(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) {
return SM3TT2(*this, Vm, imm2, Vn, Vd, SM3TTVariant::A);
}
bool TranslatorVisitor::SM3TT2B(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) {
return SM3TT2(*this, Vm, imm2, Vn, Vd, SM3TTVariant::B);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,27 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::EXT(bool Q, Vec Vm, Imm<4> imm4, Vec Vn, Vec Vd) {
if (!Q && imm4.Bit<3>()) {
return ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t position = imm4.ZeroExtend<size_t>() << 3;
const IR::U128 lo = V(datasize, Vn);
const IR::U128 hi = V(datasize, Vm);
const IR::U128 result = datasize == 64 ? ir.VectorExtractLower(lo, hi, position) : ir.VectorExtract(lo, hi, position);
V(datasize, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,109 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/bit_util.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::MOVI(bool Q, bool op, Imm<1> a, Imm<1> b, Imm<1> c, Imm<4> cmode, Imm<1> d, Imm<1> e, Imm<1> f, Imm<1> g, Imm<1> h, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
// MOVI
// also FMOV (vector, immediate) when cmode == 0b1111
const auto movi = [&]{
const u64 imm64 = AdvSIMDExpandImm(op, cmode, concatenate(a, b, c, d, e, f, g, h));
const IR::U128 imm = datasize == 64 ? ir.ZeroExtendToQuad(ir.Imm64(imm64)) : ir.VectorBroadcast(64, ir.Imm64(imm64));
V(128, Vd, imm);
return true;
};
// MVNI
const auto mvni = [&]{
const u64 imm64 = ~AdvSIMDExpandImm(op, cmode, concatenate(a, b, c, d, e, f, g, h));
const IR::U128 imm = datasize == 64 ? ir.ZeroExtendToQuad(ir.Imm64(imm64)) : ir.VectorBroadcast(64, ir.Imm64(imm64));
V(128, Vd, imm);
return true;
};
// ORR (vector, immediate)
const auto orr = [&]{
const u64 imm64 = AdvSIMDExpandImm(op, cmode, concatenate(a, b, c, d, e, f, g, h));
const IR::U128 imm = datasize == 64 ? ir.ZeroExtendToQuad(ir.Imm64(imm64)) : ir.VectorBroadcast(64, ir.Imm64(imm64));
const IR::U128 operand = V(datasize, Vd);
const IR::U128 result = ir.VectorOr(operand, imm);
V(datasize, Vd, result);
return true;
};
// BIC (vector, immediate)
const auto bic = [&]{
const u64 imm64 = ~AdvSIMDExpandImm(op, cmode, concatenate(a, b, c, d, e, f, g, h));
const IR::U128 imm = datasize == 64 ? ir.ZeroExtendToQuad(ir.Imm64(imm64)) : ir.VectorBroadcast(64, ir.Imm64(imm64));
const IR::U128 operand = V(datasize, Vd);
const IR::U128 result = ir.VectorAnd(operand, imm);
V(datasize, Vd, result);
return true;
};
switch (concatenate(cmode, Imm<1>{op}).ZeroExtend()) {
case 0b00000: case 0b00100: case 0b01000: case 0b01100:
case 0b10000: case 0b10100:
case 0b11000: case 0b11010:
case 0b11100: case 0b11101: case 0b11110:
return movi();
case 0b11111:
if (!Q) {
return UnallocatedEncoding();
}
return movi();
case 0b00001: case 0b00101: case 0b01001: case 0b01101:
case 0b10001: case 0b10101:
case 0b11001: case 0b11011:
return mvni();
case 0b00010: case 0b00110: case 0b01010: case 0b01110:
case 0b10010: case 0b10110:
return orr();
case 0b00011: case 0b00111: case 0b01011: case 0b01111:
case 0b10011: case 0b10111:
return bic();
}
UNREACHABLE();
}
bool TranslatorVisitor::FMOV_2(bool Q, bool op, Imm<1> a, Imm<1> b, Imm<1> c, Imm<1> d, Imm<1> e, Imm<1> f, Imm<1> g, Imm<1> h, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
if (op && !Q) {
return UnallocatedEncoding();
}
const u64 imm64 = AdvSIMDExpandImm(op, Imm<4>{0b1111}, concatenate(a, b, c, d, e, f, g, h));
const IR::U128 imm = datasize == 64 ? ir.ZeroExtendToQuad(ir.Imm64(imm64)) : ir.VectorBroadcast(64, ir.Imm64(imm64));
V(128, Vd, imm);
return true;
}
bool TranslatorVisitor::FMOV_3(bool Q, Imm<1> a, Imm<1> b, Imm<1> c, Imm<1> d, Imm<1> e, Imm<1> f, Imm<1> g, Imm<1> h, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
const Imm<8> imm8 = concatenate(a, b, c, d, e, f, g, h);
const u16 imm16 = [&imm8]{
u16 imm16 = 0;
imm16 |= imm8.Bit<7>() ? 0x8000 : 0;
imm16 |= imm8.Bit<6>() ? 0x3000 : 0x4000;
imm16 |= imm8.Bits<0, 5, u16>() << 6;
return imm16;
}();
const u64 imm64 = Common::Replicate<u64>(imm16, 16);
const IR::U128 imm = datasize == 64 ? ir.ZeroExtendToQuad(ir.Imm64(imm64)) : ir.VectorBroadcast(64, ir.Imm64(imm64));
V(128, Vd, imm);
return true;
}
} // namespace Dynarmic::A6

View File

@@ -0,0 +1,112 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <tuple>
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class Transposition {
TRN1,
TRN2,
};
bool VectorTranspose(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Transposition type) {
if (!Q && size == 0b11) {
return v.ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const u8 esize = static_cast<u8>(8 << size.ZeroExtend());
const IR::U128 m = v.V(datasize, Vm);
const IR::U128 n = v.V(datasize, Vn);
const IR::U128 result = v.ir.VectorTranspose(esize, n, m, type == Transposition::TRN2);
v.V(datasize, Vd, result);
return true;
}
enum class UnzipType {
Even,
Odd,
};
bool VectorUnzip(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, UnzipType type) {
if (size == 0b11 && !Q) {
return v.ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = 8 << size.ZeroExtend();
const IR::U128 n = v.V(datasize, Vn);
const IR::U128 m = v.V(datasize, Vm);
const IR::U128 result = type == UnzipType::Even
? (Q ? v.ir.VectorDeinterleaveEven(esize, n, m) : v.ir.VectorDeinterleaveEvenLower(esize, n, m))
: (Q ? v.ir.VectorDeinterleaveOdd(esize, n, m) : v.ir.VectorDeinterleaveOddLower(esize, n, m));
v.V(datasize, Vd, result);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::TRN1(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return VectorTranspose(*this, Q, size, Vm, Vn, Vd, Transposition::TRN1);
}
bool TranslatorVisitor::TRN2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return VectorTranspose(*this, Q, size, Vm, Vn, Vd, Transposition::TRN2);
}
bool TranslatorVisitor::UZP1(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return VectorUnzip(*this, Q, size, Vm, Vn, Vd, UnzipType::Even);
}
bool TranslatorVisitor::UZP2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return VectorUnzip(*this, Q, size, Vm, Vn, Vd, UnzipType::Odd);
}
bool TranslatorVisitor::ZIP1(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size == 0b11 && !Q) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend<size_t>();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(datasize, Vm);
const IR::U128 result = ir.VectorInterleaveLower(esize, operand1, operand2);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::ZIP2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size == 0b11 && !Q) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend<size_t>();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(datasize, Vm);
const IR::U128 result = [&]{
if (Q) {
return ir.VectorInterleaveUpper(esize, operand1, operand2);
}
// TODO: Urgh.
const IR::U128 interleaved = ir.VectorInterleaveLower(esize, operand1, operand2);
return ir.VectorZeroUpper(ir.VectorShuffleWords(interleaved, 0b01001110));
}();
V(datasize, Vd, result);
return true;
}
} // namespace Dynarmic::A64

Some files were not shown because too many files have changed in this diff Show More