diff --git a/src/frontend/translate/translate_arm/status_register_access.cpp b/src/frontend/translate/translate_arm/status_register_access.cpp index ba5f4a93..12f75856 100644 --- a/src/frontend/translate/translate_arm/status_register_access.cpp +++ b/src/frontend/translate/translate_arm/status_register_access.cpp @@ -77,6 +77,36 @@ bool ArmTranslatorVisitor::arm_SRS() { return InterpretThisInstruction(); } +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 cpsr = ir.GetCpsr(); + auto to = ir.GetRegister(m); + auto from = ir.GetRegister(n); + auto zero = ir.Imm1(false); + + // Extract bits 16:19 from the CPSR + auto bits = ir.And(ir.LogicalShiftRight(cpsr, ir.Imm8(16), zero).result, ir.Imm32(0xF)); + + // Perform some arithmetic to expand 0bXYZW into 0bXXXXXXXXYYYYYYYYZZZZZZZZWWWWWWWW => 0xXXYYZZWW + // The logic behind this is as follows: + // 0000 0000 0000 0000 | 0000 0000 0000 xyzw + // 0000 000x yzw0 00xy | zw00 0xyz w000 xyzw (x * 0x00204081) + // 0000 000x 0000 000y | 0000 000z 0000 000w (x & 0x01010101) + // xxxx xxxx yyyy yyyy | zzzz zzzz wwww wwww (x * 0xff) + + auto x2 = ir.Mul(bits, ir.Imm32(0x00204081)); + auto x3 = ir.And(x2, ir.Imm32(0x01010101)); + auto mask = ir.Mul(x3, ir.Imm32(0xFF)); + + auto result = ir.Or(ir.And(from, mask), ir.And(to, ir.Not(mask))); + ir.SetRegister(d, result); + } + + return true; +} } // namespace Arm } // namespace Dynarmic diff --git a/src/frontend/translate/translate_arm/translate_arm.h b/src/frontend/translate/translate_arm/translate_arm.h index df8a5f39..09b9b0ae 100644 --- a/src/frontend/translate/translate_arm/translate_arm.h +++ b/src/frontend/translate/translate_arm/translate_arm.h @@ -211,7 +211,7 @@ struct ArmTranslatorVisitor final { // Miscellaneous instructions bool arm_CLZ(Cond cond, Reg d, Reg m) { return InterpretThisInstruction(); } bool arm_NOP() { return true; } - bool arm_SEL(Cond cond, Reg n, Reg d, Reg m) { return InterpretThisInstruction(); } + 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) { return InterpretThisInstruction(); } diff --git a/tests/arm/fuzz_arm.cpp b/tests/arm/fuzz_arm.cpp index 86b3b3be..9e6318b9 100644 --- a/tests/arm/fuzz_arm.cpp +++ b/tests/arm/fuzz_arm.cpp @@ -904,3 +904,29 @@ TEST_CASE("VFP: VPUSH, VPOP", "[JitX64][vfp]") { return instructions[RandInt(0, instructions.size() - 1)].Generate(); }); } + +TEST_CASE("Test ARM SEL instruction", "[JitX64]") { + const auto is_sel_valid = [](u32 instr) -> bool { + // R15 as Rd, Rn, or Rm is UNPREDICTABLE + return Bits<0, 3>(instr) != 0b1111 && Bits<12, 15>(instr) != 0b1111 && Bits<16, 19>(instr) != 0b1111; + }; + + const auto is_msr_valid = [](u32 instr) -> bool { + // Mask can not be 0 + return Bits<18, 19>(instr) != 0b00; + }; + + const InstructionGenerator cpsr_setter = InstructionGenerator("11100011001001001111rrrrvvvvvvvv", is_msr_valid); // MSR_Imm write GE + const InstructionGenerator sel_instr = InstructionGenerator("111001101000nnnndddd11111011mmmm", is_sel_valid); // SEL + + SECTION("Fuzz SEL") { + // Alternate between a SEL and a MSR to change the CPSR, thus changing the expected result of the next SEL + bool set_cpsr = true; + FuzzJitArm(5, 6, 10000, [&sel_instr, &cpsr_setter, &set_cpsr]() -> u32 { + set_cpsr ^= true; + if (set_cpsr) + return cpsr_setter.Generate(false); + return sel_instr.Generate(false); + }); + } +} \ No newline at end of file