Label A32 specific code appropriately

This commit is contained in:
MerryMage
2018-01-01 15:23:56 +00:00
parent 89e9ce8aff
commit b3c73e2622
58 changed files with 938 additions and 831 deletions

199
src/frontend/A32/FPSCR.h Normal file
View File

@@ -0,0 +1,199 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include <boost/optional.hpp>
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Dynarmic {
namespace A32 {
/**
* Representation of the Floating-Point Status and Control Register.
*/
class FPSCR final
{
public:
enum class RoundingMode {
ToNearest,
TowardsPlusInfinity,
TowardsMinusInfinity,
TowardsZero
};
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.
RoundingMode RMode() const {
return static_cast<RoundingMode>(Common::Bits<22, 23>(value));
}
/// Indicates the stride of a vector.
boost::optional<size_t> Stride() const {
switch (Common::Bits<20, 21>(value)) {
case 0b00:
return 1;
case 0b11:
return 2;
default:
return boost::none;
}
}
/// 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 A32
} // namespace Dynarmic

225
src/frontend/A32/PSR.h Normal file
View File

@@ -0,0 +1,225 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Dynarmic {
namespace 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 = (value & ~0x80000000) | static_cast<u32>(set) << 31;
}
bool Z() const {
return Common::Bit<30>(value);
}
void Z(bool set) {
value = (value & ~0x40000000) | static_cast<u32>(set) << 30;
}
bool C() const {
return Common::Bit<29>(value);
}
void C(bool set) {
value = (value & ~0x20000000) | static_cast<u32>(set) << 29;
}
bool V() const {
return Common::Bit<28>(value);
}
void V(bool set) {
value = (value & ~0x10000000) | static_cast<u32>(set) << 28;
}
bool Q() const {
return Common::Bit<27>(value);
}
void Q(bool set) {
value = (value & ~0x8000000) | static_cast<u32>(set) << 27;
}
bool J() const {
return Common::Bit<24>(value);
}
void J(bool set) {
value = (value & ~0x1000000) | static_cast<u32>(set) << 24;
}
u32 GE() const {
return Common::Bits<16, 19>(value);
}
void GE(u32 data) {
value = (value & ~0xF0000) | (data & 0xF) << 16;
}
u32 IT() const {
return (value & 0x6000000) >> 25 | (value & 0xFC00) >> 8;
}
void IT(u32 data) {
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 = (value & ~0x200) | static_cast<u32>(set) << 9;
}
bool A() const {
return Common::Bit<8>(value);
}
void A(bool set) {
value = (value & ~0x100) | static_cast<u32>(set) << 8;
}
bool I() const {
return Common::Bit<7>(value);
}
void I(bool set) {
value = (value & ~0x80) | static_cast<u32>(set) << 7;
}
bool F() const {
return Common::Bit<6>(value);
}
void F(bool set) {
value = (value & ~0x40) | static_cast<u32>(set) << 6;
}
bool T() const {
return Common::Bit<5>(value);
}
void T(bool set) {
value = (value & ~0x20) | static_cast<u32>(set) << 5;
}
Mode M() const {
return static_cast<Mode>(Common::Bits<0, 4>(value));
}
void M(Mode mode) {
value = (value & ~0x1F) | (static_cast<u32>(mode) & 0x1F);
}
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 A32
} // namespace Dynarmic

View File

@@ -0,0 +1,334 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*
* Original version of table by Lioncash.
*/
#pragma once
#include <algorithm>
#include <functional>
#include <vector>
#include <boost/optional.hpp>
#include "common/bit_util.h"
#include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic {
namespace 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(fn, name, bitstring)
// Branch instructions
INST(&V::arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv"), // v5
INST(&V::arm_BLX_reg, "BLX (reg)", "cccc000100101111111111110011mmmm"), // v5
INST(&V::arm_B, "B", "cccc1010vvvvvvvvvvvvvvvvvvvvvvvv"), // all
INST(&V::arm_BL, "BL", "cccc1011vvvvvvvvvvvvvvvvvvvvvvvv"), // all
INST(&V::arm_BX, "BX", "cccc000100101111111111110001mmmm"), // v4T
INST(&V::arm_BXJ, "BXJ", "cccc000100101111111111110010mmmm"), // v5J
// Coprocessor instructions
INST(&V::arm_CDP, "CDP", "cccc1110ooooNNNNDDDDppppooo0MMMM"), // v2 (CDP2: v5)
INST(&V::arm_LDC, "LDC", "cccc110pudw1nnnnDDDDppppvvvvvvvv"), // v2 (LDC2: v5)
INST(&V::arm_MCR, "MCR", "cccc1110ooo0NNNNttttppppooo1MMMM"), // v2 (MCR2: v5)
INST(&V::arm_MCRR, "MCRR", "cccc11000100uuuuttttppppooooMMMM"), // v5E (MCRR2: v6)
INST(&V::arm_MRC, "MRC", "cccc1110ooo1NNNNttttppppooo1MMMM"), // v2 (MRC2: v5)
INST(&V::arm_MRRC, "MRRC", "cccc11000101uuuuttttppppooooMMMM"), // v5E (MRRC2: v6)
INST(&V::arm_STC, "STC", "cccc110pudw0nnnnDDDDppppvvvvvvvv"), // v2 (STC2: v5)
// Data Processing instructions
INST(&V::arm_ADC_imm, "ADC (imm)", "cccc0010101Snnnnddddrrrrvvvvvvvv"), // all
INST(&V::arm_ADC_reg, "ADC (reg)", "cccc0000101Snnnnddddvvvvvrr0mmmm"), // all
INST(&V::arm_ADC_rsr, "ADC (rsr)", "cccc0000101Snnnnddddssss0rr1mmmm"), // all
INST(&V::arm_ADD_imm, "ADD (imm)", "cccc0010100Snnnnddddrrrrvvvvvvvv"), // all
INST(&V::arm_ADD_reg, "ADD (reg)", "cccc0000100Snnnnddddvvvvvrr0mmmm"), // all
INST(&V::arm_ADD_rsr, "ADD (rsr)", "cccc0000100Snnnnddddssss0rr1mmmm"), // all
INST(&V::arm_AND_imm, "AND (imm)", "cccc0010000Snnnnddddrrrrvvvvvvvv"), // all
INST(&V::arm_AND_reg, "AND (reg)", "cccc0000000Snnnnddddvvvvvrr0mmmm"), // all
INST(&V::arm_AND_rsr, "AND (rsr)", "cccc0000000Snnnnddddssss0rr1mmmm"), // all
INST(&V::arm_BIC_imm, "BIC (imm)", "cccc0011110Snnnnddddrrrrvvvvvvvv"), // all
INST(&V::arm_BIC_reg, "BIC (reg)", "cccc0001110Snnnnddddvvvvvrr0mmmm"), // all
INST(&V::arm_BIC_rsr, "BIC (rsr)", "cccc0001110Snnnnddddssss0rr1mmmm"), // all
INST(&V::arm_CMN_imm, "CMN (imm)", "cccc00110111nnnn0000rrrrvvvvvvvv"), // all
INST(&V::arm_CMN_reg, "CMN (reg)", "cccc00010111nnnn0000vvvvvrr0mmmm"), // all
INST(&V::arm_CMN_rsr, "CMN (rsr)", "cccc00010111nnnn0000ssss0rr1mmmm"), // all
INST(&V::arm_CMP_imm, "CMP (imm)", "cccc00110101nnnn0000rrrrvvvvvvvv"), // all
INST(&V::arm_CMP_reg, "CMP (reg)", "cccc00010101nnnn0000vvvvvrr0mmmm"), // all
INST(&V::arm_CMP_rsr, "CMP (rsr)", "cccc00010101nnnn0000ssss0rr1mmmm"), // all
INST(&V::arm_EOR_imm, "EOR (imm)", "cccc0010001Snnnnddddrrrrvvvvvvvv"), // all
INST(&V::arm_EOR_reg, "EOR (reg)", "cccc0000001Snnnnddddvvvvvrr0mmmm"), // all
INST(&V::arm_EOR_rsr, "EOR (rsr)", "cccc0000001Snnnnddddssss0rr1mmmm"), // all
INST(&V::arm_MOV_imm, "MOV (imm)", "cccc0011101S0000ddddrrrrvvvvvvvv"), // all
INST(&V::arm_MOV_reg, "MOV (reg)", "cccc0001101S0000ddddvvvvvrr0mmmm"), // all
INST(&V::arm_MOV_rsr, "MOV (rsr)", "cccc0001101S0000ddddssss0rr1mmmm"), // all
INST(&V::arm_MVN_imm, "MVN (imm)", "cccc0011111S0000ddddrrrrvvvvvvvv"), // all
INST(&V::arm_MVN_reg, "MVN (reg)", "cccc0001111S0000ddddvvvvvrr0mmmm"), // all
INST(&V::arm_MVN_rsr, "MVN (rsr)", "cccc0001111S0000ddddssss0rr1mmmm"), // all
INST(&V::arm_ORR_imm, "ORR (imm)", "cccc0011100Snnnnddddrrrrvvvvvvvv"), // all
INST(&V::arm_ORR_reg, "ORR (reg)", "cccc0001100Snnnnddddvvvvvrr0mmmm"), // all
INST(&V::arm_ORR_rsr, "ORR (rsr)", "cccc0001100Snnnnddddssss0rr1mmmm"), // all
INST(&V::arm_RSB_imm, "RSB (imm)", "cccc0010011Snnnnddddrrrrvvvvvvvv"), // all
INST(&V::arm_RSB_reg, "RSB (reg)", "cccc0000011Snnnnddddvvvvvrr0mmmm"), // all
INST(&V::arm_RSB_rsr, "RSB (rsr)", "cccc0000011Snnnnddddssss0rr1mmmm"), // all
INST(&V::arm_RSC_imm, "RSC (imm)", "cccc0010111Snnnnddddrrrrvvvvvvvv"), // all
INST(&V::arm_RSC_reg, "RSC (reg)", "cccc0000111Snnnnddddvvvvvrr0mmmm"), // all
INST(&V::arm_RSC_rsr, "RSC (rsr)", "cccc0000111Snnnnddddssss0rr1mmmm"), // all
INST(&V::arm_SBC_imm, "SBC (imm)", "cccc0010110Snnnnddddrrrrvvvvvvvv"), // all
INST(&V::arm_SBC_reg, "SBC (reg)", "cccc0000110Snnnnddddvvvvvrr0mmmm"), // all
INST(&V::arm_SBC_rsr, "SBC (rsr)", "cccc0000110Snnnnddddssss0rr1mmmm"), // all
INST(&V::arm_SUB_imm, "SUB (imm)", "cccc0010010Snnnnddddrrrrvvvvvvvv"), // all
INST(&V::arm_SUB_reg, "SUB (reg)", "cccc0000010Snnnnddddvvvvvrr0mmmm"), // all
INST(&V::arm_SUB_rsr, "SUB (rsr)", "cccc0000010Snnnnddddssss0rr1mmmm"), // all
INST(&V::arm_TEQ_imm, "TEQ (imm)", "cccc00110011nnnn0000rrrrvvvvvvvv"), // all
INST(&V::arm_TEQ_reg, "TEQ (reg)", "cccc00010011nnnn0000vvvvvrr0mmmm"), // all
INST(&V::arm_TEQ_rsr, "TEQ (rsr)", "cccc00010011nnnn0000ssss0rr1mmmm"), // all
INST(&V::arm_TST_imm, "TST (imm)", "cccc00110001nnnn0000rrrrvvvvvvvv"), // all
INST(&V::arm_TST_reg, "TST (reg)", "cccc00010001nnnn0000vvvvvrr0mmmm"), // all
INST(&V::arm_TST_rsr, "TST (rsr)", "cccc00010001nnnn0000ssss0rr1mmmm"), // all
// Exception Generating instructions
INST(&V::arm_BKPT, "BKPT", "cccc00010010vvvvvvvvvvvv0111vvvv"), // v5
INST(&V::arm_SVC, "SVC", "cccc1111vvvvvvvvvvvvvvvvvvvvvvvv"), // all
INST(&V::arm_UDF, "UDF", "111001111111------------1111----"), // all
// Extension instructions
INST(&V::arm_SXTB, "SXTB", "cccc011010101111ddddrr000111mmmm"), // v6
INST(&V::arm_SXTB16, "SXTB16", "cccc011010001111ddddrr000111mmmm"), // v6
INST(&V::arm_SXTH, "SXTH", "cccc011010111111ddddrr000111mmmm"), // v6
INST(&V::arm_SXTAB, "SXTAB", "cccc01101010nnnnddddrr000111mmmm"), // v6
INST(&V::arm_SXTAB16, "SXTAB16", "cccc01101000nnnnddddrr000111mmmm"), // v6
INST(&V::arm_SXTAH, "SXTAH", "cccc01101011nnnnddddrr000111mmmm"), // v6
INST(&V::arm_UXTB, "UXTB", "cccc011011101111ddddrr000111mmmm"), // v6
INST(&V::arm_UXTB16, "UXTB16", "cccc011011001111ddddrr000111mmmm"), // v6
INST(&V::arm_UXTH, "UXTH", "cccc011011111111ddddrr000111mmmm"), // v6
INST(&V::arm_UXTAB, "UXTAB", "cccc01101110nnnnddddrr000111mmmm"), // v6
INST(&V::arm_UXTAB16, "UXTAB16", "cccc01101100nnnnddddrr000111mmmm"), // v6
INST(&V::arm_UXTAH, "UXTAH", "cccc01101111nnnnddddrr000111mmmm"), // v6
// Hint instructions
INST(&V::arm_PLD, "PLD", "111101-1-101----1111------------"), // v5E; different on v7
INST(&V::arm_SEV, "SEV", "----0011001000001111000000000100"), // v6K
INST(&V::arm_WFE, "WFE", "----0011001000001111000000000010"), // v6K
INST(&V::arm_WFI, "WFI", "----0011001000001111000000000011"), // v6K
INST(&V::arm_YIELD, "YIELD", "----0011001000001111000000000001"), // v6K
// Synchronization Primitive instructions
INST(&V::arm_CLREX, "CLREX", "11110101011111111111000000011111"), // v6K
INST(&V::arm_LDREX, "LDREX", "cccc00011001nnnndddd111110011111"), // v6
INST(&V::arm_LDREXB, "LDREXB", "cccc00011101nnnndddd111110011111"), // v6K
INST(&V::arm_LDREXD, "LDREXD", "cccc00011011nnnndddd111110011111"), // v6K
INST(&V::arm_LDREXH, "LDREXH", "cccc00011111nnnndddd111110011111"), // v6K
INST(&V::arm_STREX, "STREX", "cccc00011000nnnndddd11111001mmmm"), // v6
INST(&V::arm_STREXB, "STREXB", "cccc00011100nnnndddd11111001mmmm"), // v6K
INST(&V::arm_STREXD, "STREXD", "cccc00011010nnnndddd11111001mmmm"), // v6K
INST(&V::arm_STREXH, "STREXH", "cccc00011110nnnndddd11111001mmmm"), // v6K
INST(&V::arm_SWP, "SWP", "cccc00010000nnnntttt00001001uuuu"), // v2S (v6: Deprecated)
INST(&V::arm_SWPB, "SWPB", "cccc00010100nnnntttt00001001uuuu"), // v2S (v6: Deprecated)
// Load/Store instructions
INST(&V::arm_LDRBT, "LDRBT (A1)", "----0100-111--------------------"),
INST(&V::arm_LDRBT, "LDRBT (A2)", "----0110-111---------------0----"),
INST(&V::arm_LDRHT, "LDRHT (A1)", "----0000-111------------1011----"),
INST(&V::arm_LDRHT, "LDRHT (A2)", "----0000-011--------00001011----"),
INST(&V::arm_LDRSBT, "LDRSBT (A1)", "----0000-111------------1101----"),
INST(&V::arm_LDRSBT, "LDRSBT (A2)", "----0000-011--------00001101----"),
INST(&V::arm_LDRSHT, "LDRSHT (A1)", "----0000-111------------1111----"),
INST(&V::arm_LDRSHT, "LDRSHT (A2)", "----0000-011--------00001111----"),
INST(&V::arm_LDRT, "LDRT (A1)", "----0100-011--------------------"),
INST(&V::arm_LDRT, "LDRT (A2)", "----0110-011---------------0----"),
INST(&V::arm_STRBT, "STRBT (A1)", "----0100-110--------------------"),
INST(&V::arm_STRBT, "STRBT (A2)", "----0110-110---------------0----"),
INST(&V::arm_STRHT, "STRHT (A1)", "----0000-110------------1011----"),
INST(&V::arm_STRHT, "STRHT (A2)", "----0000-010--------00001011----"),
INST(&V::arm_STRT, "STRT (A1)", "----0100-010--------------------"),
INST(&V::arm_STRT, "STRT (A2)", "----0110-010---------------0----"),
INST(&V::arm_LDR_lit, "LDR (lit)", "cccc0101u0011111ttttvvvvvvvvvvvv"),
INST(&V::arm_LDR_imm, "LDR (imm)", "cccc010pu0w1nnnnttttvvvvvvvvvvvv"),
INST(&V::arm_LDR_reg, "LDR (reg)", "cccc011pu0w1nnnnttttvvvvvrr0mmmm"),
INST(&V::arm_LDRB_lit, "LDRB (lit)", "cccc0101u1011111ttttvvvvvvvvvvvv"),
INST(&V::arm_LDRB_imm, "LDRB (imm)", "cccc010pu1w1nnnnttttvvvvvvvvvvvv"),
INST(&V::arm_LDRB_reg, "LDRB (reg)", "cccc011pu1w1nnnnttttvvvvvrr0mmmm"),
INST(&V::arm_LDRD_lit, "LDRD (lit)", "cccc0001u1001111ttttvvvv1101vvvv"),
INST(&V::arm_LDRD_imm, "LDRD (imm)", "cccc000pu1w0nnnnttttvvvv1101vvvv"), // v5E
INST(&V::arm_LDRD_reg, "LDRD (reg)", "cccc000pu0w0nnnntttt00001101mmmm"), // v5E
INST(&V::arm_LDRH_lit, "LDRH (lit)", "cccc000pu1w11111ttttvvvv1011vvvv"),
INST(&V::arm_LDRH_imm, "LDRH (imm)", "cccc000pu1w1nnnnttttvvvv1011vvvv"),
INST(&V::arm_LDRH_reg, "LDRH (reg)", "cccc000pu0w1nnnntttt00001011mmmm"),
INST(&V::arm_LDRSB_lit, "LDRSB (lit)", "cccc0001u1011111ttttvvvv1101vvvv"),
INST(&V::arm_LDRSB_imm, "LDRSB (imm)", "cccc000pu1w1nnnnttttvvvv1101vvvv"),
INST(&V::arm_LDRSB_reg, "LDRSB (reg)", "cccc000pu0w1nnnntttt00001101mmmm"),
INST(&V::arm_LDRSH_lit, "LDRSH (lit)", "cccc0001u1011111ttttvvvv1111vvvv"),
INST(&V::arm_LDRSH_imm, "LDRSH (imm)", "cccc000pu1w1nnnnttttvvvv1111vvvv"),
INST(&V::arm_LDRSH_reg, "LDRSH (reg)", "cccc000pu0w1nnnntttt00001111mmmm"),
INST(&V::arm_STR_imm, "STR (imm)", "cccc010pu0w0nnnnttttvvvvvvvvvvvv"),
INST(&V::arm_STR_reg, "STR (reg)", "cccc011pu0w0nnnnttttvvvvvrr0mmmm"),
INST(&V::arm_STRB_imm, "STRB (imm)", "cccc010pu1w0nnnnttttvvvvvvvvvvvv"),
INST(&V::arm_STRB_reg, "STRB (reg)", "cccc011pu1w0nnnnttttvvvvvrr0mmmm"),
INST(&V::arm_STRD_imm, "STRD (imm)", "cccc000pu1w0nnnnttttvvvv1111vvvv"), // v5E
INST(&V::arm_STRD_reg, "STRD (reg)", "cccc000pu0w0nnnntttt00001111mmmm"), // v5E
INST(&V::arm_STRH_imm, "STRH (imm)", "cccc000pu1w0nnnnttttvvvv1011vvvv"),
INST(&V::arm_STRH_reg, "STRH (reg)", "cccc000pu0w0nnnntttt00001011mmmm"),
// Load/Store Multiple instructions
INST(&V::arm_LDM, "LDM", "cccc100010w1nnnnxxxxxxxxxxxxxxxx"), // all
INST(&V::arm_LDMDA, "LDMDA", "cccc100000w1nnnnxxxxxxxxxxxxxxxx"), // all
INST(&V::arm_LDMDB, "LDMDB", "cccc100100w1nnnnxxxxxxxxxxxxxxxx"), // all
INST(&V::arm_LDMIB, "LDMIB", "cccc100110w1nnnnxxxxxxxxxxxxxxxx"), // all
INST(&V::arm_LDM_usr, "LDM (usr reg)", "----100--101--------------------"), // all
INST(&V::arm_LDM_eret, "LDM (exce ret)", "----100--1-1----1---------------"), // all
INST(&V::arm_STM, "STM", "cccc100010w0nnnnxxxxxxxxxxxxxxxx"), // all
INST(&V::arm_STMDA, "STMDA", "cccc100000w0nnnnxxxxxxxxxxxxxxxx"), // all
INST(&V::arm_STMDB, "STMDB", "cccc100100w0nnnnxxxxxxxxxxxxxxxx"), // all
INST(&V::arm_STMIB, "STMIB", "cccc100110w0nnnnxxxxxxxxxxxxxxxx"), // all
INST(&V::arm_STM_usr, "STM (usr reg)", "----100--100--------------------"), // all
// Miscellaneous instructions
INST(&V::arm_CLZ, "CLZ", "cccc000101101111dddd11110001mmmm"), // v5
INST(&V::arm_NOP, "NOP", "----0011001000001111000000000000"), // v6K
INST(&V::arm_SEL, "SEL", "cccc01101000nnnndddd11111011mmmm"), // v6
// Unsigned Sum of Absolute Differences instructions
INST(&V::arm_USAD8, "USAD8", "cccc01111000dddd1111mmmm0001nnnn"), // v6
INST(&V::arm_USADA8, "USADA8", "cccc01111000ddddaaaammmm0001nnnn"), // v6
// Packing instructions
INST(&V::arm_PKHBT, "PKHBT", "cccc01101000nnnnddddvvvvv001mmmm"), // v6K
INST(&V::arm_PKHTB, "PKHTB", "cccc01101000nnnnddddvvvvv101mmmm"), // v6K
// Reversal instructions
INST(&V::arm_REV, "REV", "cccc011010111111dddd11110011mmmm"), // v6
INST(&V::arm_REV16, "REV16", "cccc011010111111dddd11111011mmmm"), // v6
INST(&V::arm_REVSH, "REVSH", "cccc011011111111dddd11111011mmmm"), // v6
// Saturation instructions
INST(&V::arm_SSAT, "SSAT", "cccc0110101vvvvvddddvvvvvr01nnnn"), // v6
INST(&V::arm_SSAT16, "SSAT16", "cccc01101010vvvvdddd11110011nnnn"), // v6
INST(&V::arm_USAT, "USAT", "cccc0110111vvvvvddddvvvvvr01nnnn"), // v6
INST(&V::arm_USAT16, "USAT16", "cccc01101110vvvvdddd11110011nnnn"), // v6
// Multiply (Normal) instructions
INST(&V::arm_MLA, "MLA", "cccc0000001Sddddaaaammmm1001nnnn"), // v2
INST(&V::arm_MUL, "MUL", "cccc0000000Sdddd0000mmmm1001nnnn"), // v2
// Multiply (Long) instructions
INST(&V::arm_SMLAL, "SMLAL", "cccc0000111Sddddaaaammmm1001nnnn"), // v3M
INST(&V::arm_SMULL, "SMULL", "cccc0000110Sddddaaaammmm1001nnnn"), // v3M
INST(&V::arm_UMAAL, "UMAAL", "cccc00000100ddddaaaammmm1001nnnn"), // v6
INST(&V::arm_UMLAL, "UMLAL", "cccc0000101Sddddaaaammmm1001nnnn"), // v3M
INST(&V::arm_UMULL, "UMULL", "cccc0000100Sddddaaaammmm1001nnnn"), // v3M
// Multiply (Halfword) instructions
INST(&V::arm_SMLALxy, "SMLALXY", "cccc00010100ddddaaaammmm1xy0nnnn"), // v5xP
INST(&V::arm_SMLAxy, "SMLAXY", "cccc00010000ddddaaaammmm1xy0nnnn"), // v5xP
INST(&V::arm_SMULxy, "SMULXY", "cccc00010110dddd0000mmmm1xy0nnnn"), // v5xP
// Multiply (Word by Halfword) instructions
INST(&V::arm_SMLAWy, "SMLAWY", "cccc00010010ddddaaaammmm1y00nnnn"), // v5xP
INST(&V::arm_SMULWy, "SMULWY", "cccc00010010dddd0000mmmm1y10nnnn"), // v5xP
// Multiply (Most Significant Word) instructions
INST(&V::arm_SMMUL, "SMMUL", "cccc01110101dddd1111mmmm00R1nnnn"), // v6
INST(&V::arm_SMMLA, "SMMLA", "cccc01110101ddddaaaammmm00R1nnnn"), // v6
INST(&V::arm_SMMLS, "SMMLS", "cccc01110101ddddaaaammmm11R1nnnn"), // v6
// Multiply (Dual) instructions
INST(&V::arm_SMLAD, "SMLAD", "cccc01110000ddddaaaammmm00M1nnnn"), // v6
INST(&V::arm_SMLALD, "SMLALD", "cccc01110100ddddaaaammmm00M1nnnn"), // v6
INST(&V::arm_SMLSD, "SMLSD", "cccc01110000ddddaaaammmm01M1nnnn"), // v6
INST(&V::arm_SMLSLD, "SMLSLD", "cccc01110100ddddaaaammmm01M1nnnn"), // v6
INST(&V::arm_SMUAD, "SMUAD", "cccc01110000dddd1111mmmm00M1nnnn"), // v6
INST(&V::arm_SMUSD, "SMUSD", "cccc01110000dddd1111mmmm01M1nnnn"), // v6
// Parallel Add/Subtract (Modulo) instructions
INST(&V::arm_SADD8, "SADD8", "cccc01100001nnnndddd11111001mmmm"), // v6
INST(&V::arm_SADD16, "SADD16", "cccc01100001nnnndddd11110001mmmm"), // v6
INST(&V::arm_SASX, "SASX", "cccc01100001nnnndddd11110011mmmm"), // v6
INST(&V::arm_SSAX, "SSAX", "cccc01100001nnnndddd11110101mmmm"), // v6
INST(&V::arm_SSUB8, "SSUB8", "cccc01100001nnnndddd11111111mmmm"), // v6
INST(&V::arm_SSUB16, "SSUB16", "cccc01100001nnnndddd11110111mmmm"), // v6
INST(&V::arm_UADD8, "UADD8", "cccc01100101nnnndddd11111001mmmm"), // v6
INST(&V::arm_UADD16, "UADD16", "cccc01100101nnnndddd11110001mmmm"), // v6
INST(&V::arm_UASX, "UASX", "cccc01100101nnnndddd11110011mmmm"), // v6
INST(&V::arm_USAX, "USAX", "cccc01100101nnnndddd11110101mmmm"), // v6
INST(&V::arm_USUB8, "USUB8", "cccc01100101nnnndddd11111111mmmm"), // v6
INST(&V::arm_USUB16, "USUB16", "cccc01100101nnnndddd11110111mmmm"), // v6
// Parallel Add/Subtract (Saturating) instructions
INST(&V::arm_QADD8, "QADD8", "cccc01100010nnnndddd11111001mmmm"), // v6
INST(&V::arm_QADD16, "QADD16", "cccc01100010nnnndddd11110001mmmm"), // v6
INST(&V::arm_QASX, "QASX", "cccc01100010nnnndddd11110011mmmm"), // v6
INST(&V::arm_QSAX, "QSAX", "cccc01100010nnnndddd11110101mmmm"), // v6
INST(&V::arm_QSUB8, "QSUB8", "cccc01100010nnnndddd11111111mmmm"), // v6
INST(&V::arm_QSUB16, "QSUB16", "cccc01100010nnnndddd11110111mmmm"), // v6
INST(&V::arm_UQADD8, "UQADD8", "cccc01100110nnnndddd11111001mmmm"), // v6
INST(&V::arm_UQADD16, "UQADD16", "cccc01100110nnnndddd11110001mmmm"), // v6
INST(&V::arm_UQASX, "UQASX", "cccc01100110nnnndddd11110011mmmm"), // v6
INST(&V::arm_UQSAX, "UQSAX", "cccc01100110nnnndddd11110101mmmm"), // v6
INST(&V::arm_UQSUB8, "UQSUB8", "cccc01100110nnnndddd11111111mmmm"), // v6
INST(&V::arm_UQSUB16, "UQSUB16", "cccc01100110nnnndddd11110111mmmm"), // v6
// Parallel Add/Subtract (Halving) instructions
INST(&V::arm_SHADD8, "SHADD8", "cccc01100011nnnndddd11111001mmmm"), // v6
INST(&V::arm_SHADD16, "SHADD16", "cccc01100011nnnndddd11110001mmmm"), // v6
INST(&V::arm_SHASX, "SHASX", "cccc01100011nnnndddd11110011mmmm"), // v6
INST(&V::arm_SHSAX, "SHSAX", "cccc01100011nnnndddd11110101mmmm"), // v6
INST(&V::arm_SHSUB8, "SHSUB8", "cccc01100011nnnndddd11111111mmmm"), // v6
INST(&V::arm_SHSUB16, "SHSUB16", "cccc01100011nnnndddd11110111mmmm"), // v6
INST(&V::arm_UHADD8, "UHADD8", "cccc01100111nnnndddd11111001mmmm"), // v6
INST(&V::arm_UHADD16, "UHADD16", "cccc01100111nnnndddd11110001mmmm"), // v6
INST(&V::arm_UHASX, "UHASX", "cccc01100111nnnndddd11110011mmmm"), // v6
INST(&V::arm_UHSAX, "UHSAX", "cccc01100111nnnndddd11110101mmmm"), // v6
INST(&V::arm_UHSUB8, "UHSUB8", "cccc01100111nnnndddd11111111mmmm"), // v6
INST(&V::arm_UHSUB16, "UHSUB16", "cccc01100111nnnndddd11110111mmmm"), // v6
// Saturated Add/Subtract instructions
INST(&V::arm_QADD, "QADD", "cccc00010000nnnndddd00000101mmmm"), // v5xP
INST(&V::arm_QSUB, "QSUB", "cccc00010010nnnndddd00000101mmmm"), // v5xP
INST(&V::arm_QDADD, "QDADD", "cccc00010100nnnndddd00000101mmmm"), // v5xP
INST(&V::arm_QDSUB, "QDSUB", "cccc00010110nnnndddd00000101mmmm"), // v5xP
// Status Register Access instructions
INST(&V::arm_CPS, "CPS", "111100010000---00000000---0-----"), // v6
INST(&V::arm_SETEND, "SETEND", "1111000100000001000000e000000000"), // v6
INST(&V::arm_MRS, "MRS", "cccc000100001111dddd000000000000"), // v3
INST(&V::arm_MSR_imm, "MSR (imm)", "cccc00110010mm001111rrrrvvvvvvvv"), // v3
INST(&V::arm_MSR_reg, "MSR (reg)", "cccc00010010mm00111100000000nnnn"), // v3
INST(&V::arm_RFE, "RFE", "1111100--0-1----0000101000000000"), // v6
INST(&V::arm_SRS, "SRS", "0000011--0-00000000000000001----"), // v6
#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>
boost::optional<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() ? boost::optional<const ArmMatcher<V>&>(*iter) : boost::none;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,127 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include <vector>
#include <boost/optional.hpp>
#include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic {
namespace A32 {
template <typename Visitor>
using Thumb16Matcher = Decoder::Matcher<Visitor, u16>;
template<typename V>
boost::optional<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
// 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_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() ? boost::optional<const Thumb16Matcher<V>&>(*iter) : boost::none;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,47 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include <vector>
#include <boost/optional.hpp>
#include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic {
namespace A32 {
template <typename Visitor>
using Thumb32Matcher = Decoder::Matcher<Visitor, u32>;
template<typename V>
boost::optional<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)
// 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() ? boost::optional<const Thumb32Matcher<V>&>(*iter) : boost::none;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,92 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2032 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include <vector>
#include <boost/optional.hpp>
#include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic {
namespace A32 {
template <typename Visitor>
using VFP2Matcher = Decoder::Matcher<Visitor, u32>;
template<typename V>
boost::optional<const VFP2Matcher<V>&> DecodeVFP2(u32 instruction) {
static const std::vector<VFP2Matcher<V>> table = {
#define INST(fn, name, bitstring) Decoder::detail::detail<VFP2Matcher<V>>::GetMatcher(fn, name, bitstring)
// cccc1110________----101-__-0----
// Floating-point three-register data processing instructions
INST(&V::vfp2_VMLA, "VMLA", "cccc11100D00nnnndddd101zN0M0mmmm"),
INST(&V::vfp2_VMLS, "VMLS", "cccc11100D00nnnndddd101zN1M0mmmm"),
INST(&V::vfp2_VNMLS, "VNMLS", "cccc11100D01nnnndddd101zN0M0mmmm"),
INST(&V::vfp2_VNMLA, "VNMLA", "cccc11100D01nnnndddd101zN1M0mmmm"),
INST(&V::vfp2_VMUL, "VMUL", "cccc11100D10nnnndddd101zN0M0mmmm"),
INST(&V::vfp2_VNMUL, "VNMUL", "cccc11100D10nnnndddd101zN1M0mmmm"),
INST(&V::vfp2_VADD, "VADD", "cccc11100D11nnnndddd101zN0M0mmmm"),
INST(&V::vfp2_VSUB, "VSUB", "cccc11100D11nnnndddd101zN1M0mmmm"),
INST(&V::vfp2_VDIV, "VDIV", "cccc11101D00nnnndddd101zN0M0mmmm"),
// Floating-point move instructions
INST(&V::vfp2_VMOV_u32_f64, "VMOV (core to f64)", "cccc11100000ddddtttt1011D0010000"),
INST(&V::vfp2_VMOV_f64_u32, "VMOV (f64 to core)", "cccc11100001nnnntttt1011N0010000"),
INST(&V::vfp2_VMOV_u32_f32, "VMOV (core to f32)", "cccc11100000nnnntttt1010N0010000"),
INST(&V::vfp2_VMOV_f32_u32, "VMOV (f32 to core)", "cccc11100001nnnntttt1010N0010000"),
INST(&V::vfp2_VMOV_2u32_2f32, "VMOV (2xcore to 2xf32)", "cccc11000100uuuutttt101000M1mmmm"),
INST(&V::vfp2_VMOV_2f32_2u32, "VMOV (2xf32 to 2xcore)", "cccc11000101uuuutttt101000M1mmmm"),
INST(&V::vfp2_VMOV_2u32_f64, "VMOV (2xcore to f64)", "cccc11000100uuuutttt101100M1mmmm"),
INST(&V::vfp2_VMOV_f64_2u32, "VMOV (f64 to 2xcore)", "cccc11000101uuuutttt101100M1mmmm"),
INST(&V::vfp2_VMOV_reg, "VMOV (reg)", "cccc11101D110000dddd101z01M0mmmm"),
// Floating-point other instructions
INST(&V::vfp2_VABS, "VABS", "cccc11101D110000dddd101z11M0mmmm"),
INST(&V::vfp2_VNEG, "VNEG", "cccc11101D110001dddd101z01M0mmmm"),
INST(&V::vfp2_VSQRT, "VSQRT", "cccc11101D110001dddd101z11M0mmmm"),
INST(&V::vfp2_VCVT_f_to_f, "VCVT (f32<->f64)", "cccc11101D110111dddd101z11M0mmmm"),
INST(&V::vfp2_VCVT_to_float, "VCVT (to float)", "cccc11101D111000dddd101zs1M0mmmm"),
INST(&V::vfp2_VCVT_to_u32, "VCVT (to u32)", "cccc11101D111100dddd101zr1M0mmmm"),
INST(&V::vfp2_VCVT_to_s32, "VCVT (to s32)", "cccc11101D111101dddd101zr1M0mmmm"),
INST(&V::vfp2_VCMP, "VCMP", "cccc11101D110100dddd101zE1M0mmmm"),
INST(&V::vfp2_VCMP_zero, "VCMP (with zero)", "cccc11101D110101dddd101zE1000000"),
// Floating-point system register access
INST(&V::vfp2_VMSR, "VMSR", "cccc111011100001tttt101000010000"),
INST(&V::vfp2_VMRS, "VMRS", "cccc111011110001tttt101000010000"),
// Extension register load-store instructions
INST(&V::vfp2_VPUSH, "VPUSH", "cccc11010D101101dddd101zvvvvvvvv"),
INST(&V::vfp2_VPOP, "VPOP", "cccc11001D111101dddd101zvvvvvvvv"),
INST(&V::vfp2_VLDR, "VLDR", "cccc1101UD01nnnndddd101zvvvvvvvv"),
INST(&V::vfp2_VSTR, "VSTR", "cccc1101UD00nnnndddd101zvvvvvvvv"),
INST(&V::vfp2_VSTM_a1, "VSTM (A1)", "cccc110puDw0nnnndddd1011vvvvvvvv"),
INST(&V::vfp2_VSTM_a2, "VSTM (A2)", "cccc110puDw0nnnndddd1010vvvvvvvv"),
INST(&V::vfp2_VLDM_a1, "VLDM (A1)", "cccc110puDw1nnnndddd1011vvvvvvvv"),
INST(&V::vfp2_VLDM_a2, "VLDM (A2)", "cccc110puDw1nnnndddd1010vvvvvvvv"),
#undef INST
};
if ((instruction & 0xF0000000) == 0xF0000000)
return boost::none; // Don't try matching any unconditional instructions.
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() ? boost::optional<const VFP2Matcher<V>&>(*iter) : boost::none;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,20 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include <string>
#include "common/common_types.h"
namespace Dynarmic {
namespace A32 {
std::string DisassembleArm(u32 instruction);
std::string DisassembleThumb16(u16 instruction);
} // namespace A32
} // namespace Dynarmic

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,336 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include <cstdlib>
#include <string>
#include <fmt/format.h>
#include <fmt/ostream.h>
#include "common/bit_util.h"
#include "common/string_util.h"
#include "frontend/A32/decoder/thumb16.h"
#include "frontend/A32/disassembler/disassembler.h"
#include "frontend/A32/types.h"
namespace Dynarmic {
namespace A32 {
class DisassemblerVisitor {
public:
using instruction_return_type = std::string;
std::string thumb16_LSL_imm(Imm5 imm5, Reg m, Reg d) {
return fmt::format("lsls {}, {}, #{}", d, m, imm5);
}
std::string thumb16_LSR_imm(Imm5 imm5, Reg m, Reg d) {
return fmt::format("lsrs {}, {}, #{}", d, m, imm5 != 0 ? imm5 : 32);
}
std::string thumb16_ASR_imm(Imm5 imm5, Reg m, Reg d) {
return fmt::format("asrs {}, {}, #{}", d, m, imm5 != 0 ? imm5 : 32);
}
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(Imm3 imm3, Reg n, Reg d) {
return fmt::format("adds {}, {}, #{}", d, n, imm3);
}
std::string thumb16_SUB_imm_t1(Imm3 imm3, Reg n, Reg d) {
return fmt::format("subs {}, {}, #{}", d, n, imm3);
}
std::string thumb16_MOV_imm(Reg d, Imm8 imm8) {
return fmt::format("movs {}, #{}", d, imm8);
}
std::string thumb16_CMP_imm(Reg n, Imm8 imm8) {
return fmt::format("cmp {}, #{}", n, imm8);
}
std::string thumb16_ADD_imm_t2(Reg d_n, Imm8 imm8) {
return fmt::format("adds {}, #{}", d_n, imm8);
}
std::string thumb16_SUB_imm_t2(Reg d_n, Imm8 imm8) {
return fmt::format("subs {}, #{}", d_n, imm8);
}
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) {
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) {
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) {
Reg d = d_hi ? (d_lo + 8) : d_lo;
return fmt::format("mov {}, {}", d, m);
}
std::string thumb16_LDR_literal(Reg t, Imm8 imm8) {
u32 imm32 = imm8 << 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(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5 << 2;
return fmt::format("str {}, [{}, #{}]", t, n, imm32);
}
std::string thumb16_LDR_imm_t1(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5 << 2;
return fmt::format("ldr {}, [{}, #{}]", t, n, imm32);
}
std::string thumb16_STRB_imm(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5;
return fmt::format("strb {}, [{}, #{}]", t, n, imm32);
}
std::string thumb16_LDRB_imm(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5;
return fmt::format("ldrb {}, [{}, #{}]", t, n, imm32);
}
std::string thumb16_STRH_imm(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5 << 1;
return fmt::format("strh {}, [{}, #{}]", t, n, imm32);
}
std::string thumb16_LDRH_imm(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5 << 1;
return fmt::format("ldrh {}, [{}, #{}]", t, n, imm32);
}
std::string thumb16_STR_imm_t2(Reg t, Imm5 imm5) {
u32 imm32 = imm5 << 2;
return fmt::format("str {}, [sp, #{}]", t, imm32);
}
std::string thumb16_LDR_imm_t2(Reg t, Imm5 imm5) {
u32 imm32 = imm5 << 2;
return fmt::format("ldr {}, [sp, #{}]", t, imm32);
}
std::string thumb16_ADR(Reg d, Imm8 imm8) {
u32 imm32 = imm8 << 2;
return fmt::format("adr {}, +#{}", d, imm32);
}
std::string thumb16_ADD_sp_t1(Reg d, Imm8 imm8) {
u32 imm32 = imm8 << 2;
return fmt::format("add {}, sp, #{}", d, imm32);
}
std::string thumb16_ADD_sp_t2(Imm7 imm7) {
u32 imm32 = imm7 << 2;
return fmt::format("add sp, sp, #{}", imm32);
}
std::string thumb16_SUB_sp(Imm7 imm7) {
u32 imm32 = imm7 << 2;
return fmt::format("sub sp, sp, #{}", imm32);
}
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_STMIA(Reg n, RegList reg_list) {
return fmt::format("stm {}!, {{{}}}", n, RegListToString(reg_list));
}
std::string thumb16_LDMIA(Reg n, RegList reg_list) {
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_UDF() {
return fmt::format("udf");
}
std::string thumb16_SVC(Imm8 imm8) {
return fmt::format("svc #{}", imm8);
}
std::string thumb16_B_t1(Cond cond, Imm8 imm8) {
s32 imm32 = Common::SignExtend<9, s32>(imm8 << 1) + 4;
return fmt::format("b{} {}#{}", CondToString(cond), Common::SignToChar(imm32), abs(imm32));
}
std::string thumb16_B_t2(Imm11 imm11) {
s32 imm32 = Common::SignExtend<12, s32>(imm11 << 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->call(visitor, instruction);
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,24 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include <ostream>
#include <fmt/format.h>
#include "frontend/A32/location_descriptor.h"
namespace Dynarmic {
namespace A32 {
std::ostream& operator<<(std::ostream& o, const LocationDescriptor& loc) {
o << fmt::format("{{{},{},{},{}}}",
loc.PC(),
loc.TFlag() ? "T" : "!T",
loc.EFlag() ? "E" : "!E",
loc.FPSCR().Value());
return o;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,127 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#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/ir/location_descriptor.h"
namespace Dynarmic {
namespace 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 = 0x00000220;
static constexpr u32 FPSCR_MODE_MASK = 0x03F79F00;
LocationDescriptor(u32 arm_pc, PSR cpsr, FPSCR fpscr)
: arm_pc(arm_pc), cpsr(cpsr.Value() & CPSR_MODE_MASK), fpscr(fpscr.Value() & FPSCR_MODE_MASK) {}
/*implict*/ LocationDescriptor(const IR::LocationDescriptor& o) {
arm_pc = o.value >> 32;
cpsr.T(o.value & 1);
cpsr.E(o.value & 2);
fpscr = o.value & FPSCR_MODE_MASK;
}
u32 PC() const { return arm_pc; }
bool TFlag() const { return cpsr.T(); }
bool EFlag() const { return cpsr.E(); }
PSR CPSR() const { return cpsr; }
FPSCR FPSCR() const { return fpscr; }
bool operator == (const LocationDescriptor& o) const {
return std::tie(arm_pc, cpsr, fpscr) == std::tie(o.arm_pc, o.cpsr, o.fpscr);
}
bool operator != (const LocationDescriptor& o) const {
return !operator==(o);
}
LocationDescriptor SetPC(u32 new_arm_pc) const {
return LocationDescriptor(new_arm_pc, cpsr, fpscr);
}
LocationDescriptor AdvancePC(int amount) const {
return LocationDescriptor(static_cast<u32>(arm_pc + amount), cpsr, fpscr);
}
LocationDescriptor SetTFlag(bool new_tflag) const {
PSR new_cpsr = cpsr;
new_cpsr.T(new_tflag);
return LocationDescriptor(arm_pc, new_cpsr, fpscr);
}
LocationDescriptor SetEFlag(bool new_eflag) const {
PSR new_cpsr = cpsr;
new_cpsr.E(new_eflag);
return LocationDescriptor(arm_pc, new_cpsr, fpscr);
}
LocationDescriptor SetFPSCR(u32 new_fpscr) const {
return LocationDescriptor(arm_pc, cpsr, A32::FPSCR{new_fpscr & FPSCR_MODE_MASK});
}
u64 UniqueHash() const {
// This value MUST BE UNIQUE.
// This calculation has to match up with EmitX64::EmitTerminalPopRSBHint
u64 pc_u64 = u64(arm_pc) << 32;
u64 fpscr_u64 = u64(fpscr.Value());
u64 t_u64 = cpsr.T() ? 1 : 0;
u64 e_u64 = cpsr.E() ? 2 : 0;
return pc_u64 | fpscr_u64 | t_u64 | e_u64;
}
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.
};
/**
* 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 A32
} // namespace Dynarmic
namespace std {
template <>
struct less<Dynarmic::A32::LocationDescriptor> {
bool operator()(const Dynarmic::A32::LocationDescriptor& x, const Dynarmic::A32::LocationDescriptor& y) const {
return x.UniqueHash() < y.UniqueHash();
}
};
template <>
struct hash<Dynarmic::A32::LocationDescriptor> {
size_t operator()(const Dynarmic::A32::LocationDescriptor& x) const {
return std::hash<u64>()(x.UniqueHash());
}
};
} // namespace std

View File

@@ -0,0 +1,22 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "frontend/A32/location_descriptor.h"
#include "frontend/A32/translate/translate.h"
#include "frontend/ir/basic_block.h"
namespace Dynarmic {
namespace A32 {
IR::Block TranslateArm(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code);
IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code);
IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code) {
return (descriptor.TFlag() ? TranslateThumb : TranslateArm)(descriptor, memory_read_code);
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,31 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include "common/common_types.h"
namespace Dynarmic {
namespace IR {
class Block;
} // namespace IR
namespace A32 {
class LocationDescriptor;
using MemoryReadCodeFuncType = u32 (*)(u32 vaddr);
/**
* 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.
* @return A translated basic block in the intermediate representation.
*/
IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code);
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,160 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include <algorithm>
#include "common/assert.h"
#include "frontend/A32/decoder/arm.h"
#include "frontend/A32/decoder/vfp2.h"
#include "frontend/A32/translate/translate.h"
#include "frontend/A32/translate/translate_arm/translate_arm.h"
#include "frontend/A32/types.h"
#include "frontend/ir/basic_block.h"
#include "frontend/ir/location_descriptor.h"
namespace Dynarmic {
namespace A32 {
static bool CondCanContinue(ConditionalState cond_state, const IR::A32IREmitter& 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) {
ArmTranslatorVisitor visitor{descriptor};
bool should_continue = true;
while (should_continue && CondCanContinue(visitor.cond_state, visitor.ir)) {
const u32 arm_pc = visitor.ir.current_location.PC();
const u32 arm_instruction = memory_read_code(arm_pc);
if (auto vfp_decoder = DecodeVFP2<ArmTranslatorVisitor>(arm_instruction)) {
should_continue = vfp_decoder->call(visitor, arm_instruction);
} else if (auto decoder = DecodeArm<ArmTranslatorVisitor>(arm_instruction)) {
should_continue = decoder->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);
visitor.ir.block.CycleCount()++;
}
if (visitor.cond_state == ConditionalState::Translating || visitor.cond_state == ConditionalState::Trailing) {
if (should_continue) {
visitor.ir.SetTerm(IR::Term::LinkBlockFast{visitor.ir.current_location});
}
}
ASSERT_MSG(visitor.ir.block.HasTerminal(), "Terminal has not been set");
visitor.ir.block.SetEndLocation(visitor.ir.current_location);
return std::move(visitor.ir.block);
}
bool ArmTranslatorVisitor::ConditionPassed(Cond cond) {
ASSERT_MSG(cond_state != ConditionalState::Break,
"This should never happen. We requested a break but that wasn't honored.");
ASSERT_MSG(cond != Cond::NV, "NV conditional is obsolete");
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() = 1;
return true;
}
bool ArmTranslatorVisitor::InterpretThisInstruction() {
ir.SetTerm(IR::Term::Interpret(ir.current_location));
return false;
}
bool ArmTranslatorVisitor::UnpredictableInstruction() {
ASSERT_MSG(false, "UNPREDICTABLE");
return false;
}
IR::A32IREmitter::ResultAndCarry ArmTranslatorVisitor::EmitImmShift(IR::Value value, ShiftType type, Imm5 imm5, IR::Value carry_in) {
switch (type) {
case ShiftType::LSL:
return ir.LogicalShiftLeft(value, ir.Imm8(imm5), carry_in);
case ShiftType::LSR:
imm5 = imm5 ? imm5 : 32;
return ir.LogicalShiftRight(value, ir.Imm8(imm5), carry_in);
case ShiftType::ASR:
imm5 = imm5 ? imm5 : 32;
return ir.ArithmeticShiftRight(value, ir.Imm8(imm5), carry_in);
case ShiftType::ROR:
if (imm5)
return ir.RotateRight(value, ir.Imm8(imm5), carry_in);
else
return ir.RotateRightExtended(value, carry_in);
}
ASSERT_MSG(false, "Unreachable");
return {};
}
IR::A32IREmitter::ResultAndCarry ArmTranslatorVisitor::EmitRegShift(IR::Value value, ShiftType type, IR::Value amount, IR::Value 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);
}
ASSERT_MSG(false, "Unreachable");
return {};
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,81 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "common/bit_util.h"
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
bool ArmTranslatorVisitor::arm_B(Cond cond, Imm24 imm24) {
u32 imm32 = Common::SignExtend<26, u32>(imm24 << 2) + 8;
// B <label>
if (ConditionPassed(cond)) {
auto new_location = ir.current_location.AdvancePC(imm32);
ir.SetTerm(IR::Term::LinkBlock{ new_location });
return false;
}
return true;
}
bool ArmTranslatorVisitor::arm_BL(Cond cond, Imm24 imm24) {
u32 imm32 = Common::SignExtend<26, u32>(imm24 << 2) + 8;
// BL <label>
if (ConditionPassed(cond)) {
ir.PushRSB(ir.current_location.AdvancePC(4));
ir.SetRegister(Reg::LR, ir.Imm32(ir.current_location.PC() + 4));
auto new_location = ir.current_location.AdvancePC(imm32);
ir.SetTerm(IR::Term::LinkBlock{ new_location });
return false;
}
return true;
}
bool ArmTranslatorVisitor::arm_BLX_imm(bool H, Imm24 imm24) {
u32 imm32 = Common::SignExtend<26, u32>((imm24 << 2)) + (H ? 2 : 0) + 8;
// BLX <label>
ir.PushRSB(ir.current_location.AdvancePC(4));
ir.SetRegister(Reg::LR, ir.Imm32(ir.current_location.PC() + 4));
auto new_location = ir.current_location.AdvancePC(imm32).SetTFlag(true);
ir.SetTerm(IR::Term::LinkBlock{ new_location });
return false;
}
bool ArmTranslatorVisitor::arm_BLX_reg(Cond cond, Reg m) {
if (m == Reg::PC)
return UnpredictableInstruction();
// BLX <Rm>
if (ConditionPassed(cond)) {
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::ReturnToDispatch{});
return false;
}
return true;
}
bool ArmTranslatorVisitor::arm_BX(Cond cond, Reg m) {
// BX <Rm>
if (ConditionPassed(cond)) {
ir.BXWritePC(ir.GetRegister(m));
if (m == Reg::R14)
ir.SetTerm(IR::Term::PopRSBHint{});
else
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
return true;
}
bool ArmTranslatorVisitor::arm_BXJ(Cond cond, Reg m) {
// Jazelle not supported
return arm_BX(cond, m);
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,150 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
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;
// CDP{2} <coproc_no>, #<opc1>, <CRd>, <CRn>, <CRm>, #<opc2>
if (two || ConditionPassed(cond)) {
ir.CoprocInternalOperation(coproc_no, two, opc1, CRd, CRn, CRm, opc2);
}
return true;
}
bool ArmTranslatorVisitor::arm_LDC(Cond cond, bool p, bool u, bool d, bool w, Reg n, CoprocReg CRd, size_t coproc_no, Imm8 imm8) {
if (!p && !u && !d && !w)
return arm_UDF();
if ((coproc_no & 0b1110) == 0b1010)
return arm_UDF();
const bool two = cond == Cond::NV;
const u32 imm32 = static_cast<u8>(imm8) << 2;
const bool index = p;
const bool add = u;
const bool wback = w;
const bool has_option = !p && !w && u;
// LDC{2}{L} <coproc_no>, <CRd>, [<Rn>, #+/-<imm32>]{!}
// LDC{2}{L} <coproc_no>, <CRd>, [<Rn>], #+/-<imm32>
// LDC{2}{L} <coproc_no>, <CRd>, [<Rn>], <imm8>
if (two || ConditionPassed(cond)) {
auto reg_n = ir.GetRegister(n);
auto offset_address = add ? ir.Add(reg_n, ir.Imm32(imm32)) : ir.Sub(reg_n, ir.Imm32(imm32));
auto address = index ? offset_address : reg_n;
ir.CoprocLoadWords(coproc_no, two, d, CRd, address, has_option, imm8);
if (wback) {
ir.SetRegister(n, offset_address);
}
}
return true;
}
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;
// MCR{2} <coproc_no>, #<opc1>, <Rt>, <CRn>, <CRm>, #<opc2>
if (two || ConditionPassed(cond)) {
ir.CoprocSendOneWord(coproc_no, two, opc1, CRn, CRm, opc2, ir.GetRegister(t));
}
return true;
}
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;
// MCRR{2} <coproc_no>, #<opc>, <Rt>, <Rt2>, <CRm>
if (two || ConditionPassed(cond)) {
ir.CoprocSendTwoWords(coproc_no, two, opc, CRm, ir.GetRegister(t), ir.GetRegister(t2));
}
return true;
}
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;
// MRC{2} <coproc_no>, #<opc1>, <Rt>, <CRn>, <CRm>, #<opc2>
if (two || ConditionPassed(cond)) {
auto word = ir.CoprocGetOneWord(coproc_no, two, opc1, CRn, CRm, opc2);
if (t != Reg::PC) {
ir.SetRegister(t, word);
} else {
auto new_cpsr_nzcv = ir.And(word, ir.Imm32(0xF0000000));
ir.SetCpsrNZCV(new_cpsr_nzcv);
}
}
return true;
}
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;
// MRRC{2} <coproc_no>, #<opc>, <Rt>, <Rt2>, <CRm>
if (two || ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_STC(Cond cond, bool p, bool u, bool d, bool w, Reg n, CoprocReg CRd, size_t coproc_no, Imm8 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;
const u32 imm32 = static_cast<u8>(imm8) << 2;
const bool index = p;
const bool add = u;
const bool wback = w;
const bool has_option = !p && !w && u;
// STC{2}{L} <coproc>, <CRd>, [<Rn>, #+/-<imm32>]{!}
// STC{2}{L} <coproc>, <CRd>, [<Rn>], #+/-<imm32>
// STC{2}{L} <coproc>, <CRd>, [<Rn>], <imm8>
if (two || ConditionPassed(cond)) {
auto reg_n = ir.GetRegister(n);
auto offset_address = add ? ir.Add(reg_n, ir.Imm32(imm32)) : ir.Sub(reg_n, ir.Imm32(imm32));
auto address = index ? offset_address : reg_n;
ir.CoprocStoreWords(coproc_no, two, d, CRd, address, has_option, imm8);
if (wback) {
ir.SetRegister(n, offset_address);
}
}
return true;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,893 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
bool ArmTranslatorVisitor::arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
// ADC{S}<c> <Rd>, <Rn>, #<imm>
if (ConditionPassed(cond)) {
u32 imm32 = ArmExpandImm(rotate, imm8);
auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.GetCFlag());
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_ADC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag());
auto result = ir.AddWithCarry(ir.GetRegister(n), shifted.result, ir.GetCFlag());
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_ADC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC || s == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.AddWithCarry(ir.GetRegister(n), shifted.result, ir.GetCFlag());
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_ADD_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
u32 imm32 = ArmExpandImm(rotate, imm8);
auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(0));
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_ADD_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag());
auto result = ir.AddWithCarry(ir.GetRegister(n), shifted.result, ir.Imm1(0));
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_ADD_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC || s == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.AddWithCarry(ir.GetRegister(n), shifted.result, ir.Imm1(0));
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_AND_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
auto imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag());
auto result = ir.And(ir.GetRegister(n), ir.Imm32(imm_carry.imm32));
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_AND_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto carry_in = ir.GetCFlag();
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in);
auto result = ir.And(ir.GetRegister(n), shifted.result);
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_AND_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC || s == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.And(ir.GetRegister(n), shifted.result);
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_BIC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
auto imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag());
auto result = ir.And(ir.GetRegister(n), ir.Not(ir.Imm32(imm_carry.imm32)));
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_BIC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto carry_in = ir.GetCFlag();
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in);
auto result = ir.And(ir.GetRegister(n), ir.Not(shifted.result));
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_BIC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC || s == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.And(ir.GetRegister(n), ir.Not(shifted.result));
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_CMN_imm(Cond cond, Reg n, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
u32 imm32 = ArmExpandImm(rotate, imm8);
auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), 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;
}
bool ArmTranslatorVisitor::arm_CMN_reg(Cond cond, Reg n, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag());
auto result = ir.AddWithCarry(ir.GetRegister(n), shifted.result, 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;
}
bool ArmTranslatorVisitor::arm_CMN_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m) {
if (n == Reg::PC || m == Reg::PC || s == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.AddWithCarry(ir.GetRegister(n), shifted.result, 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;
}
bool ArmTranslatorVisitor::arm_CMP_imm(Cond cond, Reg n, int rotate, Imm8 imm8) {
// CMP<c> <Rn>, #<imm>
if (ConditionPassed(cond)) {
u32 imm32 = ArmExpandImm(rotate, imm8);
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;
}
bool ArmTranslatorVisitor::arm_CMP_reg(Cond cond, Reg n, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag());
auto result = ir.SubWithCarry(ir.GetRegister(n), shifted.result, 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;
}
bool ArmTranslatorVisitor::arm_CMP_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m) {
if (n == Reg::PC || m == Reg::PC || s == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.SubWithCarry(ir.GetRegister(n), shifted.result, 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;
}
bool ArmTranslatorVisitor::arm_EOR_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
auto imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag());
auto result = ir.Eor(ir.GetRegister(n), ir.Imm32(imm_carry.imm32));
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_EOR_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto carry_in = ir.GetCFlag();
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in);
auto result = ir.Eor(ir.GetRegister(n), shifted.result);
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_EOR_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC || s == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.Eor(ir.GetRegister(n), shifted.result);
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_MOV_imm(Cond cond, bool S, Reg d, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
auto imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag());
auto result = ir.Imm32(imm_carry.imm32);
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_MOV_reg(Cond cond, bool S, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto carry_in = ir.GetCFlag();
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in);
auto result = shifted.result;
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_MOV_rsr(Cond cond, bool S, Reg d, Reg s, ShiftType shift, Reg m) {
if (d == Reg::PC || m == Reg::PC || s == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = shifted.result;
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_MVN_imm(Cond cond, bool S, Reg d, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
auto imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag());
auto result = ir.Not(ir.Imm32(imm_carry.imm32));
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_MVN_reg(Cond cond, bool S, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto carry_in = ir.GetCFlag();
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in);
auto result = ir.Not(shifted.result);
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_MVN_rsr(Cond cond, bool S, Reg d, Reg s, ShiftType shift, Reg m) {
if (d == Reg::PC || m == Reg::PC || s == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.Not(shifted.result);
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_ORR_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
auto imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag());
auto result = ir.Or(ir.GetRegister(n), ir.Imm32(imm_carry.imm32));
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_ORR_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto carry_in = ir.GetCFlag();
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in);
auto result = ir.Or(ir.GetRegister(n), shifted.result);
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_ORR_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
if (n == Reg::PC || m == Reg::PC || s == Reg::PC || d == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.Or(ir.GetRegister(n), shifted.result);
ir.SetRegister(d, result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_RSB_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
u32 imm32 = ArmExpandImm(rotate, imm8);
auto result = ir.SubWithCarry(ir.Imm32(imm32), ir.GetRegister(n), ir.Imm1(1));
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_RSB_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag());
auto result = ir.SubWithCarry(shifted.result, ir.GetRegister(n), ir.Imm1(1));
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_RSB_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
if (n == Reg::PC || m == Reg::PC || s == Reg::PC || d == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.SubWithCarry(shifted.result, ir.GetRegister(n), ir.Imm1(1));
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_RSC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
u32 imm32 = ArmExpandImm(rotate, imm8);
auto result = ir.SubWithCarry(ir.Imm32(imm32), ir.GetRegister(n), ir.GetCFlag());
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_RSC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag());
auto result = ir.SubWithCarry(shifted.result, ir.GetRegister(n), ir.GetCFlag());
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_RSC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
if (n == Reg::PC || m == Reg::PC || s == Reg::PC || d == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.SubWithCarry(shifted.result, ir.GetRegister(n), ir.GetCFlag());
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_SBC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
u32 imm32 = ArmExpandImm(rotate, imm8);
auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.GetCFlag());
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_SBC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag());
auto result = ir.SubWithCarry(ir.GetRegister(n), shifted.result, ir.GetCFlag());
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_SBC_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
if (n == Reg::PC || m == Reg::PC || s == Reg::PC || d == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.SubWithCarry(ir.GetRegister(n), shifted.result, ir.GetCFlag());
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_SUB_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
u32 imm32 = ArmExpandImm(rotate, imm8);
auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1));
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_SUB_reg(Cond cond, bool S, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, ir.GetCFlag());
auto result = ir.SubWithCarry(ir.GetRegister(n), shifted.result, ir.Imm1(1));
if (d == Reg::PC) {
ASSERT(!S);
ir.ALUWritePC(result.result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_SUB_rsr(Cond cond, bool S, Reg n, Reg d, Reg s, ShiftType shift, Reg m) {
if (n == Reg::PC || m == Reg::PC || s == Reg::PC || d == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.SubWithCarry(ir.GetRegister(n), shifted.result, ir.Imm1(1));
ir.SetRegister(d, result.result);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(result.result));
ir.SetZFlag(ir.IsZero(result.result));
ir.SetCFlag(result.carry);
ir.SetVFlag(result.overflow);
}
}
return true;
}
bool ArmTranslatorVisitor::arm_TEQ_imm(Cond cond, Reg n, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
auto imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag());
auto result = ir.Eor(ir.GetRegister(n), ir.Imm32(imm_carry.imm32));
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
}
return true;
}
bool ArmTranslatorVisitor::arm_TEQ_reg(Cond cond, Reg n, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto carry_in = ir.GetCFlag();
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in);
auto result = ir.Eor(ir.GetRegister(n), shifted.result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
return true;
}
bool ArmTranslatorVisitor::arm_TEQ_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m) {
if (n == Reg::PC || m == Reg::PC || s == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.Eor(ir.GetRegister(n), shifted.result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
return true;
}
bool ArmTranslatorVisitor::arm_TST_imm(Cond cond, Reg n, int rotate, Imm8 imm8) {
if (ConditionPassed(cond)) {
auto imm_carry = ArmExpandImm_C(rotate, imm8, ir.GetCFlag());
auto result = ir.And(ir.GetRegister(n), ir.Imm32(imm_carry.imm32));
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(imm_carry.carry);
}
return true;
}
bool ArmTranslatorVisitor::arm_TST_reg(Cond cond, Reg n, Imm5 imm5, ShiftType shift, Reg m) {
if (ConditionPassed(cond)) {
auto carry_in = ir.GetCFlag();
auto shifted = EmitImmShift(ir.GetRegister(m), shift, imm5, carry_in);
auto result = ir.And(ir.GetRegister(n), shifted.result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
return true;
}
bool ArmTranslatorVisitor::arm_TST_rsr(Cond cond, Reg n, Reg s, ShiftType shift, Reg m) {
if (n == Reg::PC || m == Reg::PC || s == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(s));
auto carry_in = ir.GetCFlag();
auto shifted = EmitRegShift(ir.GetRegister(m), shift, shift_n, carry_in);
auto result = ir.And(ir.GetRegister(n), shifted.result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
ir.SetCFlag(shifted.carry);
}
return true;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,34 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
bool ArmTranslatorVisitor::arm_BKPT(Cond /*cond*/, Imm12 /*imm12*/, Imm4 /*imm4*/) {
return InterpretThisInstruction();
}
bool ArmTranslatorVisitor::arm_SVC(Cond cond, Imm24 imm24) {
u32 imm32 = imm24;
// SVC<c> #<imm24>
if (ConditionPassed(cond)) {
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;
}
return true;
}
bool ArmTranslatorVisitor::arm_UDF() {
return InterpretThisInstruction();
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,185 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
static IR::Value Rotate(IR::A32IREmitter& ir, Reg m, SignExtendRotation rotate) {
const u8 rotate_by = static_cast<u8>(static_cast<size_t>(rotate) * 8);
return ir.RotateRight(ir.GetRegister(m), ir.Imm8(rotate_by), ir.Imm1(0)).result;
}
bool ArmTranslatorVisitor::arm_SXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// SXTAB <Rd>, <Rn>, <Rm>, <rotate>
if (ConditionPassed(cond)) {
auto rotated = Rotate(ir, m, rotate);
auto reg_n = ir.GetRegister(n);
auto result = ir.Add(reg_n, ir.SignExtendByteToWord(ir.LeastSignificantByte(rotated)));
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_SXTAB16(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// SXTAB16 <Rd>, <Rn>, <Rm>, <rotate>
if (ConditionPassed(cond)) {
auto rotated = Rotate(ir, m, rotate);
auto low_byte = ir.And(rotated, ir.Imm32(0x00FF00FF));
auto sign_bit = ir.And(rotated, ir.Imm32(0x00800080));
auto addend = ir.Or(low_byte, ir.Mul(sign_bit, ir.Imm32(0x1FE)));
auto result = ir.PackedAddU16(addend, ir.GetRegister(n)).result;
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_SXTAH(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// SXTAH <Rd>, <Rn>, <Rm>, <rotate>
if (ConditionPassed(cond)) {
auto rotated = Rotate(ir, m, rotate);
auto reg_n = ir.GetRegister(n);
auto result = ir.Add(reg_n, ir.SignExtendHalfToWord(ir.LeastSignificantHalf(rotated)));
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_SXTB(Cond cond, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// SXTB <Rd>, <Rm>, <rotate>
if (ConditionPassed(cond)) {
auto rotated = Rotate(ir, m, rotate);
auto result = ir.SignExtendByteToWord(ir.LeastSignificantByte(rotated));
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_SXTB16(Cond cond, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// SXTB16 <Rd>, <Rm>, <rotate>
if (ConditionPassed(cond)) {
auto rotated = Rotate(ir, m, rotate);
auto low_byte = ir.And(rotated, ir.Imm32(0x00FF00FF));
auto sign_bit = ir.And(rotated, ir.Imm32(0x00800080));
auto result = ir.Or(low_byte, ir.Mul(sign_bit, ir.Imm32(0x1FE)));
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_SXTH(Cond cond, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// SXTH <Rd>, <Rm>, <rotate>
if (ConditionPassed(cond)) {
auto rotated = Rotate(ir, m, rotate);
auto result = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(rotated));
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_UXTAB(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// UXTAB <Rd>, <Rn>, <Rm>, <rotate>
if (ConditionPassed(cond)) {
auto rotated = Rotate(ir, m, rotate);
auto reg_n = ir.GetRegister(n);
auto result = ir.Add(reg_n, ir.ZeroExtendByteToWord(ir.LeastSignificantByte(rotated)));
ir.SetRegister(d, result);
}
return true;
}
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();
// UXTAB16 <Rd>, <Rn>, <Rm>, <rotate>
if (ConditionPassed(cond)) {
auto rotated = Rotate(ir, m, rotate);
auto result = ir.And(rotated, ir.Imm32(0x00FF00FF));
auto reg_n = ir.GetRegister(n);
result = ir.PackedAddU16(reg_n, result).result;
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_UXTAH(Cond cond, Reg n, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// UXTAH <Rd>, <Rn>, <Rm>, <rotate>
if (ConditionPassed(cond)) {
auto rotated = Rotate(ir, m, rotate);
auto reg_n = ir.GetRegister(n);
auto result = ir.Add(reg_n, ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(rotated)));
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_UXTB(Cond cond, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// UXTB <Rd>, <Rm>, <rotate>
if (ConditionPassed(cond)) {
auto rotated = Rotate(ir, m, rotate);
auto result = ir.ZeroExtendByteToWord(ir.LeastSignificantByte(rotated));
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_UXTB16(Cond cond, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// UXTB16 <Rd>, <Rm>, <rotate>
if (ConditionPassed(cond)) {
auto rotated = Rotate(ir, m, rotate);
auto result = ir.And(rotated, ir.Imm32(0x00FF00FF));
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_UXTH(Cond cond, Reg d, SignExtendRotation rotate, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// UXTH <Rd>, <Rm>, <rotate>
if (ConditionPassed(cond)) {
auto rotated = Rotate(ir, m, rotate);
auto result = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(rotated));
ir.SetRegister(d, result);
}
return true;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,759 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
bool ArmTranslatorVisitor::arm_LDRBT() {
ASSERT_MSG(false, "System instructions unimplemented");
}
bool ArmTranslatorVisitor::arm_LDRHT() {
ASSERT_MSG(false, "System instructions unimplemented");
}
bool ArmTranslatorVisitor::arm_LDRSBT() {
ASSERT_MSG(false, "System instructions unimplemented");
}
bool ArmTranslatorVisitor::arm_LDRSHT() {
ASSERT_MSG(false, "System instructions unimplemented");
}
bool ArmTranslatorVisitor::arm_LDRT() {
ASSERT_MSG(false, "System instructions unimplemented");
}
bool ArmTranslatorVisitor::arm_STRBT() {
ASSERT_MSG(false, "System instructions unimplemented");
}
bool ArmTranslatorVisitor::arm_STRHT() {
ASSERT_MSG(false, "System instructions unimplemented");
}
bool ArmTranslatorVisitor::arm_STRT() {
ASSERT_MSG(false, "System instructions unimplemented");
}
static IR::Value GetAddress(IR::A32IREmitter& ir, bool P, bool U, bool W, Reg n, IR::Value offset) {
const bool index = P;
const bool add = U;
const bool wback = !P || W;
const auto offset_addr = add ? ir.Add(ir.GetRegister(n), offset) : ir.Sub(ir.GetRegister(n), offset);
const auto address = index ? offset_addr : ir.GetRegister(n);
if (wback) {
ir.SetRegister(n, offset_addr);
}
return address;
}
bool ArmTranslatorVisitor::arm_LDR_lit(Cond cond, bool U, Reg t, Imm12 imm12) {
const bool add = U;
// LDR <Rt>, [PC, #+/-<imm>]
if (ConditionPassed(cond)) {
const u32 base = ir.AlignPC(4);
const u32 address = add ? (base + imm12) : (base - imm12);
const auto data = ir.ReadMemory32(ir.Imm32(address));
if (t == Reg::PC) {
ir.LoadWritePC(data);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
ir.SetRegister(t, data);
}
return true;
}
bool ArmTranslatorVisitor::arm_LDR_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm12 imm12) {
if (n == Reg::PC)
return UnpredictableInstruction();
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if ((!P || W) && n == t)
return UnpredictableInstruction();
const u32 imm32 = imm12;
// LDR <Rt>, [<Rn>, #+/-<imm>]{!}
// LDR <Rt>, [<Rn>], #+/-<imm>
if (ConditionPassed(cond)) {
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::ReturnToDispatch{});
return false;
}
ir.SetRegister(t, data);
}
return true;
}
bool ArmTranslatorVisitor::arm_LDR_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm5 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();
// LDR <Rt>, [<Rn>, #+/-<Rm>]{!}
// LDR <Rt>, [<Rn>], #+/-<Rm>
if (ConditionPassed(cond)) {
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::ReturnToDispatch{});
return false;
}
ir.SetRegister(t, data);
}
return true;
}
bool ArmTranslatorVisitor::arm_LDRB_lit(Cond cond, bool U, Reg t, Imm12 imm12) {
if (t == Reg::PC)
return UnpredictableInstruction();
const u32 imm32 = imm12;
const bool add = U;
// LDRB <Rt>, [PC, #+/-<imm>]
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_LDRB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm12 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();
const u32 imm32 = imm12;
// LDRB <Rt>, [<Rn>, #+/-<imm>]{!}
// LDRB <Rt>, [<Rn>], #+/-<imm>
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_LDRB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm5 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();
// LDRB <Rt>, [<Rn>, #+/-<Rm>]{!}
// LDRB <Rt>, [<Rn>], #+/-<Rm>
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_LDRD_lit(Cond cond, bool U, Reg t, Imm4 imm8a, Imm4 imm8b) {
if (RegNumber(t) % 2 == 1)
return UnpredictableInstruction();
if (t+1 == Reg::PC)
return UnpredictableInstruction();
const Reg t2 = t+1;
const u32 imm32 = (imm8a << 4) | imm8b;
const bool add = U;
// LDRD <Rt>, <Rt2>, [PC, #+/-<imm>]
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_LDRD_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm4 imm8a, Imm4 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();
const Reg t2 = t+1;
const u32 imm32 = (imm8a << 4) | imm8b;
// LDRD <Rt>, [<Rn>, #+/-<imm>]{!}
// LDRD <Rt>, [<Rn>], #+/-<imm>
if (ConditionPassed(cond)) {
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;
}
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();
const Reg t2 = t+1;
// LDRD <Rt>, [<Rn>, #+/-<Rm>]{!}
// LDRD <Rt>, [<Rn>], #+/-<Rm>
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_LDRH_lit(Cond cond, bool P, bool U, bool W, Reg t, Imm4 imm8a, Imm4 imm8b) {
ASSERT_MSG(!(!P && W), "T form of instruction unimplemented");
if (P == W)
return UnpredictableInstruction();
if (t == Reg::PC)
return UnpredictableInstruction();
const u32 imm32 = (imm8a << 4) | imm8b;
const bool add = U;
// LDRH <Rt>, [PC, #-/+<imm>]
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_LDRH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm4 imm8a, Imm4 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();
const u32 imm32 = (imm8a << 4) | imm8b;
// LDRH <Rt>, [<Rn>, #+/-<imm>]{!}
// LDRH <Rt>, [<Rn>], #+/-<imm>
if (ConditionPassed(cond)) {
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;
}
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();
// LDRH <Rt>, [<Rn>, #+/-<Rm>]{!}
// LDRH <Rt>, [<Rn>], #+/-<Rm>
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_LDRSB_lit(Cond cond, bool U, Reg t, Imm4 imm8a, Imm4 imm8b) {
if (t == Reg::PC)
return UnpredictableInstruction();
const u32 imm32 = (imm8a << 4) | imm8b;
const bool add = U;
// LDRSB <Rt>, [PC, #+/-<imm>]
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_LDRSB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm4 imm8a, Imm4 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();
const u32 imm32 = (imm8a << 4) | imm8b;
// LDRSB <Rt>, [<Rn>, #+/-<imm>]{!}
// LDRSB <Rt>, [<Rn>], #+/-<imm>
if (ConditionPassed(cond)) {
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;
}
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();
// LDRSB <Rt>, [<Rn>, #+/-<Rm>]{!}
// LDRSB <Rt>, [<Rn>], #+/-<Rm>
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_LDRSH_lit(Cond cond, bool U, Reg t, Imm4 imm8a, Imm4 imm8b) {
if (t == Reg::PC)
return UnpredictableInstruction();
const u32 imm32 = (imm8a << 4) | imm8b;
const bool add = U;
// LDRSH <Rt>, [PC, #-/+<imm>]
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_LDRSH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm4 imm8a, Imm4 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();
const u32 imm32 = (imm8a << 4) | imm8b;
// LDRSH <Rt>, [<Rn>, #+/-<imm>]{!}
// LDRSH <Rt>, [<Rn>], #+/-<imm>
if (ConditionPassed(cond)) {
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;
}
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();
// LDRSH <Rt>, [<Rn>, #+/-<Rm>]{!}
// LDRSH <Rt>, [<Rn>], #+/-<Rm>
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_STR_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm12 imm12) {
if (W && (n == Reg::PC || n == t))
return UnpredictableInstruction();
// STR <Rt>, [<Rn>, #+/-<imm>]{!}
// STR <Rt>, [<Rn>], #+/-<imm>
if (ConditionPassed(cond)) {
const auto offset = ir.Imm32(imm12);
const auto address = GetAddress(ir, P, U, W, n, offset);
ir.WriteMemory32(address, ir.GetRegister(t));
}
return true;
}
bool ArmTranslatorVisitor::arm_STR_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm5 imm5, ShiftType shift, Reg m) {
if (m == Reg::PC)
return UnpredictableInstruction();
if (W && (n == Reg::PC || n == t))
return UnpredictableInstruction();
// STR <Rt>, [<Rn>, #+/-<Rm>]{!}
// STR <Rt>, [<Rn>], #+/-<Rm>
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_STRB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm12 imm12) {
if (t == Reg::PC)
return UnpredictableInstruction();
if (W && (n == Reg::PC || n == t))
return UnpredictableInstruction();
// STRB <Rt>, [<Rn>, #+/-<imm>]{!}
// STRB <Rt>, [<Rn>], #+/-<imm>
if (ConditionPassed(cond)) {
const auto offset = ir.Imm32(imm12);
const auto address = GetAddress(ir, P, U, W, n, offset);
ir.WriteMemory8(address, ir.LeastSignificantByte(ir.GetRegister(t)));
}
return true;
}
bool ArmTranslatorVisitor::arm_STRB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm5 imm5, ShiftType shift, Reg m) {
if (t == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
if (W && (n == Reg::PC || n == t))
return UnpredictableInstruction();
// STRB <Rt>, [<Rn>, #+/-<Rm>]{!}
// STRB <Rt>, [<Rn>], #+/-<Rm>
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_STRD_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm4 imm8a, Imm4 imm8b) {
if (size_t(t) % 2 != 0)
return UnpredictableInstruction();
if (!P && W)
return UnpredictableInstruction();
const u32 imm32 = imm8a << 4 | imm8b;
const Reg t2 = t + 1;
if (W && (n == Reg::PC || n == t || n == t2))
return UnpredictableInstruction();
if (t2 == Reg::PC)
return UnpredictableInstruction();
// STRD <Rt>, [<Rn>, #+/-<imm>]{!}
// STRD <Rt>, [<Rn>], #+/-<imm>
if (ConditionPassed(cond)) {
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;
}
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 (W && (n == Reg::PC || n == t || n == t2))
return UnpredictableInstruction();
// STRD <Rt>, [<Rn>, #+/-<Rm>]{!}
// STRD <Rt>, [<Rn>], #+/-<Rm>
if (ConditionPassed(cond)) {
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;
}
bool ArmTranslatorVisitor::arm_STRH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm4 imm8a, Imm4 imm8b) {
if (t == Reg::PC)
return UnpredictableInstruction();
if (W && (n == Reg::PC || n == t))
return UnpredictableInstruction();
const u32 imm32 = imm8a << 4 | imm8b;
// STRH <Rt>, [<Rn>, #+/-<imm>]{!}
// STRH <Rt>, [<Rn>], #+/-<imm>
if (ConditionPassed(cond)) {
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;
}
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 (W && (n == Reg::PC || n == t))
return UnpredictableInstruction();
// STRH <Rt>, [<Rn>, #+/-<Rm>]{!}
// STRH <Rt>, [<Rn>], #+/-<Rm>
if (ConditionPassed(cond)) {
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(IR::A32IREmitter& ir, bool W, Reg n, RegList list, IR::Value start_address, IR::Value 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::ReturnToDispatch{});
return false;
}
return true;
}
bool ArmTranslatorVisitor::arm_LDM(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1)
return UnpredictableInstruction();
// LDM <Rn>{!}, <reg_list>
if (ConditionPassed(cond)) {
auto start_address = ir.GetRegister(n);
auto writeback_address = ir.Add(start_address, ir.Imm32(u32(Common::BitCount(list) * 4)));
return LDMHelper(ir, W, n, list, start_address, writeback_address);
}
return true;
}
bool ArmTranslatorVisitor::arm_LDMDA(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1)
return UnpredictableInstruction();
// LDMDA <Rn>{!}, <reg_list>
if (ConditionPassed(cond)) {
auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * Common::BitCount(list) - 4)));
auto writeback_address = ir.Sub(start_address, ir.Imm32(4));
return LDMHelper(ir, W, n, list, start_address, writeback_address);
}
return true;
}
bool ArmTranslatorVisitor::arm_LDMDB(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1)
return UnpredictableInstruction();
// LDMDB <Rn>{!}, <reg_list>
if (ConditionPassed(cond)) {
auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * Common::BitCount(list))));
auto writeback_address = start_address;
return LDMHelper(ir, W, n, list, start_address, writeback_address);
}
return true;
}
bool ArmTranslatorVisitor::arm_LDMIB(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1)
return UnpredictableInstruction();
// LDMIB <Rn>{!}, <reg_list>
if (ConditionPassed(cond)) {
auto start_address = ir.Add(ir.GetRegister(n), ir.Imm32(4));
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);
}
return true;
}
bool ArmTranslatorVisitor::arm_LDM_usr() {
return InterpretThisInstruction();
}
bool ArmTranslatorVisitor::arm_LDM_eret() {
return InterpretThisInstruction();
}
static bool STMHelper(IR::A32IREmitter& ir, bool W, Reg n, RegList list, IR::Value start_address, IR::Value 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;
}
bool ArmTranslatorVisitor::arm_STM(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1)
return UnpredictableInstruction();
// STM <Rn>{!}, <reg_list>
if (ConditionPassed(cond)) {
auto start_address = ir.GetRegister(n);
auto writeback_address = ir.Add(start_address, ir.Imm32(u32(Common::BitCount(list) * 4)));
return STMHelper(ir, W, n, list, start_address, writeback_address);
}
return true;
}
bool ArmTranslatorVisitor::arm_STMDA(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1)
return UnpredictableInstruction();
// STMDA <Rn>{!}, <reg_list>
if (ConditionPassed(cond)) {
auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * Common::BitCount(list) - 4)));
auto writeback_address = ir.Sub(start_address, ir.Imm32(4));
return STMHelper(ir, W, n, list, start_address, writeback_address);
}
return true;
}
bool ArmTranslatorVisitor::arm_STMDB(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1)
return UnpredictableInstruction();
// STMDB <Rn>{!}, <reg_list>
if (ConditionPassed(cond)) {
auto start_address = ir.Sub(ir.GetRegister(n), ir.Imm32(u32(4 * Common::BitCount(list))));
auto writeback_address = start_address;
return STMHelper(ir, W, n, list, start_address, writeback_address);
}
return true;
}
bool ArmTranslatorVisitor::arm_STMIB(Cond cond, bool W, Reg n, RegList list) {
if (n == Reg::PC || Common::BitCount(list) < 1)
return UnpredictableInstruction();
// STMIB <Rn>{!}, <reg_list>
if (ConditionPassed(cond)) {
auto start_address = ir.Add(ir.GetRegister(n), ir.Imm32(4));
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);
}
return true;
}
bool ArmTranslatorVisitor::arm_STM_usr() {
return InterpretThisInstruction();
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,36 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
bool ArmTranslatorVisitor::arm_CLZ(Cond cond, Reg d, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
ir.SetRegister(d, ir.CountLeadingZeros(ir.GetRegister(m)));
}
return true;
}
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)) {
auto to = ir.GetRegister(m);
auto from = ir.GetRegister(n);
auto result = ir.PackedSelect(ir.GetGEFlags(), to, from);
ir.SetRegister(d, result);
}
return true;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,434 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
// Multiply (Normal) instructions
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)) {
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;
}
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)) {
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;
}
// Multiply (Long) instructions
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)) {
auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
auto product = ir.Mul64(n64, m64);
auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
auto result = ir.Add64(product, addend);
auto lo = ir.LeastSignificantWord(result);
auto hi = ir.MostSignificantWord(result).result;
ir.SetRegister(dLo, lo);
ir.SetRegister(dHi, hi);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi));
ir.SetZFlag(ir.IsZero64(result));
}
}
return true;
}
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)) {
auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
auto result = ir.Mul64(n64, m64);
auto lo = ir.LeastSignificantWord(result);
auto hi = ir.MostSignificantWord(result).result;
ir.SetRegister(dLo, lo);
ir.SetRegister(dHi, hi);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi));
ir.SetZFlag(ir.IsZero64(result));
}
}
return true;
}
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)) {
auto lo64 = ir.ZeroExtendWordToLong(ir.GetRegister(dLo));
auto hi64 = ir.ZeroExtendWordToLong(ir.GetRegister(dHi));
auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m));
auto result = ir.Add64(ir.Add64(ir.Mul64(n64, m64), hi64), lo64);
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
ir.SetRegister(dHi, ir.MostSignificantWord(result).result);
}
return true;
}
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)) {
auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m));
auto result = ir.Add64(ir.Mul64(n64, m64), addend);
auto lo = ir.LeastSignificantWord(result);
auto hi = ir.MostSignificantWord(result).result;
ir.SetRegister(dLo, lo);
ir.SetRegister(dHi, hi);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi));
ir.SetZFlag(ir.IsZero64(result));
}
}
return true;
}
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)) {
auto n64 = ir.ZeroExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.ZeroExtendWordToLong(ir.GetRegister(m));
auto result = ir.Mul64(n64, m64);
auto lo = ir.LeastSignificantWord(result);
auto hi = ir.MostSignificantWord(result).result;
ir.SetRegister(dLo, lo);
ir.SetRegister(dHi, hi);
if (S) {
ir.SetNFlag(ir.MostSignificantBit(hi));
ir.SetZFlag(ir.IsZero64(result));
}
}
return true;
}
// Multiply (Halfword) instructions
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)) {
auto n32 = ir.GetRegister(n);
auto m32 = ir.GetRegister(m);
auto n16 = N ? ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
auto m16 = M ? ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
auto product = ir.SignExtendWordToLong(ir.Mul(n16, m16));
auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
auto result = ir.Add64(product, addend);
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
ir.SetRegister(dHi, ir.MostSignificantWord(result).result);
}
return true;
}
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)) {
auto n32 = ir.GetRegister(n);
auto m32 = ir.GetRegister(m);
auto n16 = N ? ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
auto m16 = M ? ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
auto product = ir.Mul(n16, m16);
auto result_overflow = ir.AddWithCarry(product, ir.GetRegister(a), ir.Imm1(0));
ir.SetRegister(d, result_overflow.result);
ir.OrQFlag(result_overflow.overflow);
}
return true;
}
bool 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)) {
auto n32 = ir.GetRegister(n);
auto m32 = ir.GetRegister(m);
auto n16 = N ? ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
auto m16 = M ? ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result
: ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
auto result = ir.Mul(n16, m16);
ir.SetRegister(d, result);
}
return true;
}
// Multiply (word by halfword) instructions
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)) {
auto n32 = ir.SignExtendWordToLong(ir.GetRegister(n));
auto m32 = ir.GetRegister(m);
if (M)
m32 = ir.LogicalShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
auto m16 = ir.LeastSignificantHalf(m32);
m16 = ir.SignExtendWordToLong(ir.SignExtendHalfToWord(m16));
auto product = ir.LeastSignificantWord(ir.LogicalShiftRight64(ir.Mul64(n32, m16), ir.Imm8(16)));
auto result_overflow = ir.AddWithCarry(product, ir.GetRegister(a), ir.Imm1(0));
ir.SetRegister(d, result_overflow.result);
ir.OrQFlag(result_overflow.overflow);
}
return true;
}
bool 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)) {
auto n32 = ir.SignExtendWordToLong(ir.GetRegister(n));
auto m32 = ir.GetRegister(m);
if (M)
m32 = ir.LogicalShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
auto m16 = ir.LeastSignificantHalf(m32);
m16 = ir.SignExtendWordToLong(ir.SignExtendHalfToWord(m16));
auto result = ir.LogicalShiftRight64(ir.Mul64(n32, m16), ir.Imm8(16));
ir.SetRegister(d, ir.LeastSignificantWord(result));
}
return true;
}
// Multiply (Most significant word) instructions
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)) {
auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
auto a64 = ir.Pack2x32To1x64(ir.Imm32(0), ir.GetRegister(a));
auto temp = ir.Add64(a64, ir.Mul64(n64, m64));
auto result_carry = ir.MostSignificantWord(temp);
auto result = result_carry.result;
if (R)
result = ir.AddWithCarry(result, ir.Imm32(0), result_carry.carry).result;
ir.SetRegister(d, result);
}
return true;
}
bool 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)) {
auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
auto a64 = ir.Pack2x32To1x64(ir.Imm32(0), ir.GetRegister(a));
auto temp = ir.Sub64(a64, ir.Mul64(n64, m64));
auto result_carry = ir.MostSignificantWord(temp);
auto result = result_carry.result;
if (R)
result = ir.AddWithCarry(result, ir.Imm32(0), result_carry.carry).result;
ir.SetRegister(d, result);
}
return true;
}
bool 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)) {
auto n64 = ir.SignExtendWordToLong(ir.GetRegister(n));
auto m64 = ir.SignExtendWordToLong(ir.GetRegister(m));
auto product = ir.Mul64(n64, m64);
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;
}
// Multiply (Dual) instructions
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)) {
auto n32 = ir.GetRegister(n);
auto m32 = ir.GetRegister(m);
auto n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
auto m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
auto n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result;
auto m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
if (M)
std::swap(m_lo, m_hi);
auto product_lo = ir.Mul(n_lo, m_lo);
auto product_hi = ir.Mul(n_hi, m_hi);
auto addend = ir.GetRegister(a);
auto result_overflow = ir.AddWithCarry(product_lo, product_hi, ir.Imm1(0));
ir.OrQFlag(result_overflow.overflow);
result_overflow = ir.AddWithCarry(result_overflow.result, addend, ir.Imm1(0));
ir.SetRegister(d, result_overflow.result);
ir.OrQFlag(result_overflow.overflow);
}
return true;
}
bool 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)) {
auto n32 = ir.GetRegister(n);
auto m32 = ir.GetRegister(m);
auto n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
auto m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
auto n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result;
auto m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
if (M)
std::swap(m_lo, m_hi);
auto product_lo = ir.SignExtendWordToLong(ir.Mul(n_lo, m_lo));
auto product_hi = ir.SignExtendWordToLong(ir.Mul(n_hi, m_hi));
auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
auto result = ir.Add64(ir.Add64(product_lo, product_hi), addend);
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
ir.SetRegister(dHi, ir.MostSignificantWord(result).result);
}
return true;
}
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)) {
auto n32 = ir.GetRegister(n);
auto m32 = ir.GetRegister(m);
auto n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
auto m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
auto n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result;
auto m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
if (M)
std::swap(m_lo, m_hi);
auto product_lo = ir.Mul(n_lo, m_lo);
auto product_hi = ir.Mul(n_hi, m_hi);
auto addend = ir.GetRegister(a);
auto result_overflow = ir.AddWithCarry(ir.Sub(product_lo, product_hi), addend, ir.Imm1(0));
ir.SetRegister(d, result_overflow.result);
ir.OrQFlag(result_overflow.overflow);
}
return true;
}
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)) {
auto n32 = ir.GetRegister(n);
auto m32 = ir.GetRegister(m);
auto n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
auto m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
auto n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result;
auto m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
if (M)
std::swap(m_lo, m_hi);
auto product_lo = ir.SignExtendWordToLong(ir.Mul(n_lo, m_lo));
auto product_hi = ir.SignExtendWordToLong(ir.Mul(n_hi, m_hi));
auto addend = ir.Pack2x32To1x64(ir.GetRegister(dLo), ir.GetRegister(dHi));
auto result = ir.Add64(ir.Sub64(product_lo, product_hi), addend);
ir.SetRegister(dLo, ir.LeastSignificantWord(result));
ir.SetRegister(dHi, ir.MostSignificantWord(result).result);
}
return true;
}
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)) {
auto n32 = ir.GetRegister(n);
auto m32 = ir.GetRegister(m);
auto n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
auto m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
auto n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result;
auto m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
if (M)
std::swap(m_lo, m_hi);
auto product_lo = ir.Mul(n_lo, m_lo);
auto product_hi = ir.Mul(n_hi, m_hi);
auto result_overflow = ir.AddWithCarry(product_lo, product_hi, ir.Imm1(0));
ir.SetRegister(d, result_overflow.result);
ir.OrQFlag(result_overflow.overflow);
}
return true;
}
bool 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)) {
auto n32 = ir.GetRegister(n);
auto m32 = ir.GetRegister(m);
auto n_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(n32));
auto m_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(m32));
auto n_hi = ir.ArithmeticShiftRight(n32, ir.Imm8(16), ir.Imm1(0)).result;
auto m_hi = ir.ArithmeticShiftRight(m32, ir.Imm8(16), ir.Imm1(0)).result;
if (M)
std::swap(m_lo, m_hi);
auto product_lo = ir.Mul(n_lo, m_lo);
auto product_hi = ir.Mul(n_hi, m_hi);
auto result = ir.Sub(product_lo, product_hi);
ir.SetRegister(d, result);
}
return true;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,41 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
bool ArmTranslatorVisitor::arm_PKHBT(Cond cond, Reg n, Reg d, Imm5 imm5, Reg m) {
if (n == Reg::PC || d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shifted = EmitImmShift(ir.GetRegister(m), ShiftType::LSL, imm5, ir.Imm1(false)).result;
auto lower_half = ir.And(ir.GetRegister(n), ir.Imm32(0x0000FFFF));
auto upper_half = ir.And(shifted, ir.Imm32(0xFFFF0000));
ir.SetRegister(d, ir.Or(lower_half, upper_half));
}
return true;
}
bool ArmTranslatorVisitor::arm_PKHTB(Cond cond, Reg n, Reg d, Imm5 imm5, Reg m) {
if (n == Reg::PC || d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto shifted = EmitImmShift(ir.GetRegister(m), ShiftType::ASR, imm5, ir.Imm1(false)).result;
auto lower_half = ir.And(shifted, ir.Imm32(0x0000FFFF));
auto upper_half = ir.And(ir.GetRegister(n), ir.Imm32(0xFFFF0000));
ir.SetRegister(d, ir.Or(lower_half, upper_half));
}
return true;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,369 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
// Parallel Add/Subtract (Modulo arithmetic) instructions
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)) {
auto result = ir.PackedAddS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
}
return true;
}
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)) {
auto result = ir.PackedAddS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
}
return true;
}
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)) {
auto result = ir.PackedAddSubS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
}
return true;
}
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)) {
auto result = ir.PackedSubAddS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
}
return true;
}
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)) {
auto result = ir.PackedSubS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
}
return true;
}
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)) {
auto result = ir.PackedSubS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
}
return true;
}
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)) {
auto result = ir.PackedAddU8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
}
return true;
}
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)) {
auto result = ir.PackedAddU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
}
return true;
}
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)) {
auto result = ir.PackedAddSubU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
}
return true;
}
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)) {
auto result = ir.PackedSubAddU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
}
return true;
}
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)) {
auto result = ir.PackedAbsDiffSumS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto tmp = ir.PackedAbsDiffSumS8(ir.GetRegister(n), ir.GetRegister(m));
auto result = ir.AddWithCarry(ir.GetRegister(a), tmp, ir.Imm1(0));
ir.SetRegister(d, result.result);
}
return true;
}
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)) {
auto result = ir.PackedSubU8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result.result);
ir.SetGEFlags(result.ge);
}
return true;
}
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)) {
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
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)) {
auto result = ir.PackedSaturatedAddS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedSaturatedAddS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedSaturatedSubS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedSaturatedSubS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedSaturatedAddU8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedSaturatedAddU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedSaturatedSubU8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedSaturatedSubU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
// Parallel Add/Subtract (Halving) instructions
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)) {
auto result = ir.PackedHalvingAddS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedHalvingAddS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedHalvingAddSubS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedHalvingSubAddS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedHalvingSubS8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedHalvingSubS16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedHalvingAddU8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedHalvingAddU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedHalvingAddSubU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedHalvingSubAddU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedHalvingSubU8(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
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)) {
auto result = ir.PackedHalvingSubU16(ir.GetRegister(n), ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,51 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
bool ArmTranslatorVisitor::arm_REV(Cond cond, Reg d, Reg m) {
// REV<c> <Rd>, <Rm>
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto result = ir.ByteReverseWord(ir.GetRegister(m));
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_REV16(Cond cond, Reg d, Reg m) {
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto reg_m = ir.GetRegister(m);
auto lo = ir.And(ir.LogicalShiftRight(reg_m, ir.Imm8(8), ir.Imm1(0)).result, ir.Imm32(0x00FF00FF));
auto hi = ir.And(ir.LogicalShiftLeft(reg_m, ir.Imm8(8), ir.Imm1(0)).result, ir.Imm32(0xFF00FF00));
auto result = ir.Or(lo, hi);
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_REVSH(Cond cond, Reg d, Reg m) {
// REVSH<c> <Rd>, <Rm>
if (d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
if (ConditionPassed(cond)) {
auto rev_half = ir.ByteReverseHalf(ir.LeastSignificantHalf(ir.GetRegister(m)));
ir.SetRegister(d, ir.SignExtendHalfToWord(rev_half));
}
return true;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,244 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
static IR::Value Pack2x16To1x32(IR::A32IREmitter& ir, IR::Value lo, IR::Value hi) {
return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result);
}
static IR::Value MostSignificantHalf(IR::A32IREmitter& ir, IR::Value value) {
return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result);
}
// Saturation instructions
bool ArmTranslatorVisitor::arm_SSAT(Cond cond, Imm5 sat_imm, Reg d, Imm5 imm5, bool sh, Reg n) {
if (d == Reg::PC || n == Reg::PC)
return UnpredictableInstruction();
size_t saturate_to = static_cast<size_t>(sat_imm) + 1;
ShiftType shift = !sh ? ShiftType::LSL : ShiftType::ASR;
// SSAT <Rd>, #<saturate_to>, <Rn>
if (ConditionPassed(cond)) {
auto operand = EmitImmShift(ir.GetRegister(n), shift, imm5, ir.GetCFlag());
auto result = ir.SignedSaturation(operand.result, saturate_to);
ir.SetRegister(d, result.result);
ir.OrQFlag(result.overflow);
}
return true;
}
bool ArmTranslatorVisitor::arm_SSAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n) {
if (d == Reg::PC || n == Reg::PC)
return UnpredictableInstruction();
size_t saturate_to = static_cast<size_t>(sat_imm) + 1;
// SSAT16 <Rd>, #<saturate_to>, <Rn>
if (ConditionPassed(cond)) {
auto lo_operand = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(ir.GetRegister(n)));
auto hi_operand = ir.SignExtendHalfToWord(MostSignificantHalf(ir, ir.GetRegister(n)));
auto lo_result = ir.SignedSaturation(lo_operand, saturate_to);
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;
}
bool ArmTranslatorVisitor::arm_USAT(Cond cond, Imm5 sat_imm, Reg d, Imm5 imm5, bool sh, Reg n) {
if (d == Reg::PC || n == Reg::PC)
return UnpredictableInstruction();
size_t saturate_to = static_cast<size_t>(sat_imm);
ShiftType shift = !sh ? ShiftType::LSL : ShiftType::ASR;
// USAT <Rd>, #<saturate_to>, <Rn>
if (ConditionPassed(cond)) {
auto operand = EmitImmShift(ir.GetRegister(n), shift, imm5, ir.GetCFlag());
auto result = ir.UnsignedSaturation(operand.result, saturate_to);
ir.SetRegister(d, result.result);
ir.OrQFlag(result.overflow);
}
return true;
}
bool ArmTranslatorVisitor::arm_USAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n) {
if (d == Reg::PC || n == Reg::PC)
return UnpredictableInstruction();
size_t saturate_to = static_cast<size_t>(sat_imm);
// USAT16 <Rd>, #<saturate_to>, <Rn>
if (ConditionPassed(cond)) {
// UnsignedSaturation takes a *signed* value as input, hence sign extension is required.
auto lo_operand = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(ir.GetRegister(n)));
auto hi_operand = ir.SignExtendHalfToWord(MostSignificantHalf(ir, ir.GetRegister(n)));
auto lo_result = ir.UnsignedSaturation(lo_operand, saturate_to);
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
bool ArmTranslatorVisitor::arm_QADD(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// QADD <Rd>, <Rm>, <Rn>
if (ConditionPassed(cond)) {
auto a = ir.GetRegister(m);
auto b = ir.GetRegister(n);
auto result = ir.SignedSaturatedAdd(a, b);
ir.SetRegister(d, result.result);
ir.OrQFlag(result.overflow);
}
return true;
}
bool ArmTranslatorVisitor::arm_QSUB(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// QSUB <Rd>, <Rm>, <Rn>
if (ConditionPassed(cond)) {
auto a = ir.GetRegister(m);
auto b = ir.GetRegister(n);
auto result = ir.SignedSaturatedSub(a, b);
ir.SetRegister(d, result.result);
ir.OrQFlag(result.overflow);
}
return true;
}
bool ArmTranslatorVisitor::arm_QDADD(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// QDADD <Rd>, <Rm>, <Rn>
if (ConditionPassed(cond)) {
auto a = ir.GetRegister(m);
auto b = ir.GetRegister(n);
auto doubled = ir.SignedSaturatedAdd(b, b);
ir.OrQFlag(doubled.overflow);
auto result = ir.SignedSaturatedAdd(a, doubled.result);
ir.SetRegister(d, result.result);
ir.OrQFlag(result.overflow);
}
return true;
}
bool ArmTranslatorVisitor::arm_QDSUB(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// QDSUB <Rd>, <Rm>, <Rn>
if (ConditionPassed(cond)) {
auto a = ir.GetRegister(m);
auto b = ir.GetRegister(n);
auto doubled = ir.SignedSaturatedAdd(b, b);
ir.OrQFlag(doubled.overflow);
auto result = ir.SignedSaturatedSub(a, doubled.result);
ir.SetRegister(d, result.result);
ir.OrQFlag(result.overflow);
}
return true;
}
// Parallel saturated instructions
bool ArmTranslatorVisitor::arm_QASX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// QASX <Rd>, <Rn>, <Rm>
if (ConditionPassed(cond)) {
auto Rn = ir.GetRegister(n);
auto Rm = ir.GetRegister(m);
auto Rn_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rn));
auto Rn_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rn));
auto Rm_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rm));
auto Rm_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rm));
auto diff = ir.SignedSaturation(ir.Sub(Rn_lo, Rm_hi), 16).result;
auto sum = ir.SignedSaturation(ir.Add(Rn_hi, Rm_lo), 16).result;
auto result = Pack2x16To1x32(ir, diff, sum);
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_QSAX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// QSAX <Rd>, <Rn>, <Rm>
if (ConditionPassed(cond)) {
auto Rn = ir.GetRegister(n);
auto Rm = ir.GetRegister(m);
auto Rn_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rn));
auto Rn_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rn));
auto Rm_lo = ir.SignExtendHalfToWord(ir.LeastSignificantHalf(Rm));
auto Rm_hi = ir.SignExtendHalfToWord(MostSignificantHalf(ir, Rm));
auto sum = ir.SignedSaturation(ir.Add(Rn_lo, Rm_hi), 16).result;
auto diff = ir.SignedSaturation(ir.Sub(Rn_hi, Rm_lo), 16).result;
auto result = Pack2x16To1x32(ir, sum, diff);
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_UQASX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// UQASX <Rd>, <Rn>, <Rm>
if (ConditionPassed(cond)) {
auto Rn = ir.GetRegister(n);
auto Rm = ir.GetRegister(m);
auto Rn_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rn));
auto Rn_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rn));
auto Rm_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rm));
auto Rm_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rm));
auto diff = ir.UnsignedSaturation(ir.Sub(Rn_lo, Rm_hi), 16).result;
auto sum = ir.UnsignedSaturation(ir.Add(Rn_hi, Rm_lo), 16).result;
auto result = Pack2x16To1x32(ir, diff, sum);
ir.SetRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::arm_UQSAX(Cond cond, Reg n, Reg d, Reg m) {
if (d == Reg::PC || n == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
// UQSAX <Rd>, <Rn>, <Rm>
if (ConditionPassed(cond)) {
auto Rn = ir.GetRegister(n);
auto Rm = ir.GetRegister(m);
auto Rn_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rn));
auto Rn_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rn));
auto Rm_lo = ir.ZeroExtendHalfToWord(ir.LeastSignificantHalf(Rm));
auto Rm_hi = ir.ZeroExtendHalfToWord(MostSignificantHalf(ir, Rm));
auto sum = ir.UnsignedSaturation(ir.Add(Rn_lo, Rm_hi), 16).result;
auto diff = ir.UnsignedSaturation(ir.Sub(Rn_hi, Rm_lo), 16).result;
auto result = Pack2x16To1x32(ir, sum, diff);
ir.SetRegister(d, result);
}
return true;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,80 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
#include "common/bit_util.h"
namespace Dynarmic {
namespace A32 {
bool ArmTranslatorVisitor::arm_CPS() {
return InterpretThisInstruction();
}
bool ArmTranslatorVisitor::arm_MRS(Cond cond, Reg d) {
if (d == Reg::PC)
return UnpredictableInstruction();
// MRS <Rd>, APSR
if (ConditionPassed(cond)) {
ir.SetRegister(d, ir.GetCpsr());
}
return true;
}
bool ArmTranslatorVisitor::arm_MSR_imm(Cond cond, int mask, int rotate, Imm8 imm8) {
bool write_nzcvq = Common::Bit<1>(mask);
bool write_g = Common::Bit<0>(mask);
u32 imm32 = ArmExpandImm(rotate, imm8);
ASSERT_MSG(write_nzcvq || write_g, "Decode error");
// MSR <spec_reg>, #<imm32>
if (ConditionPassed(cond)) {
if (write_nzcvq) {
ir.SetCpsrNZCVQ(ir.Imm32(imm32 & 0xF8000000));
}
if (write_g) {
ir.SetGEFlagsCompressed(ir.Imm32(imm32 & 0x000F0000));
}
}
return true;
}
bool ArmTranslatorVisitor::arm_MSR_reg(Cond cond, int mask, Reg n) {
bool write_nzcvq = Common::Bit<1>(mask);
bool write_g = Common::Bit<0>(mask);
if (!write_nzcvq && !write_g)
return UnpredictableInstruction();
if (n == Reg::PC)
return UnpredictableInstruction();
// MSR <spec_reg>, #<imm32>
if (ConditionPassed(cond)) {
auto value = ir.GetRegister(n);
if (write_nzcvq){
ir.SetCpsrNZCVQ(ir.And(value, ir.Imm32(0xF8000000)));
}
if (write_g){
ir.SetGEFlagsCompressed(ir.And(value, ir.Imm32(0x000F0000)));
}
}
return true;
}
bool ArmTranslatorVisitor::arm_RFE() {
return InterpretThisInstruction();
}
bool ArmTranslatorVisitor::arm_SETEND(bool E) {
// SETEND {BE,LE}
ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(4).SetEFlag(E)});
return false;
}
bool ArmTranslatorVisitor::arm_SRS() {
return InterpretThisInstruction();
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,162 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
bool ArmTranslatorVisitor::arm_CLREX() {
// CLREX
ir.ClearExclusive();
return true;
}
bool ArmTranslatorVisitor::arm_LDREX(Cond cond, Reg n, Reg d) {
if (d == Reg::PC || n == Reg::PC)
return UnpredictableInstruction();
// LDREX <Rd>, [<Rn>]
if (ConditionPassed(cond)) {
auto address = ir.GetRegister(n);
ir.SetExclusive(address, 4);
ir.SetRegister(d, ir.ReadMemory32(address));
}
return true;
}
bool ArmTranslatorVisitor::arm_LDREXB(Cond cond, Reg n, Reg d) {
if (d == Reg::PC || n == Reg::PC)
return UnpredictableInstruction();
// LDREXB <Rd>, [<Rn>]
if (ConditionPassed(cond)) {
auto address = ir.GetRegister(n);
ir.SetExclusive(address, 1);
ir.SetRegister(d, ir.ZeroExtendByteToWord(ir.ReadMemory8(address)));
}
return true;
}
bool ArmTranslatorVisitor::arm_LDREXD(Cond cond, Reg n, Reg d) {
if (d == Reg::LR || d == Reg::PC || n == Reg::PC)
return UnpredictableInstruction();
// LDREXD <Rd>, <Rd1>, [<Rn>]
if (ConditionPassed(cond)) {
auto address = ir.GetRegister(n);
ir.SetExclusive(address, 8);
// DO NOT SWAP hi AND lo IN BIG ENDIAN MODE, THIS IS CORRECT BEHAVIOUR
auto lo = ir.ReadMemory32(address);
ir.SetRegister(d, lo);
auto hi = ir.ReadMemory32(ir.Add(address, ir.Imm32(4)));
ir.SetRegister(d+1, hi);
}
return true;
}
bool ArmTranslatorVisitor::arm_LDREXH(Cond cond, Reg n, Reg d) {
if (d == Reg::PC || n == Reg::PC)
return UnpredictableInstruction();
// LDREXH <Rd>, [<Rn>]
if (ConditionPassed(cond)) {
auto address = ir.GetRegister(n);
ir.SetExclusive(address, 2);
ir.SetRegister(d, ir.ZeroExtendHalfToWord(ir.ReadMemory16(address)));
}
return true;
}
bool ArmTranslatorVisitor::arm_STREX(Cond cond, Reg n, Reg d, Reg m) {
if (n == Reg::PC || d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
if (d == n || d == m)
return UnpredictableInstruction();
// STREX <Rd>, <Rm>, [<Rn>]
if (ConditionPassed(cond)) {
auto address = ir.GetRegister(n);
auto value = ir.GetRegister(m);
auto passed = ir.ExclusiveWriteMemory32(address, value);
ir.SetRegister(d, passed);
}
return true;
}
bool ArmTranslatorVisitor::arm_STREXB(Cond cond, Reg n, Reg d, Reg m) {
if (n == Reg::PC || d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
if (d == n || d == m)
return UnpredictableInstruction();
// STREXB <Rd>, <Rm>, [<Rn>]
if (ConditionPassed(cond)) {
auto address = ir.GetRegister(n);
auto value = ir.LeastSignificantByte(ir.GetRegister(m));
auto passed = ir.ExclusiveWriteMemory8(address, value);
ir.SetRegister(d, passed);
}
return true;
}
bool ArmTranslatorVisitor::arm_STREXD(Cond cond, Reg n, Reg d, Reg m) {
if (n == Reg::PC || d == Reg::PC || m == Reg::LR || static_cast<size_t>(m) % 2 == 1)
return UnpredictableInstruction();
if (d == n || d == m || d == m+1)
return UnpredictableInstruction();
Reg m2 = m + 1;
// STREXD <Rd>, <Rm>, <Rm2>, [<Rn>]
if (ConditionPassed(cond)) {
auto address = ir.GetRegister(n);
auto value_lo = ir.GetRegister(m);
auto value_hi = ir.GetRegister(m2);
auto passed = ir.ExclusiveWriteMemory64(address, value_lo, value_hi);
ir.SetRegister(d, passed);
}
return true;
}
bool ArmTranslatorVisitor::arm_STREXH(Cond cond, Reg n, Reg d, Reg m) {
if (n == Reg::PC || d == Reg::PC || m == Reg::PC)
return UnpredictableInstruction();
if (d == n || d == m)
return UnpredictableInstruction();
// STREXH <Rd>, <Rm>, [<Rn>]
if (ConditionPassed(cond)) {
auto address = ir.GetRegister(n);
auto value = ir.LeastSignificantHalf(ir.GetRegister(m));
auto passed = ir.ExclusiveWriteMemory16(address, value);
ir.SetRegister(d, passed);
}
return true;
}
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();
// TODO: UNDEFINED if current mode is Hypervisor
// SWP <Rt>, <Rt2>, [<Rn>]
if (ConditionPassed(cond)) {
auto data = ir.ReadMemory32(ir.GetRegister(n));
ir.WriteMemory32(ir.GetRegister(n), ir.GetRegister(t2));
// TODO: Alignment check
ir.SetRegister(t, data);
}
return true;
}
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();
// TODO: UNDEFINED if current mode is Hypervisor
// SWPB <Rt>, <Rt2>, [<Rn>]
if (ConditionPassed(cond)) {
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;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,388 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include "frontend/ir/ir_emitter.h"
#include "frontend/ir/location_descriptor.h"
namespace Dynarmic {
namespace A32 {
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(LocationDescriptor descriptor) : ir(descriptor) {
ASSERT_MSG(!descriptor.TFlag(), "The processor must be in Arm mode");
}
IR::A32IREmitter ir;
ConditionalState cond_state = ConditionalState::None;
bool ConditionPassed(Cond cond);
bool InterpretThisInstruction();
bool UnpredictableInstruction();
static u32 rotr(u32 x, int shift) {
shift &= 31;
if (!shift) return x;
return (x >> shift) | (x << (32 - shift));
}
static u32 ArmExpandImm(int rotate, Imm8 imm8) {
return rotr(static_cast<u32>(imm8), rotate*2);
}
struct ImmAndCarry {
u32 imm32;
IR::Value carry;
};
ImmAndCarry ArmExpandImm_C(int rotate, u32 imm8, IR::Value carry_in) {
u32 imm32 = imm8;
auto carry_out = carry_in;
if (rotate) {
imm32 = rotr(imm8, rotate * 2);
carry_out = ir.Imm1(imm32 >> 31 == 1);
}
return {imm32, carry_out};
}
IR::A32IREmitter::ResultAndCarry EmitImmShift(IR::Value value, ShiftType type, Imm5 imm5, IR::Value carry_in);
IR::A32IREmitter::ResultAndCarry EmitRegShift(IR::Value value, ShiftType type, IR::Value amount, IR::Value 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);
// Branch instructions
bool arm_B(Cond cond, Imm24 imm24);
bool arm_BL(Cond cond, Imm24 imm24);
bool arm_BLX_imm(bool H, Imm24 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, Imm8 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, Imm8 imm8);
// Data processing instructions
bool arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm8 imm8);
bool arm_ADC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 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, Imm8 imm8);
bool arm_ADD_reg(Cond cond, bool S, Reg n, Reg d, Imm5 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, Imm8 imm8);
bool arm_AND_reg(Cond cond, bool S, Reg n, Reg d, Imm5 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, Imm8 imm8);
bool arm_BIC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 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, Imm8 imm8);
bool arm_CMN_reg(Cond cond, Reg n, Imm5 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, Imm8 imm8);
bool arm_CMP_reg(Cond cond, Reg n, Imm5 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, Imm8 imm8);
bool arm_EOR_reg(Cond cond, bool S, Reg n, Reg d, Imm5 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, Imm8 imm8);
bool arm_MOV_reg(Cond cond, bool S, Reg d, Imm5 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, Imm8 imm8);
bool arm_MVN_reg(Cond cond, bool S, Reg d, Imm5 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, Imm8 imm8);
bool arm_ORR_reg(Cond cond, bool S, Reg n, Reg d, Imm5 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, Imm8 imm8);
bool arm_RSB_reg(Cond cond, bool S, Reg n, Reg d, Imm5 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, Imm8 imm8);
bool arm_RSC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 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, Imm8 imm8);
bool arm_SBC_reg(Cond cond, bool S, Reg n, Reg d, Imm5 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, Imm8 imm8);
bool arm_SUB_reg(Cond cond, bool S, Reg n, Reg d, Imm5 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, Imm8 imm8);
bool arm_TEQ_reg(Cond cond, Reg n, Imm5 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, Imm8 imm8);
bool arm_TST_reg(Cond cond, Reg n, Imm5 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, Imm12 imm12, Imm4 imm4);
bool arm_SVC(Cond cond, Imm24 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() { return true; }
bool arm_SEV() { return true; }
bool arm_WFE() { return true; }
bool arm_WFI() { return true; }
bool arm_YIELD() { return true; }
// 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, Imm12 imm12);
bool arm_LDR_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm12 imm12);
bool arm_LDR_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg d, Imm5 imm5, ShiftType shift, Reg m);
bool arm_LDRB_lit(Cond cond, bool U, Reg t, Imm12 imm12);
bool arm_LDRB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm12 imm12);
bool arm_LDRB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm5 imm5, ShiftType shift, Reg m);
bool arm_LDRD_lit(Cond cond, bool U, Reg t, Imm4 imm8a, Imm4 imm8b);
bool arm_LDRD_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm4 imm8a, Imm4 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, Imm4 imm8a, Imm4 imm8b);
bool arm_LDRH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm4 imm8a, Imm4 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, Imm4 imm8a, Imm4 imm8b);
bool arm_LDRSB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm4 imm8a, Imm4 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, Imm4 imm8a, Imm4 imm8b);
bool arm_LDRSH_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm4 imm8a, Imm4 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, Imm12 imm12);
bool arm_STR_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm5 imm5, ShiftType shift, Reg m);
bool arm_STRB_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm12 imm12);
bool arm_STRB_reg(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm5 imm5, ShiftType shift, Reg m);
bool arm_STRD_imm(Cond cond, bool P, bool U, bool W, Reg n, Reg t, Imm4 imm8a, Imm4 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, Imm4 imm8a, Imm4 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_CLZ(Cond cond, Reg d, Reg m);
bool arm_NOP() { return true; }
bool arm_SEL(Cond cond, Reg n, Reg d, Reg m);
// 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, Imm5 imm5, Reg m);
bool arm_PKHTB(Cond cond, Reg n, Reg d, Imm5 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, Imm5 sat_imm, Reg d, Imm5 imm5, bool sh, Reg n);
bool arm_SSAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n);
bool arm_USAT(Cond cond, Imm5 sat_imm, Reg d, Imm5 imm5, bool sh, Reg n);
bool arm_USAT16(Cond cond, Imm4 sat_imm, Reg d, Reg n);
// Multiply (Normal) instructions
bool arm_MLA(Cond cond, bool S, 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_LDREX(Cond cond, Reg n, Reg d);
bool arm_LDREXB(Cond cond, Reg n, Reg d);
bool arm_LDREXD(Cond cond, Reg n, Reg d);
bool arm_LDREXH(Cond cond, Reg n, Reg d);
bool arm_STREX(Cond cond, Reg n, Reg d, Reg m);
bool arm_STREXB(Cond cond, Reg n, Reg d, Reg m);
bool arm_STREXD(Cond cond, Reg n, Reg d, Reg m);
bool arm_STREXH(Cond cond, Reg n, Reg d, Reg m);
bool arm_SWP(Cond cond, Reg n, Reg d, Reg m);
bool arm_SWPB(Cond cond, Reg n, Reg d, Reg m);
// Status register access instructions
bool arm_CPS();
bool arm_MRS(Cond cond, Reg d);
bool arm_MSR_imm(Cond cond, int mask, int rotate, Imm8 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 vfp2_VADD(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp2_VSUB(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp2_VMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp2_VMLA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp2_VMLS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp2_VNMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp2_VNMLA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp2_VNMLS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
bool vfp2_VDIV(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm);
// Floating-point move instructions
bool vfp2_VMOV_u32_f64(Cond cond, size_t Vd, Reg t, bool D);
bool vfp2_VMOV_f64_u32(Cond cond, size_t Vn, Reg t, bool N);
bool vfp2_VMOV_u32_f32(Cond cond, size_t Vn, Reg t, bool N);
bool vfp2_VMOV_f32_u32(Cond cond, size_t Vn, Reg t, bool N);
bool vfp2_VMOV_2u32_2f32(Cond cond, Reg t2, Reg t, bool M, size_t Vm);
bool vfp2_VMOV_2f32_2u32(Cond cond, Reg t2, Reg t, bool M, size_t Vm);
bool vfp2_VMOV_2u32_f64(Cond cond, Reg t2, Reg t, bool M, size_t Vm);
bool vfp2_VMOV_f64_2u32(Cond cond, Reg t2, Reg t, bool M, size_t Vm);
bool vfp2_VMOV_reg(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
// Floating-point misc instructions
bool vfp2_VABS(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp2_VNEG(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp2_VSQRT(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp2_VCVT_f_to_f(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm);
bool vfp2_VCVT_to_float(Cond cond, bool D, size_t Vd, bool sz, bool is_signed, bool M, size_t Vm);
bool vfp2_VCVT_to_u32(Cond cond, bool D, size_t Vd, bool sz, bool round_towards_zero, bool M, size_t Vm);
bool vfp2_VCVT_to_s32(Cond cond, bool D, size_t Vd, bool sz, bool round_towards_zero, bool M, size_t Vm);
bool vfp2_VCMP(Cond cond, bool D, size_t Vd, bool sz, bool E, bool M, size_t Vm);
bool vfp2_VCMP_zero(Cond cond, bool D, size_t Vd, bool sz, bool E);
// Floating-point system register access
bool vfp2_VMSR(Cond cond, Reg t);
bool vfp2_VMRS(Cond cond, Reg t);
// Floating-point load-store instructions
bool vfp2_VLDR(Cond cond, bool U, bool D, Reg n, size_t Vd, bool sz, Imm8 imm8);
bool vfp2_VSTR(Cond cond, bool U, bool D, Reg n, size_t Vd, bool sz, Imm8 imm8);
bool vfp2_VPOP(Cond cond, bool D, size_t Vd, bool sz, Imm8 imm8);
bool vfp2_VPUSH(Cond cond, bool D, size_t Vd, bool sz, Imm8 imm8);
bool vfp2_VSTM_a1(Cond cond, bool p, bool u, bool D, bool w, Reg n, size_t Vd, Imm8 imm8);
bool vfp2_VSTM_a2(Cond cond, bool p, bool u, bool D, bool w, Reg n, size_t Vd, Imm8 imm8);
bool vfp2_VLDM_a1(Cond cond, bool p, bool u, bool D, bool w, Reg n, size_t Vd, Imm8 imm8);
bool vfp2_VLDM_a2(Cond cond, bool p, bool u, bool D, bool w, Reg n, size_t Vd, Imm8 imm8);
};
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,794 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include "translate_arm.h"
namespace Dynarmic {
namespace A32 {
static ExtReg ToExtReg(bool sz, size_t base, bool bit) {
if (sz) {
return static_cast<ExtReg>(static_cast<size_t>(ExtReg::D0) + base + (bit ? 16 : 0));
} else {
return static_cast<ExtReg>(static_cast<size_t>(ExtReg::S0) + (base << 1) + (bit ? 1 : 0));
}
}
template <typename FnT>
bool ArmTranslatorVisitor::EmitVfpVectorOperation(bool sz, ExtReg d, ExtReg n, ExtReg m, const FnT& fn) {
// VFP register banks are 8 single-precision registers in size.
const size_t register_bank_size = sz ? 4 : 8;
if (!ir.current_location.FPSCR().Stride()) {
return UnpredictableInstruction();
}
size_t vector_length = ir.current_location.FPSCR().Len();
size_t vector_stride = *ir.current_location.FPSCR().Stride();
// Unpredictable case
if (vector_stride * vector_length > register_bank_size) {
return UnpredictableInstruction();
}
// Scalar case
if (vector_length == 1) {
if (vector_stride != 1)
return UnpredictableInstruction();
fn(d, n, m);
return true;
}
// The VFP register file is divided into banks each containing:
// * eight single-precision registers, or
// * four double-precision reigsters.
// VFP vector instructions access these registers in a circular manner.
const auto bank_increment = [register_bank_size](ExtReg reg, size_t stride) -> ExtReg {
size_t reg_number = static_cast<size_t>(reg);
size_t bank_index = reg_number % register_bank_size;
size_t bank_start = reg_number - bank_index;
size_t next_reg_number = bank_start + ((bank_index + stride) % register_bank_size);
return static_cast<ExtReg>(next_reg_number);
};
// The first and fifth banks in the register file are scalar banks.
// All the other banks are vector banks.
const auto belongs_to_scalar_bank = [](ExtReg reg) -> bool {
return (reg >= ExtReg::D0 && reg <= ExtReg::D3)
|| (reg >= ExtReg::D16 && reg <= ExtReg::D19)
|| (reg >= ExtReg::S0 && reg <= ExtReg::S7);
};
const bool d_is_scalar = belongs_to_scalar_bank(d);
const bool m_is_scalar = belongs_to_scalar_bank(m);
if (d_is_scalar) {
// If destination register is in a scalar bank, the operands and results are all scalars.
vector_length = 1;
}
for (size_t i = 0; i < vector_length; i++) {
fn(d, n, m);
d = bank_increment(d, vector_stride);
n = bank_increment(n, vector_stride);
if (!m_is_scalar)
m = bank_increment(m, vector_stride);
}
return true;
}
template <typename FnT>
bool ArmTranslatorVisitor::EmitVfpVectorOperation(bool sz, ExtReg d, ExtReg m, const FnT& fn) {
return EmitVfpVectorOperation(sz, d, ExtReg::S0, m, [fn](ExtReg d, ExtReg, ExtReg m){
fn(d, m);
});
}
bool ArmTranslatorVisitor::vfp2_VADD(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg n = ToExtReg(sz, Vn, N);
ExtReg m = ToExtReg(sz, Vm, M);
// VADD.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, n, m, [sz, this](ExtReg d, ExtReg n, ExtReg m) {
auto reg_n = ir.GetExtendedRegister(n);
auto reg_m = ir.GetExtendedRegister(m);
auto result = sz
? ir.FPAdd64(reg_n, reg_m, true)
: ir.FPAdd32(reg_n, reg_m, true);
ir.SetExtendedRegister(d, result);
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VSUB(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg n = ToExtReg(sz, Vn, N);
ExtReg m = ToExtReg(sz, Vm, M);
// VSUB.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, n, m, [sz, this](ExtReg d, ExtReg n, ExtReg m) {
auto reg_n = ir.GetExtendedRegister(n);
auto reg_m = ir.GetExtendedRegister(m);
auto result = sz
? ir.FPSub64(reg_n, reg_m, true)
: ir.FPSub32(reg_n, reg_m, true);
ir.SetExtendedRegister(d, result);
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg n = ToExtReg(sz, Vn, N);
ExtReg m = ToExtReg(sz, Vm, M);
// VMUL.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, n, m, [sz, this](ExtReg d, ExtReg n, ExtReg m) {
auto reg_n = ir.GetExtendedRegister(n);
auto reg_m = ir.GetExtendedRegister(m);
auto result = sz
? ir.FPMul64(reg_n, reg_m, true)
: ir.FPMul32(reg_n, reg_m, true);
ir.SetExtendedRegister(d, result);
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMLA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg n = ToExtReg(sz, Vn, N);
ExtReg m = ToExtReg(sz, Vm, M);
// VMLA.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, n, m, [sz, this](ExtReg d, ExtReg n, ExtReg m) {
auto reg_n = ir.GetExtendedRegister(n);
auto reg_m = ir.GetExtendedRegister(m);
auto reg_d = ir.GetExtendedRegister(d);
auto result = sz
? ir.FPAdd64(reg_d, ir.FPMul64(reg_n, reg_m, true), true)
: ir.FPAdd32(reg_d, ir.FPMul32(reg_n, reg_m, true), true);
ir.SetExtendedRegister(d, result);
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMLS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg n = ToExtReg(sz, Vn, N);
ExtReg m = ToExtReg(sz, Vm, M);
// VMLS.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, n, m, [sz, this](ExtReg d, ExtReg n, ExtReg m) {
auto reg_n = ir.GetExtendedRegister(n);
auto reg_m = ir.GetExtendedRegister(m);
auto reg_d = ir.GetExtendedRegister(d);
auto result = sz
? ir.FPAdd64(reg_d, ir.FPNeg64(ir.FPMul64(reg_n, reg_m, true)), true)
: ir.FPAdd32(reg_d, ir.FPNeg32(ir.FPMul32(reg_n, reg_m, true)), true);
ir.SetExtendedRegister(d, result);
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VNMUL(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg n = ToExtReg(sz, Vn, N);
ExtReg m = ToExtReg(sz, Vm, M);
// VNMUL.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, n, m, [sz, this](ExtReg d, ExtReg n, ExtReg m) {
auto reg_n = ir.GetExtendedRegister(n);
auto reg_m = ir.GetExtendedRegister(m);
auto result = sz
? ir.FPNeg64(ir.FPMul64(reg_n, reg_m, true))
: ir.FPNeg32(ir.FPMul32(reg_n, reg_m, true));
ir.SetExtendedRegister(d, result);
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VNMLA(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg n = ToExtReg(sz, Vn, N);
ExtReg m = ToExtReg(sz, Vm, M);
// VNMLA.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, n, m, [sz, this](ExtReg d, ExtReg n, ExtReg m) {
auto reg_n = ir.GetExtendedRegister(n);
auto reg_m = ir.GetExtendedRegister(m);
auto reg_d = ir.GetExtendedRegister(d);
auto result = sz
? ir.FPAdd64(ir.FPNeg64(reg_d), ir.FPNeg64(ir.FPMul64(reg_n, reg_m, true)), true)
: ir.FPAdd32(ir.FPNeg32(reg_d), ir.FPNeg32(ir.FPMul32(reg_n, reg_m, true)), true);
ir.SetExtendedRegister(d, result);
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VNMLS(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg n = ToExtReg(sz, Vn, N);
ExtReg m = ToExtReg(sz, Vm, M);
// VNMLS.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, n, m, [sz, this](ExtReg d, ExtReg n, ExtReg m) {
auto reg_n = ir.GetExtendedRegister(n);
auto reg_m = ir.GetExtendedRegister(m);
auto reg_d = ir.GetExtendedRegister(d);
auto result = sz
? ir.FPAdd64(ir.FPNeg64(reg_d), ir.FPMul64(reg_n, reg_m, true), true)
: ir.FPAdd32(ir.FPNeg32(reg_d), ir.FPMul32(reg_n, reg_m, true), true);
ir.SetExtendedRegister(d, result);
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VDIV(Cond cond, bool D, size_t Vn, size_t Vd, bool sz, bool N, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg n = ToExtReg(sz, Vn, N);
ExtReg m = ToExtReg(sz, Vm, M);
// VDIV.{F32,F64} <{S,D}d>, <{S,D}n>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, n, m, [sz, this](ExtReg d, ExtReg n, ExtReg m) {
auto reg_n = ir.GetExtendedRegister(n);
auto reg_m = ir.GetExtendedRegister(m);
auto result = sz
? ir.FPDiv64(reg_n, reg_m, true)
: ir.FPDiv32(reg_n, reg_m, true);
ir.SetExtendedRegister(d, result);
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMOV_u32_f64(Cond cond, size_t Vd, Reg t, bool D) {
ExtReg d = ToExtReg(true, Vd, D);
if (t == Reg::PC)
return UnpredictableInstruction();
// VMOV.32 <Dd[0]>, <Rt>
if (ConditionPassed(cond)) {
auto d_f64 = ir.GetExtendedRegister(d);
auto t_u32 = ir.GetRegister(t);
auto d_u64 = ir.TransferFromFP64(d_f64);
auto result = ir.Pack2x32To1x64(t_u32, ir.MostSignificantWord(d_u64).result);
ir.SetExtendedRegister(d, ir.TransferToFP64(result));
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMOV_f64_u32(Cond cond, size_t Vn, Reg t, bool N) {
ExtReg n = ToExtReg(true, Vn, N);
if (t == Reg::PC)
return UnpredictableInstruction();
// VMOV.32 <Rt>, <Dn[0]>
if (ConditionPassed(cond)) {
auto n_f64 = ir.GetExtendedRegister(n);
ir.SetRegister(t, ir.LeastSignificantWord(ir.TransferFromFP64(n_f64)));
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMOV_u32_f32(Cond cond, size_t Vn, Reg t, bool N) {
ExtReg n = ToExtReg(false, Vn, N);
if (t == Reg::PC)
return UnpredictableInstruction();
// VMOV <Sn>, <Rt>
if (ConditionPassed(cond)) {
ir.SetExtendedRegister(n, ir.TransferToFP32(ir.GetRegister(t)));
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMOV_f32_u32(Cond cond, size_t Vn, Reg t, bool N) {
ExtReg n = ToExtReg(false, Vn, N);
if (t == Reg::PC)
return UnpredictableInstruction();
// VMOV <Rt>, <Sn>
if (ConditionPassed(cond)) {
ir.SetRegister(t, ir.TransferFromFP32(ir.GetExtendedRegister(n)));
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMOV_2u32_2f32(Cond cond, Reg t2, Reg t, bool M, size_t Vm) {
ExtReg m = ToExtReg(false, Vm, M);
if (t == Reg::PC || t2 == Reg::PC || m == ExtReg::S31)
return UnpredictableInstruction();
// VMOV <Sm>, <Sm1>, <Rt>, <Rt2>
if (ConditionPassed(cond)) {
ir.SetExtendedRegister(m, ir.TransferToFP32(ir.GetRegister(t)));
ir.SetExtendedRegister(m+1, ir.TransferToFP32(ir.GetRegister(t2)));
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMOV_2f32_2u32(Cond cond, Reg t2, Reg t, bool M, size_t Vm) {
ExtReg m = ToExtReg(false, Vm, M);
if (t == Reg::PC || t2 == Reg::PC || m == ExtReg::S31)
return UnpredictableInstruction();
if (t == t2)
return UnpredictableInstruction();
// VMOV <Rt>, <Rt2>, <Sm>, <Sm1>
if (ConditionPassed(cond)) {
ir.SetRegister(t, ir.TransferFromFP32(ir.GetExtendedRegister(m)));
ir.SetRegister(t2, ir.TransferFromFP32(ir.GetExtendedRegister(m+1)));
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMOV_2u32_f64(Cond cond, Reg t2, Reg t, bool M, size_t Vm) {
ExtReg m = ToExtReg(true, Vm, M);
if (t == Reg::PC || t2 == Reg::PC || m == ExtReg::S31)
return UnpredictableInstruction();
// VMOV<c> <Dm>, <Rt>, <Rt2>
if (ConditionPassed(cond)) {
auto value = ir.Pack2x32To1x64(ir.GetRegister(t), ir.GetRegister(t2));
ir.SetExtendedRegister(m, ir.TransferToFP64(value));
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMOV_f64_2u32(Cond cond, Reg t2, Reg t, bool M, size_t Vm) {
ExtReg m = ToExtReg(true, Vm, M);
if (t == Reg::PC || t2 == Reg::PC || m == ExtReg::S31)
return UnpredictableInstruction();
if (t == t2)
return UnpredictableInstruction();
// VMOV<c> <Rt>, <Rt2>, <Dm>
if (ConditionPassed(cond)) {
auto value = ir.TransferFromFP64(ir.GetExtendedRegister(m));
ir.SetRegister(t, ir.LeastSignificantWord(value));
ir.SetRegister(t2, ir.MostSignificantWord(value).result);
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMOV_reg(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg m = ToExtReg(sz, Vm, M);
// VMOV.{F32,F64} <{S,D}d>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, m, [this](ExtReg d, ExtReg m) {
ir.SetExtendedRegister(d, ir.GetExtendedRegister(m));
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VABS(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg m = ToExtReg(sz, Vm, M);
// VABS.{F32,F64} <{S,D}d>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, m, [sz, this](ExtReg d, ExtReg m) {
auto reg_m = ir.GetExtendedRegister(m);
auto result = sz
? ir.FPAbs64(reg_m)
: ir.FPAbs32(reg_m);
ir.SetExtendedRegister(d, result);
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VNEG(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg m = ToExtReg(sz, Vm, M);
// VNEG.{F32,F64} <{S,D}d>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, m, [sz, this](ExtReg d, ExtReg m) {
auto reg_m = ir.GetExtendedRegister(m);
auto result = sz
? ir.FPNeg64(reg_m)
: ir.FPNeg32(reg_m);
ir.SetExtendedRegister(d, result);
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VSQRT(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg m = ToExtReg(sz, Vm, M);
// VSQRT.{F32,F64} <{S,D}d>, <{S,D}m>
if (ConditionPassed(cond)) {
return EmitVfpVectorOperation(sz, d, m, [sz, this](ExtReg d, ExtReg m) {
auto reg_m = ir.GetExtendedRegister(m);
auto result = sz
? ir.FPSqrt64(reg_m)
: ir.FPSqrt32(reg_m);
ir.SetExtendedRegister(d, result);
});
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VCVT_f_to_f(Cond cond, bool D, size_t Vd, bool sz, bool M, size_t Vm) {
ExtReg d = ToExtReg(!sz, Vd, D); // Destination is of opposite size to source
ExtReg m = ToExtReg(sz, Vm, M);
// VCVT.F64.F32 <Sd> <Dm>
// VCVT.F32.F64 <Dd> <Sm>
if (ConditionPassed(cond)) {
auto reg_m = ir.GetExtendedRegister(m);
auto result = sz
? ir.FPDoubleToSingle(reg_m, true)
: ir.FPSingleToDouble(reg_m, true);
ir.SetExtendedRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VCVT_to_float(Cond cond, bool D, size_t Vd, bool sz, bool is_signed, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg m = ToExtReg(false, Vm, M);
bool round_to_nearest = false;
// VCVT.F32.{S32,U32} <Sd>, <Sm>
// VCVT.F64.{S32,U32} <Sd>, <Dm>
if (ConditionPassed(cond)) {
auto reg_m = ir.GetExtendedRegister(m);
auto result = sz
? is_signed
? ir.FPS32ToDouble(reg_m, round_to_nearest, true)
: ir.FPU32ToDouble(reg_m, round_to_nearest, true)
: is_signed
? ir.FPS32ToSingle(reg_m, round_to_nearest, true)
: ir.FPU32ToSingle(reg_m, round_to_nearest, true);
ir.SetExtendedRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VCVT_to_u32(Cond cond, bool D, size_t Vd, bool sz, bool round_towards_zero, bool M, size_t Vm) {
ExtReg d = ToExtReg(false, Vd, D);
ExtReg m = ToExtReg(sz, Vm, M);
// VCVT{,R}.U32.F32 <Sd>, <Sm>
// VCVT{,R}.U32.F64 <Sd>, <Dm>
if (ConditionPassed(cond)) {
auto reg_m = ir.GetExtendedRegister(m);
auto result = sz
? ir.FPDoubleToU32(reg_m, round_towards_zero, true)
: ir.FPSingleToU32(reg_m, round_towards_zero, true);
ir.SetExtendedRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VCVT_to_s32(Cond cond, bool D, size_t Vd, bool sz, bool round_towards_zero, bool M, size_t Vm) {
ExtReg d = ToExtReg(false, Vd, D);
ExtReg m = ToExtReg(sz, Vm, M);
// VCVT{,R}.S32.F32 <Sd>, <Sm>
// VCVT{,R}.S32.F64 <Sd>, <Dm>
if (ConditionPassed(cond)) {
auto reg_m = ir.GetExtendedRegister(m);
auto result = sz
? ir.FPDoubleToS32(reg_m, round_towards_zero, true)
: ir.FPSingleToS32(reg_m, round_towards_zero, true);
ir.SetExtendedRegister(d, result);
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VCMP(Cond cond, bool D, size_t Vd, bool sz, bool E, bool M, size_t Vm) {
ExtReg d = ToExtReg(sz, Vd, D);
ExtReg m = ToExtReg(sz, Vm, M);
bool exc_on_qnan = E;
// VCMP{E}.F32 <Sd>, <Sm>
// VCMP{E}.F64 <Dd>, <Dm>
if (ConditionPassed(cond)) {
auto reg_d = ir.GetExtendedRegister(d);
auto reg_m = ir.GetExtendedRegister(m);
if (sz) {
ir.FPCompare64(reg_d, reg_m, exc_on_qnan, true);
} else {
ir.FPCompare32(reg_d, reg_m, exc_on_qnan, true);
}
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VCMP_zero(Cond cond, bool D, size_t Vd, bool sz, bool E) {
ExtReg d = ToExtReg(sz, Vd, D);
bool exc_on_qnan = E;
// VCMP{E}.F32 <Sd>, #0.0
// VCMP{E}.F64 <Dd>, #0.0
if (ConditionPassed(cond)) {
auto reg_d = ir.GetExtendedRegister(d);
auto zero = sz
? ir.TransferToFP64(ir.Imm64(0))
: ir.TransferToFP32(ir.Imm32(0));
if (sz) {
ir.FPCompare64(reg_d, zero, exc_on_qnan, true);
} else {
ir.FPCompare32(reg_d, zero, exc_on_qnan, true);
}
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMSR(Cond cond, Reg t) {
if (t == Reg::PC)
return UnpredictableInstruction();
// VMSR FPSCR, <Rt>
if (ConditionPassed(cond)) {
ir.PushRSB(ir.current_location.AdvancePC(4)); // TODO: Replace this with a local cache.
ir.SetFpscr(ir.GetRegister(t));
ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 4));
ir.SetTerm(IR::Term::PopRSBHint{});
return false;
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VMRS(Cond cond, Reg t) {
// VMRS <Rt>, FPSCR
if (ConditionPassed(cond)) {
if (t == Reg::R15) {
// This encodes ASPR_nzcv access
auto nzcv = ir.GetFpscrNZCV();
ir.SetCpsrNZCV(nzcv);
} else {
ir.SetRegister(t, ir.GetFpscr());
}
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VPOP(Cond cond, bool D, size_t Vd, bool sz, Imm8 imm8) {
const ExtReg d = ToExtReg(sz, Vd, D);
const size_t regs = sz ? imm8 >> 1 : imm8;
if (regs == 0 || RegNumber(d)+regs > 32)
return UnpredictableInstruction();
if (sz && regs > 16)
return UnpredictableInstruction();
// VPOP.{F32,F64} <list>
if (ConditionPassed(cond)) {
auto address = ir.GetRegister(Reg::SP);
for (size_t i = 0; i < regs; ++i) {
if (sz) {
auto lo = ir.ReadMemory32(address);
address = ir.Add(address, ir.Imm32(4));
auto hi = ir.ReadMemory32(address);
address = ir.Add(address, ir.Imm32(4));
if (ir.current_location.EFlag()) std::swap(lo, hi);
ir.SetExtendedRegister(d + i, ir.TransferToFP64(ir.Pack2x32To1x64(lo, hi)));
} else {
auto res = ir.ReadMemory32(address);
ir.SetExtendedRegister(d + i, ir.TransferToFP32(res));
address = ir.Add(address, ir.Imm32(4));
}
}
ir.SetRegister(Reg::SP, address);
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VPUSH(Cond cond, bool D, size_t Vd, bool sz, Imm8 imm8) {
u32 imm32 = imm8 << 2;
const ExtReg d = ToExtReg(sz, Vd, D);
const size_t regs = sz ? imm8 >> 1 : imm8;
if (regs == 0 || RegNumber(d)+regs > 32)
return UnpredictableInstruction();
if (sz && regs > 16)
return UnpredictableInstruction();
// VPUSH.{F32,F64} <list>
if (ConditionPassed(cond)) {
auto address = ir.Sub(ir.GetRegister(Reg::SP), ir.Imm32(imm32));
ir.SetRegister(Reg::SP, address);
for (size_t i = 0; i < regs; ++i) {
if (sz) {
const auto d_u64 = ir.TransferFromFP64(ir.GetExtendedRegister(d + i));
auto lo = ir.LeastSignificantWord(d_u64);
auto hi = ir.MostSignificantWord(d_u64).result;
if (ir.current_location.EFlag()) std::swap(lo, hi);
ir.WriteMemory32(address, lo);
address = ir.Add(address, ir.Imm32(4));
ir.WriteMemory32(address, hi);
address = ir.Add(address, ir.Imm32(4));
} else {
ir.WriteMemory32(address, ir.TransferFromFP32(ir.GetExtendedRegister(d + i)));
address = ir.Add(address, ir.Imm32(4));
}
}
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VLDR(Cond cond, bool U, bool D, Reg n, size_t Vd, bool sz, Imm8 imm8) {
u32 imm32 = imm8 << 2;
ExtReg d = ToExtReg(sz, Vd, D);
// VLDR <{S,D}d>, [<Rn>, #+/-<imm32>]
if (ConditionPassed(cond)) {
auto base = n == Reg::PC ? ir.Imm32(ir.AlignPC(4)) : ir.GetRegister(n);
auto address = U ? ir.Add(base, ir.Imm32(imm32)) : ir.Sub(base, ir.Imm32(imm32));
if (sz) {
auto lo = ir.ReadMemory32(address);
auto hi = ir.ReadMemory32(ir.Add(address, ir.Imm32(4)));
if (ir.current_location.EFlag()) std::swap(lo, hi);
ir.SetExtendedRegister(d, ir.TransferToFP64(ir.Pack2x32To1x64(lo, hi)));
} else {
ir.SetExtendedRegister(d, ir.TransferToFP32(ir.ReadMemory32(address)));
}
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VSTR(Cond cond, bool U, bool D, Reg n, size_t Vd, bool sz, Imm8 imm8) {
u32 imm32 = imm8 << 2;
ExtReg d = ToExtReg(sz, Vd, D);
// VSTR <{S,D}d>, [<Rn>, #+/-<imm32>]
if (ConditionPassed(cond)) {
auto base = n == Reg::PC ? ir.Imm32(ir.AlignPC(4)) : ir.GetRegister(n);
auto address = U ? ir.Add(base, ir.Imm32(imm32)) : ir.Sub(base, ir.Imm32(imm32));
if (sz) {
auto d_u64 = ir.TransferFromFP64(ir.GetExtendedRegister(d));
auto lo = ir.LeastSignificantWord(d_u64);
auto hi = ir.MostSignificantWord(d_u64).result;
if (ir.current_location.EFlag()) std::swap(lo, hi);
ir.WriteMemory32(address, lo);
ir.WriteMemory32(ir.Add(address, ir.Imm32(4)), hi);
} else {
ir.WriteMemory32(address, ir.TransferFromFP32(ir.GetExtendedRegister(d)));
}
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VSTM_a1(Cond cond, bool p, bool u, bool D, bool w, Reg n, size_t Vd, Imm8 imm8) {
if (!p && !u && !w)
ASSERT_MSG(false, "Decode error");
if (p && !w)
ASSERT_MSG(false, "Decode error");
if (p == u && w)
return arm_UDF();
if (n == Reg::PC && w)
return UnpredictableInstruction();
ExtReg d = ToExtReg(true, Vd, D);
u32 imm32 = imm8 << 2;
size_t regs = imm8 / 2;
if (regs == 0 || regs > 16 || A32::RegNumber(d)+regs > 32)
return UnpredictableInstruction();
// VSTM<mode>.F64 <Rn>{!}, <list of double registers>
if (ConditionPassed(cond)) {
auto address = u ? ir.GetRegister(n) : ir.Sub(ir.GetRegister(n), ir.Imm32(imm32));
if (w)
ir.SetRegister(n, u ? ir.Add(address, ir.Imm32(imm32)) : address);
for (size_t i = 0; i < regs; i++) {
auto value = ir.TransferFromFP64(ir.GetExtendedRegister(d + i));
auto word1 = ir.LeastSignificantWord(value);
auto word2 = ir.MostSignificantWord(value).result;
if (ir.current_location.EFlag()) std::swap(word1, word2);
ir.WriteMemory32(address, word1);
address = ir.Add(address, ir.Imm32(4));
ir.WriteMemory32(address, word2);
address = ir.Add(address, ir.Imm32(4));
}
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VSTM_a2(Cond cond, bool p, bool u, bool D, bool w, Reg n, size_t Vd, Imm8 imm8) {
if (!p && !u && !w)
ASSERT_MSG(false, "Decode error");
if (p && !w)
ASSERT_MSG(false, "Decode error");
if (p == u && w)
return arm_UDF();
if (n == Reg::PC && w)
return UnpredictableInstruction();
ExtReg d = ToExtReg(false, Vd, D);
u32 imm32 = imm8 << 2;
size_t regs = imm8;
if (regs == 0 || A32::RegNumber(d)+regs > 32)
return UnpredictableInstruction();
// VSTM<mode>.F32 <Rn>{!}, <list of single registers>
if (ConditionPassed(cond)) {
auto address = u ? ir.GetRegister(n) : ir.Sub(ir.GetRegister(n), ir.Imm32(imm32));
if (w)
ir.SetRegister(n, u ? ir.Add(address, ir.Imm32(imm32)) : address);
for (size_t i = 0; i < regs; i++) {
auto word = ir.TransferFromFP32(ir.GetExtendedRegister(d + i));
ir.WriteMemory32(address, word);
address = ir.Add(address, ir.Imm32(4));
}
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VLDM_a1(Cond cond, bool p, bool u, bool D, bool w, Reg n, size_t Vd, Imm8 imm8) {
if (!p && !u && !w)
ASSERT_MSG(false, "Decode error");
if (p && !w)
ASSERT_MSG(false, "Decode error");
if (p == u && w)
return arm_UDF();
if (n == Reg::PC && w)
return UnpredictableInstruction();
ExtReg d = ToExtReg(true, Vd, D);
u32 imm32 = imm8 << 2;
size_t regs = imm8 / 2;
if (regs == 0 || regs > 16 || A32::RegNumber(d)+regs > 32)
return UnpredictableInstruction();
// VLDM<mode>.F64 <Rn>{!}, <list of double registers>
if (ConditionPassed(cond)) {
auto address = u ? ir.GetRegister(n) : ir.Sub(ir.GetRegister(n), ir.Imm32(imm32));
if (w)
ir.SetRegister(n, u ? ir.Add(address, ir.Imm32(imm32)) : address);
for (size_t i = 0; i < regs; i++) {
auto word1 = ir.ReadMemory32(address);
address = ir.Add(address, ir.Imm32(4));
auto word2 = ir.ReadMemory32(address);
address = ir.Add(address, ir.Imm32(4));
if (ir.current_location.EFlag()) std::swap(word1, word2);
ir.SetExtendedRegister(d + i, ir.TransferToFP64(ir.Pack2x32To1x64(word1, word2)));
}
}
return true;
}
bool ArmTranslatorVisitor::vfp2_VLDM_a2(Cond cond, bool p, bool u, bool D, bool w, Reg n, size_t Vd, Imm8 imm8) {
if (!p && !u && !w)
ASSERT_MSG(false, "Decode error");
if (p && !w)
ASSERT_MSG(false, "Decode error");
if (p == u && w)
return arm_UDF();
if (n == Reg::PC && w)
return UnpredictableInstruction();
ExtReg d = ToExtReg(false, Vd, D);
u32 imm32 = imm8 << 2;
size_t regs = imm8;
if (regs == 0 || A32::RegNumber(d)+regs > 32)
return UnpredictableInstruction();
// VLDM<mode>.F32 <Rn>{!}, <list of single registers>
if (ConditionPassed(cond)) {
auto address = u ? ir.GetRegister(n) : ir.Sub(ir.GetRegister(n), ir.Imm32(imm32));
if (w)
ir.SetRegister(n, u ? ir.Add(address, ir.Imm32(imm32)) : address);
for (size_t i = 0; i < regs; i++) {
auto word = ir.ReadMemory32(address);
address = ir.Add(address, ir.Imm32(4));
ir.SetExtendedRegister(d + i, ir.TransferToFP32(word));
}
}
return true;
}
} // namespace A32
} // namespace Dynarmic

View File

@@ -0,0 +1,918 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include <tuple>
#include "common/assert.h"
#include "common/bit_util.h"
#include "frontend/A32/decoder/thumb16.h"
#include "frontend/A32/decoder/thumb32.h"
#include "frontend/A32/translate/translate.h"
#include "frontend/A32/types.h"
#include "frontend/ir/ir_emitter.h"
#include "frontend/ir/location_descriptor.h"
namespace Dynarmic {
namespace A32 {
namespace {
struct ThumbTranslatorVisitor final {
using instruction_return_type = bool;
explicit ThumbTranslatorVisitor(LocationDescriptor descriptor) : ir(descriptor) {
ASSERT_MSG(descriptor.TFlag(), "The processor must be in Thumb mode");
}
IR::A32IREmitter ir;
bool InterpretThisInstruction() {
ir.SetTerm(IR::Term::Interpret(ir.current_location));
return false;
}
bool UnpredictableInstruction() {
ASSERT_MSG(false, "UNPREDICTABLE");
return false;
}
bool thumb16_LSL_imm(Imm5 imm5, Reg m, Reg d) {
u8 shift_n = imm5;
// LSLS <Rd>, <Rm>, #<imm5>
auto cpsr_c = ir.GetCFlag();
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;
}
bool thumb16_LSR_imm(Imm5 imm5, Reg m, Reg d) {
u8 shift_n = imm5 != 0 ? imm5 : 32;
// LSRS <Rd>, <Rm>, #<imm5>
auto cpsr_c = ir.GetCFlag();
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;
}
bool thumb16_ASR_imm(Imm5 imm5, Reg m, Reg d) {
u8 shift_n = imm5 != 0 ? imm5 : 32;
// ASRS <Rd>, <Rm>, #<imm5>
auto cpsr_c = ir.GetCFlag();
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;
}
bool thumb16_ADD_reg_t1(Reg m, Reg n, Reg d) {
// ADDS <Rd>, <Rn>, <Rm>
// Note that it is not possible to encode Rd == R15.
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;
}
bool thumb16_SUB_reg(Reg m, Reg n, Reg d) {
// SUBS <Rd>, <Rn>, <Rm>
// Note that it is not possible to encode Rd == R15.
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;
}
bool thumb16_ADD_imm_t1(Imm3 imm3, Reg n, Reg d) {
u32 imm32 = imm3 & 0x7;
// ADDS <Rd>, <Rn>, #<imm3>
// Rd can never encode R15.
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;
}
bool thumb16_SUB_imm_t1(Imm3 imm3, Reg n, Reg d) {
u32 imm32 = imm3 & 0x7;
// SUBS <Rd>, <Rn>, #<imm3>
// Rd can never encode R15.
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;
}
bool thumb16_MOV_imm(Reg d, Imm8 imm8) {
u32 imm32 = imm8 & 0xFF;
// MOVS <Rd>, #<imm8>
// Rd can never encode R15.
auto result = ir.Imm32(imm32);
ir.SetRegister(d, result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
return true;
}
bool thumb16_CMP_imm(Reg n, Imm8 imm8) {
u32 imm32 = imm8 & 0xFF;
// CMP <Rn>, #<imm8>
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;
}
bool thumb16_ADD_imm_t2(Reg d_n, Imm8 imm8) {
u32 imm32 = imm8 & 0xFF;
Reg d = d_n, n = d_n;
// ADDS <Rdn>, #<imm8>
// Rd can never encode R15.
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;
}
bool thumb16_SUB_imm_t2(Reg d_n, Imm8 imm8) {
u32 imm32 = imm8 & 0xFF;
Reg d = d_n, n = d_n;
// SUBS <Rd>, <Rn>, #<imm3>
// Rd can never encode R15.
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;
}
bool thumb16_AND_reg(Reg m, Reg d_n) {
const Reg d = d_n, n = d_n;
// ANDS <Rdn>, <Rm>
// Note that it is not possible to encode Rdn == R15.
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;
}
bool thumb16_EOR_reg(Reg m, Reg d_n) {
const Reg d = d_n, n = d_n;
// EORS <Rdn>, <Rm>
// Note that it is not possible to encode Rdn == R15.
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;
}
bool thumb16_LSL_reg(Reg m, Reg d_n) {
const Reg d = d_n, n = d_n;
// LSLS <Rdn>, <Rm>
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
auto apsr_c = ir.GetCFlag();
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;
}
bool thumb16_LSR_reg(Reg m, Reg d_n) {
const Reg d = d_n, n = d_n;
// LSRS <Rdn>, <Rm>
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
auto cpsr_c = ir.GetCFlag();
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;
}
bool thumb16_ASR_reg(Reg m, Reg d_n) {
const Reg d = d_n, n = d_n;
// ASRS <Rdn>, <Rm>
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
auto cpsr_c = ir.GetCFlag();
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;
}
bool thumb16_ADC_reg(Reg m, Reg d_n) {
Reg d = d_n, n = d_n;
// ADCS <Rdn>, <Rm>
// Note that it is not possible to encode Rd == R15.
auto aspr_c = ir.GetCFlag();
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;
}
bool thumb16_SBC_reg(Reg m, Reg d_n) {
Reg d = d_n, n = d_n;
// SBCS <Rdn>, <Rm>
// Note that it is not possible to encode Rd == R15.
auto aspr_c = ir.GetCFlag();
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;
}
bool thumb16_ROR_reg(Reg m, Reg d_n) {
Reg d = d_n, n = d_n;
// RORS <Rdn>, <Rm>
auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m));
auto cpsr_c = ir.GetCFlag();
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;
}
bool thumb16_TST_reg(Reg m, Reg n) {
// TST <Rn>, <Rm>
auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m));
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
return true;
}
bool thumb16_RSB_imm(Reg n, Reg d) {
// RSBS <Rd>, <Rn>, #0
// Rd can never encode R15.
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;
}
bool thumb16_CMP_reg_t1(Reg m, Reg n) {
// CMP <Rn>, <Rm>
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;
}
bool thumb16_CMN_reg(Reg m, Reg n) {
// CMN <Rn>, <Rm>
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;
}
bool thumb16_ORR_reg(Reg m, Reg d_n) {
Reg d = d_n, n = d_n;
// ORRS <Rdn>, <Rm>
// Rd cannot encode R15.
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;
}
bool thumb16_MUL_reg(Reg n, Reg d_m) {
Reg d = d_m, m = d_m;
// MULS <Rdn>, <Rm>, <Rdn>
// Rd cannot encode R15.
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;
}
bool thumb16_BIC_reg(Reg m, Reg d_n) {
Reg d = d_n, n = d_n;
// BICS <Rdn>, <Rm>
// Rd cannot encode R15.
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;
}
bool thumb16_MVN_reg(Reg m, Reg d) {
// MVNS <Rd>, <Rm>
// Rd cannot encode R15.
auto result = ir.Not(ir.GetRegister(m));
ir.SetRegister(d, result);
ir.SetNFlag(ir.MostSignificantBit(result));
ir.SetZFlag(ir.IsZero(result));
return true;
}
bool thumb16_ADD_reg_t2(bool d_n_hi, Reg m, Reg d_n_lo) {
Reg d_n = d_n_hi ? (d_n_lo + 8) : d_n_lo;
Reg d = d_n, n = d_n;
if (n == Reg::PC && m == Reg::PC) {
return UnpredictableInstruction();
}
// ADD <Rdn>, <Rm>
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::ReturnToDispatch{});
return false;
} else {
ir.SetRegister(d, result.result);
return true;
}
}
bool thumb16_CMP_reg_t2(bool n_hi, Reg m, Reg n_lo) {
Reg n = n_hi ? (n_lo + 8) : n_lo;
if (n < Reg::R8 && m < Reg::R8) {
return UnpredictableInstruction();
} else if (n == Reg::PC || m == Reg::PC) {
return UnpredictableInstruction();
}
// CMP <Rn>, <Rm>
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;
}
bool thumb16_MOV_reg(bool d_hi, Reg m, Reg d_lo) {
Reg d = d_hi ? (d_lo + 8) : d_lo;
// MOV <Rd>, <Rm>
auto result = ir.GetRegister(m);
if (d == Reg::PC) {
ir.ALUWritePC(result);
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
} else {
ir.SetRegister(d, result);
return true;
}
}
bool thumb16_LDR_literal(Reg t, Imm8 imm8) {
u32 imm32 = imm8 << 2;
// LDR <Rt>, <label>
// Rt cannot encode R15.
u32 address = ir.AlignPC(4) + imm32;
auto data = ir.ReadMemory32(ir.Imm32(address));
ir.SetRegister(t, data);
return true;
}
bool thumb16_STR_reg(Reg m, Reg n, Reg t) {
// STR <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
auto data = ir.GetRegister(t);
ir.WriteMemory32(address, data);
return true;
}
bool thumb16_STRH_reg(Reg m, Reg n, Reg t) {
// STRH <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
auto data = ir.LeastSignificantHalf(ir.GetRegister(t));
ir.WriteMemory16(address, data);
return true;
}
bool thumb16_STRB_reg(Reg m, Reg n, Reg t) {
// STRB <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
auto data = ir.LeastSignificantByte(ir.GetRegister(t));
ir.WriteMemory8(address, data);
return true;
}
bool thumb16_LDRSB_reg(Reg m, Reg n, Reg t) {
// LDRSB <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
auto data = ir.SignExtendByteToWord(ir.ReadMemory8(address));
ir.SetRegister(t, data);
return true;
}
bool thumb16_LDR_reg(Reg m, Reg n, Reg t) {
// LDR <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
auto data = ir.ReadMemory32(address);
ir.SetRegister(t, data);
return true;
}
bool thumb16_LDRH_reg(Reg m, Reg n, Reg t) {
// LDRH <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address));
ir.SetRegister(t, data);
return true;
}
bool thumb16_LDRB_reg(Reg m, Reg n, Reg t) {
// LDRB <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address));
ir.SetRegister(t, data);
return true;
}
bool thumb16_LDRSH_reg(Reg m, Reg n, Reg t) {
// LDRH <Rt>, [<Rn>, <Rm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
auto data = ir.SignExtendHalfToWord(ir.ReadMemory16(address));
ir.SetRegister(t, data);
return true;
}
bool thumb16_STR_imm_t1(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5 << 2;
// STR <Rt>, [<Rn>, #<imm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
auto data = ir.GetRegister(t);
ir.WriteMemory32(address, data);
return true;
}
bool thumb16_LDR_imm_t1(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5 << 2;
// LDR <Rt>, [<Rn>, #<imm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
auto data = ir.ReadMemory32(address);
ir.SetRegister(t, data);
return true;
}
bool thumb16_STRB_imm(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5;
// STRB <Rt>, [<Rn>, #<imm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
auto data = ir.LeastSignificantByte(ir.GetRegister(t));
ir.WriteMemory8(address, data);
return true;
}
bool thumb16_LDRB_imm(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5;
// LDRB <Rt>, [<Rn>, #<imm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
auto data = ir.ZeroExtendByteToWord(ir.ReadMemory8(address));
ir.SetRegister(t, data);
return true;
}
bool thumb16_STRH_imm(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5 << 1;
// STRH <Rt>, [<Rn>, #<imm5>]
auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
auto data = ir.LeastSignificantHalf(ir.GetRegister(t));
ir.WriteMemory16(address, data);
return true;
}
bool thumb16_LDRH_imm(Imm5 imm5, Reg n, Reg t) {
u32 imm32 = imm5 << 1;
// LDRH <Rt>, [<Rn>, #<imm5>]
auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
auto data = ir.ZeroExtendHalfToWord(ir.ReadMemory16(address));
ir.SetRegister(t, data);
return true;
}
bool thumb16_STR_imm_t2(Reg t, Imm5 imm5) {
u32 imm32 = imm5 << 2;
Reg n = Reg::SP;
// STR <Rt>, [<Rn>, #<imm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
auto data = ir.GetRegister(t);
ir.WriteMemory32(address, data);
return true;
}
bool thumb16_LDR_imm_t2(Reg t, Imm5 imm5) {
u32 imm32 = imm5 << 2;
Reg n = Reg::SP;
// LDR <Rt>, [<Rn>, #<imm>]
// Rt cannot encode R15.
auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
auto data = ir.ReadMemory32(address);
ir.SetRegister(t, data);
return true;
}
bool thumb16_ADR(Reg d, Imm8 imm8) {
u32 imm32 = imm8 << 2;
// ADR <Rd>, <label>
// Rd cannot encode R15.
auto result = ir.Imm32(ir.AlignPC(4) + imm32);
ir.SetRegister(d, result);
return true;
}
bool thumb16_ADD_sp_t1(Reg d, Imm8 imm8) {
u32 imm32 = imm8 << 2;
// ADD <Rd>, SP, #<imm>
auto result = ir.AddWithCarry(ir.GetRegister(Reg::SP), ir.Imm32(imm32), ir.Imm1(0));
ir.SetRegister(d, result.result);
return true;
}
bool thumb16_ADD_sp_t2(Imm7 imm7) {
u32 imm32 = imm7 << 2;
Reg d = Reg::SP;
// ADD SP, SP, #<imm>
auto result = ir.AddWithCarry(ir.GetRegister(Reg::SP), ir.Imm32(imm32), ir.Imm1(0));
ir.SetRegister(d, result.result);
return true;
}
bool thumb16_SUB_sp(Imm7 imm7) {
u32 imm32 = imm7 << 2;
Reg d = Reg::SP;
// SUB SP, SP, #<imm>
auto result = ir.SubWithCarry(ir.GetRegister(Reg::SP), ir.Imm32(imm32), ir.Imm1(1));
ir.SetRegister(d, result.result);
return true;
}
bool thumb16_SXTH(Reg m, Reg d) {
// SXTH <Rd>, <Rm>
// Rd cannot encode R15.
auto half = ir.LeastSignificantHalf(ir.GetRegister(m));
ir.SetRegister(d, ir.SignExtendHalfToWord(half));
return true;
}
bool thumb16_SXTB(Reg m, Reg d) {
// SXTB <Rd>, <Rm>
// Rd cannot encode R15.
auto byte = ir.LeastSignificantByte(ir.GetRegister(m));
ir.SetRegister(d, ir.SignExtendByteToWord(byte));
return true;
}
bool thumb16_UXTH(Reg m, Reg d) {
// UXTH <Rd>, <Rm>
// Rd cannot encode R15.
auto half = ir.LeastSignificantHalf(ir.GetRegister(m));
ir.SetRegister(d, ir.ZeroExtendHalfToWord(half));
return true;
}
bool thumb16_UXTB(Reg m, Reg d) {
// UXTB <Rd>, <Rm>
// Rd cannot encode R15.
auto byte = ir.LeastSignificantByte(ir.GetRegister(m));
ir.SetRegister(d, ir.ZeroExtendByteToWord(byte));
return true;
}
bool thumb16_PUSH(bool M, RegList reg_list) {
if (M) reg_list |= 1 << 14;
if (Common::BitCount(reg_list) < 1) {
return UnpredictableInstruction();
}
// PUSH <reg_list>
// reg_list cannot encode for R15.
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
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;
}
bool thumb16_POP(bool P, RegList reg_list) {
if (P) reg_list |= 1 << 15;
if (Common::BitCount(reg_list) < 1) {
return UnpredictableInstruction();
}
// POP <reg_list>
auto address = ir.GetRegister(Reg::SP);
for (size_t i = 0; i < 15; i++) {
if (Common::Bit(i, reg_list)) {
// TODO: Deal with alignment
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.
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;
}
}
bool thumb16_SETEND(bool E) {
// SETEND <endianness>
if (E == ir.current_location.EFlag()) {
return true;
}
ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(2).SetEFlag(E)});
return false;
}
bool thumb16_CPS(bool, bool, bool, bool) {
// CPS{IE,ID} <a,i,f>
// A CPS is treated as a NOP in User mode.
return true;
}
bool thumb16_REV(Reg m, Reg d) {
// REV <Rd>, <Rm>
// Rd cannot encode R15.
ir.SetRegister(d, ir.ByteReverseWord(ir.GetRegister(m)));
return true;
}
bool thumb16_REV16(Reg m, Reg d) {
// REV16 <Rd>, <Rm>
// Rd cannot encode R15.
// TODO: Consider optimizing
auto Rm = ir.GetRegister(m);
auto upper_half = ir.LeastSignificantHalf(ir.LogicalShiftRight(Rm, ir.Imm8(16), ir.Imm1(0)).result);
auto lower_half = ir.LeastSignificantHalf(Rm);
auto rev_upper_half = ir.ZeroExtendHalfToWord(ir.ByteReverseHalf(upper_half));
auto rev_lower_half = ir.ZeroExtendHalfToWord(ir.ByteReverseHalf(lower_half));
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;
}
bool thumb16_REVSH(Reg m, Reg d) {
// REVSH <Rd>, <Rm>
// Rd cannot encode R15.
auto rev_half = ir.ByteReverseHalf(ir.LeastSignificantHalf(ir.GetRegister(m)));
ir.SetRegister(d, ir.SignExtendHalfToWord(rev_half));
return true;
}
bool thumb16_STMIA(Reg n, RegList reg_list) {
// STM <Rn>!, <reg_list>
auto address = ir.GetRegister(n);
for (size_t i = 0; i < 8; i++) {
if (Common::Bit(i, reg_list)) {
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;
}
bool thumb16_LDMIA(Reg n, RegList reg_list) {
bool write_back = !Dynarmic::Common::Bit(static_cast<size_t>(n), reg_list);
// STM <Rn>!, <reg_list>
auto address = ir.GetRegister(n);
for (size_t i = 0; i < 8; i++) {
if (Common::Bit(i, reg_list)) {
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;
}
bool thumb16_UDF() {
return InterpretThisInstruction();
}
bool thumb16_BX(Reg m) {
// BX <Rm>
ir.BXWritePC(ir.GetRegister(m));
if (m == Reg::R14)
ir.SetTerm(IR::Term::PopRSBHint{});
else
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
bool thumb16_BLX_reg(Reg m) {
// BLX <Rm>
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::ReturnToDispatch{});
return false;
}
bool thumb16_SVC(Imm8 imm8) {
u32 imm32 = imm8;
// SVC #<imm8>
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;
}
bool thumb16_B_t1(Cond cond, Imm8 imm8) {
s32 imm32 = Common::SignExtend<9, s32>(imm8 << 1) + 4;
if (cond == Cond::AL) {
return thumb16_UDF();
}
// B<cond> <label>
auto then_location = ir.current_location.AdvancePC(imm32);
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;
}
bool thumb16_B_t2(Imm11 imm11) {
s32 imm32 = Common::SignExtend<12, s32>(imm11 << 1) + 4;
// B <label>
auto next_location = ir.current_location.AdvancePC(imm32);
ir.SetTerm(IR::Term::LinkBlock{next_location});
return false;
}
bool thumb32_BL_imm(Imm11 hi, Imm11 lo) {
s32 imm32 = Common::SignExtend<23, s32>((hi << 12) | (lo << 1)) + 4;
// BL <label>
ir.PushRSB(ir.current_location.AdvancePC(4));
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1));
auto new_location = ir.current_location.AdvancePC(imm32);
ir.SetTerm(IR::Term::LinkBlock{new_location});
return false;
}
bool thumb32_BLX_imm(Imm11 hi, Imm11 lo) {
s32 imm32 = Common::SignExtend<23, s32>((hi << 12) | (lo << 1));
if ((lo & 1) != 0) {
return UnpredictableInstruction();
}
// BLX <label>
ir.PushRSB(ir.current_location.AdvancePC(4));
ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1));
auto new_location = ir.current_location
.SetPC(ir.AlignPC(4) + imm32)
.SetTFlag(false);
ir.SetTerm(IR::Term::LinkBlock{new_location});
return false;
}
bool thumb32_UDF() {
return thumb16_UDF();
}
};
enum class ThumbInstSize {
Thumb16, Thumb32
};
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 ((first_part & 0xF800) <= 0xE800) {
// 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) {
ThumbTranslatorVisitor visitor{descriptor};
bool should_continue = true;
while (should_continue) {
const u32 arm_pc = visitor.ir.current_location.PC();
u32 thumb_instruction;
ThumbInstSize inst_size;
std::tie(thumb_instruction, inst_size) = ReadThumbInstruction(arm_pc, memory_read_code);
if (inst_size == ThumbInstSize::Thumb16) {
auto decoder = DecodeThumb16<ThumbTranslatorVisitor>(static_cast<u16>(thumb_instruction));
if (decoder) {
should_continue = decoder->call(visitor, static_cast<u16>(thumb_instruction));
} else {
should_continue = visitor.thumb16_UDF();
}
} else {
auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction);
if (decoder) {
should_continue = decoder->call(visitor, thumb_instruction);
} else {
should_continue = visitor.thumb32_UDF();
}
}
s32 advance_pc = (inst_size == ThumbInstSize::Thumb16) ? 2 : 4;
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(advance_pc);
visitor.ir.block.CycleCount()++;
}
visitor.ir.block.SetEndLocation(visitor.ir.current_location);
return std::move(visitor.ir.block);
}
} // namespace A32
} // namepsace Dynarmic

View File

@@ -0,0 +1,82 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include <array>
#include <ostream>
#include "common/bit_util.h"
#include "frontend/A32/types.h"
namespace Dynarmic {
namespace A32 {
const char* CondToString(Cond cond, bool explicit_al) {
constexpr std::array<const char*, 15> cond_strs = {
"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "al"
};
return (!explicit_al && cond == Cond::AL) ? "" : cond_strs.at(static_cast<size_t>(cond));
}
const char* RegToString(Reg reg) {
constexpr std::array<const char*, 16> 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) {
constexpr std::array<const char*, 64> 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",
};
return reg_strs.at(static_cast<size_t>(reg));
}
const char* CoprocRegToString(CoprocReg reg) {
constexpr std::array<const char*, 16> 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 A32
} // namespace Dynarmic

122
src/frontend/A32/types.h Normal file
View File

@@ -0,0 +1,122 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include <iosfwd>
#include <string>
#include <utility>
#include <dynarmic/coprocessor_util.h>
#include "common/assert.h"
#include "common/common_types.h"
#include "frontend/ir/cond.h"
namespace Dynarmic {
namespace 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,
};
using Imm3 = u8;
using Imm4 = u8;
using Imm5 = u8;
using Imm7 = u8;
using Imm8 = u8;
using Imm11 = u16;
using Imm12 = u16;
using Imm24 = u32;
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;
}
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);
}
ASSERT_MSG(false, "Invalid extended register");
}
inline Reg operator+(Reg reg, size_t number) {
ASSERT(reg != Reg::INVALID_REG);
size_t new_reg = static_cast<size_t>(reg) + number;
ASSERT(new_reg <= 15);
return static_cast<Reg>(new_reg);
}
inline ExtReg operator+(ExtReg reg, size_t number) {
ExtReg new_reg = static_cast<ExtReg>(static_cast<size_t>(reg) + number);
ASSERT((IsSingleExtReg(reg) && IsSingleExtReg(new_reg)) ||
(IsDoubleExtReg(reg) && IsDoubleExtReg(new_reg)));
return new_reg;
}
} // namespace A32
} // namespace Dynarmic