Implemented the ARM SEL instruction, with tests. (#39)

The test for this instruction is very peculiar. As the instruction's behavior depends on the value of the CPSR, we generate a MSR instruction after each SEL instruction to change the CPSR.
This commit is contained in:
Sebastian Valle 2016-11-23 13:14:07 -05:00 committed by Merry
parent 6520c7537e
commit d589c63107
3 changed files with 57 additions and 1 deletions

View file

@ -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

View file

@ -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(); }

View file

@ -904,3 +904,29 @@ TEST_CASE("VFP: VPUSH, VPOP", "[JitX64][vfp]") {
return instructions[RandInt<size_t>(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);
});
}
}