diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5a0394ac..3692c24a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,6 +24,7 @@ set(SRCS frontend/translate/translate_arm/extension.cpp frontend/translate/translate_arm/load_store.cpp frontend/translate/translate_arm/multiply.cpp + frontend/translate/translate_arm/packing.cpp frontend/translate/translate_arm/parallel.cpp frontend/translate/translate_arm/reversal.cpp frontend/translate/translate_arm/status_register_access.cpp diff --git a/src/frontend/translate/translate_arm/packing.cpp b/src/frontend/translate/translate_arm/packing.cpp new file mode 100644 index 00000000..8c48c6ec --- /dev/null +++ b/src/frontend/translate/translate_arm/packing.cpp @@ -0,0 +1,41 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2016 MerryMage + * This software may be used and distributed according to the terms of the GNU + * General Public License version 2 or any later version. + */ + +#include "translate_arm.h" + +namespace Dynarmic { +namespace Arm { + +bool ArmTranslatorVisitor::arm_PKHBT(Cond cond, Reg n, Reg d, Imm5 imm5, Reg m) { + if (n == Reg::PC || d == Reg::PC || m == Reg::PC) + return UnpredictableInstruction(); + + if (ConditionPassed(cond)) { + auto shifted = EmitImmShift(ir.GetRegister(m), ShiftType::LSL, imm5, ir.Imm1(false)).result; + auto lower_half = ir.And(ir.GetRegister(n), ir.Imm32(0x0000FFFF)); + auto upper_half = ir.And(shifted, ir.Imm32(0xFFFF0000)); + ir.SetRegister(d, ir.Or(lower_half, upper_half)); + } + + return true; +} + +bool ArmTranslatorVisitor::arm_PKHTB(Cond cond, Reg n, Reg d, Imm5 imm5, Reg m) { + if (n == Reg::PC || d == Reg::PC || m == Reg::PC) + return UnpredictableInstruction(); + + if (ConditionPassed(cond)) { + auto shifted = EmitImmShift(ir.GetRegister(m), ShiftType::ASR, imm5, ir.Imm1(false)).result; + auto lower_half = ir.And(shifted, ir.Imm32(0x0000FFFF)); + auto upper_half = ir.And(ir.GetRegister(n), ir.Imm32(0xFFFF0000)); + ir.SetRegister(d, ir.Or(lower_half, upper_half)); + } + + 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 09b9b0ae..6b7d3631 100644 --- a/src/frontend/translate/translate_arm/translate_arm.h +++ b/src/frontend/translate/translate_arm/translate_arm.h @@ -218,8 +218,8 @@ struct ArmTranslatorVisitor final { bool arm_USADA8(Cond cond, Reg d, Reg a, Reg m, Reg n) { return InterpretThisInstruction(); } // Packing instructions - bool arm_PKHBT(Cond cond, Reg n, Reg d, Imm5 imm5, Reg m) { return InterpretThisInstruction(); } - bool arm_PKHTB(Cond cond, Reg n, Reg d, Imm5 imm5, Reg m) { return InterpretThisInstruction(); } + bool arm_PKHBT(Cond cond, Reg n, Reg d, Imm5 imm5, Reg m); + bool arm_PKHTB(Cond cond, Reg n, Reg d, Imm5 imm5, Reg m); // Reversal instructions bool arm_REV(Cond cond, Reg d, Reg m); diff --git a/tests/arm/fuzz_arm.cpp b/tests/arm/fuzz_arm.cpp index 9e6318b9..31ec1027 100644 --- a/tests/arm/fuzz_arm.cpp +++ b/tests/arm/fuzz_arm.cpp @@ -929,4 +929,24 @@ TEST_CASE("Test ARM SEL instruction", "[JitX64]") { return sel_instr.Generate(false); }); } -} \ No newline at end of file +} + +TEST_CASE("Fuzz ARM packing instructions", "[JitX64]") { + auto is_pkh_valid = [](u32 inst) -> bool { + // R15 as Rd, Rn, or Rm is UNPREDICTABLE + return Bits<16, 19>(inst) != 0b1111 && + Bits<12, 15>(inst) != 0b1111 && + Bits<0, 3>(inst) != 0b1111; + }; + + const std::array instructions = {{ + InstructionGenerator("cccc01101000nnnnddddvvvvv001mmmm", is_pkh_valid), // PKHBT + InstructionGenerator("cccc01101000nnnnddddvvvvv101mmmm", is_pkh_valid), // PKHTB + }}; + + SECTION("Packing") { + FuzzJitArm(1, 1, 10000, [&instructions]() -> u32 { + return instructions[RandInt(0, instructions.size() - 1)].Generate(); + }); + } +}