394 lines
15 KiB
C++
394 lines
15 KiB
C++
|
// Copyright 2012 Michael Kang, 2014 Citra Emulator Project
|
||
|
// Licensed under GPLv2 or any later version
|
||
|
// Refer to the license.txt file included.
|
||
|
|
||
|
// We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding
|
||
|
// ARM instruction, and using the existing ARM simulator.
|
||
|
|
||
|
#include "tests/skyeye_interpreter/dyncom/arm_dyncom_thumb.h"
|
||
|
#include "tests/skyeye_interpreter/skyeye_common/armsupp.h"
|
||
|
|
||
|
// Decode a 16bit Thumb instruction. The instruction is in the low 16-bits of the tinstr field,
|
||
|
// with the following Thumb instruction held in the high 16-bits. Passing in two Thumb instructions
|
||
|
// allows easier simulation of the special dual BL instruction.
|
||
|
|
||
|
ThumbDecodeStatus TranslateThumbInstruction(u32 addr, u32 instr, u32* ainstr, u32* inst_size) {
|
||
|
ThumbDecodeStatus valid = ThumbDecodeStatus::UNINITIALIZED;
|
||
|
u32 tinstr = GetThumbInstruction(instr, addr);
|
||
|
|
||
|
*ainstr = 0xDEADC0DE; // Debugging to catch non updates
|
||
|
|
||
|
switch ((tinstr & 0xF800) >> 11) {
|
||
|
case 0: // LSL
|
||
|
case 1: // LSR
|
||
|
case 2: // ASR
|
||
|
*ainstr = 0xE1B00000 // base opcode
|
||
|
| ((tinstr & 0x1800) >> (11 - 5)) // shift type
|
||
|
|((tinstr & 0x07C0) << (7 - 6)) // imm5
|
||
|
|((tinstr & 0x0038) >> 3) // Rs
|
||
|
|((tinstr & 0x0007) << 12); // Rd
|
||
|
break;
|
||
|
|
||
|
case 3: // ADD/SUB
|
||
|
{
|
||
|
static const u32 subset[4] = {
|
||
|
0xE0900000, // ADDS Rd,Rs,Rn
|
||
|
0xE0500000, // SUBS Rd,Rs,Rn
|
||
|
0xE2900000, // ADDS Rd,Rs,#imm3
|
||
|
0xE2500000 // SUBS Rd,Rs,#imm3
|
||
|
};
|
||
|
// It is quicker indexing into a table, than performing switch or conditionals:
|
||
|
*ainstr = subset[(tinstr & 0x0600) >> 9] // base opcode
|
||
|
|((tinstr & 0x01C0) >> 6) // Rn or imm3
|
||
|
|((tinstr & 0x0038) << (16 - 3)) // Rs
|
||
|
|((tinstr & 0x0007) << (12 - 0)); // Rd
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 4: // MOV
|
||
|
case 5: // CMP
|
||
|
case 6: // ADD
|
||
|
case 7: // SUB
|
||
|
{
|
||
|
static const u32 subset[4] = {
|
||
|
0xE3B00000, // MOVS Rd,#imm8
|
||
|
0xE3500000, // CMP Rd,#imm8
|
||
|
0xE2900000, // ADDS Rd,Rd,#imm8
|
||
|
0xE2500000, // SUBS Rd,Rd,#imm8
|
||
|
};
|
||
|
|
||
|
*ainstr = subset[(tinstr & 0x1800) >> 11] // base opcode
|
||
|
|((tinstr & 0x00FF) >> 0) // imm8
|
||
|
|((tinstr & 0x0700) << (16 - 8)) // Rn
|
||
|
|((tinstr & 0x0700) << (12 - 8)); // Rd
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 8: // Arithmetic and high register transfers
|
||
|
|
||
|
// TODO: Since the subsets for both Format 4 and Format 5 instructions are made up of
|
||
|
// different ARM encodings, we could save the following conditional, and just have one
|
||
|
// large subset
|
||
|
|
||
|
if ((tinstr & (1 << 10)) == 0) {
|
||
|
enum otype {
|
||
|
t_norm,
|
||
|
t_shift,
|
||
|
t_neg,
|
||
|
t_mul
|
||
|
};
|
||
|
|
||
|
static const struct {
|
||
|
u32 opcode;
|
||
|
otype type;
|
||
|
} subset[16] = {
|
||
|
{ 0xE0100000, t_norm }, // ANDS Rd,Rd,Rs
|
||
|
{ 0xE0300000, t_norm }, // EORS Rd,Rd,Rs
|
||
|
{ 0xE1B00010, t_shift }, // MOVS Rd,Rd,LSL Rs
|
||
|
{ 0xE1B00030, t_shift }, // MOVS Rd,Rd,LSR Rs
|
||
|
{ 0xE1B00050, t_shift }, // MOVS Rd,Rd,ASR Rs
|
||
|
{ 0xE0B00000, t_norm }, // ADCS Rd,Rd,Rs
|
||
|
{ 0xE0D00000, t_norm }, // SBCS Rd,Rd,Rs
|
||
|
{ 0xE1B00070, t_shift }, // MOVS Rd,Rd,ROR Rs
|
||
|
{ 0xE1100000, t_norm }, // TST Rd,Rs
|
||
|
{ 0xE2700000, t_neg }, // RSBS Rd,Rs,#0
|
||
|
{ 0xE1500000, t_norm }, // CMP Rd,Rs
|
||
|
{ 0xE1700000, t_norm }, // CMN Rd,Rs
|
||
|
{ 0xE1900000, t_norm }, // ORRS Rd,Rd,Rs
|
||
|
{ 0xE0100090, t_mul }, // MULS Rd,Rd,Rs
|
||
|
{ 0xE1D00000, t_norm }, // BICS Rd,Rd,Rs
|
||
|
{ 0xE1F00000, t_norm } // MVNS Rd,Rs
|
||
|
};
|
||
|
|
||
|
*ainstr = subset[(tinstr & 0x03C0) >> 6].opcode; // base
|
||
|
|
||
|
switch (subset[(tinstr & 0x03C0) >> 6].type) {
|
||
|
case t_norm:
|
||
|
*ainstr |= ((tinstr & 0x0007) << 16) // Rn
|
||
|
|((tinstr & 0x0007) << 12) // Rd
|
||
|
|((tinstr & 0x0038) >> 3); // Rs
|
||
|
break;
|
||
|
case t_shift:
|
||
|
*ainstr |= ((tinstr & 0x0007) << 12) // Rd
|
||
|
|((tinstr & 0x0007) >> 0) // Rm
|
||
|
|((tinstr & 0x0038) << (8 - 3)); // Rs
|
||
|
break;
|
||
|
case t_neg:
|
||
|
*ainstr |= ((tinstr & 0x0007) << 12) // Rd
|
||
|
|((tinstr & 0x0038) << (16 - 3)); // Rn
|
||
|
break;
|
||
|
case t_mul:
|
||
|
*ainstr |= ((tinstr & 0x0007) << 16) // Rd
|
||
|
|((tinstr & 0x0007) << 8) // Rs
|
||
|
|((tinstr & 0x0038) >> 3); // Rm
|
||
|
break;
|
||
|
}
|
||
|
} else {
|
||
|
u32 Rd = ((tinstr & 0x0007) >> 0);
|
||
|
u32 Rs = ((tinstr & 0x0078) >> 3);
|
||
|
|
||
|
if (tinstr & (1 << 7))
|
||
|
Rd += 8;
|
||
|
|
||
|
switch ((tinstr & 0x03C0) >> 6) {
|
||
|
case 0x0: // ADD Rd,Rd,Rs
|
||
|
case 0x1: // ADD Rd,Rd,Hs
|
||
|
case 0x2: // ADD Hd,Hd,Rs
|
||
|
case 0x3: // ADD Hd,Hd,Hs
|
||
|
*ainstr = 0xE0800000 // base
|
||
|
| (Rd << 16) // Rn
|
||
|
|(Rd << 12) // Rd
|
||
|
|(Rs << 0); // Rm
|
||
|
break;
|
||
|
case 0x4: // CMP Rd,Rs
|
||
|
case 0x5: // CMP Rd,Hs
|
||
|
case 0x6: // CMP Hd,Rs
|
||
|
case 0x7: // CMP Hd,Hs
|
||
|
*ainstr = 0xE1500000 // base
|
||
|
| (Rd << 16) // Rn
|
||
|
|(Rs << 0); // Rm
|
||
|
break;
|
||
|
case 0x8: // MOV Rd,Rs
|
||
|
case 0x9: // MOV Rd,Hs
|
||
|
case 0xA: // MOV Hd,Rs
|
||
|
case 0xB: // MOV Hd,Hs
|
||
|
*ainstr = 0xE1A00000 // base
|
||
|
|(Rd << 12) // Rd
|
||
|
|(Rs << 0); // Rm
|
||
|
break;
|
||
|
case 0xC: // BX Rs
|
||
|
case 0xD: // BX Hs
|
||
|
*ainstr = 0xE12FFF10 // base
|
||
|
| ((tinstr & 0x0078) >> 3); // Rd
|
||
|
break;
|
||
|
case 0xE: // BLX
|
||
|
case 0xF: // BLX
|
||
|
*ainstr = 0xE1200030 // base
|
||
|
| (Rs << 0); // Rm
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 9: // LDR Rd,[PC,#imm8]
|
||
|
*ainstr = 0xE59F0000 // base
|
||
|
| ((tinstr & 0x0700) << (12 - 8)) // Rd
|
||
|
|((tinstr & 0x00FF) << (2 - 0)); // off8
|
||
|
break;
|
||
|
|
||
|
case 10:
|
||
|
case 11:
|
||
|
{
|
||
|
static const u32 subset[8] = {
|
||
|
0xE7800000, // STR Rd,[Rb,Ro]
|
||
|
0xE18000B0, // STRH Rd,[Rb,Ro]
|
||
|
0xE7C00000, // STRB Rd,[Rb,Ro]
|
||
|
0xE19000D0, // LDRSB Rd,[Rb,Ro]
|
||
|
0xE7900000, // LDR Rd,[Rb,Ro]
|
||
|
0xE19000B0, // LDRH Rd,[Rb,Ro]
|
||
|
0xE7D00000, // LDRB Rd,[Rb,Ro]
|
||
|
0xE19000F0 // LDRSH Rd,[Rb,Ro]
|
||
|
};
|
||
|
|
||
|
*ainstr = subset[(tinstr & 0xE00) >> 9] // base
|
||
|
|((tinstr & 0x0007) << (12 - 0)) // Rd
|
||
|
|((tinstr & 0x0038) << (16 - 3)) // Rb
|
||
|
|((tinstr & 0x01C0) >> 6); // Ro
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 12: // STR Rd,[Rb,#imm5]
|
||
|
case 13: // LDR Rd,[Rb,#imm5]
|
||
|
case 14: // STRB Rd,[Rb,#imm5]
|
||
|
case 15: // LDRB Rd,[Rb,#imm5]
|
||
|
{
|
||
|
static const u32 subset[4] = {
|
||
|
0xE5800000, // STR Rd,[Rb,#imm5]
|
||
|
0xE5900000, // LDR Rd,[Rb,#imm5]
|
||
|
0xE5C00000, // STRB Rd,[Rb,#imm5]
|
||
|
0xE5D00000 // LDRB Rd,[Rb,#imm5]
|
||
|
};
|
||
|
// The offset range defends on whether we are transferring a byte or word value:
|
||
|
*ainstr = subset[(tinstr & 0x1800) >> 11] // base
|
||
|
|((tinstr & 0x0007) << (12 - 0)) // Rd
|
||
|
|((tinstr & 0x0038) << (16 - 3)) // Rb
|
||
|
|((tinstr & 0x07C0) >> (6 - ((tinstr & (1 << 12)) ? 0 : 2))); // off5
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 16: // STRH Rd,[Rb,#imm5]
|
||
|
case 17: // LDRH Rd,[Rb,#imm5]
|
||
|
*ainstr = ((tinstr & (1 << 11)) // base
|
||
|
? 0xE1D000B0 // LDRH
|
||
|
: 0xE1C000B0) // STRH
|
||
|
|((tinstr & 0x0007) << (12 - 0)) // Rd
|
||
|
|((tinstr & 0x0038) << (16 - 3)) // Rb
|
||
|
|((tinstr & 0x01C0) >> (6 - 1)) // off5, low nibble
|
||
|
|((tinstr & 0x0600) >> (9 - 8)); // off5, high nibble
|
||
|
break;
|
||
|
|
||
|
case 18: // STR Rd,[SP,#imm8]
|
||
|
case 19: // LDR Rd,[SP,#imm8]
|
||
|
*ainstr = ((tinstr & (1 << 11)) // base
|
||
|
? 0xE59D0000 // LDR
|
||
|
: 0xE58D0000) // STR
|
||
|
|((tinstr & 0x0700) << (12 - 8)) // Rd
|
||
|
|((tinstr & 0x00FF) << 2); // off8
|
||
|
break;
|
||
|
|
||
|
case 20: // ADD Rd,PC,#imm8
|
||
|
case 21: // ADD Rd,SP,#imm8
|
||
|
|
||
|
if ((tinstr & (1 << 11)) == 0) {
|
||
|
|
||
|
// NOTE: The PC value used here should by word aligned. We encode shift-left-by-2 in the
|
||
|
// rotate immediate field, so no shift of off8 is needed.
|
||
|
|
||
|
*ainstr = 0xE28F0F00 // base
|
||
|
| ((tinstr & 0x0700) << (12 - 8)) // Rd
|
||
|
|(tinstr & 0x00FF); // off8
|
||
|
} else {
|
||
|
// We encode shift-left-by-2 in the rotate immediate field, so no shift of off8 is needed.
|
||
|
*ainstr = 0xE28D0F00 // base
|
||
|
| ((tinstr & 0x0700) << (12 - 8)) // Rd
|
||
|
|(tinstr & 0x00FF); // off8
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 22:
|
||
|
case 23:
|
||
|
if ((tinstr & 0x0F00) == 0x0000) {
|
||
|
// NOTE: The instruction contains a shift left of 2 equivalent (implemented as ROR #30):
|
||
|
*ainstr = ((tinstr & (1 << 7)) // base
|
||
|
? 0xE24DDF00 // SUB
|
||
|
: 0xE28DDF00) // ADD
|
||
|
|(tinstr & 0x007F); // off7
|
||
|
} else if ((tinstr & 0x0F00) == 0x0e00) {
|
||
|
// BKPT
|
||
|
*ainstr = 0xEF000000 // base
|
||
|
| BITS(tinstr, 0, 3) // imm4 field;
|
||
|
| (BITS(tinstr, 4, 7) << 8); // beginning 4 bits of imm12
|
||
|
} else if ((tinstr & 0x0F00) == 0x0200) {
|
||
|
static const u32 subset[4] = {
|
||
|
0xE6BF0070, // SXTH
|
||
|
0xE6AF0070, // SXTB
|
||
|
0xE6FF0070, // UXTH
|
||
|
0xE6EF0070, // UXTB
|
||
|
};
|
||
|
|
||
|
*ainstr = subset[BITS(tinstr, 6, 7)] // base
|
||
|
| (BITS(tinstr, 0, 2) << 12) // Rd
|
||
|
| BITS(tinstr, 3, 5); // Rm
|
||
|
} else if ((tinstr & 0x0F00) == 0x600) {
|
||
|
if (BIT(tinstr, 5) == 0) {
|
||
|
// SETEND
|
||
|
*ainstr = 0xF1010000 // base
|
||
|
| (BIT(tinstr, 3) << 9); // endian specifier
|
||
|
} else {
|
||
|
// CPS
|
||
|
*ainstr = 0xF1080000 // base
|
||
|
| (BIT(tinstr, 0) << 6) // fiq bit
|
||
|
| (BIT(tinstr, 1) << 7) // irq bit
|
||
|
| (BIT(tinstr, 2) << 8) // abort bit
|
||
|
| (BIT(tinstr, 4) << 18); // enable bit
|
||
|
}
|
||
|
} else if ((tinstr & 0x0F00) == 0x0a00) {
|
||
|
static const u32 subset[3] = {
|
||
|
0xE6BF0F30, // REV
|
||
|
0xE6BF0FB0, // REV16
|
||
|
0xE6FF0FB0, // REVSH
|
||
|
};
|
||
|
|
||
|
*ainstr = subset[BITS(tinstr, 6, 7)] // base
|
||
|
| (BITS(tinstr, 0, 2) << 12) // Rd
|
||
|
| BITS(tinstr, 3, 5); // Rm
|
||
|
} else {
|
||
|
static const u32 subset[4] = {
|
||
|
0xE92D0000, // STMDB sp!,{rlist}
|
||
|
0xE92D4000, // STMDB sp!,{rlist,lr}
|
||
|
0xE8BD0000, // LDMIA sp!,{rlist}
|
||
|
0xE8BD8000 // LDMIA sp!,{rlist,pc}
|
||
|
};
|
||
|
*ainstr = subset[((tinstr & (1 << 11)) >> 10) | ((tinstr & (1 << 8)) >> 8)] // base
|
||
|
|(tinstr & 0x00FF); // mask8
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 24: // STMIA
|
||
|
case 25: // LDMIA
|
||
|
if (tinstr & (1 << 11))
|
||
|
{
|
||
|
unsigned int base = 0xE8900000;
|
||
|
unsigned int rn = BITS(tinstr, 8, 10);
|
||
|
|
||
|
// Writeback
|
||
|
if ((tinstr & (1 << rn)) == 0)
|
||
|
base |= (1 << 21);
|
||
|
|
||
|
*ainstr = base // base (LDMIA)
|
||
|
| (rn << 16) // Rn
|
||
|
| (tinstr & 0x00FF); // Register list
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*ainstr = 0xE8A00000 // base (STMIA)
|
||
|
| (BITS(tinstr, 8, 10) << 16) // Rn
|
||
|
| (tinstr & 0x00FF); // Register list
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 26: // Bcc
|
||
|
case 27: // Bcc/SWI
|
||
|
if ((tinstr & 0x0F00) == 0x0F00) {
|
||
|
// Format 17 : SWI
|
||
|
*ainstr = 0xEF000000;
|
||
|
// Breakpoint must be handled specially.
|
||
|
if ((tinstr & 0x00FF) == 0x18)
|
||
|
*ainstr |= ((tinstr & 0x00FF) << 16);
|
||
|
// New breakpoint value. See gdb/arm-tdep.c
|
||
|
else if ((tinstr & 0x00FF) == 0xFE)
|
||
|
*ainstr |= 0x180000; // base |= BKPT mask
|
||
|
else
|
||
|
*ainstr |= (tinstr & 0x00FF);
|
||
|
} else if ((tinstr & 0x0F00) != 0x0E00)
|
||
|
valid = ThumbDecodeStatus::BRANCH;
|
||
|
else // UNDEFINED : cc=1110(AL) uses different format
|
||
|
valid = ThumbDecodeStatus::UNDEFINED;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 28: // B
|
||
|
valid = ThumbDecodeStatus::BRANCH;
|
||
|
break;
|
||
|
|
||
|
case 29:
|
||
|
if (tinstr & 0x1)
|
||
|
valid = ThumbDecodeStatus::UNDEFINED;
|
||
|
else
|
||
|
valid = ThumbDecodeStatus::BRANCH;
|
||
|
break;
|
||
|
|
||
|
case 30: // BL instruction 1
|
||
|
|
||
|
// There is no single ARM instruction equivalent for this Thumb instruction. To keep the
|
||
|
// simulation simple (from the user perspective) we check if the following instruction is
|
||
|
// the second half of this BL, and if it is we simulate it immediately
|
||
|
|
||
|
valid = ThumbDecodeStatus::BRANCH;
|
||
|
break;
|
||
|
|
||
|
case 31: // BL instruction 2
|
||
|
|
||
|
// There is no single ARM instruction equivalent for this instruction. Also, it should only
|
||
|
// ever be matched with the fmt19 "BL instruction 1" instruction. However, we do allow the
|
||
|
// simulation of it on its own, with undefined results if r14 is not suitably initialised.
|
||
|
|
||
|
valid = ThumbDecodeStatus::BRANCH;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
*inst_size = 2;
|
||
|
|
||
|
return valid;
|
||
|
}
|