1271 lines
47 KiB
C
1271 lines
47 KiB
C
/*
|
|
|
|
Target CPU: ARM946E-S (Nintendo DS main CPU) and ARM7TDMI (Nintendo DS secondary CPU, Nintendo GBA main CPU)
|
|
Target Architecture: ARMv5TE and ARMv4T
|
|
ARM version: 5, 4
|
|
THUMB version: 2, 1
|
|
Documentation: https://www.intel.com/content/dam/support/us/en/programmable/support-resources/bulk-container/pdfs/literature/third-party/ddi0100e-arm-arm.pdf
|
|
|
|
*/
|
|
|
|
/* Instructions marked with an asterisk * are not available in ARMv4T */
|
|
|
|
/* Alphabetical list of ARM instructions (number of variants) */
|
|
/*
|
|
ADC Add with Carry
|
|
ADD Add
|
|
AND Logical AND
|
|
B Branch
|
|
BL Branch and Link
|
|
BIC Bit Clear
|
|
BKPT Breakpoint*
|
|
BLX (2) Branch with Link and Exchange*
|
|
BX Branch and Exchange
|
|
CDP Coprocessor Data Processing
|
|
CLZ Count Leading Zeros*
|
|
CMN Compare Negative
|
|
CMP Compare
|
|
EOR Logical Exclusive OR
|
|
LDC Load Coprocessor
|
|
LDC2 Load Coprocessor 2*
|
|
LDM (3) Load Multiple
|
|
LDR Load Register
|
|
LDRB Load Register Byte
|
|
LDRBT Load Register Byte with Translation
|
|
LDRH Load Register Halfword
|
|
LDRSB Load Register Signed Byte
|
|
LDRSH Load Register Signed Halfword
|
|
LDRT Load Register with Translation
|
|
MCR Move to Coprocessor from ARM Register
|
|
MCR2 Move to Coprocessor from ARM Register 2*
|
|
MLA Multiply Accumulate
|
|
MOV Move
|
|
MRC Move to ARM Register from Coprocessor
|
|
MRC2 Move to ARM Register from Coprocessor 2*
|
|
MRS Move PSR to General-purpose Register
|
|
MSR Move to Status Register from ARM Register
|
|
MUL Multiply
|
|
MVN Move Negative
|
|
ORR Logical OR
|
|
RSB Reverse Substract
|
|
RSC Reverse Substract with Carry
|
|
SBC Substract with Carry
|
|
SMLAL Signed Multply Accumulate Long
|
|
SMULL Signed Multply Long
|
|
STC Store Coprocessor
|
|
STC2 Store Coprocessor 2*
|
|
STM (2) Store Multiple
|
|
STR Store Register
|
|
STRB Store Register Byte
|
|
STRBT Store Register Byte with Translation
|
|
STRH Store Register Halfword
|
|
STRT Store Register with Translation
|
|
SUB Substract
|
|
SWI Software Interrupt
|
|
SWP Swap
|
|
SWPB Swap Byte
|
|
TEQ Test Equivalence
|
|
TST Test
|
|
UMLAL Unsigned Multply Accumulate Long
|
|
UMULL Unsigned Multply Long
|
|
|
|
//DSP enhanced (ARMv5TE exclusive)
|
|
|
|
LDRD Load Register Dual
|
|
MCRR Move to Coprocessor from Registers
|
|
MRRC Move to Registers from Coprocessor
|
|
PLD Preload Data
|
|
QADD Saturating signed Add
|
|
QDADD ;Performs a saturated integer doubling of one operand followed by a saturated integer addition with the other operand
|
|
QDSUB ;Performs a saturated integer doubling of one operand followed by a saturated integer substraction from the other operand
|
|
QSUB Saturating signed Subtraction
|
|
SMLA Signed Multiply Accumulate
|
|
SMLAL Signed Multiply Accumulate Long
|
|
SMLAW Signed Multiply Accumulate Word
|
|
SMUL Signed Multiply
|
|
SMULW Signed Multiply Word
|
|
STRD Store Register Dual
|
|
*/
|
|
|
|
/* Alphabetical list of THUMB instructions (number of variants) */
|
|
/*
|
|
ADC Add with Carry
|
|
ADD (7) Add
|
|
AND Logical AND
|
|
ASR (2) Arithmetic Shift Right
|
|
B (2) Branch
|
|
BIC Bit Clear
|
|
BKPT Breakpoint*
|
|
BL Branch with Link
|
|
BLX (2) Branch with Link and Exchange*
|
|
BX Branch and Exchange
|
|
CMN Compare Negative
|
|
CMP (3) Compare
|
|
EOR Logical Exclusive OR
|
|
LDMIA Load Multiple Increment After
|
|
LDR (4) Load Register
|
|
LDRB (2) Load Register Byte
|
|
LDRH (2) Load Register Halfword
|
|
LDRSB Load Register Signed Byte
|
|
LDRSH Load Register Signed Halfword
|
|
LSL (2) Logical Shift Left
|
|
LSR (2) Logical Shift Right
|
|
MOV (3) Move
|
|
MUL Multiply
|
|
MVN Move NOT
|
|
NEG Negate
|
|
ORR Logical OR
|
|
POP Pop Multiple Registers
|
|
PUSH Push Multiple Registers
|
|
ROR Rotate Right Register
|
|
SBC Substract with Carry
|
|
STMIA Store Multiple Increment After
|
|
STR (3) Store Register
|
|
STRB (2) Store Register Byte
|
|
STRH (2) Store Register Halfword
|
|
SUB (4) Substract
|
|
SWI Software Interrupt
|
|
TST Test
|
|
*/
|
|
|
|
/* INCLUDES */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
/* MACROS */
|
|
|
|
//#define _CRT_SECURE_NO_WARNINGS 1
|
|
#define STRING_LENGTH (80) //can fail at 64 in SubstituteSubString
|
|
#define CONDITIONS_MAX (16)
|
|
|
|
#define BITS(x, b, n) ((x >> b) & ((1 << n) - 1)) //retrieves n bits from x starting at bit b
|
|
#define SIGNEX32_BITS(x, b, n) ((BITS(x,b,n) ^ (1<<(n-1))) - (1<<(n-1))) //convert n-bit value to signed 32 bits
|
|
#define SIGNEX32_VAL(x, n) ((x ^ (1<<(n-1))) - (1<<(n-1))) //convert n-bit value to signed 32 bits
|
|
#define ROR(x, n) ((x>>n)|(x<<(32-n))) //rotate right 32-bit value x by n bits
|
|
|
|
/* TYPEDEFS */
|
|
|
|
typedef uint32_t u32;
|
|
typedef uint16_t u16;
|
|
typedef uint8_t u8;
|
|
|
|
typedef enum {
|
|
SIZE_16,
|
|
SIZE_32
|
|
}THUMBSIZE;
|
|
|
|
typedef enum {
|
|
ARMv4T, //ARM v4, THUMB v1
|
|
ARMv5TE, //ARM v5, THUMB v2
|
|
ARMv6 //ARM v6, THUMB v3
|
|
}ARMARCH; //only 32-bit legacy architectures with THUMB support
|
|
|
|
typedef enum {
|
|
EQ, //equal, Z set
|
|
NE, //not equal, Z clear
|
|
CS, //carry set, C set (or HS, unsigned higher or same)
|
|
CC, //carry clear, C clear (or LO, unsigned lower)
|
|
MI, //minus/negative, N set
|
|
PL, //plus/positive/zero, N clear
|
|
VS, //overflow, V set
|
|
VC, //no overflow, V clear
|
|
HI, //unsigned higher, C set and Z clear
|
|
LS, //unsigned lower or same, C clear or Z set
|
|
GE, //signed greater than or equal, N==V
|
|
LT, //signed less than, N!=V
|
|
GT, //signed greater than, Z==0 and N==V
|
|
LE, //signed less than or equal, Z==1 or N!=V
|
|
AL, //unconditional, only with IT instructions
|
|
NV //unconditional, usually undefined
|
|
}CONDITION;
|
|
|
|
/* GLOBALS */
|
|
|
|
//todo: maybe put "2" instead of "nv" (or nothing) in the last one
|
|
const char Conditions[CONDITIONS_MAX][3] = { "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "", "" }; //last two are "al" and "nv", but never displayed
|
|
|
|
const char AddressingModes[4][3] = {
|
|
"da", //Decrement after
|
|
"ia", //Increment after
|
|
"db", //Decrement before
|
|
"ib" //Increment before
|
|
};
|
|
|
|
const char DataProcessing_thumb[16][4] = {
|
|
"and",
|
|
"eor",
|
|
"lsl",
|
|
"lsr",
|
|
"asr",
|
|
"adc",
|
|
"sbc",
|
|
"ror",
|
|
"tst",
|
|
"neg",
|
|
"cmp",
|
|
"cmn",
|
|
"orr",
|
|
"mul",
|
|
"bic",
|
|
"mvn"
|
|
};
|
|
|
|
const char DataProcessing_arm[16][4] = {
|
|
"and",
|
|
"eor",
|
|
"sub",
|
|
"rsb",
|
|
"add",
|
|
"adc",
|
|
"sbc",
|
|
"rsc",
|
|
"tst", //no s
|
|
"teq", //no s
|
|
"cmp", //no s
|
|
"cmn", //no s
|
|
"orr",
|
|
"mov", //only 1 source operand
|
|
"bic",
|
|
"mvn" //only 1 source operand
|
|
};
|
|
|
|
const char MSR_cxsf[16][5] = {
|
|
"",
|
|
"c",
|
|
"x",
|
|
"xc",
|
|
"s",
|
|
"sc",
|
|
"sx",
|
|
"sxc",
|
|
"f",
|
|
"fc",
|
|
"fx",
|
|
"fxc",
|
|
"fs",
|
|
"fsc",
|
|
"fsx",
|
|
"fsxc"
|
|
};
|
|
|
|
const char LoadStoreRegister[8][6] = {
|
|
"str", //STR (2)
|
|
"strh", //STRH (2)
|
|
"strb", //STRB (2)
|
|
"ldrsb", //LDRSB
|
|
"ldr", //LDR (2)
|
|
"ldrh", //LDRH (2)
|
|
"ldrb", //LDRB (2)
|
|
"ldrsh" //LDRSH
|
|
};
|
|
|
|
const char DSP_AddSub[4][6] = { "qadd", "qsub", "qdadd", "qdsub" };
|
|
const char DSP_Multiplies[4][6] = { "smla", "", "smlal", "smul" }; //slot 1 empty, decided elsehow
|
|
const char MultiplyLong[4][6] = { "umull", "umlal", "smull", "smlal" };
|
|
const char AddCmpMovHighRegisters[3][4] = { "add", "cmp", "mov" };
|
|
const char MovCmpAddSubImmediate[4][4] = { "mov", "cmp", "add", "sub" };
|
|
const char Shifters[4][4] = { "lsl", "lsr", "asr", "ror" }; //+rrx
|
|
const char ShiftImmediate[4][4] = { "lsl", "lsr", "asr", "" }; //thumb
|
|
|
|
u32 debug_na_count = 0;
|
|
|
|
/* LIBRARY FUNCTIONS */
|
|
|
|
static void SubstituteSubString(char dst[STRING_LENGTH], u32 index, const char* sub, u32 size) {
|
|
/* Insert sub string of length size (< STRING_LENGTH) at dst[index] */
|
|
char tmp[STRING_LENGTH] = { 0 }; //zinit
|
|
memcpy(&tmp, sub, size); //init
|
|
memcpy(&tmp[size], &dst[index + size + 1], STRING_LENGTH - index - size - 1); //save
|
|
memcpy(&dst[index], tmp, STRING_LENGTH - index - 1); //restore
|
|
}
|
|
|
|
static void CheckSpecialRegister(char* str, int size) {
|
|
/* Replace special registers by their common names */
|
|
//(r12->ip), r13->sp, r14->lr, r15->pc
|
|
//todo: check for size != -1
|
|
for (int i = 0; i < size; i++) //when using a variable string size (return value of sprintf), 10% faster than constant size of STRING_LENGTH 64
|
|
{
|
|
if (str[i] == 'r')
|
|
{
|
|
switch (*(u16*)&str[i + 1])
|
|
{
|
|
case 0x3331: //"13"
|
|
{
|
|
SubstituteSubString(str, i, "sp", sizeof("sp") - 1);
|
|
break;
|
|
}
|
|
case 0x3431: //"14"
|
|
{
|
|
SubstituteSubString(str, i, "lr", sizeof("lr") - 1);
|
|
break;
|
|
}
|
|
case 0x3531: //"15"
|
|
{
|
|
SubstituteSubString(str, i, "pc", sizeof("pc") - 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static u32 FormatStringRegisterList_thumb(char str[STRING_LENGTH], u16 reg, const char* pclr, u8 pclr_size) {
|
|
/* Format str according to the reg bifield, group consecutive registers together */
|
|
/* Support for a special 9th register name */
|
|
u32 bits = 0; //return value, number of 1 bits in reg
|
|
u32 streak = 0; //current streak, used to group registers together
|
|
u32 pos = 0; //position in the str array
|
|
char tmp[4] = { 0 }; //temporary buffer for register write
|
|
for (u32 i = 0; i < 9; i++) //reg is a 9-bit value
|
|
{
|
|
if (BITS(reg, i, 1))
|
|
{
|
|
bits++;
|
|
switch (streak)
|
|
{
|
|
case 0: //streak ended, print current register
|
|
{
|
|
if (i == 8)
|
|
{
|
|
sprintf(tmp, "%s", pclr); //pc or lr override
|
|
memcpy(&str[pos], tmp, sizeof(tmp)); //write
|
|
pos += pclr_size; //sizeof(pclr)
|
|
}
|
|
else
|
|
{
|
|
sprintf(tmp, "r%u,", i); //r0-r7
|
|
memcpy(&str[pos], tmp, sizeof(tmp)); //write
|
|
pos += 3; //sizeof("r0")
|
|
}
|
|
break;
|
|
}
|
|
case 1: break; //used to catch default cases later
|
|
case 2: //hyphenation if at least 3 in a row
|
|
{
|
|
str[pos - 1] = '-'; //replaces the comma
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
//todo: a bit repetitive, tidy up
|
|
if (i == 8) //if on last bit, close the current streak by writing previous and last register
|
|
{
|
|
sprintf(tmp, "r%u,", i - 1); //previous register (can't be LR/PC)
|
|
memcpy(&str[pos], tmp, sizeof(tmp)); //write
|
|
pos += 3; //depends on size of tmp
|
|
sprintf(tmp, "%s", pclr); //pc or lr override
|
|
memcpy(&str[pos], tmp, sizeof(tmp)); //write
|
|
pos += pclr_size; //sizeof(pclr)
|
|
}
|
|
}
|
|
}
|
|
streak = (i < 8) ? streak + 1 : 0; //avoids grouping LR/PC
|
|
}
|
|
else
|
|
{
|
|
if (streak > 1) //if broke with at least 3 in a row, close
|
|
{
|
|
sprintf(tmp, "r%u,", i - 1); //previous register (can't be LR/PC)
|
|
memcpy(&str[pos], tmp, sizeof(tmp)); //write
|
|
pos += 3; //depends on size of tmp
|
|
}
|
|
streak = 0; //reset
|
|
}
|
|
}
|
|
if (pos) str[pos - 1] = 0; //removes the coma on the last register
|
|
return bits; //number of 1 bits
|
|
}
|
|
|
|
static u32 FormatStringRegisterList_arm(char str[STRING_LENGTH], u16 reg) {
|
|
/* Format str according to the reg bifield, group consecutive registers together */
|
|
u32 bits = 0; //return value, number of 1 bits in reg
|
|
u32 streak = 0; //current streak, used to group registers together
|
|
u32 pos = 0; //position in the str array
|
|
char tmp[5] = { 0 }; //temporary buffer for register write
|
|
for (u32 i = 0; i < 16; i++) //all registers
|
|
{
|
|
if (BITS(reg, i, 1))
|
|
{
|
|
bits++;
|
|
switch (streak)
|
|
{
|
|
case 0: //streak started, print current register
|
|
{
|
|
sprintf(tmp, "r%u,", i); //r0-r15
|
|
memcpy(&str[pos], tmp, sizeof(tmp)); //write
|
|
pos = (i < 10) ? pos + sizeof("r0") : pos + sizeof("r10");
|
|
break;
|
|
}
|
|
case 1: break;
|
|
case 2: //hyphenation if at least 3 in a row
|
|
{
|
|
str[pos - 1] = '-'; //replaces the comma
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
if (i == 15) //if on last bit, close the current streak by writing last register
|
|
{
|
|
str[pos++] = 'p';
|
|
str[pos++] = 'c';
|
|
str[pos++] = ','; //comma will get deleted later
|
|
}
|
|
}
|
|
}
|
|
streak++;
|
|
}
|
|
else
|
|
{
|
|
if (streak > 1) //if broke with at least 3 in a row, close
|
|
{
|
|
sprintf(tmp, "r%u,", i - 1); //previous register
|
|
memcpy(&str[pos], tmp, sizeof(tmp)); //write
|
|
pos = (i - 1 < 10) ? pos + sizeof("r0") : pos + sizeof("r10");
|
|
}
|
|
streak = 0; //reset
|
|
}
|
|
}
|
|
if (pos) str[pos - 1] = 0; //removes the coma on the last register
|
|
return bits; //number of 1 bits
|
|
}
|
|
|
|
static int FormatExtraLoadStore(u32 c, char* str, u8 cond, const char* op) {
|
|
/* */
|
|
u8 rd = BITS(c, 12, 4);
|
|
u8 rn = BITS(c, 16, 4);
|
|
u8 w = BITS(c, 21, 1);
|
|
u8 p = BITS(c, 24, 1);
|
|
if (BITS(c, 22, 1)) //immediate
|
|
{
|
|
u8 ofs = (BITS(c, 8, 4) << 4) | BITS(c, 0, 4);
|
|
const char* sign = BITS(c, 23, 1) ? "+" : "-";
|
|
if (p) //offset, pre-indexed
|
|
{
|
|
const char* pre = w ? "!" : "";
|
|
return sprintf(str, "%s%s r%u, [r%u, #%s0x%X]%s", op, Conditions[cond], rd, rn, sign, ofs, pre);
|
|
}
|
|
else //post-indexed
|
|
{
|
|
if (w) return 0; //w must be 0, else UNPREDICTABLE //todo: check if really invalid
|
|
return sprintf(str, "%s%s r%u, [r%u], #%s0x%X", op, Conditions[cond], rd, rn, sign, ofs);
|
|
}
|
|
}
|
|
else //register
|
|
{
|
|
u8 rm = BITS(c, 0, 4);
|
|
const char* sign = BITS(c, 23, 1) ? "" : "-"; //implicit "+" in front of registers
|
|
if (p) //offset, pre-indexed
|
|
{
|
|
const char* pre = w ? "!" : "";
|
|
return sprintf(str, "%s%s r%u, [r%u, %sr%u]%s", op, Conditions[cond], rd, rn, sign, rm, pre);
|
|
}
|
|
else //post-indexed
|
|
{
|
|
if (w) return 0; //w must be 0, else UNPREDICTABLE //todo: check if really invalid
|
|
return sprintf(str, "%s%s r%u, [r%u], %sr%u", op, Conditions[cond], rd, rn, sign, rm);
|
|
}
|
|
}
|
|
}
|
|
|
|
u32 Disassemble_thumb(u32 code, char str[STRING_LENGTH], ARMARCH tv) {
|
|
/* Convert a code into a string, return size of the processed code (SIZE_16 or SIZE_32) */
|
|
|
|
THUMBSIZE thumb_size = SIZE_16; //return value
|
|
u16 c = code & 0xffff; //low 16 bits
|
|
int size = 0; //return value of sprintf to be passed to CheckSpecialRegister
|
|
|
|
switch (c >> 13)
|
|
{
|
|
case 0: //0x0000 //LSL, LSR, ASR, ADD, SUB
|
|
{
|
|
u8 rd = BITS(c, 0, 3);
|
|
u8 rn = BITS(c, 3, 3);
|
|
u8 index = BITS(c, 11, 2);
|
|
if (index == 3) //ADD, SUB, MOV
|
|
{
|
|
if (BITS(c, 6, 5) == 16) //MOV (2) (technically ADD (1) with imm==0)
|
|
{
|
|
size = sprintf(str, "mov r%u, r%u", rd, rn);
|
|
}
|
|
else
|
|
{
|
|
u8 rm_imm = BITS(c, 6, 3);
|
|
const char* op = (BITS(c, 9, 1)) ? "sub" : "add";
|
|
if (BITS(c, 10, 1)) //ADD (1), SUB (1) -immediate
|
|
{
|
|
size = sprintf(str, "%s r%u, r%u, #0x%X", op, rd, rn, rm_imm);
|
|
}
|
|
else //ADD (3), SUB (3) -register
|
|
{
|
|
size = sprintf(str, "%s r%u, r%u, r%u", op, rd, rn, rm_imm);
|
|
}
|
|
}
|
|
}
|
|
else //Shift by immediate: LSL (1), LSR (1), ASR (1)
|
|
{
|
|
size = sprintf(str, "%s r%u, r%u, #0x%X", ShiftImmediate[index], rd, rn, BITS(c, 6, 5));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 1: //0x2000 //MOV (1), CMP (1), ADD (2), SUB (2)
|
|
{
|
|
size = sprintf(str, "%s r%u, #0x%X", MovCmpAddSubImmediate[BITS(c, 11, 2)], BITS(c, 8, 3), BITS(c, 0, 8));
|
|
break;
|
|
}
|
|
|
|
case 2: //0x4000 //lots...
|
|
{
|
|
switch (BITS(c, 10, 3))
|
|
{
|
|
case 0: //Data-processing registers
|
|
{
|
|
size = sprintf(str, "%s r%u, r%u", DataProcessing_thumb[BITS(c, 6, 4)], BITS(c, 0, 3), BITS(c, 3, 3));
|
|
break;
|
|
}
|
|
case 1: //Special data processing
|
|
{
|
|
u8 rd = (BITS(c, 7, 1) << 3) | (BITS(c, 0, 3));
|
|
u8 rm = BITS(c, 3, 4);
|
|
u8 op = BITS(c, 8, 2);
|
|
if (op == 3) //Branch/exchange instruction set
|
|
{
|
|
if (BITS(c, 0, 3)) break; //Should-Be-Zero
|
|
if (BITS(c, 7, 1)) //BLX (2)
|
|
{
|
|
if (tv < ARMv5TE) break; //UNPREDICTABLE prior to ARM version 5
|
|
size = sprintf(str, "blx r%u", rm);
|
|
}
|
|
else //BX
|
|
{
|
|
size = sprintf(str, "bx r%u", rm);
|
|
}
|
|
}
|
|
else //ADD (4), CMP (3), MOV (3)
|
|
{
|
|
if (!BITS(c, 6, 2)) break; //UNPREDICTABLE
|
|
size = sprintf(str, "%s r%u, r%u", AddCmpMovHighRegisters[op], rd, rm);
|
|
}
|
|
break;
|
|
}
|
|
default: //Load from literal pool, Load/Store register offset
|
|
{
|
|
if (BITS(c, 12, 1)) //Load/store register offset
|
|
{
|
|
size = sprintf(str, "%s r%u, [r%u, r%u]", LoadStoreRegister[BITS(c, 9, 3)], BITS(c, 0, 3), BITS(c, 3, 3), BITS(c, 6, 3));
|
|
}
|
|
else //LDR (3)
|
|
{
|
|
if (!BITS(c, 11, 1)) break; //Should-Be-One
|
|
size = sprintf(str, "ldr r%u, [pc, #0x%X]", BITS(c, 8, 3), 4 * BITS(c, 0, 8));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 3: //0x6000 //STR, LDR, STRB, LDRB
|
|
{
|
|
switch (BITS(c, 11, 2))
|
|
{
|
|
case 0: //STR (1)
|
|
{
|
|
size = sprintf(str, "str r%u, [r%u, #0x%X]", BITS(c, 0, 3), BITS(c, 3, 3), 4 * BITS(c, 6, 5));
|
|
break;
|
|
}
|
|
case 1: //LDR (1)
|
|
{
|
|
size = sprintf(str, "ldr r%u, [r%u, #0x%X]", BITS(c, 0, 3), BITS(c, 3, 3), 4 * BITS(c, 6, 5));
|
|
break;
|
|
}
|
|
case 2: //STRB (1)
|
|
{
|
|
size = sprintf(str, "strb r%u, [r%u, #0x%X]", BITS(c, 0, 3), BITS(c, 3, 3), BITS(c, 6, 5));
|
|
break;
|
|
}
|
|
case 3: //LDRB (1)
|
|
{
|
|
size = sprintf(str, "ldrb r%u, [r%u, #0x%X]", BITS(c, 0, 3), BITS(c, 3, 3), BITS(c, 6, 5));
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 4: //0x8000 //STR, LDR, STRH, LDRH
|
|
{
|
|
if (BITS(c, 12, 1)) //LDR (4), STR (3)
|
|
{
|
|
const char* op = (BITS(c, 11, 1)) ? "ldr" : "str";
|
|
size = sprintf(str, "%s r%u, [sp, #0x%X]", op, BITS(c, 8, 3), 4 * BITS(c, 0, 8));
|
|
}
|
|
else //LDRH (1), STRH (1)
|
|
{
|
|
const char* op = (BITS(c, 11, 1)) ? "ldrh" : "strh";
|
|
size = sprintf(str, "%s r%u, [r%u, #0x%X]", op, BITS(c, 0, 3), BITS(c, 3, 3), 2 * BITS(c, 6, 5));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 5: //0xA000 //Misc and ADD to sp or pc
|
|
{
|
|
if (BITS(c, 12, 1)) //Misc, fig 6-2
|
|
{
|
|
switch (BITS(c, 8, 4))
|
|
{
|
|
case 0: //ADD (4), SUB (7) to/from SP
|
|
{
|
|
const char* op = (BITS(c, 7, 1)) ? "sub" : "add";
|
|
size = sprintf(str, "%s sp, #0x%X", op, 4 * BITS(c, 0, 7));
|
|
break;
|
|
}
|
|
//PUSH/POP
|
|
case 4:
|
|
case 5:
|
|
case 12:
|
|
case 13:
|
|
{
|
|
char reglist[STRING_LENGTH] = { 0 };
|
|
u16 registers = BITS(c, 0, 9);
|
|
if (BITS(c, 11, 1)) //POP
|
|
{
|
|
if (FormatStringRegisterList_thumb(reglist, registers, "pc", sizeof("pc"))) //if BitCount(registers) < 1 then UNPREDICTABLE
|
|
{
|
|
size = sprintf(str, "pop {%s}", reglist);
|
|
}
|
|
}
|
|
else //PUSH
|
|
{
|
|
if (FormatStringRegisterList_thumb(reglist, registers, "lr", sizeof("lr"))) //if BitCount(registers) < 1 then UNPREDICTABLE
|
|
{
|
|
size = sprintf(str, "push {%s}", reglist);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 14: //BKPT
|
|
{
|
|
if (tv >= ARMv5TE) //undefined prior to ARM version 5
|
|
{
|
|
size = sprintf(str, "bkpt #0x%X", BITS(c, 0, 8));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else //ADD (5), ADD (6) to SP or PC
|
|
{
|
|
const char* sppc = (BITS(c, 11, 2)) ? "sp" : "pc";
|
|
size = sprintf(str, "add r%u, %s, #0x%X", BITS(c, 8, 3), sppc, 4 * BITS(c, 0, 8));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 6: //0xC000 //B, SWI, LDMIA, STMIA
|
|
{
|
|
if (BITS(c, 12, 1)) //Conditional branch, Undefined, System call
|
|
{
|
|
switch (BITS(c, 8, 4))
|
|
{
|
|
case 14: //UDF "Permanently undefined space", OS dependant
|
|
{
|
|
//size = sprintf(str, "udf #0x%X", BITS(c, 0, 8)); //note: not an instruction in ARMv5TE, just UNDEFINED
|
|
break;
|
|
}
|
|
case 15: //SWI
|
|
{
|
|
size = sprintf(str, "swi #0x%X", BITS(c, 0, 8));
|
|
break;
|
|
}
|
|
default: //B (1) conditional
|
|
{
|
|
size = sprintf(str, "b%s #0x%X", Conditions[BITS(c, 8, 4)], 4 + 2 * SIGNEX32_BITS(c, 0, 8));
|
|
}
|
|
}
|
|
}
|
|
else //LDMIA/STMIA
|
|
{
|
|
char reglist[STRING_LENGTH] = { 0 };
|
|
const char* op = (BITS(c, 11, 1)) ? "ldmia" : "stmia";
|
|
if (FormatStringRegisterList_thumb(reglist, BITS(c, 0, 8), "", 0))
|
|
{
|
|
size = sprintf(str, "%s r%u!, {%s}", op, BITS(c, 8, 3), reglist);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 7: //0xE000 //B, then 32-bit instructions
|
|
{
|
|
switch (BITS(c, 11, 2))
|
|
{
|
|
case 0:
|
|
{
|
|
size = sprintf(str, "b #0x%X", 4 + 2 * SIGNEX32_BITS(c, 0, 11)); //11 bits to signed 32 bits
|
|
break;
|
|
}
|
|
//case 1: break; //undefined on first pass
|
|
case 2: //BL/BLX prefix
|
|
{
|
|
c = code >> 16; //get high 16 bits
|
|
if (c >> 13 == 7)
|
|
{
|
|
switch (BITS(c, 11, 2))
|
|
{
|
|
case 1: //BLX (1)
|
|
{
|
|
if (tv < ARMv5TE) break;
|
|
if (!BITS(c, 0, 1))
|
|
{
|
|
thumb_size = SIZE_32;
|
|
|
|
int ofs = (BITS((code & 0xffff), 0, 11)) << 12;
|
|
ofs = SIGNEX32_VAL(ofs, 23);
|
|
ofs += 4;
|
|
ofs += 2 * BITS(c, 0, 11);
|
|
//note: bit 1 should be cleared (word aligned target address)
|
|
size = sprintf(str, "blx #0x%X", ofs);
|
|
}
|
|
break;
|
|
}
|
|
//case 2: break; //BL/BLX prefix
|
|
case 3: //BL
|
|
{
|
|
thumb_size = SIZE_32;
|
|
|
|
int ofs = (BITS((code & 0xffff), 0, 11)) << 12;
|
|
ofs = SIGNEX32_VAL(ofs, 23);
|
|
ofs += 4;
|
|
ofs += 2 * BITS(c, 0, 11);
|
|
|
|
size = sprintf(str, "bl #0x%X", ofs);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!str[0]) //in case nothing was written to it
|
|
{
|
|
debug_na_count++;
|
|
sprintf(str, "n/a");
|
|
}
|
|
else
|
|
{
|
|
CheckSpecialRegister(str, size); //formatting
|
|
}
|
|
return thumb_size;
|
|
}
|
|
|
|
void Disassemble_arm(u32 code, char str[STRING_LENGTH], ARMARCH av) {
|
|
/**/
|
|
//Reference: page 68 of 811 from the ARM Architecture reference manual june 2000 edition
|
|
//todo: proper support for ARMv4T
|
|
//todo: extra caution for UNPREDICTABLE instructions, need to remove them? or decode regardless?
|
|
|
|
int size = 0; //return value of sprintf to be passed to CheckSpecialRegister
|
|
u32 c = code; //alias
|
|
u8 cond = BITS(c, 28, 4); //condition bits
|
|
|
|
//todo: check unconditional instructions first to avoid putting a check later at each stage
|
|
|
|
switch (BITS(c, 25, 3))
|
|
{
|
|
case 0: //Data processing, DSP instructions, ...
|
|
{
|
|
if (cond == NV) break; //undefined
|
|
if (BITS(c, 4, 1))
|
|
{
|
|
if (BITS(c, 7, 1)) //Multiplies, extra load/stores: see fig 3-2
|
|
{
|
|
u8 oplo = BITS(c, 5, 2);
|
|
if (!oplo)
|
|
{
|
|
if (!BITS(c, 22, 3)) //Multiply (accumulate)
|
|
{
|
|
u8 rm = BITS(c, 0, 4);
|
|
u8 rs = BITS(c, 8, 4);
|
|
u8 rn = BITS(c, 12, 4);
|
|
u8 rd = BITS(c, 16, 4);
|
|
const char* s = BITS(c, 20, 1) ? "s" : "";
|
|
if (BITS(c, 21, 1)) //MLA
|
|
{
|
|
size = sprintf(str, "mla%s%s r%u, r%u, r%u, r%u", s, Conditions[cond], rd, rm, rs, rn);
|
|
}
|
|
else //MUL
|
|
{
|
|
if (rn) break; //Should-Be-Zero
|
|
size = sprintf(str, "mul%s%s r%u, r%u, r%u", s, Conditions[cond], rd, rm, rs);
|
|
}
|
|
}
|
|
else if (BITS(c, 23, 1)) //Multiply (accumulate) long
|
|
{
|
|
const char* s = BITS(c, 20, 1) ? "s" : "";
|
|
size = sprintf(str, "%s%s%s r%u, r%u, r%u, r%u", MultiplyLong[BITS(c, 21, 2)], s, Conditions[cond], BITS(c, 12, 4), BITS(c, 16, 4), BITS(c, 0, 4), BITS(c, 8, 4));
|
|
}
|
|
else //Swap/swap byte (SWP, SWPB)
|
|
{
|
|
if (BITS(c, 8, 4)) break; //Should-Be-Zero
|
|
const char* b = BITS(c, 22, 1) ? "b" : ""; //byte or no
|
|
size = sprintf(str, "swp%s%s r%u, r%u, [r%u]", b, Conditions[cond], BITS(c, 12, 4), BITS(c, 0, 4), BITS(c, 16, 4));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!BITS(c, 22, 1) && BITS(c, 8, 4)) break; //Should-Be-Zero if register offset
|
|
if (oplo == 1) //Load/store halfword
|
|
{
|
|
const char* l = BITS(c, 20, 1) ? "ldrh" : "strh"; //load or store
|
|
size = FormatExtraLoadStore(c, str, cond, l);
|
|
}
|
|
else
|
|
{
|
|
if (BITS(c, 20, 1)) //Load signed halfword/byte
|
|
{
|
|
const char* h = BITS(c, 5, 1) ? "ldrsh" : "ldrsb"; //halfword/byte
|
|
size = FormatExtraLoadStore(c, str, cond, h);
|
|
}
|
|
else //Load/store two words
|
|
{
|
|
if (BITS(c, 12, 1)) break; //undefined if Rd is odd
|
|
const char* s = BITS(c, 5, 1) ? "strd" : "ldrd"; //store or load
|
|
size = FormatExtraLoadStore(c, str, cond, s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (BITS(c, 23, 2) == 2 && !BITS(c, 20, 1)) //Miscellanous instructions, see fig 3-3
|
|
{
|
|
u8 oplo = BITS(c, 5, 2);
|
|
u8 ophi = BITS(c, 21, 2);
|
|
switch (oplo)
|
|
{
|
|
case 0:
|
|
{
|
|
if (ophi == 3) //CLZ
|
|
{
|
|
//note: if PC is in either register, UNPREDICTABLE
|
|
if (av < ARMv5TE) break;
|
|
if (!BITS(c, 16, 4) || !BITS(c, 8, 4)) break; //Should-Be-One
|
|
size = sprintf(str, "clz%s r%u, r%u", Conditions[cond], BITS(c, 12, 4), BITS(c, 0, 4));
|
|
}
|
|
else if (ophi == 1)//Branch/exchange instruction set (BX)
|
|
{
|
|
if (BITS(c, 8, 12) != 0xfff) break; //Should-Be-One
|
|
size = sprintf(str, "bx%s r%u", Conditions[cond], BITS(c, 0, 4));
|
|
}
|
|
break;
|
|
}
|
|
case 1: //BLX (2)
|
|
{
|
|
if (av < ARMv5TE) break;
|
|
if (ophi != 1) break;
|
|
if (BITS(c, 8, 12) != 0xfff) break; //Should-Be-One
|
|
size = sprintf(str, "blx%s r%u", Conditions[cond], BITS(c, 0, 4));
|
|
break;
|
|
}
|
|
case 2: //Enhanced DSP add/sub (QADD, QDADD, QSUB, QDSUB)
|
|
{
|
|
//note: if PC is in either register, UNPREDICTABLE
|
|
if (av < ARMv5TE) break;
|
|
if (BITS(c, 8, 4)) break; //Should-Be-Zero
|
|
size = sprintf(str, "%s%s r%u, r%u, r%u", DSP_AddSub[ophi], Conditions[cond], BITS(c, 12, 4), BITS(c, 0, 4), BITS(c, 16, 4));
|
|
break;
|
|
}
|
|
case 3: //Software breakpoint (BKPT)
|
|
{
|
|
if (av < ARMv5TE) break;
|
|
if (ophi != 1) break;
|
|
size = sprintf(str, "bkpt #0x%X", (BITS(c, 8, 12) << 4) | BITS(c, 0, 4));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else //Data processing register shift
|
|
{
|
|
u8 rm = BITS(c, 0, 4);
|
|
u8 shift = BITS(c, 5, 2);
|
|
u8 rs = BITS(c, 8, 4);
|
|
u8 rd = BITS(c, 12, 4);
|
|
u8 rn = BITS(c, 16, 4);
|
|
u8 op = BITS(c, 21, 4);
|
|
const char* s = BITS(c, 20, 1) ? "s" : "";
|
|
switch (op)
|
|
{
|
|
case 8: //TST
|
|
case 9: //TEQ
|
|
case 10: //CMP
|
|
case 11: //CMN
|
|
{
|
|
size = sprintf(str, "%s%s r%u, r%u, %s r%u", DataProcessing_arm[op], Conditions[cond], rn, rm, Shifters[shift], rs);
|
|
break;
|
|
}
|
|
case 13: //MOV
|
|
case 15: //MVN
|
|
{
|
|
size = sprintf(str, "%s%s%s r%u, r%u, %s r%u", DataProcessing_arm[op], s, Conditions[cond], rd, rm, Shifters[shift], rs);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
size = sprintf(str, "%s%s%s r%u, r%u, r%u, %s r%u", DataProcessing_arm[op], s, Conditions[cond], rd, rn, rm, Shifters[shift], rs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else //bit 4 == 0
|
|
{
|
|
if (BITS(c, 23, 2) == 2 && !BITS(c, 20, 1)) //Miscellanous instructions, see fig 3-3
|
|
{
|
|
if (BITS(c, 7, 1)) //Enhanced DSP multiplies
|
|
{
|
|
if (av < ARMv5TE) break;
|
|
//note: PC for any register is UNPREDICTABLE
|
|
u8 rm = BITS(c, 0, 4);
|
|
const char* x = BITS(c, 5, 1) ? "t" : "b";
|
|
const char* y = BITS(c, 6, 1) ? "t" : "b";
|
|
u8 rs = BITS(c, 8, 4);
|
|
u8 rn_rdlo = BITS(c, 12, 4);
|
|
u8 rd_rdhi = BITS(c, 16, 4);
|
|
u8 op = BITS(c, 21, 2);
|
|
switch (op)
|
|
{
|
|
case 0: //SMLA
|
|
{
|
|
size = sprintf(str, "%s%s%s%s r%u, r%u, r%u, r%u", DSP_Multiplies[op], x, y, Conditions[cond], rd_rdhi, rm, rs, rn_rdlo);
|
|
break;
|
|
}
|
|
case 1: //SMLAW, SMULW
|
|
{
|
|
if (BITS(c, 5, 1)) //SMULW
|
|
{
|
|
if (rn_rdlo) break; //Should-Be-Zero
|
|
size = sprintf(str, "smulw%s%s r%u, r%u, r%u", y, Conditions[cond], rd_rdhi, rm, rs);
|
|
}
|
|
else //SMLAW
|
|
{
|
|
size = sprintf(str, "smlaw%s%s r%u, r%u, r%u, r%u", y, Conditions[cond], rd_rdhi, rm, rs, rn_rdlo);
|
|
}
|
|
break;
|
|
}
|
|
case 2: //SMLAL
|
|
{
|
|
size = sprintf(str, "%s%s%s%s r%u, r%u, r%u, r%u", DSP_Multiplies[op], x, y, Conditions[cond], rn_rdlo, rd_rdhi, rm, rs);
|
|
break;
|
|
}
|
|
case 3: //SMUL
|
|
{
|
|
if (rn_rdlo) break; //Should-Be-Zero
|
|
size = sprintf(str, "%s%s%s%s r%u, r%u, r%u", DSP_Multiplies[op], x, y, Conditions[cond], rd_rdhi, rm, rs);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!BITS(c, 0, 12) && BITS(c, 16, 4) == 15) //Move status reg to reg (MRS)
|
|
{
|
|
//note: if Rd == PC, UNPREDICTABLE
|
|
char sreg = BITS(c, 22, 1) ? 's' : 'c'; //SPSR (1) or CPSR (0)
|
|
size = sprintf(str, "mrs%s r%u, %cpsr", Conditions[cond], BITS(c, 12, 4), sreg);
|
|
}
|
|
else if (BITS(c, 12, 4) == 15 && !BITS(c, 4, 8) && BITS(c, 21, 1)) //Move reg to status reg (MSR register)
|
|
{
|
|
char sreg = BITS(c, 22, 1) ? 's' : 'c'; //SPSR (1) or CPSR (0)
|
|
size = sprintf(str, "msr%s %cpsr_%s, r%u", Conditions[cond], sreg, MSR_cxsf[BITS(c, 16, 4)], BITS(c, 0, 4));
|
|
}
|
|
}
|
|
}
|
|
else //Data processing immediate shift
|
|
{
|
|
//todo: write a function for all 3 Data Processing switch-cases
|
|
u8 op = BITS(c, 21, 4);
|
|
u8 rm = BITS(c, 0, 4);
|
|
u8 shift = BITS(c, 5, 2);
|
|
u8 shift_imm = BITS(c, 7, 5);
|
|
char sstr[STRING_LENGTH] = "rrx"; //default, overwrite if incorrect
|
|
if ((shift == 1 || shift == 2) && !shift_imm) shift_imm = 32; //0~31 for LSL, 1~32 for LSR, ASR and ROR, always 0 for RRX
|
|
if (!(shift == 3 && !shift_imm)) sprintf(sstr, "%s #%u", Shifters[shift], shift_imm);
|
|
u8 rd = BITS(c, 12, 4);
|
|
u8 rn = BITS(c, 16, 4);
|
|
const char* s = BITS(c, 20, 1) ? "s" : "";
|
|
switch (op)
|
|
{
|
|
case 8: //TST
|
|
case 9: //TEQ
|
|
case 10: //CMP
|
|
case 11: //CMN
|
|
{
|
|
size = sprintf(str, "%s%s r%u, r%u, %s", DataProcessing_arm[op], Conditions[cond], rn, rm, sstr);
|
|
break;
|
|
}
|
|
case 13: //MOV
|
|
case 15: //MVN
|
|
{
|
|
size = sprintf(str, "%s%s%s r%u, r%u, %s", DataProcessing_arm[op], s, Conditions[cond], rd, rm, sstr);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
size = sprintf(str, "%s%s%s r%u, r%u, r%u, %s", DataProcessing_arm[op], s, Conditions[cond], rd, rn, rm, sstr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 1: //Data processing and MSR immediate
|
|
{
|
|
if (cond == NV) break; //undefined
|
|
u32 imm = ROR(BITS(c, 0, 8), 2 * BITS(c, 8, 4));
|
|
|
|
if (BITS(c, 12, 4) == 15 && !BITS(c, 20, 1)) //MSR immediate
|
|
{
|
|
char sreg = BITS(c, 22, 1) ? 's' : 'c'; //SPSR (1) or CPSR (0)
|
|
size = sprintf(str, "msr%s %cpsr_%s, #0x%X", Conditions[cond], sreg, MSR_cxsf[BITS(c, 16, 4)], imm);
|
|
}
|
|
else //Data processing immediate
|
|
{
|
|
u8 op = BITS(c, 21, 4);
|
|
u8 rd = BITS(c, 12, 4);
|
|
u8 rn = BITS(c, 16, 4);
|
|
const char* s = BITS(c, 20, 1) ? "s" : "";
|
|
switch (op)
|
|
{
|
|
case 8: //TST
|
|
case 9: //TEQ
|
|
case 10: //CMP
|
|
case 11: //CMN
|
|
//always update the condition codes: <op>{<cond>} <Rn>, <shift>
|
|
{
|
|
size = sprintf(str, "%s%s r%u, #0x%X", DataProcessing_arm[op], Conditions[cond], rn, imm);
|
|
break;
|
|
}
|
|
case 13: //MOV
|
|
case 15: //MVN
|
|
//only one source operand: <op>{<cond>}{S} <Rd>, <shift>
|
|
{
|
|
size = sprintf(str, "%s%s%s r%u, #0x%X", DataProcessing_arm[op], s, Conditions[cond], rd, imm);
|
|
break;
|
|
}
|
|
|
|
default: //others: <op>{<cond>}{S} <Rd>, <Rn>, <shift>
|
|
{
|
|
size = sprintf(str, "%s%s%s r%u, r%u, #0x%X", DataProcessing_arm[op], s, Conditions[cond], rd, rn, imm);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 2: //Load/store immediate offset
|
|
{
|
|
if (cond == NV) break; //undefined
|
|
//bits 25, 24, 23 and 21 decide the addressing mode
|
|
u8 rd = BITS(c, 12, 4);
|
|
u8 rn = BITS(c, 16, 4);
|
|
u16 imm = BITS(c, 0, 12); //12 bits for LDR and LDRB (8 bits for LDRH and LDRSB)
|
|
const char* sign = BITS(c, 23, 1) ? "+" : "-"; //sign of the immediate offset
|
|
const char* ls = BITS(c, 20, 1) ? "ldr" : "str"; //load or store
|
|
const char* b = BITS(c, 22, 1) ? "b" : ""; //byte or word
|
|
if (BITS(c, 24, 1)) //offset or pre-indexed
|
|
{
|
|
const char* w = BITS(c, 21, 1) ? "!" : ""; //pre-indexed if 1
|
|
size = sprintf(str, "%s%s%s r%u, [r%u, #%s0x%X]%s", ls, b, Conditions[cond], rd, rn, sign, imm, w);
|
|
}
|
|
else //post-indexed
|
|
{
|
|
const char* w = BITS(c, 21, 1) ? "t" : ""; //user mode
|
|
size = sprintf(str, "%s%s%s%s r%u, [r%u], #%s0x%X", ls, b, w, Conditions[cond], rd, rn, sign, imm);
|
|
}
|
|
break;
|
|
}
|
|
case 3: //Load/store register offset
|
|
{
|
|
if (cond == NV) break; //undefined
|
|
if (BITS(c, 4, 1)) break; //undefined
|
|
u8 rm = BITS(c, 0, 4);
|
|
u8 shift = BITS(c, 5, 2);
|
|
u16 shift_imm = BITS(c, 7, 5);
|
|
u8 rd = BITS(c, 12, 4);
|
|
u8 rn = BITS(c, 16, 4);
|
|
const char* sign = BITS(c, 23, 1) ? "" : "-"; //sign of the immediate offset, + implicit
|
|
const char* ls = BITS(c, 20, 1) ? "ldr" : "str"; //load or store
|
|
const char* b = BITS(c, 22, 1) ? "b" : ""; //byte or word
|
|
char sstr[STRING_LENGTH] = "rrx"; //default, overwrite if incorrect
|
|
if ((shift == 1 || shift == 2) && !shift_imm) shift_imm = 32; //0~31 for LSL, 1~32 for LSR, ASR and ROR, always 0 for RRX
|
|
if (!(shift == 3 && !shift_imm)) sprintf(sstr, "%s #%u", Shifters[shift], shift_imm);
|
|
//note: if rm==r15 or rn==r15 then UNPREDICTABLE
|
|
//note: if rn==rm then UNPREDICTABLE
|
|
if (BITS(c, 24, 1)) //offset or pre-indexed
|
|
{
|
|
const char* w = BITS(c, 21, 1) ? "!" : ""; //pre-indexed if 1
|
|
size = sprintf(str, "%s%s%s r%u, [r%u, %sr%u, %s]%s", ls, b, Conditions[cond], rd, rn, sign, rm, sstr, w);
|
|
}
|
|
else //post-indexed
|
|
{
|
|
const char* w = BITS(c, 21, 1) ? "t" : ""; //user mode
|
|
size = sprintf(str, "%s%s%s%s r%u, [r%u], %sr%u, %s", ls, b, w, Conditions[cond], rd, rn, sign, rm, sstr);
|
|
}
|
|
break;
|
|
}
|
|
case 4: //Load/store multiple
|
|
{
|
|
if (cond == NV) break; //undefined
|
|
char reglist[STRING_LENGTH] = { 0 };
|
|
FormatStringRegisterList_arm(reglist, BITS(c, 0, 16));
|
|
u8 rn = BITS(c, 16, 4);
|
|
const char* w = BITS(c, 21, 1) ? "!" : ""; //W bit
|
|
const char* s = BITS(c, 22, 1) ? "^" : ""; //S bit
|
|
u8 am = BITS(c, 23, 2); //PU bits
|
|
const char* op = (BITS(c, 20, 1)) ? "ldm" : "stm"; //LDM or STM
|
|
size = sprintf(str, "%s%s%s r%u%s, {%s}%s", op, AddressingModes[am], Conditions[cond], rn, w, reglist, s);
|
|
break;
|
|
}
|
|
case 5: //Branch instructions
|
|
{
|
|
if (cond == NV && av >= ARMv5TE) //BLX (1)
|
|
{
|
|
size = sprintf(str, "blx #0x%X", 8 + 4 * SIGNEX32_BITS(c, 0, 24) + 2 * BITS(c, 24, 1));
|
|
}
|
|
else //B, BL
|
|
{
|
|
const char* l = (BITS(c, 24, 1)) ? "l" : "";
|
|
size = sprintf(str, "b%s%s #0x%X", l, Conditions[cond], 8 + 4 * SIGNEX32_BITS(c, 0, 24));
|
|
}
|
|
break;
|
|
}
|
|
case 6: //Coprocessor load/store, Double register transfers
|
|
{
|
|
if (av < ARMv5TE) break;
|
|
//if (cond == NV) break; //only unpredictable prior to ARMv5
|
|
if (BITS(c, 21, 4) == 2) //MCRR, MRRC
|
|
{
|
|
//note: if PC is specified for Rn or Rd, UNPREDICTABLE
|
|
const char* op = BITS(c, 20, 1) ? "mrrc" : "mcrr";
|
|
size = sprintf(str, "%s%s p%u, #0x%X, r%u, r%u, c%u", op, Conditions[cond], BITS(c, 8, 4), BITS(c, 4, 4), BITS(c, 12, 4), BITS(c, 16, 4), BITS(c, 0, 4));
|
|
}
|
|
else //LDC, STC
|
|
{
|
|
//todo: reformat the cond checks into only 1
|
|
if (cond == NV && av < ARMv5TE) break;
|
|
|
|
const char* op = BITS(c, 20, 1) ? "ldc" : "stc";
|
|
const char* str_cond = (cond == NV) ? "2" : Conditions[cond]; //LDC2, STC2
|
|
const char* l = BITS(c, 22, 1) ? "l" : ""; //long
|
|
char str_cond_long[8] = { 0 };
|
|
if (cond == NV) sprintf(str_cond_long, "%s%s", str_cond, l);
|
|
else sprintf(str_cond_long, "%s%s", l, str_cond);
|
|
u8 ofs_opt = BITS(c, 0, 8);
|
|
u8 cp_num = BITS(c, 8, 4);
|
|
u8 crd = BITS(c, 12, 4);
|
|
u8 rn = BITS(c, 16, 4);
|
|
const char* sign = BITS(c, 23, 1) ? "+" : "-";
|
|
switch ((2 * BITS(c, 24, 1)) | BITS(c, 21, 1)) //(p*2) | w
|
|
{
|
|
case 0: //p==0, w==0 //unindexed: [<Rn>], <option>
|
|
{
|
|
size = sprintf(str, "%s%s p%u, c%u, [r%u], {0x%X}", op, str_cond_long, cp_num, crd, rn, ofs_opt);
|
|
break;
|
|
}
|
|
case 1: //p==0, w==1 //post indexed: [<Rn>], #+/-<offset_8>*4
|
|
{
|
|
size = sprintf(str, "%s%s p%u, c%u, [r%u], #%s0x%X", op, str_cond_long, cp_num, crd, rn, sign, 4 * ofs_opt);
|
|
break;
|
|
}
|
|
case 2: //p==1, w==0 //immediate offset: [<Rn>, #+/-<offset_8>*4]
|
|
{
|
|
size = sprintf(str, "%s%s p%u, c%u, [r%u, #%s0x%X]", op, str_cond_long, cp_num, crd, rn, sign, 4 * ofs_opt);
|
|
break;
|
|
}
|
|
case 3: //p==1, w==1 //pre indexed: [<Rn>, #+/-<offset_8>*4]!
|
|
{
|
|
size = sprintf(str, "%s%s p%u, c%u, [r%u, #%s0x%X]!", op, str_cond_long, cp_num, crd, rn, sign, 4 * ofs_opt);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 7: //Software Interrupt, Coprocessor register transfer, Coprocessor data processing
|
|
{
|
|
if (BITS(c, 24, 1)) //SWI
|
|
{
|
|
if (cond == NV) break;
|
|
size = sprintf(str, "swi%s #0x%X", Conditions[cond], BITS(c, 0, 24));
|
|
}
|
|
else
|
|
{
|
|
if (cond == NV && av < ARMv5TE) break;
|
|
|
|
u8 crn = BITS(c, 16, 4);
|
|
u8 p = BITS(c, 8, 4);
|
|
u8 rd_crd = BITS(c, 12, 4);
|
|
u8 crm = BITS(c, 0, 4);
|
|
u8 op2 = BITS(c, 5, 3);
|
|
const char* str_cond = (cond == NV) ? "2" : Conditions[cond]; //suffix
|
|
if (BITS(c, 4, 1)) //MCR, MRC
|
|
{
|
|
const char* str_op = BITS(c, 20, 1) ? "mrc" : "mcr";
|
|
size = sprintf(str, "%s%s p%u, #0x%X, r%u, c%u, c%u, #0x%X", str_op, str_cond, p, BITS(c, 21, 3), rd_crd, crn, crm, op2);
|
|
}
|
|
else //CDP
|
|
{
|
|
size = sprintf(str, "cdp%s p%u, #0x%X, c%u, c%u, c%u, #0x%X", str_cond, p, BITS(c, 20, 4), rd_crd, crn, crm, op2); //no condition suffix for cdp2
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//also: unconditionnal instructions if bits 28 to 31 are 1111
|
|
if (!str[0] && (c & 0xFD70F000) == 0xF550F000) //Cache preload (PLD)
|
|
{
|
|
//todo: placing it here is wasteful, find a workaround
|
|
//bit-pattern: 1111 01x1 x101 xxxx 1111 xxxx xxxx xxxx
|
|
//data mask : 1111 1101 0111 0000 1111 0000 0000 0000
|
|
//inst. mask : 1111 0101 0101 0000 1111 0000 0000 0000
|
|
//note: only offset addressing modes
|
|
// [<Rn>, #+/-<offset_12>] //immediate
|
|
// [<Rn>, +/-<Rm>] //register
|
|
// [<Rn>, +/-<Rm>, <shift> #<shift_imm>] //scaled register
|
|
u8 rn = BITS(c, 16, 4);
|
|
if (BITS(c, 25, 1)) //(scaled) register
|
|
{
|
|
u8 rm = BITS(c, 0, 4);
|
|
u8 shift = BITS(c, 5, 2);
|
|
u16 shift_imm = BITS(c, 7, 5);
|
|
const char* sign = BITS(c, 23, 1) ? "" : "-";
|
|
char sstr[STRING_LENGTH] = "rrx"; //default, overwrite if incorrect
|
|
if ((shift == 1 || shift == 2) && !shift_imm) shift_imm = 32; //0~31 for LSL, 1~32 for LSR, ASR and ROR, always 0 for RRX
|
|
if (!(shift == 3 && !shift_imm)) sprintf(sstr, "%s #%u", Shifters[shift], shift_imm); //intermediate sprintf
|
|
size = sprintf(str, "pld [r%u, %sr%u, %s]", rn, sign, rm, sstr);
|
|
}
|
|
else //immediate
|
|
{
|
|
const char* sign = BITS(c, 23, 1) ? "+" : "-";
|
|
size = sprintf(str, "pld [r%u, #%s0x%X]", rn, sign, BITS(c, 0, 12));
|
|
}
|
|
}
|
|
|
|
if (!str[0]) //in case nothing was written to it
|
|
{
|
|
debug_na_count++;
|
|
sprintf(str, "n/a"); //don't need to get size, no special register formatting
|
|
}
|
|
else
|
|
{
|
|
CheckSpecialRegister(str, size); //formatting of SP, LR, PC
|
|
}
|
|
}
|