/* 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 "common/assert.h" #include "common/bit_util.h" #include "frontend/arm_types.h" #include "frontend/decoder/thumb16.h" #include "frontend/ir/ir_emitter.h" #include "frontend/translate/translate.h" namespace Dynarmic { namespace Arm { namespace { struct ThumbTranslatorVisitor final { explicit ThumbTranslatorVisitor(LocationDescriptor descriptor) : ir(descriptor) { ASSERT_MSG(descriptor.TFlag, "The processor must be in Thumb mode"); } IREmitter ir; bool InterpretThisInstruction() { ir.SetTerm(IR::Term::Interpret(ir.current_location)); return false; } bool UnpredictableInstruction() { ASSERT_MSG(false, "UNPREDICTABLE"); return false; } bool thumb16_LSL_imm(Imm5 imm5, Reg m, Reg d) { u8 shift_n = imm5; // LSLS , , # auto cpsr_c = ir.GetCFlag(); 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; } bool thumb16_LSR_imm(Imm5 imm5, Reg m, Reg d) { u8 shift_n = imm5 != 0 ? imm5 : 32; // LSRS , , # auto cpsr_c = ir.GetCFlag(); 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; } bool thumb16_ASR_imm(Imm5 imm5, Reg m, Reg d) { u8 shift_n = imm5 != 0 ? imm5 : 32; // ASRS , , # auto cpsr_c = ir.GetCFlag(); 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; } bool thumb16_ADD_reg_t1(Reg m, Reg n, Reg d) { // ADDS , , // Note that it is not possible to encode Rd == R15. 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; } bool thumb16_SUB_reg(Reg m, Reg n, Reg d) { // SUBS , , // Note that it is not possible to encode Rd == R15. 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; } bool thumb16_ADD_imm_t1(Imm3 imm3, Reg n, Reg d) { u32 imm32 = imm3 & 0x7; // ADDS , , # // Rd can never encode R15. 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; } bool thumb16_SUB_imm_t1(Imm3 imm3, Reg n, Reg d) { u32 imm32 = imm3 & 0x7; // SUBS , , # // Rd can never encode R15. 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; } bool thumb16_MOV_imm(Reg d, Imm8 imm8) { u32 imm32 = imm8 & 0xFF; // MOVS , # // Rd can never encode R15. auto result = ir.Imm32(imm32); ir.SetRegister(d, result); ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); return true; } bool thumb16_CMP_imm(Reg n, Imm8 imm8) { u32 imm32 = imm8 & 0xFF; // CMP , # 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; } bool thumb16_ADD_imm_t2(Reg d_n, Imm8 imm8) { u32 imm32 = imm8 & 0xFF; Reg d = d_n, n = d_n; // ADDS , # // Rd can never encode R15. 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; } bool thumb16_SUB_imm_t2(Reg d_n, Imm8 imm8) { u32 imm32 = imm8 & 0xFF; Reg d = d_n, n = d_n; // SUBS , , # // Rd can never encode R15. 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; } bool thumb16_AND_reg(Reg m, Reg d_n) { const Reg d = d_n, n = d_n; // ANDS , // Note that it is not possible to encode Rdn == R15. 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; } bool thumb16_EOR_reg(Reg m, Reg d_n) { const Reg d = d_n, n = d_n; // EORS , // Note that it is not possible to encode Rdn == R15. 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; } bool thumb16_LSL_reg(Reg m, Reg d_n) { const Reg d = d_n, n = d_n; // LSLS , auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m)); auto apsr_c = ir.GetCFlag(); 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; } bool thumb16_LSR_reg(Reg m, Reg d_n) { const Reg d = d_n, n = d_n; // LSRS , auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m)); auto cpsr_c = ir.GetCFlag(); 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; } bool thumb16_ASR_reg(Reg m, Reg d_n) { const Reg d = d_n, n = d_n; // ASRS , auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m)); auto cpsr_c = ir.GetCFlag(); 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; } bool thumb16_ADC_reg(Reg m, Reg d_n) { Reg d = d_n, n = d_n; // ADCS , // Note that it is not possible to encode Rd == R15. auto aspr_c = ir.GetCFlag(); 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; } bool thumb16_SBC_reg(Reg m, Reg d_n) { Reg d = d_n, n = d_n; // SBCS , // Note that it is not possible to encode Rd == R15. auto aspr_c = ir.GetCFlag(); 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; } bool thumb16_ROR_reg(Reg m, Reg d_n) { Reg d = d_n, n = d_n; // RORS , auto shift_n = ir.LeastSignificantByte(ir.GetRegister(m)); auto cpsr_c = ir.GetCFlag(); 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; } bool thumb16_TST_reg(Reg m, Reg n) { // TST , auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m)); ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); return true; } bool thumb16_RSB_imm(Reg n, Reg d) { // RSBS , , #0 // Rd can never encode R15. 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; } bool thumb16_CMP_reg_t1(Reg m, Reg n) { // CMP , 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; } bool thumb16_CMN_reg(Reg m, Reg n) { // CMN , 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; } bool thumb16_ORR_reg(Reg m, Reg d_n) { Reg d = d_n, n = d_n; // ORRS , // Rd cannot encode R15. 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; } bool thumb16_BIC_reg(Reg m, Reg d_n) { Reg d = d_n, n = d_n; // BICS , // Rd cannot encode R15. 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; } bool thumb16_MVN_reg(Reg m, Reg d) { // MVNS , // Rd cannot encode R15. auto result = ir.Not(ir.GetRegister(m)); ir.SetRegister(d, result); ir.SetNFlag(ir.MostSignificantBit(result)); ir.SetZFlag(ir.IsZero(result)); return true; } bool thumb16_ADD_reg_t2(bool d_n_hi, Reg m, Reg d_n_lo) { Reg d_n = d_n_hi ? (d_n_lo + 8) : d_n_lo; Reg d = d_n, n = d_n; if (n == Reg::PC && m == Reg::PC) { return UnpredictableInstruction(); } // ADD , 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::ReturnToDispatch{}); return false; } else { ir.SetRegister(d, result.result); return true; } } bool thumb16_CMP_reg_t2(bool n_hi, Reg m, Reg n_lo) { Reg n = n_hi ? (n_lo + 8) : n_lo; if (n < Reg::R8 && m < Reg::R8) { return UnpredictableInstruction(); } else if (n == Reg::PC || m == Reg::PC) { return UnpredictableInstruction(); } // CMP , 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; } bool thumb16_MOV_reg(bool d_hi, Reg m, Reg d_lo) { Reg d = d_hi ? (d_lo + 8) : d_lo; // MOV , auto result = ir.GetRegister(m); if (d == Reg::PC) { ir.ALUWritePC(result); ir.SetTerm(IR::Term::ReturnToDispatch{}); return false; } else { ir.SetRegister(d, result); return true; } } bool thumb16_LDR_literal(Reg t, Imm8 imm8) { u32 imm32 = imm8 << 2; // LDR ,