2016-07-04 10:22:11 +01:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
2016-07-04 14:37:50 +01:00
|
|
|
#include <tuple>
|
2016-07-04 10:22:11 +01:00
|
|
|
|
2016-07-04 14:37:50 +01:00
|
|
|
#include "common/assert.h"
|
2016-07-04 10:22:11 +01:00
|
|
|
#include "frontend/arm_types.h"
|
2016-07-12 11:09:34 +01:00
|
|
|
#include "frontend/decoder/thumb16.h"
|
2016-07-14 14:39:43 +01:00
|
|
|
#include "frontend/ir/ir_emitter.h"
|
2016-07-18 09:25:33 +01:00
|
|
|
#include "frontend/translate/translate.h"
|
2016-07-04 10:22:11 +01:00
|
|
|
|
|
|
|
namespace Dynarmic {
|
|
|
|
namespace Arm {
|
|
|
|
|
2016-07-14 13:28:20 +01:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
struct ThumbTranslatorVisitor final {
|
|
|
|
explicit ThumbTranslatorVisitor(LocationDescriptor descriptor) : ir(descriptor) {
|
2016-07-04 14:37:50 +01:00
|
|
|
ASSERT_MSG(descriptor.TFlag, "The processor must be in Thumb mode");
|
|
|
|
}
|
|
|
|
|
2016-07-04 10:22:11 +01:00
|
|
|
IREmitter ir;
|
|
|
|
|
2016-07-14 20:02:41 +01:00
|
|
|
bool InterpretThisInstruction() {
|
2016-07-07 10:53:09 +01:00
|
|
|
ir.SetTerm(IR::Term::Interpret(ir.current_location));
|
|
|
|
return false;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-08 10:09:18 +01:00
|
|
|
bool UnpredictableInstruction() {
|
|
|
|
ASSERT_MSG(false, "UNPREDICTABLE");
|
|
|
|
return false;
|
|
|
|
}
|
2016-07-07 10:53:09 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_LSL_imm(Imm5 imm5, Reg m, Reg d) {
|
2016-07-04 10:22:11 +01:00
|
|
|
u8 shift_n = imm5;
|
|
|
|
// LSLS <Rd>, <Rm>, #<imm5>
|
|
|
|
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);
|
2016-07-04 14:37:50 +01:00
|
|
|
return true;
|
2016-07-04 10:22:11 +01:00
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_LSR_imm(Imm5 imm5, Reg m, Reg d) {
|
2016-07-04 10:22:11 +01:00
|
|
|
u8 shift_n = imm5 != 0 ? imm5 : 32;
|
|
|
|
// LSRS <Rd>, <Rm>, #<imm5>
|
|
|
|
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);
|
2016-07-04 14:37:50 +01:00
|
|
|
return true;
|
2016-07-04 10:22:11 +01:00
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_ASR_imm(Imm5 imm5, Reg m, Reg d) {
|
2016-07-04 10:22:11 +01:00
|
|
|
u8 shift_n = imm5 != 0 ? imm5 : 32;
|
|
|
|
// ASRS <Rd>, <Rm>, #<imm5>
|
|
|
|
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);
|
2016-07-04 14:37:50 +01:00
|
|
|
return true;
|
2016-07-04 10:22:11 +01:00
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_ADD_reg_t1(Reg m, Reg n, Reg d) {
|
2016-07-08 10:09:18 +01:00
|
|
|
// ADDS <Rd>, <Rn>, <Rm>
|
|
|
|
// 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;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_SUB_reg(Reg m, Reg n, Reg d) {
|
2016-07-08 11:49:30 +01:00
|
|
|
// SUBS <Rd>, <Rn>, <Rm>
|
|
|
|
// 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;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_ADD_imm_t1(Imm3 imm3, Reg n, Reg d) {
|
2016-07-08 12:15:30 +01:00
|
|
|
u32 imm32 = imm3 & 0x7;
|
|
|
|
// ADDS <Rd>, <Rn>, #<imm3>
|
|
|
|
// 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;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_SUB_imm_t1(Imm3 imm3, Reg n, Reg d) {
|
2016-07-08 13:57:53 +01:00
|
|
|
u32 imm32 = imm3 & 0x7;
|
|
|
|
// SUBS <Rd>, <Rn>, #<imm3>
|
|
|
|
// 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;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_MOV_imm(Reg d, Imm8 imm8) {
|
2016-07-08 14:16:40 +01:00
|
|
|
u32 imm32 = imm8 & 0xFF;
|
|
|
|
// MOVS <Rd>, #<imm8>
|
|
|
|
// 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;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_CMP_imm(Reg n, Imm8 imm8) {
|
2016-07-08 14:32:01 +01:00
|
|
|
u32 imm32 = imm8 & 0xFF;
|
|
|
|
// CMP <Rn>, #<imm8>
|
|
|
|
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;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_ADD_imm_t2(Reg d_n, Imm8 imm8) {
|
2016-07-08 14:38:43 +01:00
|
|
|
u32 imm32 = imm8 & 0xFF;
|
|
|
|
Reg d = d_n, n = d_n;
|
|
|
|
// ADDS <Rdn>, #<imm8>
|
|
|
|
// 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;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_SUB_imm_t2(Reg d_n, Imm8 imm8) {
|
2016-07-08 14:48:55 +01:00
|
|
|
u32 imm32 = imm8 & 0xFF;
|
|
|
|
Reg d = d_n, n = d_n;
|
|
|
|
// SUBS <Rd>, <Rn>, #<imm3>
|
|
|
|
// 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;
|
|
|
|
}
|
2016-07-04 14:37:50 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_AND_reg(Reg m, Reg d_n) {
|
2016-07-08 10:43:28 +01:00
|
|
|
const Reg d = d_n, n = d_n;
|
|
|
|
// ANDS <Rdn>, <Rm>
|
|
|
|
// 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;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_EOR_reg(Reg m, Reg d_n) {
|
2016-07-08 11:14:50 +01:00
|
|
|
const Reg d = d_n, n = d_n;
|
|
|
|
// EORS <Rdn>, <Rm>
|
|
|
|
// 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;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_LSL_reg(Reg m, Reg d_n) {
|
2016-07-04 10:22:11 +01:00
|
|
|
const Reg d = d_n, n = d_n;
|
|
|
|
// LSLS <Rdn>, <Rm>
|
|
|
|
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);
|
2016-07-04 14:37:50 +01:00
|
|
|
return true;
|
2016-07-04 10:22:11 +01:00
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_LSR_reg(Reg m, Reg d_n) {
|
2016-07-04 10:22:11 +01:00
|
|
|
const Reg d = d_n, n = d_n;
|
|
|
|
// LSRS <Rdn>, <Rm>
|
|
|
|
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);
|
2016-07-04 14:37:50 +01:00
|
|
|
return true;
|
2016-07-04 10:22:11 +01:00
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_ASR_reg(Reg m, Reg d_n) {
|
2016-07-04 10:22:11 +01:00
|
|
|
const Reg d = d_n, n = d_n;
|
|
|
|
// ASRS <Rdn>, <Rm>
|
|
|
|
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);
|
2016-07-04 14:37:50 +01:00
|
|
|
return true;
|
2016-07-04 10:22:11 +01:00
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_ADC_reg(Reg m, Reg d_n) {
|
2016-07-08 15:17:39 +01:00
|
|
|
Reg d = d_n, n = d_n;
|
|
|
|
// ADCS <Rdn>, <Rm>
|
|
|
|
// 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));
|
2016-07-09 01:27:41 +01:00
|
|
|
ir.SetCFlag(result.carry);
|
|
|
|
ir.SetVFlag(result.overflow);
|
|
|
|
return true;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_SBC_reg(Reg m, Reg d_n) {
|
2016-07-09 01:27:41 +01:00
|
|
|
Reg d = d_n, n = d_n;
|
|
|
|
// SBCS <Rdn>, <Rm>
|
|
|
|
// 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));
|
2016-07-08 15:17:39 +01:00
|
|
|
ir.SetCFlag(result.carry);
|
|
|
|
ir.SetVFlag(result.overflow);
|
|
|
|
return true;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_ROR_reg(Reg m, Reg d_n) {
|
2016-07-10 01:18:17 +01:00
|
|
|
Reg d = d_n, n = d_n;
|
|
|
|
// RORS <Rdn>, <Rm>
|
|
|
|
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;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_TST_reg(Reg m, Reg n) {
|
2016-07-10 01:35:58 +01:00
|
|
|
// TST <Rn>, <Rm>
|
|
|
|
auto result = ir.And(ir.GetRegister(n), ir.GetRegister(m));
|
|
|
|
ir.SetNFlag(ir.MostSignificantBit(result));
|
|
|
|
ir.SetZFlag(ir.IsZero(result));
|
|
|
|
return true;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_RSB_imm(Reg n, Reg d) {
|
2016-07-10 01:44:07 +01:00
|
|
|
// RSBS <Rd>, <Rn>, #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));
|
2016-07-10 01:52:28 +01:00
|
|
|
ir.SetCFlag(result.carry);
|
|
|
|
ir.SetVFlag(result.overflow);
|
|
|
|
return true;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_CMP_reg_t1(Reg m, Reg n) {
|
2016-07-10 01:52:28 +01:00
|
|
|
// CMP <Rn>, <Rm>
|
|
|
|
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));
|
2016-07-10 01:55:56 +01:00
|
|
|
ir.SetCFlag(result.carry);
|
|
|
|
ir.SetVFlag(result.overflow);
|
|
|
|
return true;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_CMN_reg(Reg m, Reg n) {
|
2016-07-10 01:55:56 +01:00
|
|
|
// CMN <Rn>, <Rm>
|
|
|
|
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));
|
2016-07-10 01:44:07 +01:00
|
|
|
ir.SetCFlag(result.carry);
|
|
|
|
ir.SetVFlag(result.overflow);
|
|
|
|
return true;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_ORR_reg(Reg m, Reg d_n) {
|
2016-07-10 02:06:38 +01:00
|
|
|
Reg d = d_n, n = d_n;
|
|
|
|
// ORRS <Rdn>, <Rm>
|
|
|
|
// Rd cannot encode R15.
|
|
|
|
auto result = ir.Or(ir.GetRegister(m), ir.GetRegister(n));
|
|
|
|
ir.SetRegister(d, result);
|
|
|
|
ir.SetNFlag(ir.MostSignificantBit(result));
|
2016-07-10 03:44:45 +01:00
|
|
|
ir.SetZFlag(ir.IsZero(result));
|
|
|
|
return true;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_BIC_reg(Reg m, Reg d_n) {
|
2016-07-10 03:44:45 +01:00
|
|
|
Reg d = d_n, n = d_n;
|
|
|
|
// BICS <Rdn>, <Rm>
|
|
|
|
// 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));
|
2016-07-10 03:49:01 +01:00
|
|
|
ir.SetZFlag(ir.IsZero(result));
|
|
|
|
return true;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_MVN_reg(Reg m, Reg d) {
|
2016-07-10 03:49:01 +01:00
|
|
|
// MVNS <Rd>, <Rm>
|
|
|
|
// Rd cannot encode R15.
|
|
|
|
auto result = ir.Not(ir.GetRegister(m));
|
|
|
|
ir.SetRegister(d, result);
|
|
|
|
ir.SetNFlag(ir.MostSignificantBit(result));
|
2016-07-10 02:06:38 +01:00
|
|
|
ir.SetZFlag(ir.IsZero(result));
|
|
|
|
return true;
|
|
|
|
}
|
2016-07-04 10:22:11 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_ADD_reg_t2(bool d_n_hi, Reg m, Reg d_n_lo) {
|
2016-07-08 10:09:18 +01:00
|
|
|
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 <Rdn>, <Rm>
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_CMP_reg_t2(bool n_hi, Reg m, Reg n_lo) {
|
2016-07-10 05:23:16 +01:00
|
|
|
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 <Rn>, <Rm>
|
|
|
|
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;
|
|
|
|
}
|
2016-07-14 13:28:20 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_MOV_reg(bool d_hi, Reg m, Reg d_lo) {
|
2016-07-10 06:10:06 +01:00
|
|
|
Reg d = d_hi ? (d_lo + 8) : d_lo;
|
|
|
|
// MOV <Rd>, <Rm>
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2016-07-10 05:23:16 +01:00
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_LDR_literal(Reg t, Imm8 imm8) {
|
2016-07-11 22:43:53 +01:00
|
|
|
u32 imm32 = imm8 << 2;
|
|
|
|
// LDR <Rt>, <label>
|
|
|
|
// Rt cannot encode R15.
|
|
|
|
u32 address = ir.AlignPC(4) + imm32;
|
|
|
|
auto data = ir.ReadMemory32(ir.Imm32(address));
|
|
|
|
ir.SetRegister(t, data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_STR_reg(Reg m, Reg n, Reg t) {
|
2016-07-11 23:06:35 +01:00
|
|
|
// STR <Rt>, [<Rn>, <Rm>]
|
|
|
|
// Rt cannot encode R15.
|
|
|
|
auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
|
|
|
|
auto data = ir.GetRegister(t);
|
|
|
|
ir.WriteMemory32(address, data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_STRH_reg(Reg m, Reg n, Reg t) {
|
2016-07-11 23:06:35 +01:00
|
|
|
// STRH <Rt>, [<Rn>, <Rm>]
|
|
|
|
// Rt cannot encode R15.
|
|
|
|
auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
|
|
|
|
auto data = ir.LeastSignificantHalf(ir.GetRegister(t));
|
|
|
|
ir.WriteMemory16(address, data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_STRB_reg(Reg m, Reg n, Reg t) {
|
2016-07-11 23:06:35 +01:00
|
|
|
// STRB <Rt>, [<Rn>, <Rm>]
|
|
|
|
// Rt cannot encode R15.
|
|
|
|
auto address = ir.Add(ir.GetRegister(n), ir.GetRegister(m));
|
|
|
|
auto data = ir.LeastSignificantByte(ir.GetRegister(t));
|
|
|
|
ir.WriteMemory8(address, data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_LDR_imm_t1(Imm5 imm5, Reg n, Reg t) {
|
2016-07-11 22:43:53 +01:00
|
|
|
u32 imm32 = imm5 << 2;
|
|
|
|
// LDR <Rt>, [<Rn>, #<imm>}
|
|
|
|
// Rt cannot encode R15.
|
|
|
|
auto address = ir.Add(ir.GetRegister(n), ir.Imm32(imm32));
|
|
|
|
auto data = ir.ReadMemory32(address);
|
|
|
|
ir.SetRegister(t, data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-18 09:25:33 +01:00
|
|
|
bool thumb16_ADR(Reg d, Imm8 imm8) {
|
|
|
|
u32 imm32 = imm8 << 2;
|
|
|
|
// ADR <Rd>, <label>
|
|
|
|
// Rd cannot encode R15.
|
|
|
|
auto result = ir.Imm32(ir.AlignPC(4) + imm32);
|
|
|
|
ir.SetRegister(d, result);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-16 19:23:42 +01:00
|
|
|
bool thumb16_SXTH(Reg m, Reg d) {
|
|
|
|
// SXTH <Rd>, <Rm>
|
|
|
|
// Rd cannot encode R15.
|
|
|
|
auto half = ir.LeastSignificantHalf(ir.GetRegister(m));
|
|
|
|
ir.SetRegister(d, ir.SignExtendHalfToWord(half));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool thumb16_SXTB(Reg m, Reg d) {
|
|
|
|
// SXTB <Rd>, <Rm>
|
|
|
|
// Rd cannot encode R15.
|
|
|
|
auto byte = ir.LeastSignificantByte(ir.GetRegister(m));
|
|
|
|
ir.SetRegister(d, ir.SignExtendByteToWord(byte));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool thumb16_UXTH(Reg m, Reg d) {
|
|
|
|
// UXTH <Rd>, <Rm>
|
|
|
|
// Rd cannot encode R15.
|
|
|
|
auto half = ir.LeastSignificantHalf(ir.GetRegister(m));
|
|
|
|
ir.SetRegister(d, ir.ZeroExtendHalfToWord(half));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool thumb16_UXTB(Reg m, Reg d) {
|
|
|
|
// UXTB <Rd>, <Rm>
|
|
|
|
// Rd cannot encode R15.
|
|
|
|
auto byte = ir.LeastSignificantByte(ir.GetRegister(m));
|
|
|
|
ir.SetRegister(d, ir.ZeroExtendByteToWord(byte));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool thumb16_REV(Reg m, Reg d) {
|
|
|
|
// REV <Rd>, <Rm>
|
|
|
|
// Rd cannot encode R15.
|
|
|
|
ir.SetRegister(d, ir.ByteReverseWord(ir.GetRegister(m)));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool thumb16_REV16(Reg m, Reg d) {
|
|
|
|
// REV16 <Rd>, <Rm>
|
|
|
|
// Rd cannot encode R15.
|
|
|
|
// TODO: Consider optimizing
|
|
|
|
auto Rm = ir.GetRegister(m);
|
|
|
|
auto upper_half = ir.LeastSignificantHalf(ir.LogicalShiftRight(Rm, ir.Imm8(16), ir.Imm1(0)).result);
|
|
|
|
auto lower_half = ir.LeastSignificantHalf(Rm);
|
|
|
|
auto rev_upper_half = ir.ZeroExtendHalfToWord(ir.ByteReverseHalf(upper_half));
|
|
|
|
auto rev_lower_half = ir.ZeroExtendHalfToWord(ir.ByteReverseHalf(lower_half));
|
|
|
|
auto result = ir.Or(ir.LogicalShiftLeft(rev_upper_half, ir.Imm8(16), ir.Imm1(0)).result,
|
|
|
|
rev_lower_half);
|
|
|
|
ir.SetRegister(d, result);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool thumb16_REVSH(Reg m, Reg d) {
|
|
|
|
// REVSH <Rd>, <Rm>
|
|
|
|
// Rd cannot encode R15.
|
|
|
|
auto rev_half = ir.ByteReverseHalf(ir.LeastSignificantHalf(ir.GetRegister(m)));
|
|
|
|
ir.SetRegister(d, ir.SignExtendHalfToWord(rev_half));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-12 11:09:34 +01:00
|
|
|
bool thumb16_UDF() {
|
2016-07-14 20:02:41 +01:00
|
|
|
return InterpretThisInstruction();
|
2016-07-04 14:37:50 +01:00
|
|
|
}
|
2016-07-14 15:01:30 +01:00
|
|
|
|
|
|
|
bool thumb16_SVC(Imm8 imm8) {
|
|
|
|
u32 imm32 = imm8;
|
|
|
|
// SVC #<imm8>
|
|
|
|
ir.CallSupervisor(ir.Imm32(imm32));
|
|
|
|
return false;
|
|
|
|
}
|
2016-07-04 14:37:50 +01:00
|
|
|
};
|
2016-07-04 10:22:11 +01:00
|
|
|
|
2016-07-04 14:37:50 +01:00
|
|
|
enum class ThumbInstSize {
|
|
|
|
Thumb16, Thumb32
|
2016-07-04 10:22:11 +01:00
|
|
|
};
|
|
|
|
|
2016-07-18 10:28:17 +01:00
|
|
|
std::tuple<u32, ThumbInstSize> ReadThumbInstruction(u32 arm_pc, MemoryRead32FuncType memory_read_32) {
|
2016-07-04 14:37:50 +01:00
|
|
|
u32 first_part = (*memory_read_32)(arm_pc & 0xFFFFFFFC);
|
|
|
|
if ((arm_pc & 0x2) != 0)
|
|
|
|
first_part >>= 16;
|
|
|
|
first_part &= 0xFFFF;
|
|
|
|
|
2016-07-07 10:53:09 +01:00
|
|
|
if ((first_part & 0xF800) <= 0xE800) {
|
2016-07-04 14:37:50 +01:00
|
|
|
// 16-bit thumb instruction
|
|
|
|
return std::make_tuple(first_part, ThumbInstSize::Thumb16);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 32-bit thumb instruction
|
2016-07-07 10:53:09 +01:00
|
|
|
// These always start with 0b11101, 0b11110 or 0b11111.
|
2016-07-04 14:37:50 +01:00
|
|
|
|
2016-07-14 13:28:20 +01:00
|
|
|
u32 second_part = (*memory_read_32)((arm_pc + 2) & 0xFFFFFFFC);
|
|
|
|
if (((arm_pc + 2) & 0x2) != 0)
|
2016-07-04 14:37:50 +01:00
|
|
|
second_part >>= 16;
|
|
|
|
second_part &= 0xFFFF;
|
|
|
|
|
|
|
|
return std::make_tuple(static_cast<u32>((first_part << 16) | second_part), ThumbInstSize::Thumb32);
|
|
|
|
}
|
|
|
|
|
2016-07-14 13:28:20 +01:00
|
|
|
} // local namespace
|
|
|
|
|
2016-07-04 14:37:50 +01:00
|
|
|
IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryRead32FuncType memory_read_32) {
|
2016-07-14 13:28:20 +01:00
|
|
|
ThumbTranslatorVisitor visitor{descriptor};
|
2016-07-04 14:37:50 +01:00
|
|
|
|
|
|
|
bool should_continue = true;
|
|
|
|
while (should_continue) {
|
|
|
|
const u32 arm_pc = visitor.ir.current_location.arm_pc;
|
|
|
|
|
|
|
|
u32 thumb_instruction;
|
|
|
|
ThumbInstSize inst_size;
|
|
|
|
std::tie(thumb_instruction, inst_size) = ReadThumbInstruction(arm_pc, memory_read_32);
|
|
|
|
|
|
|
|
if (inst_size == ThumbInstSize::Thumb16) {
|
2016-07-14 13:28:20 +01:00
|
|
|
auto decoder = DecodeThumb16<ThumbTranslatorVisitor>(static_cast<u16>(thumb_instruction));
|
2016-07-04 14:37:50 +01:00
|
|
|
if (decoder) {
|
|
|
|
should_continue = decoder->call(visitor, static_cast<u16>(thumb_instruction));
|
|
|
|
} else {
|
2016-07-12 11:09:34 +01:00
|
|
|
should_continue = visitor.thumb16_UDF();
|
2016-07-04 14:37:50 +01:00
|
|
|
}
|
|
|
|
} else {
|
2016-07-14 13:28:20 +01:00
|
|
|
/*auto decoder = DecodeThumb32<ThumbTranslatorVisitor>(thumb_instruction);
|
2016-07-04 14:37:50 +01:00
|
|
|
if (decoder) {
|
|
|
|
should_continue = decoder->call(visitor, thumb_instruction);
|
|
|
|
} else {
|
2016-07-12 11:09:34 +01:00
|
|
|
should_continue = visitor.thumb32_UDF();
|
2016-07-04 14:37:50 +01:00
|
|
|
}*/
|
2016-07-14 20:02:41 +01:00
|
|
|
should_continue = visitor.InterpretThisInstruction();
|
2016-07-04 14:37:50 +01:00
|
|
|
}
|
|
|
|
|
2016-07-07 10:53:09 +01:00
|
|
|
visitor.ir.current_location.arm_pc += (inst_size == ThumbInstSize::Thumb16) ? 2 : 4;
|
2016-07-04 14:37:50 +01:00
|
|
|
visitor.ir.block.cycle_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return visitor.ir.block;
|
|
|
|
}
|
|
|
|
|
2016-07-04 10:22:11 +01:00
|
|
|
} // namespace Arm
|
|
|
|
} // namepsace Dynarmic
|