summaryrefslogtreecommitdiff
path: root/Source/Core/Common/Src/ArmEmitter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/Common/Src/ArmEmitter.cpp')
-rw-r--r--Source/Core/Common/Src/ArmEmitter.cpp1110
1 files changed, 1110 insertions, 0 deletions
diff --git a/Source/Core/Common/Src/ArmEmitter.cpp b/Source/Core/Common/Src/ArmEmitter.cpp
new file mode 100644
index 0000000000..9b738be030
--- /dev/null
+++ b/Source/Core/Common/Src/ArmEmitter.cpp
@@ -0,0 +1,1110 @@
+// Copyright (C) 2003 Dolphin Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official SVN repository and contact information can be found at
+// http://code.google.com/p/dolphin-emu/
+
+#include "Common.h"
+#include "ArmEmitter.h"
+#include "CPUDetect.h"
+
+#include <assert.h>
+#include <stdarg.h>
+
+// For cache flushing on Symbian/iOS/Blackberry
+#ifdef __SYMBIAN32__
+#include <e32std.h>
+#endif
+
+#ifdef IOS
+#include <libkern/OSCacheControl.h>
+#include <sys/mman.h>
+#endif
+
+#ifdef BLACKBERRY
+#include <sys/mman.h>
+#endif
+
+namespace ArmGen
+{
+
+inline u32 RotR(u32 a, int amount) {
+ if (!amount) return a;
+ return (a >> amount) | (a << (32 - amount));
+}
+
+inline u32 RotL(u32 a, int amount) {
+ if (!amount) return a;
+ return (a << amount) | (a >> (32 - amount));
+}
+
+bool TryMakeOperand2(u32 imm, Operand2 &op2) {
+ // Just brute force it.
+ for (int i = 0; i < 16; i++) {
+ int mask = RotR(0xFF, i * 2);
+ if ((imm & mask) == imm) {
+ op2 = Operand2((u8)(RotL(imm, i * 2)), (u8)i);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse)
+{
+ if (!TryMakeOperand2(imm, op2)) {
+ *inverse = true;
+ return TryMakeOperand2(~imm, op2);
+ } else {
+ *inverse = false;
+ return true;
+ }
+}
+
+bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated)
+{
+ if (!TryMakeOperand2(imm, op2)) {
+ *negated = true;
+ return TryMakeOperand2(-imm, op2);
+ } else {
+ *negated = false;
+ return true;
+ }
+}
+
+void ARMXEmitter::MOVI2F(ARMReg dest, float val, ARMReg tempReg)
+{
+ union {float f; u32 u;} conv;
+ conv.f = val;
+ MOVI2R(tempReg, conv.u);
+ VMOV(dest, tempReg);
+ // TODO: VMOV an IMM directly if possible
+ // Otherwise, use a literal pool and VLDR directly (+- 1020)
+}
+
+void ARMXEmitter::ANDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch)
+{
+ Operand2 op2;
+ bool inverse;
+ if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) {
+ if (!inverse) {
+ AND(rd, rs, op2);
+ } else {
+ BIC(rd, rs, op2);
+ }
+ } else {
+ MOVI2R(scratch, val);
+ AND(rd, rs, scratch);
+ }
+}
+
+void ARMXEmitter::ORI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch)
+{
+ Operand2 op2;
+ if (TryMakeOperand2(val, op2)) {
+ ORR(rd, rs, op2);
+ } else {
+ MOVI2R(scratch, val);
+ ORR(rd, rs, scratch);
+ }
+}
+
+void ARMXEmitter::FlushLitPool()
+{
+ for(std::vector<LiteralPool>::iterator it = currentLitPool.begin(); it != currentLitPool.end(); ++it) {
+ // Search for duplicates
+ for(std::vector<LiteralPool>::iterator old_it = currentLitPool.begin(); old_it != it; ++old_it) {
+ if ((*old_it).val == (*it).val)
+ (*it).loc = (*old_it).loc;
+ }
+
+ // Write the constant to Literal Pool
+ if (!(*it).loc)
+ {
+ (*it).loc = (s32)code;
+ Write32((*it).val);
+ }
+ s32 offset = (*it).loc - (s32)(*it).ldr_address - 8;
+
+ // Backpatch the LDR
+ *(u32*)(*it).ldr_address |= (offset >= 0) << 23 | abs(offset);
+ }
+ // TODO: Save a copy of previous pools in case they are still in range.
+ currentLitPool.clear();
+}
+
+void ARMXEmitter::AddNewLit(u32 val)
+{
+ LiteralPool pool_item;
+ pool_item.loc = 0;
+ pool_item.val = val;
+ pool_item.ldr_address = code;
+ currentLitPool.push_back(pool_item);
+}
+
+void ARMXEmitter::MOVI2R(ARMReg reg, u32 val, bool optimize)
+{
+ Operand2 op2;
+ bool inverse;
+
+ if (cpu_info.bArmV7 && !optimize)
+ {
+ // For backpatching on ARMv7
+ MOVW(reg, val & 0xFFFF);
+ MOVT(reg, val, true);
+ }
+ else if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) {
+ inverse ? MVN(reg, op2) : MOV(reg, op2);
+ } else {
+ if (cpu_info.bArmV7)
+ {
+ // Use MOVW+MOVT for ARMv7+
+ MOVW(reg, val & 0xFFFF);
+ if(val & 0xFFFF0000)
+ MOVT(reg, val, true);
+ } else {
+ // Use literal pool for ARMv6.
+ AddNewLit(val);
+ LDR(reg, _PC); // To be backpatched later
+ }
+ }
+}
+
+void ARMXEmitter::QuickCallFunction(ARMReg reg, void *func) {
+ MOVI2R(reg, (u32)(func));
+ BL(reg);
+}
+
+void ARMXEmitter::SetCodePtr(u8 *ptr)
+{
+ code = ptr;
+ startcode = code;
+#ifdef IOS
+ lastCacheFlushEnd = ptr;
+#endif
+}
+
+const u8 *ARMXEmitter::GetCodePtr() const
+{
+ return code;
+}
+
+u8 *ARMXEmitter::GetWritableCodePtr()
+{
+ return code;
+}
+
+void ARMXEmitter::ReserveCodeSpace(u32 bytes)
+{
+ for (u32 i = 0; i < bytes/4; i++)
+ Write32(0xE1200070); //bkpt 0
+}
+
+const u8 *ARMXEmitter::AlignCode16()
+{
+ ReserveCodeSpace((-(s32)code) & 15);
+ return code;
+}
+
+const u8 *ARMXEmitter::AlignCodePage()
+{
+ ReserveCodeSpace((-(s32)code) & 4095);
+ return code;
+}
+
+void ARMXEmitter::FlushIcache()
+{
+ FlushIcacheSection(lastCacheFlushEnd, code);
+ lastCacheFlushEnd = code;
+}
+
+void ARMXEmitter::FlushIcacheSection(u8 *start, u8 *end)
+{
+#ifdef __SYMBIAN32__
+ User::IMB_Range(start, end);
+#elif defined(BLACKBERRY)
+ msync(start, end - start, MS_SYNC | MS_INVALIDATE_ICACHE);
+#elif defined(IOS)
+ if (start != NULL)
+ sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start);
+#elif !defined(_WIN32)
+#ifndef ANDROID
+ start = startcode; // Should be Linux Only
+#endif
+ __builtin___clear_cache(start, end);
+#endif
+}
+
+void ARMXEmitter::SetCC(CCFlags cond)
+{
+ condition = cond << 28;
+}
+
+void ARMXEmitter::NOP(int count)
+{
+ for (int i = 0; i < count; i++) {
+ Write32(condition | 0x01A00000);
+ }
+}
+
+void ARMXEmitter::SETEND(bool BE)
+{
+ //SETEND is non-conditional
+ Write32( 0xF1010000 | (BE << 9));
+}
+void ARMXEmitter::BKPT(u16 arg)
+{
+ Write32(condition | 0x01200070 | (arg << 4 & 0x000FFF00) | (arg & 0x0000000F));
+}
+void ARMXEmitter::YIELD()
+{
+ Write32(condition | 0x0320F001);
+}
+
+FixupBranch ARMXEmitter::B()
+{
+ FixupBranch branch;
+ branch.type = 0; // Zero for B
+ branch.ptr = code;
+ branch.condition = condition;
+ //We'll write NOP here for now.
+ Write32(condition | 0x01A00000);
+ return branch;
+}
+FixupBranch ARMXEmitter::BL()
+{
+ FixupBranch branch;
+ branch.type = 1; // Zero for B
+ branch.ptr = code;
+ branch.condition = condition;
+ //We'll write NOP here for now.
+ Write32(condition | 0x01A00000);
+ return branch;
+}
+
+FixupBranch ARMXEmitter::B_CC(CCFlags Cond)
+{
+ FixupBranch branch;
+ branch.type = 0; // Zero for B
+ branch.ptr = code;
+ branch.condition = Cond << 28;
+ //We'll write NOP here for now.
+ Write32(condition | 0x01A00000);
+ return branch;
+}
+void ARMXEmitter::B_CC(CCFlags Cond, const void *fnptr)
+{
+ s32 distance = (s32)fnptr - (s32(code) + 8);
+ _assert_msg_(DYNA_REC, distance > -33554432
+ && distance <= 33554432,
+ "B_CC out of range (%p calls %p)", code, fnptr);
+
+ Write32((Cond << 28) | 0x0A000000 | ((distance >> 2) & 0x00FFFFFF));
+}
+FixupBranch ARMXEmitter::BL_CC(CCFlags Cond)
+{
+ FixupBranch branch;
+ branch.type = 1; // Zero for B
+ branch.ptr = code;
+ branch.condition = Cond << 28;
+ //We'll write NOP here for now.
+ Write32(condition | 0x01A00000);
+ return branch;
+}
+void ARMXEmitter::SetJumpTarget(FixupBranch const &branch)
+{
+ s32 distance = (s32(code) - 8) - (s32)branch.ptr;
+ _assert_msg_(DYNA_REC, distance > -33554432
+ && distance <= 33554432,
+ "SetJumpTarget out of range (%p calls %p)", code,
+ branch.ptr);
+ if(branch.type == 0) // B
+ *(u32*)branch.ptr = (u32)(branch.condition | (10 << 24) | ((distance >> 2) &
+ 0x00FFFFFF));
+ else // BL
+ *(u32*)branch.ptr = (u32)(branch.condition | 0x0B000000 | ((distance >> 2)
+ & 0x00FFFFFF));
+}
+void ARMXEmitter::B (const void *fnptr)
+{
+ s32 distance = (s32)fnptr - (s32(code) + 8);
+ _assert_msg_(DYNA_REC, distance > -33554432
+ && distance <= 33554432,
+ "B out of range (%p calls %p)", code, fnptr);
+
+ Write32(condition | 0x0A000000 | ((distance >> 2) & 0x00FFFFFF));
+}
+
+void ARMXEmitter::B(ARMReg src)
+{
+ Write32(condition | 0x12FFF10 | src);
+}
+
+void ARMXEmitter::BL(const void *fnptr)
+{
+ s32 distance = (s32)fnptr - (s32(code) + 8);
+ _assert_msg_(DYNA_REC, distance > -33554432
+ && distance <= 33554432,
+ "BL out of range (%p calls %p)", code, fnptr);
+ Write32(condition | 0x0B000000 | ((distance >> 2) & 0x00FFFFFF));
+}
+void ARMXEmitter::BL(ARMReg src)
+{
+ Write32(condition | 0x12FFF30 | src);
+}
+void ARMXEmitter::PUSH(const int num, ...)
+{
+ u16 RegList = 0;
+ u8 Reg;
+ int i;
+ va_list vl;
+ va_start(vl, num);
+ for (i=0;i<num;i++)
+ {
+ Reg = va_arg(vl, u32);
+ RegList |= (1 << Reg);
+ }
+ va_end(vl);
+ Write32(condition | (2349 << 16) | RegList);
+}
+void ARMXEmitter::POP(const int num, ...)
+{
+ u16 RegList = 0;
+ u8 Reg;
+ int i;
+ va_list vl;
+ va_start(vl, num);
+ for (i=0;i<num;i++)
+ {
+ Reg = va_arg(vl, u32);
+ RegList |= (1 << Reg);
+ }
+ va_end(vl);
+ Write32(condition | (2237 << 16) | RegList);
+}
+
+void ARMXEmitter::WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, Operand2 op2)
+{
+ Write32(condition | (13 << 21) | (SetFlags << 20) | (dest << 12) | op2.Imm5() | (op << 4) | src);
+}
+void ARMXEmitter::WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, ARMReg op2)
+{
+ Write32(condition | (13 << 21) | (SetFlags << 20) | (dest << 12) | (op2 << 8) | (op << 4) | src);
+}
+
+// IMM, REG, IMMSREG, RSR
+// -1 for invalid if the instruction doesn't support that
+const s32 InstOps[][4] = {{16, 0, 0, 0}, // AND(s)
+ {17, 1, 1, 1}, // EOR(s)
+ {18, 2, 2, 2}, // SUB(s)
+ {19, 3, 3, 3}, // RSB(s)
+ {20, 4, 4, 4}, // ADD(s)
+ {21, 5, 5, 5}, // ADC(s)
+ {22, 6, 6, 6}, // SBC(s)
+ {23, 7, 7, 7}, // RSC(s)
+ {24, 8, 8, 8}, // TST
+ {25, 9, 9, 9}, // TEQ
+ {26, 10, 10, 10}, // CMP
+ {27, 11, 11, 11}, // CMN
+ {28, 12, 12, 12}, // ORR(s)
+ {29, 13, 13, 13}, // MOV(s)
+ {30, 14, 14, 14}, // BIC(s)
+ {31, 15, 15, 15}, // MVN(s)
+ {24, -1, -1, -1}, // MOVW
+ {26, -1, -1, -1}, // MOVT
+ };
+
+const char *InstNames[] = { "AND",
+ "EOR",
+ "SUB",
+ "RSB",
+ "ADD",
+ "ADC",
+ "SBC",
+ "RSC",
+ "TST",
+ "TEQ",
+ "CMP",
+ "CMN",
+ "ORR",
+ "MOV",
+ "BIC",
+ "MVN"
+ };
+
+void ARMXEmitter::AND (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(0, Rd, Rn, Rm); }
+void ARMXEmitter::ANDS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(0, Rd, Rn, Rm, true); }
+void ARMXEmitter::EOR (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(1, Rd, Rn, Rm); }
+void ARMXEmitter::EORS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(1, Rd, Rn, Rm, true); }
+void ARMXEmitter::SUB (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(2, Rd, Rn, Rm); }
+void ARMXEmitter::SUBS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(2, Rd, Rn, Rm, true); }
+void ARMXEmitter::RSB (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(3, Rd, Rn, Rm); }
+void ARMXEmitter::RSBS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(3, Rd, Rn, Rm, true); }
+void ARMXEmitter::ADD (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(4, Rd, Rn, Rm); }
+void ARMXEmitter::ADDS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(4, Rd, Rn, Rm, true); }
+void ARMXEmitter::ADC (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(5, Rd, Rn, Rm); }
+void ARMXEmitter::ADCS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(5, Rd, Rn, Rm, true); }
+void ARMXEmitter::SBC (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(6, Rd, Rn, Rm); }
+void ARMXEmitter::SBCS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(6, Rd, Rn, Rm, true); }
+void ARMXEmitter::RSC (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(7, Rd, Rn, Rm); }
+void ARMXEmitter::RSCS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(7, Rd, Rn, Rm, true); }
+void ARMXEmitter::TST ( ARMReg Rn, Operand2 Rm) { WriteInstruction(8, R0, Rn, Rm, true); }
+void ARMXEmitter::TEQ ( ARMReg Rn, Operand2 Rm) { WriteInstruction(9, R0, Rn, Rm, true); }
+void ARMXEmitter::CMP ( ARMReg Rn, Operand2 Rm) { WriteInstruction(10, R0, Rn, Rm, true); }
+void ARMXEmitter::CMN ( ARMReg Rn, Operand2 Rm) { WriteInstruction(11, R0, Rn, Rm, true); }
+void ARMXEmitter::ORR (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(12, Rd, Rn, Rm); }
+void ARMXEmitter::ORRS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(12, Rd, Rn, Rm, true); }
+void ARMXEmitter::MOV (ARMReg Rd, Operand2 Rm) { WriteInstruction(13, Rd, R0, Rm); }
+void ARMXEmitter::MOVS(ARMReg Rd, Operand2 Rm) { WriteInstruction(13, Rd, R0, Rm, true); }
+void ARMXEmitter::BIC (ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(14, Rd, Rn, Rm); }
+void ARMXEmitter::BICS(ARMReg Rd, ARMReg Rn, Operand2 Rm) { WriteInstruction(14, Rd, Rn, Rm, true); }
+void ARMXEmitter::MVN (ARMReg Rd, Operand2 Rm) { WriteInstruction(15, Rd, R0, Rm); }
+void ARMXEmitter::MVNS(ARMReg Rd, Operand2 Rm) { WriteInstruction(15, Rd, R0, Rm, true); }
+void ARMXEmitter::MOVW(ARMReg Rd, Operand2 Rm) { WriteInstruction(16, Rd, R0, Rm); }
+void ARMXEmitter::MOVT(ARMReg Rd, Operand2 Rm, bool TopBits) { WriteInstruction(17, Rd, R0, TopBits ? Rm.Value >> 16 : Rm); }
+
+void ARMXEmitter::WriteInstruction (u32 Op, ARMReg Rd, ARMReg Rn, Operand2 Rm, bool SetFlags) // This can get renamed later
+{
+ s32 op = InstOps[Op][Rm.GetType()]; // Type always decided by last operand
+ u32 Data = Rm.GetData();
+ if (Rm.GetType() == TYPE_IMM)
+ {
+ switch (Op)
+ {
+ // MOV cases that support IMM16
+ case 16:
+ case 17:
+ Data = Rm.Imm16();
+ break;
+ default:
+ break;
+ }
+ }
+ if (op == -1)
+ _assert_msg_(DYNA_REC, false, "%s not yet support %d", InstNames[Op], Rm.GetType());
+ Write32(condition | (op << 21) | (SetFlags ? (1 << 20) : 0) | Rn << 16 | Rd << 12 | Data);
+}
+
+// Data Operations
+void ARMXEmitter::WriteSignedMultiply(u32 Op, u32 Op2, u32 Op3, ARMReg dest, ARMReg r1, ARMReg r2)
+{
+ Write32(condition | (0x7 << 24) | (Op << 20) | (dest << 16) | (Op2 << 12) | (r1 << 8) | (Op3 << 5) | (1 << 4) | r2);
+}
+void ARMXEmitter::UDIV(ARMReg dest, ARMReg dividend, ARMReg divisor)
+{
+ if (!cpu_info.bIDIVa)
+ PanicAlert("Trying to use integer divide on hardware that doesn't support it. Bad programmer.");
+ WriteSignedMultiply(3, 0xF, 0, dest, divisor, dividend);
+}
+void ARMXEmitter::SDIV(ARMReg dest, ARMReg dividend, ARMReg divisor)
+{
+ if (!cpu_info.bIDIVa)
+ PanicAlert("Trying to use integer divide on hardware that doesn't support it. Bad programmer.");
+ WriteSignedMultiply(1, 0xF, 0, dest, divisor, dividend);
+}
+void ARMXEmitter::LSL (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, false, dest, src, op2);}
+void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, true, dest, src, op2);}
+void ARMXEmitter::LSL (ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, false, dest, src, op2);}
+void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, true, dest, src, op2);}
+void ARMXEmitter::LSR (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(3, false, dest, src, op2);}
+void ARMXEmitter::MUL (ARMReg dest, ARMReg src, ARMReg op2)
+{
+ Write32(condition | (dest << 16) | (src << 8) | (9 << 4) | op2);
+}
+void ARMXEmitter::MULS(ARMReg dest, ARMReg src, ARMReg op2)
+{
+ Write32(condition | (1 << 20) | (dest << 16) | (src << 8) | (9 << 4) | op2);
+}
+
+void ARMXEmitter::Write4OpMultiply(u32 op, ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn) {
+ Write32(condition | (op << 20) | (destHi << 16) | (destLo << 12) | (rm << 8) | (9 << 4) | rn);
+}
+
+void ARMXEmitter::UMULL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn)
+{
+ Write4OpMultiply(0x8, destLo, destHi, rn, rm);
+}
+
+void ARMXEmitter::SMULL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn)
+{
+ Write4OpMultiply(0xC, destLo, destHi, rn, rm);
+}
+
+void ARMXEmitter::UMLAL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn)
+{
+ Write4OpMultiply(0xA, destLo, destHi, rn, rm);
+}
+
+void ARMXEmitter::SMLAL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn)
+{
+ Write4OpMultiply(0xE, destLo, destHi, rn, rm);
+}
+
+void ARMXEmitter::UBFX(ARMReg dest, ARMReg rn, u8 lsb, u8 width)
+{
+ Write32(condition | (0x7E0 << 16) | ((width - 1) << 16) | (dest << 12) | (lsb << 7) | (5 << 4) | rn);
+}
+
+void ARMXEmitter::CLZ(ARMReg rd, ARMReg rm)
+{
+ Write32(condition | (0x16F << 16) | (rd << 12) | (0xF1 << 4) | rm);
+}
+
+void ARMXEmitter::BFI(ARMReg rd, ARMReg rn, u8 lsb, u8 width)
+{
+ u32 msb = (lsb + width - 1);
+ if (msb > 31) msb = 31;
+ Write32(condition | (0x7C0 << 16) | (msb << 16) | (rd << 12) | (lsb << 7) | (1 << 4) | rn);
+}
+
+void ARMXEmitter::SXTB (ARMReg dest, ARMReg op2)
+{
+ Write32(condition | (0x6AF << 16) | (dest << 12) | (7 << 4) | op2);
+}
+
+void ARMXEmitter::SXTH (ARMReg dest, ARMReg op2, u8 rotation)
+{
+ SXTAH(dest, (ARMReg)15, op2, rotation);
+}
+void ARMXEmitter::SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation)
+{
+ // bits ten and 11 are the rotation amount, see 8.8.232 for more
+ // information
+ Write32(condition | (0x6B << 20) | (src << 16) | (dest << 12) | (rotation << 10) | (7 << 4) | op2);
+}
+void ARMXEmitter::RBIT(ARMReg dest, ARMReg src)
+{
+ Write32(condition | (0x6F << 20) | (0xF << 16) | (dest << 12) | (0xF3 << 4) | src);
+}
+void ARMXEmitter::REV (ARMReg dest, ARMReg src)
+{
+ Write32(condition | (0x6B << 20) | (0xF << 16) | (dest << 12) | (0xF3 << 4) | src);
+}
+void ARMXEmitter::REV16(ARMReg dest, ARMReg src)
+{
+ Write32(condition | (0x3DF << 16) | (dest << 12) | (0xFD << 4) | src);
+}
+
+void ARMXEmitter::_MSR (bool write_nzcvq, bool write_g, Operand2 op2)
+{
+ Write32(condition | (0x320F << 12) | (write_nzcvq << 19) | (write_g << 18) | op2.Imm12Mod());
+}
+void ARMXEmitter::_MSR (bool write_nzcvq, bool write_g, ARMReg src)
+{
+ Write32(condition | (0x120F << 12) | (write_nzcvq << 19) | (write_g << 18) | src);
+}
+void ARMXEmitter::MRS (ARMReg dest)
+{
+ Write32(condition | (16 << 20) | (15 << 16) | (dest << 12));
+}
+void ARMXEmitter::LDREX(ARMReg dest, ARMReg base)
+{
+ Write32(condition | (25 << 20) | (base << 16) | (dest << 12) | 0xF9F);
+}
+void ARMXEmitter::STREX(ARMReg result, ARMReg base, ARMReg op)
+{
+ _assert_msg_(DYNA_REC, (result != base && result != op), "STREX dest can't be other two registers");
+ Write32(condition | (24 << 20) | (base << 16) | (result << 12) | (0xF9 << 4) | op);
+}
+void ARMXEmitter::DMB ()
+{
+ Write32(0xF57FF05E);
+}
+void ARMXEmitter::SVC(Operand2 op)
+{
+ Write32(condition | (0x0F << 24) | op.Imm24());
+}
+
+// IMM, REG, IMMSREG, RSR
+// -1 for invalid if the instruction doesn't support that
+const s32 LoadStoreOps[][4] = { {0x40, 0x60, 0x60, -1}, // STR
+ {0x41, 0x61, 0x61, -1}, // LDR
+ {0x44, 0x64, 0x64, -1}, // STRB
+ {0x45, 0x65, 0x65, -1}, // LDRB
+ // Special encodings
+ { 0x4, 0x0, -1, -1}, // STRH
+ { 0x5, 0x1, -1, -1}, // LDRH
+ { 0x5, 0x1, -1, -1}, // LDRSB
+ { 0x5, 0x1, -1, -1}, // LDRSH
+ };
+const char *LoadStoreNames[] = { "STR",
+ "LDR",
+ "STRB",
+ "LDRB",
+ "STRH",
+ "LDRH",
+ "LDRSB",
+ "LDRSH",
+ };
+
+void ARMXEmitter::WriteStoreOp(u32 Op, ARMReg Rt, ARMReg Rn, Operand2 Rm, bool RegAdd)
+{
+ s32 op = LoadStoreOps[Op][Rm.GetType()]; // Type always decided by last operand
+ u32 Data;
+
+ // Qualcomm chipsets get /really/ angry if you don't use index, even if the offset is zero.
+ // Some of these encodings require Index at all times anyway. Doesn't really matter.
+ // bool Index = op2 != 0 ? true : false;
+ bool Index = true;
+ bool Add = false;
+
+ // Special Encoding
+ bool SpecialOp = false;
+ bool Half = false;
+ bool SignedLoad = false;
+
+ if (op == -1)
+ _assert_msg_(DYNA_REC, false, "%s does not support %d", LoadStoreNames[Op], Rm.GetType());
+
+ switch (Op)
+ {
+ case 4: // STRH
+ SpecialOp = true;
+ Half = true;
+ SignedLoad = false;
+ break;
+ case 5: // LDRH
+ SpecialOp = true;
+ Half = true;
+ SignedLoad = false;
+ break;
+ case 6: // LDRSB
+ SpecialOp = true;
+ Half = false;
+ SignedLoad = true;
+ break;
+ case 7: // LDRSH
+ SpecialOp = true;
+ Half = true;
+ SignedLoad = true;
+ break;
+ }
+ switch (Rm.GetType())
+ {
+ case TYPE_IMM:
+ {
+ s32 Temp = (s32)Rm.Value;
+ Data = abs(Temp);
+ // The offset is encoded differently on this one.
+ if (SpecialOp)
+ Data = (Data & 0xF0 << 4) | (Data & 0xF);
+ if (Temp >= 0) Add = true;
+ }
+ break;
+ case TYPE_REG:
+ case TYPE_IMMSREG:
+ if (!SpecialOp)
+ {
+ Data = Rm.GetData();
+ Add = RegAdd;
+ break;
+ }
+ default:
+ // RSR not supported for any of these
+ // We already have the warning above
+ BKPT(0x2);
+ return;
+ break;
+ }
+ if (SpecialOp)
+ {
+ // Add SpecialOp things
+ Data = (0x5 << 4) | (SignedLoad << 6) | (Half << 5) | Data;
+ }
+ Write32(condition | (op << 20) | (Index << 24) | (Add << 23) | (Rn << 16) | (Rt << 12) | Data);
+}
+
+void ARMXEmitter::LDR (ARMReg dest, ARMReg base, Operand2 op2, bool RegAdd) { WriteStoreOp(1, dest, base, op2, RegAdd);}
+void ARMXEmitter::LDRB(ARMReg dest, ARMReg base, Operand2 op2, bool RegAdd) { WriteStoreOp(3, dest, base, op2, RegAdd);}
+void ARMXEmitter::LDRH(ARMReg dest, ARMReg base, Operand2 op2, bool RegAdd) { WriteStoreOp(5, dest, base, op2, RegAdd);}
+void ARMXEmitter::LDRSB(ARMReg dest, ARMReg base, Operand2 op2, bool RegAdd) { WriteStoreOp(6, dest, base, op2, RegAdd);}
+void ARMXEmitter::LDRSH(ARMReg dest, ARMReg base, Operand2 op2, bool RegAdd) { WriteStoreOp(7, dest, base, op2, RegAdd);}
+void ARMXEmitter::STR (ARMReg result, ARMReg base, Operand2 op2, bool RegAdd) { WriteStoreOp(0, result, base, op2, RegAdd);}
+void ARMXEmitter::STRH (ARMReg result, ARMReg base, Operand2 op2, bool RegAdd) { WriteStoreOp(4, result, base, op2, RegAdd);}
+void ARMXEmitter::STRB (ARMReg result, ARMReg base, Operand2 op2, bool RegAdd) { WriteStoreOp(2, result, base, op2, RegAdd);}
+
+void ARMXEmitter::WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList)
+{
+ Write32(condition | (op << 20) | (WriteBack << 21) | (dest << 16) | RegList);
+}
+void ARMXEmitter::STMFD(ARMReg dest, bool WriteBack, const int Regnum, ...)
+{
+ u16 RegList = 0;
+ u8 Reg;
+ int i;
+ va_list vl;
+ va_start(vl, Regnum);
+ for (i=0;i<Regnum;i++)
+ {
+ Reg = va_arg(vl, u32);
+ RegList |= (1 << Reg);
+ }
+ va_end(vl);
+ WriteRegStoreOp(0x90, dest, WriteBack, RegList);
+}
+void ARMXEmitter::LDMFD(ARMReg dest, bool WriteBack, const int Regnum, ...)
+{
+ u16 RegList = 0;
+ u8 Reg;
+ int i;
+ va_list vl;
+ va_start(vl, Regnum);
+ for (i=0;i<Regnum;i++)
+ {
+ Reg = va_arg(vl, u32);
+ RegList |= (1 << Reg);
+ }
+ va_end(vl);
+ WriteRegStoreOp(0x89, dest, WriteBack, RegList);
+}
+
+ARMReg ARMXEmitter::SubBase(ARMReg Reg)
+{
+ if (Reg >= S0)
+ {
+ if (Reg >= D0)
+ {
+ if (Reg >= Q0)
+ return (ARMReg)((Reg - Q0) * 2); // Always gets encoded as a double register
+ return (ARMReg)(Reg - D0);
+ }
+ return (ARMReg)(Reg - S0);
+ }
+ return Reg;
+}
+
+// NEON Specific
+void ARMXEmitter::VADD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
+{
+ _assert_msg_(DYNA_REC, Vd >= D0, "Pass invalid register to VADD(integer)");
+ _assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VADD(integer) when CPU doesn't support it");
+
+ bool register_quad = Vd >= Q0;
+
+ // Gets encoded as a double register
+ Vd = SubBase(Vd);
+ Vn = SubBase(Vn);
+ Vm = SubBase(Vm);
+
+ Write32((0xF2 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \
+ | ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (register_quad << 6) \
+ | ((Vm & 0x10) << 2) | (Vm & 0xF));
+
+}
+void ARMXEmitter::VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
+{
+ _assert_msg_(DYNA_REC, Vd >= Q0, "Pass invalid register to VSUB(integer)");
+ _assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VSUB(integer) when CPU doesn't support it");
+
+ // Gets encoded as a double register
+ Vd = SubBase(Vd);
+ Vn = SubBase(Vn);
+ Vm = SubBase(Vm);
+
+ Write32((0xF3 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \
+ | ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (1 << 6) \
+ | ((Vm & 0x10) << 2) | (Vm & 0xF));
+}
+
+// VFP Specific
+struct VFPEnc
+{
+ s16 opc1;
+ s16 opc2;
+};
+// Double/single, Neon
+const VFPEnc VFPOps[][2] = {
+ {{0xE0, 0xA0}, {0x20, 0xD1}}, // 0: VMLA
+ {{0xE0, 0xA4}, {0x22, 0xD1}}, // 1: VMLS
+ {{0xE3, 0xA0}, {0x20, 0xD0}}, // 2: VADD
+ {{0xE3, 0xA4}, {0x22, 0xD0}}, // 3: VSUB
+ {{0xE2, 0xA0}, {0x30, 0xD1}}, // 4: VMUL
+ {{0xEB, 0xAC}, { -1 /* 0x3B */, -1 /* 0x70 */}}, // 5: VABS(Vn(0x0) used for encoding)
+ {{0xE8, 0xA0}, { -1, -1}}, // 6: VDIV
+ {{0xEB, 0xA4}, { -1 /* 0x3B */, -1 /* 0x78 */}}, // 7: VNEG(Vn(0x1) used for encoding)
+ {{0xEB, 0xAC}, { -1, -1}}, // 8: VSQRT (Vn(0x1) used for encoding)
+ {{0xEB, 0xA4}, { -1, -1}}, // 9: VCMP (Vn(0x4 | #0 ? 1 : 0) used for encoding)
+ {{0xEB, 0xAC}, { -1, -1}}, // 10: VCMPE (Vn(0x4 | #0 ? 1 : 0) used for encoding)
+ {{ -1, -1}, {0x3B, 0x30}}, // 11: VABSi
+ };
+const char *VFPOpNames[] = {
+ "VMLA",
+ "VMLS",
+ "VADD",
+ "VSUB",
+ "VMUL",
+ "VABS",
+ "VDIV",
+ "VNEG",
+ "VSQRT",
+ "VCMP",
+ "VCMPE",
+ "VABSi",
+};
+
+u32 ARMXEmitter::EncodeVd(ARMReg Vd)
+{
+ bool quad_reg = Vd >= Q0;
+ bool double_reg = Vd >= D0;
+
+ ARMReg Reg = SubBase(Vd);
+
+ if (quad_reg)
+ return ((Reg & 0x10) << 18) | ((Reg & 0xF) << 12);
+ else
+ if (double_reg)
+ return ((Reg & 0x10) << 18) | ((Reg & 0xF) << 12);
+ else
+ return ((Reg & 0x1) << 22) | ((Reg & 0x1E) << 11);
+}
+u32 ARMXEmitter::EncodeVn(ARMReg Vn)
+{
+ bool quad_reg = Vn >= Q0;
+ bool double_reg = Vn >= D0;
+
+ ARMReg Reg = SubBase(Vn);
+ if (quad_reg)
+ return ((Reg & 0xF) << 16) | ((Reg & 0x10) << 3);
+ else
+ if (double_reg)
+ return ((Reg & 0xF) << 16) | ((Reg & 0x10) << 3);
+ else
+ return ((Reg & 0x1E) << 15) | ((Reg & 0x1) << 7);
+}
+u32 ARMXEmitter::EncodeVm(ARMReg Vm)
+{
+ bool quad_reg = Vm >= Q0;
+ bool double_reg = Vm >= D0;
+
+ ARMReg Reg = SubBase(Vm);
+
+ if (quad_reg)
+ return ((Reg & 0x10) << 2) | (Reg & 0xF);
+ else
+ if (double_reg)
+ return ((Reg & 0x10) << 2) | (Reg & 0xF);
+ else
+ return ((Reg & 0x1) << 5) | (Reg >> 1);
+}
+void ARMXEmitter::WriteVFPDataOp(u32 Op, ARMReg Vd, ARMReg Vn, ARMReg Vm)
+{
+ bool quad_reg = Vd >= Q0;
+ bool double_reg = Vd >= D0 && Vd < Q0;
+
+ VFPEnc enc = VFPOps[Op][quad_reg];
+ if (enc.opc1 == -1 && enc.opc2 == -1)
+ _assert_msg_(DYNA_REC, false, "%s does not support %s", VFPOpNames[Op], quad_reg ? "NEON" : "VFP");
+ u32 VdEnc = EncodeVd(Vd);
+ u32 VnEnc = EncodeVn(Vn);
+ u32 VmEnc = EncodeVm(Vm);
+ u32 cond = quad_reg ? (0xF << 28) : condition;
+
+ Write32(cond | (enc.opc1 << 20) | VnEnc | VdEnc | (enc.opc2 << 4) | (quad_reg << 6) | (double_reg << 8) | VmEnc);
+}
+void ARMXEmitter::VMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(0, Vd, Vn, Vm); }
+void ARMXEmitter::VMLS(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(1, Vd, Vn, Vm); }
+void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(2, Vd, Vn, Vm); }
+void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(3, Vd, Vn, Vm); }
+void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(4, Vd, Vn, Vm); }
+void ARMXEmitter::VABS(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(5, Vd, D0, Vm); }
+void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(6, Vd, Vn, Vm); }
+void ARMXEmitter::VNEG(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(7, Vd, D1, Vm); }
+void ARMXEmitter::VSQRT(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(8, Vd, D1, Vm); }
+void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(9, Vd, D4, Vm); }
+void ARMXEmitter::VCMPE(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(10, Vd, D4, Vm); }
+void ARMXEmitter::VCMP(ARMReg Vd){ WriteVFPDataOp(9, Vd, D5, D0); }
+void ARMXEmitter::VCMPE(ARMReg Vd){ WriteVFPDataOp(10, Vd, D5, D0); }
+
+void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, s16 offset)
+{
+ _assert_msg_(DYNA_REC, Dest >= S0 && Dest <= D31, "Passed Invalid dest register to VLDR");
+ _assert_msg_(DYNA_REC, Base <= R15, "Passed invalid Base register to VLDR");
+
+ bool Add = offset >= 0 ? true : false;
+ u32 imm = abs(offset);
+
+ _assert_msg_(DYNA_REC, (imm & 0xC03) == 0, "VLDR: Offset needs to be word aligned and small enough");
+
+ if (imm & 0xC03)
+ ERROR_LOG(DYNA_REC, "VLDR: Bad offset %08x", imm);
+
+ bool single_reg = Dest < D0;
+
+ Dest = SubBase(Dest);
+
+ if (single_reg)
+ {
+ Write32(condition | (0xD << 24) | (Add << 23) | ((Dest & 0x1) << 22) | (1 << 20) | (Base << 16) \
+ | ((Dest & 0x1E) << 11) | (10 << 8) | (imm >> 2));
+
+ }
+ else
+ {
+ Write32(condition | (0xD << 24) | (Add << 23) | ((Dest & 0x10) << 18) | (1 << 20) | (Base << 16) \
+ | ((Dest & 0xF) << 12) | (11 << 8) | (imm >> 2));
+ }
+}
+void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, s16 offset)
+{
+ _assert_msg_(DYNA_REC, Src >= S0 && Src <= D31, "Passed invalid src register to VSTR");
+ _assert_msg_(DYNA_REC, Base <= R15, "Passed invalid base register to VSTR");
+
+ bool Add = offset >= 0 ? true : false;
+ u32 imm = abs(offset);
+
+ _assert_msg_(DYNA_REC, (imm & 0xC03) == 0, "VSTR: Offset needs to be word aligned and small enough");
+
+ if (imm & 0xC03)
+ ERROR_LOG(DYNA_REC, "VSTR: Bad offset %08x", imm);
+
+ bool single_reg = Src < D0;
+
+ Src = SubBase(Src);
+
+ if (single_reg)
+ {
+ Write32(condition | (0xD << 24) | (Add << 23) | ((Src & 0x1) << 22) | (Base << 16) \
+ | ((Src & 0x1E) << 11) | (10 << 8) | (imm >> 2));
+ }
+ else
+ {
+ Write32(condition | (0xD << 24) | (Add << 23) | ((Src & 0x10) << 18) | (Base << 16) \
+ | ((Src & 0xF) << 12) | (11 << 8) | (imm >> 2));
+ }
+}
+
+void ARMXEmitter::VMRS_APSR() {
+ Write32(condition | 0xEF10A10 | (15 << 12));
+}
+void ARMXEmitter::VMRS(ARMReg Rt) {
+ Write32(condition | (0xEF << 20) | (1 << 16) | (Rt << 12) | 0xA10);
+}
+void ARMXEmitter::VMSR(ARMReg Rt) {
+ Write32(condition | (0xEE << 20) | (1 << 16) | (Rt << 12) | 0xA10);
+}
+
+// VFP and ASIMD
+void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src, bool high)
+{
+ _assert_msg_(DYNA_REC, Src < S0, "This VMOV doesn't support SRC other than ARM Reg");
+ _assert_msg_(DYNA_REC, Dest >= D0, "This VMOV doesn't support DEST other than VFP");
+
+ Dest = SubBase(Dest);
+
+ Write32(condition | (0xE << 24) | (high << 21) | ((Dest & 0xF) << 16) | (Src << 12) \
+ | (11 << 8) | ((Dest & 0x10) << 3) | (1 << 4));
+}
+
+void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
+{
+ if (Dest > R15)
+ {
+ if (Src < S0)
+ {
+ if (Dest < D0)
+ {
+ // Moving to a Neon register FROM ARM Reg
+ Dest = (ARMReg)(Dest - S0);
+ Write32(condition | (0xE0 << 20) | ((Dest & 0x1E) << 15) | (Src << 12) \
+ | (0xA << 8) | ((Dest & 0x1) << 7) | (1 << 4));
+ return;
+ }
+ else
+ {
+ // Move 64bit from Arm reg
+ _assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM to NEON");
+ return;
+ }
+ }
+ }
+ else
+ {
+ if (Src > R15)
+ {
+ if (Src < D0)
+ {
+ // Moving to ARM Reg from Neon Register
+ Src = (ARMReg)(Src - S0);
+ Write32(condition | (0xE1 << 20) | ((Src & 0x1E) << 15) | (Dest << 12) \
+ | (0xA << 8) | ((Src & 0x1) << 7) | (1 << 4));
+ return;
+ }
+ else
+ {
+ // Move 64bit To Arm reg
+ _assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM From NEON");
+ return;
+ }
+ }
+ else
+ {
+ // Move Arm reg to Arm reg
+ _assert_msg_(DYNA_REC, false, "VMOV doesn't support moving ARM registers");
+ }
+ }
+ // Moving NEON registers
+ int SrcSize = Src < D0 ? 1 : Src < Q0 ? 2 : 4;
+ int DestSize = Dest < D0 ? 1 : Dest < Q0 ? 2 : 4;
+ bool Single = DestSize == 1;
+ bool Quad = DestSize == 4;
+
+ _assert_msg_(DYNA_REC, SrcSize == DestSize, "VMOV doesn't support moving different register sizes");
+
+ Dest = SubBase(Dest);
+ Src = SubBase(Src);
+
+ if (Single)
+ {
+ Write32(condition | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x3 << 20) | ((Dest & 0x1E) << 11) \
+ | (0x5 << 9) | (1 << 6) | ((Src & 0x1) << 5) | ((Src & 0x1E) >> 1));
+ }
+ else
+ {
+ // Double and quad
+ if (Quad)
+ {
+ _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use quad registers when you don't support ASIMD.");
+ // Gets encoded as a Double register
+ Write32((0xF2 << 24) | ((Dest & 0x10) << 18) | (2 << 20) | ((Src & 0xF) << 16) \
+ | ((Dest & 0xF) << 12) | (1 << 8) | ((Src & 0x10) << 3) | (1 << 6) \
+ | ((Src & 0x10) << 1) | (1 << 4) | (Src & 0xF));
+
+ }
+ else
+ {
+ Write32(condition | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x3 << 20) | ((Dest & 0xF) << 12) \
+ | (0x2D << 6) | ((Src & 0x10) << 1) | (Src & 0xF));
+ }
+ }
+}
+
+void ARMXEmitter::VCVT(ARMReg Dest, ARMReg Source, int flags)
+{
+ bool single_reg = (Dest < D0) && (Source < D0);
+ int op = ((flags & TO_INT) ? (flags & ROUND_TO_ZERO) : (flags & IS_SIGNED)) ? 1 : 0;
+ int op2 = ((flags & TO_INT) ? (flags & IS_SIGNED) : 0) ? 1 : 0;
+ Dest = SubBase(Dest);
+ Source = SubBase(Source);
+
+ if (single_reg)
+ {
+ Write32(condition | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x7 << 19) | ((flags & TO_INT) << 18) | (op2 << 16) \
+ | ((Dest & 0x1E) << 11) | (op << 7) | (0x29 << 6) | ((Source & 0x1) << 5) | (Source >> 1));
+ } else {
+ Write32(condition | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x7 << 19) | ((flags & TO_INT) << 18) | (op2 << 16) \
+ | ((Dest & 0xF) << 12) | (1 << 8) | (op << 7) | (0x29 << 6) | ((Source & 0x10) << 1) | (Source & 0xF));
+ }
+}
+
+}