/* 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 #include #include "common/assert.h" #include "common/bit_util.h" #include "frontend/imm.h" #include "frontend/A32/decoder/thumb16.h" #include "frontend/A32/decoder/thumb32.h" #include "frontend/A32/ir_emitter.h" #include "frontend/A32/location_descriptor.h" #include "frontend/A32/translate/translate.h" #include "frontend/A32/types.h" namespace Dynarmic::A32 { namespace { struct ThumbTranslatorVisitor final { using instruction_return_type = bool; explicit ThumbTranslatorVisitor(IR::Block& block, LocationDescriptor descriptor, const TranslationOptions& options) : ir(block, descriptor), options(options) { ASSERT_MSG(descriptor.TFlag(), "The processor must be in Thumb mode"); } A32::IREmitter ir; TranslationOptions options; bool InterpretThisInstruction() { ir.SetTerm(IR::Term::Interpret(ir.current_location)); return false; } bool UnpredictableInstruction() { ASSERT_MSG(false, "UNPREDICTABLE"); return false; } bool RaiseException(Exception exception) { ir.BranchWritePC(ir.Imm32(ir.current_location.PC() + 2)); ir.ExceptionRaised(exception); ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}}); return false; } // LSLS , , # bool thumb16_LSL_imm(Imm<5> imm5, Reg m, Reg d) { const u8 shift_n = imm5.ZeroExtend(); 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); ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); return true; } // LSRS , , # bool 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); ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); return true; } // ASRS , , # bool 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); 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 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); 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 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); 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 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); 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 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); 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 thumb16_MOV_imm(Reg d, Imm<8> imm8) { const u32 imm32 = imm8.ZeroExtend(); const auto result = ir.Imm32(imm32); ir.SetRegister(d, result); ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); return true; } // CMP , # bool 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 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); 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 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); 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 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); ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); return true; } // EORS , // Note that it is not possible to encode Rdn == R15. bool 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); ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); return true; } // LSLS , bool 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); ir.SetNFlag(ir.MostSignificantBit(result_carry.result)); ir.SetZFlag(ir.IsZero(result_carry.result)); ir.SetCFlag(result_carry.carry); return true; } // LSRS , bool 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); ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); return true; } // ASRS , bool 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); 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 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); 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 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); ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); return true; } // RORS , bool 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); ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); return true; } // TST , bool 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 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); ir.SetNFlag(ir.MostSignificantBit(result.result)); ir.SetZFlag(ir.IsZero(result.result)); ir.SetCFlag(result.carry); ir.SetVFlag(result.overflow); return true; } // CMP , bool 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 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 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); ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); return true; } // MULS , , // Rd cannot encode R15. bool 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); ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); return true; } // BICS , // Rd cannot encode R15. bool 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); ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); return true; } // MVNS , // Rd cannot encode R15. bool thumb16_MVN_reg(Reg m, Reg d) { const auto result = ir.Not(ir.GetRegister(m)); ir.SetRegister(d, result); ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); return true; } // ADD , bool 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; if (n == Reg::PC && m == Reg::PC) { return UnpredictableInstruction(); } const Reg d = d_n; const auto result = ir.AddWithCarry(ir.GetRegister(n), ir.GetRegister(m), ir.Imm1(0)); if (d == Reg::PC) { 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 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 thumb16_MOV_reg(bool d_hi, Reg m, Reg d_lo) { const Reg d = d_hi ? (d_lo + 8) : d_lo; const auto result = ir.GetRegister(m); if (d == Reg::PC) { ir.ALUWritePC(result); ir.SetTerm(IR::Term::FastDispatchHint{}); return false; } else { ir.SetRegister(d, result); return true; } } // LDR ,