/* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * SPDX-License-Identifier: 0BSD */ #include #include "frontend/A32/translate/impl/translate_thumb.h" namespace Dynarmic::A32 { // LSLS , , # bool ThumbTranslatorVisitor::thumb16_LSL_imm(Imm<5> imm5, Reg m, Reg d) { const u8 shift_n = imm5.ZeroExtend(); if (shift_n == 0 && ir.current_location.IT().IsInITBlock()) { return UnpredictableInstruction(); } const auto cpsr_c = ir.GetCFlag(); const auto result = ir.LogicalShiftLeft(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); } return true; } // LSRS , , # bool ThumbTranslatorVisitor::thumb16_LSR_imm(Imm<5> imm5, Reg m, Reg d) { const u8 shift_n = imm5 != 0 ? imm5.ZeroExtend() : u8(32); const auto cpsr_c = ir.GetCFlag(); const auto result = ir.LogicalShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); } return true; } // ASRS , , # bool ThumbTranslatorVisitor::thumb16_ASR_imm(Imm<5> imm5, Reg m, Reg d) { const u8 shift_n = imm5 != 0 ? imm5.ZeroExtend() : u8(32); const auto cpsr_c = ir.GetCFlag(); const auto result = ir.ArithmeticShiftRight(ir.GetRegister(m), ir.Imm8(shift_n), cpsr_c); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); } return true; } // ADDS , , // Note that it is not possible to encode Rd == R15. bool ThumbTranslatorVisitor::thumb16_ADD_reg_t1(Reg m, Reg n, Reg d) { const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0)); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); } return true; } // SUBS , , // Note that it is not possible to encode Rd == R15. bool ThumbTranslatorVisitor::thumb16_SUB_reg(Reg m, Reg n, Reg d) { const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(1)); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); } return true; } // ADDS , , # // Rd can never encode R15. bool ThumbTranslatorVisitor::thumb16_ADD_imm_t1(Imm<3> imm3, Reg n, Reg d) { const u32 imm32 = imm3.ZeroExtend(); const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(0)); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); } return true; } // SUBS , , # // Rd can never encode R15. bool ThumbTranslatorVisitor::thumb16_SUB_imm_t1(Imm<3> imm3, Reg n, Reg d) { const u32 imm32 = imm3.ZeroExtend(); const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1)); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); } return true; } // MOVS , # // Rd can never encode R15. bool ThumbTranslatorVisitor::thumb16_MOV_imm(Reg d, Imm<8> imm8) { const u32 imm32 = imm8.ZeroExtend(); const auto result = ir.Imm32(imm32); ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); } return true; } // CMP , # bool ThumbTranslatorVisitor::thumb16_CMP_imm(Reg n, Imm<8> imm8) { const u32 imm32 = imm8.ZeroExtend(); const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1)); ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); return true; } // ADDS , # // Rd can never encode R15. bool ThumbTranslatorVisitor::thumb16_ADD_imm_t2(Reg d_n, Imm<8> imm8) { const u32 imm32 = imm8.ZeroExtend(); const Reg d = d_n; const Reg n = d_n; const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(0)); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); } return true; } // SUBS , , # // Rd can never encode R15. bool ThumbTranslatorVisitor::thumb16_SUB_imm_t2(Reg d_n, Imm<8> imm8) { const u32 imm32 = imm8.ZeroExtend(); const Reg d = d_n; const Reg n = d_n; const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.Imm32(imm32), ir.Imm1(1)); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); } return true; } // ANDS , // Note that it is not possible to encode Rdn == R15. bool ThumbTranslatorVisitor::thumb16_AND_reg(Reg m, Reg d_n) { const Reg d = d_n; const Reg n = d_n; const auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m)); ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); } return true; } // EORS , // Note that it is not possible to encode Rdn == R15. bool ThumbTranslatorVisitor::thumb16_EOR_reg(Reg m, Reg d_n) { const Reg d = d_n; const Reg n = d_n; const auto result = ir.Eor(ir.GetRegister(n), ir.GetRegister(m)); ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); } return true; } // LSLS , bool ThumbTranslatorVisitor::thumb16_LSL_reg(Reg m, Reg d_n) { const Reg d = d_n; const Reg n = d_n; const auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m)); const auto apsr_c = ir.GetCFlag(); const auto result_carry = ir.LogicalShiftLeft(ir.GetRegister(n), shift_n, apsr_c); ir.SetRegister(d, result_carry.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result_carry.result)); ir.SetZFlag(ir.IsZero(result_carry.result)); ir.SetCFlag(result_carry.carry); } return true; } // LSRS , bool ThumbTranslatorVisitor::thumb16_LSR_reg(Reg m, Reg d_n) { const Reg d = d_n; const Reg n = d_n; const auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m)); const auto cpsr_c = ir.GetCFlag(); const auto result = ir.LogicalShiftRight(ir.GetRegister(n), shift_n, cpsr_c); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); } return true; } // ASRS , bool ThumbTranslatorVisitor::thumb16_ASR_reg(Reg m, Reg d_n) { const Reg d = d_n; const Reg n = d_n; const auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m)); const auto cpsr_c = ir.GetCFlag(); const auto result = ir.ArithmeticShiftRight(ir.GetRegister(n), shift_n, cpsr_c); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); } return true; } // ADCS , // Note that it is not possible to encode Rd == R15. bool ThumbTranslatorVisitor::thumb16_ADC_reg(Reg m, Reg d_n) { const Reg d = d_n; const Reg n = d_n; const auto aspr_c = ir.GetCFlag(); const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), aspr_c); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); } return true; } // SBCS , // Note that it is not possible to encode Rd == R15. bool ThumbTranslatorVisitor::thumb16_SBC_reg(Reg m, Reg d_n) { const Reg d = d_n; const Reg n = d_n; const auto aspr_c = ir.GetCFlag(); const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), aspr_c); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); } return true; } // RORS , bool ThumbTranslatorVisitor::thumb16_ROR_reg(Reg m, Reg d_n) { const Reg d = d_n; const Reg n = d_n; const auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m)); const auto cpsr_c = ir.GetCFlag(); const auto result = ir.RotateRight(ir.GetRegister(n), shift_n, cpsr_c); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); } return true; } // TST , bool ThumbTranslatorVisitor::thumb16_TST_reg(Reg m, Reg n) { const auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m)); ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); return true; } // RSBS , , #0 // Rd can never encode R15. bool ThumbTranslatorVisitor::thumb16_RSB_imm(Reg n, Reg d) { const auto result = ir.SubWithCarry(ir.Imm32(0), ir.GetRegister(n), ir.Imm1(1)); ir.SetRegister(d, result.result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); } return true; } // CMP , bool ThumbTranslatorVisitor::thumb16_CMP_reg_t1(Reg m, Reg n) { const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(1)); ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); return true; } // CMN , bool ThumbTranslatorVisitor::thumb16_CMN_reg(Reg m, Reg n) { const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0)); ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); return true; } // ORRS , // Rd cannot encode R15. bool ThumbTranslatorVisitor::thumb16_ORR_reg(Reg m, Reg d_n) { const Reg d = d_n; const Reg n = d_n; const auto result = ir.Or(ir.GetRegister(m), ir.GetRegister(n)); ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); } return true; } // MULS , , // Rd cannot encode R15. bool ThumbTranslatorVisitor::thumb16_MUL_reg(Reg n, Reg d_m) { const Reg d = d_m; const Reg m = d_m; const auto result = ir.Mul(ir.GetRegister(m), ir.GetRegister(n)); ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); } return true; } // BICS , // Rd cannot encode R15. bool ThumbTranslatorVisitor::thumb16_BIC_reg(Reg m, Reg d_n) { const Reg d = d_n; const Reg n = d_n; const auto result = ir.And(ir.GetRegister(n), ir.Not(ir.GetRegister(m))); ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); } return true; } // MVNS , // Rd cannot encode R15. bool ThumbTranslatorVisitor::thumb16_MVN_reg(Reg m, Reg d) { const auto result = ir.Not(ir.GetRegister(m)); ir.SetRegister(d, result); if (!ir.current_location.IT().IsInITBlock()) { ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); } return true; } // ADD , bool ThumbTranslatorVisitor::thumb16_ADD_reg_t2(bool d_n_hi, Reg m, Reg d_n_lo) { const Reg d_n = d_n_hi ? (d_n_lo + 8) : d_n_lo; const Reg n = d_n; const Reg d = d_n; if (n == Reg::PC && m == Reg::PC) { return UnpredictableInstruction(); } if (d == Reg::PC && ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) { return UnpredictableInstruction(); } const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0)); if (d == Reg::PC) { ir.UpdateUpperLocationDescriptor(); ir.ALUWritePC(result.result); // Return to dispatch as we can't predict what PC is going to be. Stop compilation. ir.SetTerm(IR::Term::FastDispatchHint{}); return false; } else { ir.SetRegister(d, result.result); return true; } } // CMP , bool ThumbTranslatorVisitor::thumb16_CMP_reg_t2(bool n_hi, Reg m, Reg n_lo) { const Reg n = n_hi ? (n_lo + 8) : n_lo; if (n < Reg::R8 && m < Reg::R8) { return UnpredictableInstruction(); } if (n == Reg::PC || m == Reg::PC) { return UnpredictableInstruction(); } const auto result = ir.SubWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(1)); ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); return true; } // MOV , bool ThumbTranslatorVisitor::thumb16_MOV_reg(bool d_hi, Reg m, Reg d_lo) { const Reg d = d_hi ? (d_lo + 8) : d_lo; if (d == Reg::PC && ir.current_location.IT().IsInITBlock() && !ir.current_location.IT().IsLastInITBlock()) { return UnpredictableInstruction(); } const auto result = ir.GetRegister(m); if (d == Reg::PC) { ir.UpdateUpperLocationDescriptor(); ir.ALUWritePC(result); ir.SetTerm(IR::Term::FastDispatchHint{}); return false; } else { ir.SetRegister(d, result); return true; } } // LDR ,