diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c3c748cd..820f3700 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -99,6 +99,7 @@ add_library(dynarmic frontend/A32/translate/translate_arm/barrier.cpp frontend/A32/translate/translate_arm/branch.cpp frontend/A32/translate/translate_arm/coprocessor.cpp + frontend/A32/translate/translate_arm/crc32.cpp frontend/A32/translate/translate_arm/data_processing.cpp frontend/A32/translate/translate_arm/divide.cpp frontend/A32/translate/translate_arm/exception_generating.cpp diff --git a/src/frontend/A32/decoder/arm.inc b/src/frontend/A32/decoder/arm.inc index ea3a96b5..0935d7ca 100644 --- a/src/frontend/A32/decoder/arm.inc +++ b/src/frontend/A32/decoder/arm.inc @@ -11,6 +11,10 @@ INST(arm_BL, "BL", "cccc1011vvvvvvvvvvvvvvvvvvvvvvvv INST(arm_BX, "BX", "cccc000100101111111111110001mmmm") // v4T INST(arm_BXJ, "BXJ", "cccc000100101111111111110010mmmm") // v5J +// CRC32 instructions +INST(arm_CRC32, "CRC32", "cccc00010zz0nnnndddd00000100mmmm") // v8 +INST(arm_CRC32C, "CRC32C", "cccc00010zz0nnnndddd00100100mmmm") // v8 + // Coprocessor instructions INST(arm_CDP, "CDP", "cccc1110ooooNNNNDDDDppppooo0MMMM") // v2 (CDP2: v5) INST(arm_LDC, "LDC", "cccc110pudw1nnnnDDDDppppvvvvvvvv") // v2 (LDC2: v5) diff --git a/src/frontend/A32/disassembler/disassembler_arm.cpp b/src/frontend/A32/disassembler/disassembler_arm.cpp index f922bc6e..337d7ec7 100644 --- a/src/frontend/A32/disassembler/disassembler_arm.cpp +++ b/src/frontend/A32/disassembler/disassembler_arm.cpp @@ -224,6 +224,22 @@ public: return ""; } + // CRC32 instructions + std::string arm_CRC32([[maybe_unused]] Cond cond, Imm<2> sz, Reg n, Reg d, Reg m) { + static constexpr std::array data_type{ + "b", "h", "w", "invalid", + }; + + return fmt::format("crc32{} {}, {}, {}", data_type[sz.ZeroExtend()], d, n, m); + } + std::string arm_CRC32C([[maybe_unused]] Cond cond, Imm<2> sz, Reg n, Reg d, Reg m) { + static constexpr std::array data_type{ + "b", "h", "w", "invalid", + }; + + return fmt::format("crc32c{} {}, {}, {}", data_type[sz.ZeroExtend()], d, n, m); + } + // Data processing instructions std::string arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8) { return fmt::format("adc{}{} {}, {}, #{}", CondToString(cond), S ? "s" : "", d, n, ArmExpandImm(rotate, imm8)); diff --git a/src/frontend/A32/translate/translate_arm/crc32.cpp b/src/frontend/A32/translate/translate_arm/crc32.cpp new file mode 100644 index 00000000..7ce1ec07 --- /dev/null +++ b/src/frontend/A32/translate/translate_arm/crc32.cpp @@ -0,0 +1,97 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2019 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::A32 { + +// It's considered constrained UNPREDICTABLE behavior if either +// CRC32 instruction variant is executed with a condition code +// that is *not* 0xE (Always execute). ARM defines one of the following +// as being a requirement in this case. Either: +// +// 1. The instruction is undefined. +// 2. The instruction executes as a NOP. +// 3. The instruction executes unconditionally. +// 4. The instruction executes conditionally. +// +// It's also considered constrained UNPREDICTABLE behavior if +// either CRC32 instruction variant is executed with a size specifier +// of 64-bit (sz -> 0b11) +// +// In this case, either: +// +// 1. The instruction is undefined +// 2. The instruction executes as a NOP. +// 3. The instruction executes with the additional decode: size = 32. +// +// In both cases, we treat as unpredictable, to allow +// library users to provide their own intended behavior +// in the unpredictable exception handler. + +namespace { +enum class CRCType { + Castagnoli, + ISO, +}; + +bool CRC32Variant(ArmTranslatorVisitor& v, Cond cond, Imm<2> sz, Reg n, Reg d, Reg m, CRCType type) { + if (d == Reg::PC || n == Reg::PC || m == Reg::PC) { + return v.UnpredictableInstruction(); + } + + if (sz == 0b11) { + return v.UnpredictableInstruction(); + } + + if (cond != Cond::AL) { + return v.UnpredictableInstruction(); + } + + const IR::U32 result = [m, n, sz, type, &v] { + const IR::U32 accumulator = v.ir.GetRegister(n); + const IR::U32 data = v.ir.GetRegister(m); + + if (type == CRCType::ISO) { + switch (sz.ZeroExtend()) { + case 0b00: + return v.ir.CRC32ISO8(accumulator, v.ir.And(data, v.ir.Imm32(0xFF))); + case 0b01: + return v.ir.CRC32ISO16(accumulator, v.ir.And(data, v.ir.Imm32(0xFFFF))); + case 0b10: + return v.ir.CRC32ISO32(accumulator, data); + } + } else { + switch (sz.ZeroExtend()) { + case 0b00: + return v.ir.CRC32Castagnoli8(accumulator, v.ir.And(data, v.ir.Imm32(0xFF))); + case 0b01: + return v.ir.CRC32Castagnoli16(accumulator, v.ir.And(data, v.ir.Imm32(0xFFFF))); + case 0b10: + return v.ir.CRC32Castagnoli32(accumulator, data); + } + } + + UNREACHABLE(); + return IR::U32{}; + }(); + + v.ir.SetRegister(d, result); + return true; +} +} // Anonymous namespace + +// CRC32{B,H,W}{} , , +bool ArmTranslatorVisitor::arm_CRC32(Cond cond, Imm<2> sz, Reg n, Reg d, Reg m) { + return CRC32Variant(*this, cond, sz, n, d, m, CRCType::ISO); +} + +// CRC32C{B,H,W}{} , , +bool ArmTranslatorVisitor::arm_CRC32C(Cond cond, Imm<2> sz, Reg n, Reg d, Reg m) { + return CRC32Variant(*this, cond, sz, n, d, m, CRCType::Castagnoli); +} + +} // namespace Dynarmic::A32 diff --git a/src/frontend/A32/translate/translate_arm/translate_arm.h b/src/frontend/A32/translate/translate_arm/translate_arm.h index 0a1160b9..ea6e3b12 100644 --- a/src/frontend/A32/translate/translate_arm/translate_arm.h +++ b/src/frontend/A32/translate/translate_arm/translate_arm.h @@ -87,6 +87,10 @@ struct ArmTranslatorVisitor final { bool arm_MRRC(Cond cond, Reg t2, Reg t, size_t coproc_no, size_t opc, CoprocReg CRm); bool arm_STC(Cond cond, bool p, bool u, bool d, bool w, Reg n, CoprocReg CRd, size_t coproc_no, Imm<8> imm8); + // CRC32 instructions + bool arm_CRC32(Cond cond, Imm<2> sz, Reg n, Reg d, Reg m); + bool arm_CRC32C(Cond cond, Imm<2> sz, Reg n, Reg d, Reg m); + // Data processing instructions bool arm_ADC_imm(Cond cond, bool S, Reg n, Reg d, int rotate, Imm<8> imm8); bool arm_ADC_reg(Cond cond, bool S, Reg n, Reg d, Imm<5> imm5, ShiftType shift, Reg m);