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:
parent
6520c7537e
commit
d589c63107
3 changed files with 57 additions and 1 deletions
|
@ -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
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue