diff --git a/src/frontend/A32/decoder/thumb32.inc b/src/frontend/A32/decoder/thumb32.inc index 42bd9216..aee79da8 100644 --- a/src/frontend/A32/decoder/thumb32.inc +++ b/src/frontend/A32/decoder/thumb32.inc @@ -72,21 +72,20 @@ INST(thumb32_RSB_imm, "RSB (imm)", "11110v01110Snnnn0vvvdd // Data Processing (Plain Binary Immediate) //INST(thumb32_ADR, "ADR", "11110-10000011110---------------") -//INST(thumb32_ADD_imm_2, "ADD (imm)", "11110-100000----0---------------") +INST(thumb32_ADD_imm_2, "ADD (imm)", "11110i10000011010iiiddddiiiiiiii") INST(thumb32_MOVW_imm, "MOVW (imm)", "11110i100100iiii0iiiddddiiiiiiii") //INST(thumb32_ADR, "ADR", "11110-10101011110---------------") -//INST(thumb32_SUB_imm_2, "SUB (imm)", "11110-101010----0---------------") +INST(thumb32_SUB_imm_2, "SUB (imm)", "11110i10101011010iiiddddiiiiiiii") INST(thumb32_MOVT, "MOVT", "11110i101100iiii0iiiddddiiiiiiii") -//INST(thumb32_SSAT, "SSAT", "11110-110000----0---------------") -//INST(thumb32_SSAT16, "SSAT16", "11110-110010----0000----00------") -//INST(thumb32_SSAT, "SSAT", "11110-110010----0---------------") -//INST(thumb32_SBFX, "SBFX", "11110-110100----0---------------") -//INST(thumb32_BFC, "BFC", "11110-11011011110---------------") -//INST(thumb32_BFI, "BFI", "11110-110110----0---------------") -//INST(thumb32_USAT, "USAT", "11110-111000----0---------------") -//INST(thumb32_USAT16, "USAT16", "11110-111010----0000----00------") -//INST(thumb32_USAT, "USAT", "11110-111010----0---------------") -//INST(thumb32_UBFX, "UBFX", "11110-111100----0---------------") +INST(thumb32_UDF, "Invalid decoding", "11110011-010----0000----0001----") +INST(thumb32_SSAT16, "SSAT16", "111100110010nnnn0000dddd0000iiii") +INST(thumb32_USAT16, "USAT16", "111100111010nnnn0000dddd0000iiii") +INST(thumb32_SSAT, "SSAT", "1111001100s0nnnn0iiiddddii0bbbbb") +INST(thumb32_USAT, "USAT", "1111001110s0nnnn0iiiddddii0bbbbb") +INST(thumb32_SBFX, "SBFX", "111100110100nnnn0iiiddddii0wwwww") +INST(thumb32_BFC, "BFC", "11110011011011110iiiddddii0bbbbb") +INST(thumb32_BFI, "BFI", "111100110110nnnn0iiiddddii0bbbbb") +INST(thumb32_UBFX, "UBFX", "111100111100nnnn0iiiddddii0wwwww") // Branches and Miscellaneous Control //INST(thumb32_MSR_banked, "MSR (banked)", "11110011100-----10-0------1-----") diff --git a/src/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp b/src/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp index 8244ddc2..5094ac31 100644 --- a/src/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp +++ b/src/frontend/A32/translate/impl/thumb32_data_processing_plain_binary_immediate.cpp @@ -3,9 +3,109 @@ * SPDX-License-Identifier: 0BSD */ +#include "common/assert.h" +#include "common/bit_util.h" #include "frontend/A32/translate/impl/translate_thumb.h" namespace Dynarmic::A32 { +static IR::U32 Pack2x16To1x32(A32::IREmitter& ir, IR::U32 lo, IR::U32 hi) { + return ir.Or(ir.And(lo, ir.Imm32(0xFFFF)), ir.LogicalShiftLeft(hi, ir.Imm8(16), ir.Imm1(0)).result); +} + +static IR::U16 MostSignificantHalf(A32::IREmitter& ir, IR::U32 value) { + return ir.LeastSignificantHalf(ir.LogicalShiftRight(value, ir.Imm8(16), ir.Imm1(0)).result); +} + +using SaturationFunction = IR::ResultAndOverflow (IREmitter::*)(const IR::U32&, size_t); + +static bool Saturation(ThumbTranslatorVisitor& v, bool sh, Reg n, Reg d, Imm<5> shift_amount, size_t saturate_to, SaturationFunction sat_fn) { + ASSERT_MSG(!(sh && shift_amount == 0), "Invalid decode"); + + if (d == Reg::PC || n == Reg::PC) { + return v.UnpredictableInstruction(); + } + + const auto shift = sh ? ShiftType::ASR : ShiftType::LSL; + const auto operand = v.EmitImmShift(v.ir.GetRegister(n), shift, shift_amount, v.ir.GetCFlag()); + const auto result = (v.ir.*sat_fn)(operand.result, saturate_to); + + v.ir.SetRegister(d, result.result); + v.ir.OrQFlag(result.overflow); + return true; +} + +static bool Saturation16(ThumbTranslatorVisitor& v, Reg n, Reg d, size_t saturate_to, SaturationFunction sat_fn) { + if (d == Reg::PC || n == Reg::PC) { + return v.UnpredictableInstruction(); + } + + const auto reg_n = v.ir.GetRegister(n); + + const auto lo_operand = v.ir.SignExtendHalfToWord(v.ir.LeastSignificantHalf(reg_n)); + const auto hi_operand = v.ir.SignExtendHalfToWord(MostSignificantHalf(v.ir, reg_n)); + const auto lo_result = (v.ir.*sat_fn)(lo_operand, saturate_to); + const auto hi_result = (v.ir.*sat_fn)(hi_operand, saturate_to); + + v.ir.SetRegister(d, Pack2x16To1x32(v.ir, lo_result.result, hi_result.result)); + v.ir.OrQFlag(lo_result.overflow); + v.ir.OrQFlag(hi_result.overflow); + return true; +} + +bool ThumbTranslatorVisitor::thumb32_ADD_imm_2(Imm<1> imm1, Imm<3> imm3, Reg d, Imm<8> imm8) { + if (d == Reg::PC) { + return UnpredictableInstruction(); + } + + const u32 imm = concatenate(imm1, imm3, imm8).ZeroExtend(); + const auto sp = ir.GetRegister(Reg::SP); + const auto result = ir.AddWithCarry(sp, ir.Imm32(imm), ir.Imm1(0)); + + ir.SetRegister(d, result.result); + return true; +} + +bool ThumbTranslatorVisitor::thumb32_BFC(Imm<3> imm3, Reg d, Imm<2> imm2, Imm<5> msb) { + if (d == Reg::PC) { + return UnpredictableInstruction(); + } + + const u32 lsbit = concatenate(imm3, imm2).ZeroExtend(); + const u32 msbit = msb.ZeroExtend(); + + if (msbit < lsbit) { + return UnpredictableInstruction(); + } + + const u32 mask = ~(Common::Ones(msbit - lsbit + 1) << lsbit); + const auto reg_d = ir.GetRegister(d); + const auto result = ir.And(reg_d, ir.Imm32(mask)); + + ir.SetRegister(d, result); + return true; +} + +bool ThumbTranslatorVisitor::thumb32_BFI(Reg n, Imm<3> imm3, Reg d, Imm<2> imm2, Imm<5> msb) { + if (d == Reg::PC || n == Reg::PC) { + return UnpredictableInstruction(); + } + + const u32 lsbit = concatenate(imm3, imm2).ZeroExtend(); + const u32 msbit = msb.ZeroExtend(); + + if (msbit < lsbit) { + return UnpredictableInstruction(); + } + + const u32 inclusion_mask = Common::Ones(msbit - lsbit + 1) << lsbit; + const u32 exclusion_mask = ~inclusion_mask; + const IR::U32 operand1 = ir.And(ir.GetRegister(d), ir.Imm32(exclusion_mask)); + const IR::U32 operand2 = ir.And(ir.LogicalShiftLeft(ir.GetRegister(n), ir.Imm8(u8(lsbit))), ir.Imm32(inclusion_mask)); + const IR::U32 result = ir.Or(operand1, operand2); + + ir.SetRegister(d, result); + return true; +} bool ThumbTranslatorVisitor::thumb32_MOVT(Imm<1> imm1, Imm<4> imm4, Imm<3> imm3, Reg d, Imm<8> imm8) { if (d == Reg::PC) { @@ -31,4 +131,77 @@ bool ThumbTranslatorVisitor::thumb32_MOVW_imm(Imm<1> imm1, Imm<4> imm4, Imm<3> i return true; } +bool ThumbTranslatorVisitor::thumb32_SBFX(Reg n, Imm<3> imm3, Reg d, Imm<2> imm2, Imm<5> widthm1) { + if (d == Reg::PC || n == Reg::PC) { + return UnpredictableInstruction(); + } + + const u32 lsbit = concatenate(imm3, imm2).ZeroExtend(); + const u32 widthm1_value = widthm1.ZeroExtend(); + const u32 msb = lsbit + widthm1_value; + if (msb >= Common::BitSize()) { + return UnpredictableInstruction(); + } + + constexpr size_t max_width = Common::BitSize(); + const auto width = widthm1_value + 1; + const auto left_shift_amount = static_cast(max_width - width - lsbit); + const auto right_shift_amount = static_cast(max_width - width); + const auto operand = ir.GetRegister(n); + const auto tmp = ir.LogicalShiftLeft(operand, ir.Imm8(left_shift_amount)); + const auto result = ir.ArithmeticShiftRight(tmp, ir.Imm8(right_shift_amount)); + + ir.SetRegister(d, result); + return true; +} + +bool ThumbTranslatorVisitor::thumb32_SSAT(bool sh, Reg n, Imm<3> imm3, Reg d, Imm<2> imm2, Imm<5> sat_imm) { + return Saturation(*this, sh, n, d, concatenate(imm3, imm2), sat_imm.ZeroExtend() + 1, &IREmitter::SignedSaturation); +} + +bool ThumbTranslatorVisitor::thumb32_SSAT16(Reg n, Reg d, Imm<4> sat_imm) { + return Saturation16(*this, n, d, sat_imm.ZeroExtend() + 1, &IREmitter::SignedSaturation); +} + +bool ThumbTranslatorVisitor::thumb32_SUB_imm_2(Imm<1> imm1, Imm<3> imm3, Reg d, Imm<8> imm8) { + if (d == Reg::PC) { + return UnpredictableInstruction(); + } + + const u32 imm = concatenate(imm1, imm3, imm8).ZeroExtend(); + const auto sp = ir.GetRegister(Reg::SP); + const auto result = ir.SubWithCarry(sp, ir.Imm32(imm), ir.Imm1(1)); + + ir.SetRegister(d, result.result); + return true; +} + +bool ThumbTranslatorVisitor::thumb32_UBFX(Reg n, Imm<3> imm3, Reg d, Imm<2> imm2, Imm<5> widthm1) { + if (d == Reg::PC || n == Reg::PC) { + return UnpredictableInstruction(); + } + + const u32 lsbit = concatenate(imm3, imm2).ZeroExtend(); + const u32 widthm1_value = widthm1.ZeroExtend(); + const u32 msb = lsbit + widthm1_value; + if (msb >= Common::BitSize()) { + return UnpredictableInstruction(); + } + + const auto operand = ir.GetRegister(n); + const auto mask = ir.Imm32(Common::Ones(widthm1_value + 1)); + const auto result = ir.And(ir.LogicalShiftRight(operand, ir.Imm8(u8(lsbit))), mask); + + ir.SetRegister(d, result); + return true; +} + +bool ThumbTranslatorVisitor::thumb32_USAT(bool sh, Reg n, Imm<3> imm3, Reg d, Imm<2> imm2, Imm<5> sat_imm) { + return Saturation(*this, sh, n, d, concatenate(imm3, imm2), sat_imm.ZeroExtend(), &IREmitter::UnsignedSaturation); +} + +bool ThumbTranslatorVisitor::thumb32_USAT16(Reg n, Reg d, Imm<4> sat_imm) { + return Saturation16(*this, n, d, sat_imm.ZeroExtend(), &IREmitter::UnsignedSaturation); +} + } // namespace Dynarmic::A32 diff --git a/src/frontend/A32/translate/impl/translate_thumb.h b/src/frontend/A32/translate/impl/translate_thumb.h index 5d84d638..ddaa7a3e 100644 --- a/src/frontend/A32/translate/impl/translate_thumb.h +++ b/src/frontend/A32/translate/impl/translate_thumb.h @@ -67,6 +67,8 @@ struct ThumbTranslatorVisitor final { bool UndefinedInstruction(); bool RaiseException(Exception exception); + IR::ResultAndCarry EmitImmShift(IR::U32 value, ShiftType type, Imm<5> imm5, IR::U1 carry_in); + // thumb16 bool thumb16_LSL_imm(Imm<5> imm5, Reg m, Reg d); bool thumb16_LSR_imm(Imm<5> imm5, Reg m, Reg d); @@ -167,8 +169,18 @@ struct ThumbTranslatorVisitor final { bool thumb32_RSB_imm(Imm<1> i, bool S, Reg n, Imm<3> imm3, Reg d, Imm<8> imm8); // thumb32 data processing (plain binary immediate) instructions. + bool thumb32_ADD_imm_2(Imm<1> imm1, Imm<3> imm3, Reg d, Imm<8> imm8); + bool thumb32_BFC(Imm<3> imm3, Reg d, Imm<2> imm2, Imm<5> msb); + bool thumb32_BFI(Reg n, Imm<3> imm3, Reg d, Imm<2> imm2, Imm<5> msb); bool thumb32_MOVT(Imm<1> imm1, Imm<4> imm4, Imm<3> imm3, Reg d, Imm<8> imm8); bool thumb32_MOVW_imm(Imm<1> imm1, Imm<4> imm4, Imm<3> imm3, Reg d, Imm<8> imm8); + bool thumb32_SBFX(Reg n, Imm<3> imm3, Reg d, Imm<2> imm2, Imm<5> widthm1); + bool thumb32_SSAT(bool sh, Reg n, Imm<3> imm3, Reg d, Imm<2> imm2, Imm<5> sat_imm); + bool thumb32_SSAT16(Reg n, Reg d, Imm<4> sat_imm); + bool thumb32_SUB_imm_2(Imm<1> imm1, Imm<3> imm3, Reg d, Imm<8> imm8); + bool thumb32_UBFX(Reg n, Imm<3> imm3, Reg d, Imm<2> imm2, Imm<5> widthm1); + bool thumb32_USAT(bool sh, Reg n, Imm<3> imm3, Reg d, Imm<2> imm2, Imm<5> sat_imm); + bool thumb32_USAT16(Reg n, Reg d, Imm<4> sat_imm); // thumb32 miscellaneous control instructions bool thumb32_BXJ(Reg m); diff --git a/src/frontend/A32/translate/translate_thumb.cpp b/src/frontend/A32/translate/translate_thumb.cpp index 7077c52c..0e52cde7 100644 --- a/src/frontend/A32/translate/translate_thumb.cpp +++ b/src/frontend/A32/translate/translate_thumb.cpp @@ -176,4 +176,25 @@ bool ThumbTranslatorVisitor::RaiseException(Exception exception) { return false; } +IR::ResultAndCarry ThumbTranslatorVisitor::EmitImmShift(IR::U32 value, ShiftType type, Imm<5> imm5, IR::U1 carry_in) { + u8 imm5_value = imm5.ZeroExtend(); + switch (type) { + case ShiftType::LSL: + return ir.LogicalShiftLeft(value, ir.Imm8(imm5_value), carry_in); + case ShiftType::LSR: + imm5_value = imm5_value ? imm5_value : 32; + return ir.LogicalShiftRight(value, ir.Imm8(imm5_value), carry_in); + case ShiftType::ASR: + imm5_value = imm5_value ? imm5_value : 32; + return ir.ArithmeticShiftRight(value, ir.Imm8(imm5_value), carry_in); + case ShiftType::ROR: + if (imm5_value) { + return ir.RotateRight(value, ir.Imm8(imm5_value), carry_in); + } else { + return ir.RotateRightExtended(value, carry_in); + } + } + UNREACHABLE(); +} + } // namespace Dynarmic::A32