diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index e2ea3ffd23..0be19bbfff 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -1488,13 +1488,79 @@ void ARMXEmitter::VMSR(ARMReg Rt) { Write32(condition | (0xEE << 20) | (1 << 16) | (Rt << 12) | 0xA10); } -// VFP and ASIMD void ARMXEmitter::VMOV(ARMReg Dest, Operand2 op2) { _assert_msg_(JIT, cpu_info.bVFPv3, "VMOV #imm requires VFPv3"); int sz = Dest >= D0 ? (1 << 8) : 0; Write32(condition | (0xEB << 20) | EncodeVd(Dest) | (5 << 9) | sz | op2.Imm8VFP()); } + +void ARMXEmitter::VMOV_neon(u32 Size, ARMReg Vd, u32 imm) +{ + _assert_msg_(JIT, cpu_info.bNEON, "VMOV_neon #imm requires NEON"); + _assert_msg_(JIT, Vd >= D0, "VMOV_neon #imm must target a double or quad"); + bool register_quad = Vd >= Q0; + + int cmode = 0; + int op = 0; + Operand2 op2(0, TYPE_IMM); + + u32 imm8 = imm & 0xFF; + imm8 = imm8 | (imm8 << 8) | (imm8 << 16) | (imm8 << 24); + + if (Size == I_8) { + imm = imm8; + } else if (Size == I_16) { + imm &= 0xFFFF; + imm = imm | (imm << 16); + } + + if ((imm & 0x000000FF) == imm) { + op = 0; + cmode = 0 << 1; + op2 = Operand2(imm, TYPE_IMM); + } else if ((imm & 0x0000FF00) == imm) { + op = 0; + cmode = 1 << 1; + op2 = Operand2(imm >> 8, TYPE_IMM); + } else if ((imm & 0x00FF0000) == imm) { + op = 0; + cmode = 2 << 1; + op2 = Operand2(imm >> 16, TYPE_IMM); + } else if ((imm & 0xFF000000) == imm) { + op = 0; + cmode = 3 << 1; + op2 = Operand2(imm >> 24, TYPE_IMM); + } else if ((imm & 0x00FF00FF) == imm && (imm >> 16) == (imm & 0x00FF)) { + op = 0; + cmode = 4 << 1; + op2 = Operand2(imm & 0xFF, TYPE_IMM); + } else if ((imm & 0xFF00FF00) == imm && (imm >> 16) == (imm & 0xFF00)) { + op = 0; + cmode = 5 << 1; + op2 = Operand2(imm & 0xFF, TYPE_IMM); + } else if ((imm & 0x0000FFFF) == (imm | 0x000000FF)) { + op = 0; + cmode = (6 << 1) | 0; + op2 = Operand2(imm >> 8, TYPE_IMM); + } else if ((imm & 0x00FFFFFF) == (imm | 0x0000FFFF)) { + op = 0; + cmode = (6 << 1) | 1; + op2 = Operand2(imm >> 16, TYPE_IMM); + } else if (imm == imm8) { + op = 0; + cmode = (7 << 1) | 0; + op2 = Operand2(imm & 0xFF, TYPE_IMM); + } else { + // TODO: Float constant form aBbbbbbcdefgh0000000000000000000. + // TODO: 64-bit constant form (FF or 00 all bytes.) + _assert_msg_(JIT, false, "VMOV_neon #imm invalid constant value"); + } + + // No condition allowed. + Write32((15 << 28) | (0x28 << 20) | EncodeVd(Vd) | (cmode << 8) | (register_quad << 6) | (op << 5) | (1 << 4) | op2.Imm8ASIMD()); +} + void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src, bool high) { _assert_msg_(JIT, Src < S0, "This VMOV doesn't support SRC other than ARM Reg"); diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index ecffd50863..19a7a772b6 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -698,9 +698,19 @@ public: void VEOR(ARMReg Vd, ARMReg Vn, ARMReg Vm); void VORN(ARMReg Vd, ARMReg Vn, ARMReg Vm); void VORR(ARMReg Vd, ARMReg Vn, ARMReg Vm); - inline void VMOV_neon(ARMReg Dest, ARMReg Src) { - VORR(Dest, Src, Src); - } + inline void VMOV_neon(ARMReg Dest, ARMReg Src) { + VORR(Dest, Src, Src); + } + void VMOV_neon(u32 Size, ARMReg Vd, u32 imm); + void VMOV_neon(u32 Size, ARMReg Vd, float imm) { + _dbg_assert_msg_(JIT, Size == F_32, "Expecting F_32 immediate for VMOV_neon float arg."); + union { + float f; + u32 u; + } val; + val.f = imm; + VMOV_neon(I_32, Vd, val.u); + } void VNEG(u32 Size, ARMReg Vd, ARMReg Vm); void VPADAL(u32 Size, ARMReg Vd, ARMReg Vm);