3938 lines
87 KiB
C
3938 lines
87 KiB
C
/* Instruction printing code for the ARC.
|
|
Copyright 1994, 1995, 1997, 1998, 2000, 2001, 2002, 2005, 2006, 2007, 2008, 2009
|
|
Free Software Foundation, Inc.
|
|
Contributed by Doug Evans (dje@cygnus.com).
|
|
|
|
Copyright 2008-2012 Synopsys Inc.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
#include <ansidecl.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include "disas-asm.h"
|
|
#include "arc.h"
|
|
#include "arc-ext.h"
|
|
#include "arc-dis.h"
|
|
#include "arcompact-dis.h"
|
|
#include "elf-bfd.h"
|
|
|
|
static bfd_vma bfd_getm32(unsigned int);
|
|
static bfd_vma bfd_getm32_ac(unsigned int) ATTRIBUTE_UNUSED;
|
|
|
|
#undef _NELEM
|
|
#define _NELEM(ary) (sizeof(ary) / sizeof((ary)[0]))
|
|
|
|
#define BIT(word, n) ((word) & (1 << (n)))
|
|
/* START ARC LOCAL */
|
|
#define BITS(word, s, e) (((word) << (sizeof(word) * 8 - 1 - (e))) >> ((s) + (sizeof(word) * 8 - 1 - (e))))
|
|
/* END ARC LOCAL */
|
|
#define OPCODE(word) (BITS((word), 27, 31))
|
|
#define FIELDA(word) (BITS((word), 0, 5))
|
|
#define FIELDb(word) (BITS((word), 24, 26))
|
|
#define FIELDB(word) (BITS((word), 12, 14))
|
|
#define FIELDC(word) (BITS((word), 6, 11))
|
|
#define OPCODE_AC(word) (BITS((word), 11, 15))
|
|
#define FIELDA_AC(word) (BITS((word), 0, 2))
|
|
#define FIELDB_AC(word) (BITS((word), 8, 10))
|
|
#define FIELDC_AC(word) (BITS((word), 5, 7))
|
|
#define FIELDU_AC(word) (BITS((word), 0, 4))
|
|
|
|
/*
|
|
* FIELDS_AC is the 11-bit signed immediate value used for
|
|
* GP-relative instructions.
|
|
*/
|
|
#define FIELDS_AC(word) (BITS(((signed int)(word)), 0, 8))
|
|
|
|
/*
|
|
* FIELDD is signed in all of its uses, so we make sure argument is
|
|
* treated as signed for bit shifting purposes.
|
|
*/
|
|
#define FIELDD(word) (BITS(((signed int)(word)), 16, 23))
|
|
|
|
/*
|
|
* FIELDD9 is the 9-bit signed immediate value used for
|
|
* load/store instructions.
|
|
*/
|
|
#define FIELDD9(word) ((BITS(((signed int)(word)), 15, 15) << 8) | (BITS((word), 16, 23)))
|
|
|
|
/*
|
|
* FIELDS is the 12-bit signed immediate value
|
|
*/
|
|
#define FIELDS(word) ((BITS(((signed int)(word)), 0, 5) << 6) | (BITS((word), 6, 11)))
|
|
|
|
/*
|
|
* FIELD S9 is the 9-bit signed immediate value used for
|
|
* bbit0/bbit instruction
|
|
*/
|
|
#define FIELDS9(word) (((BITS(((signed int)(word)), 15, 15) << 7) | (BITS((word), 17, 23))) << 1)
|
|
#define FIELDS9_FLAG(word) (((BITS(((signed int)(word)), 0, 5) << 6) | (BITS((word), 6, 11))))
|
|
|
|
#define PUT_NEXT_WORD_IN(a) \
|
|
{ \
|
|
if (is_limm == 1 && !NEXT_WORD(1)) { \
|
|
mwerror(state, "Illegal limm reference in last instruction!\n"); \
|
|
} \
|
|
if (info->endian == BFD_ENDIAN_LITTLE) { \
|
|
(a) = ((state->words[1] & 0xff00) | (state->words[1] & 0xff)) << 16; \
|
|
(a) |= ((state->words[1] & 0xff0000) | (state->words[1] & 0xff000000)) >> 16; \
|
|
} else { \
|
|
(a) = state->words[1]; \
|
|
} \
|
|
}
|
|
|
|
#define CHECK_NULLIFY() \
|
|
do { \
|
|
state->nullifyMode = BITS(state->words[0], 5, 5); \
|
|
} while (0)
|
|
|
|
#define CHECK_COND_NULLIFY() \
|
|
do { \
|
|
state->nullifyMode = BITS(state->words[0], 5, 5); \
|
|
cond = BITS(state->words[0], 0, 4); \
|
|
} while (0)
|
|
|
|
#define CHECK_FLAG_COND_NULLIFY() \
|
|
do { \
|
|
if (is_shimm == 0) { \
|
|
flag = BIT(state->words[0], 15); \
|
|
state->nullifyMode = BITS(state->words[0], 5, 5); \
|
|
cond = BITS(state->words[0], 0, 4); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define CHECK_FLAG_COND() \
|
|
{ \
|
|
if (is_shimm == 0) { \
|
|
flag = BIT(state->words[0], 15); \
|
|
cond = BITS(state->words[0], 0, 4); \
|
|
} \
|
|
}
|
|
|
|
#define CHECK_FLAG() \
|
|
{ \
|
|
flag = BIT(state->words[0], 15); \
|
|
}
|
|
|
|
#define CHECK_COND() \
|
|
{ \
|
|
if (is_shimm == 0) { \
|
|
cond = BITS(state->words[0], 0, 4); \
|
|
} \
|
|
}
|
|
|
|
#define CHECK_FIELD(field) \
|
|
{ \
|
|
if ((field) == 62) { \
|
|
is_limm++; \
|
|
field##isReg = 0; \
|
|
PUT_NEXT_WORD_IN(field); \
|
|
} \
|
|
}
|
|
|
|
#define CHECK_FIELD_A() \
|
|
{ \
|
|
fieldA = FIELDA(state->words[0]); \
|
|
if (fieldA == 62) { \
|
|
fieldAisReg = 0; \
|
|
fieldA = 0; \
|
|
} \
|
|
}
|
|
|
|
#define FIELD_B() \
|
|
{ \
|
|
fieldB = (FIELDB(state->words[0]) << 3); \
|
|
fieldB |= FIELDb(state->words[0]); \
|
|
if (fieldB == 62) { \
|
|
fieldBisReg = 0; \
|
|
fieldB = 0; \
|
|
} \
|
|
}
|
|
|
|
#define FIELD_C() \
|
|
{ \
|
|
fieldC = FIELDC(state->words[0]); \
|
|
if (fieldC == 62) { \
|
|
fieldCisReg = 0; \
|
|
} \
|
|
}
|
|
/********** Aurora SIMD ARC 8 - bit constant **********/
|
|
#define FIELD_U8() \
|
|
{ \
|
|
\
|
|
fieldC = BITS(state->words[0], 15, 16); \
|
|
fieldC = fieldC << 6; \
|
|
fieldC |= FIELDC(state->words[0]); \
|
|
fieldCisReg = 0; \
|
|
}
|
|
|
|
#define CHECK_FIELD_B() \
|
|
{ \
|
|
fieldB = (FIELDB(state->words[0]) << 3); \
|
|
fieldB |= FIELDb(state->words[0]); \
|
|
CHECK_FIELD(fieldB); \
|
|
}
|
|
|
|
#define CHECK_FIELD_C() \
|
|
{ \
|
|
fieldC = FIELDC(state->words[0]); \
|
|
CHECK_FIELD(fieldC); \
|
|
}
|
|
|
|
#define FIELD_C_S() \
|
|
{ \
|
|
fieldC_S = (FIELDC_S(state->words[0]) << 3); \
|
|
}
|
|
|
|
#define FIELD_B_S() \
|
|
{ \
|
|
fieldB_S = (FIELDB_S(state->words[0]) << 3); \
|
|
}
|
|
|
|
#define CHECK_FIELD_H_AC() \
|
|
{ \
|
|
fieldC = ((FIELDA_AC(state->words[0])) << 3); \
|
|
fieldC |= FIELDC_AC(state->words[0]); \
|
|
CHECK_FIELD(fieldC); \
|
|
}
|
|
|
|
#define FIELD_H_AC() \
|
|
{ \
|
|
fieldC = ((FIELDA_AC(state->words[0])) << 3); \
|
|
fieldC |= FIELDC_AC(state->words[0]); \
|
|
if (fieldC > 60) { \
|
|
fieldCisReg = 0; \
|
|
fieldC = 0; \
|
|
} \
|
|
}
|
|
|
|
#define FIELD_C_AC() \
|
|
{ \
|
|
fieldC = FIELDC_AC(state->words[0]); \
|
|
if (fieldC > 3) { \
|
|
fieldC += 8; \
|
|
} \
|
|
}
|
|
|
|
#define FIELD_B_AC() \
|
|
{ \
|
|
fieldB = FIELDB_AC(state->words[0]); \
|
|
if (fieldB > 3) { \
|
|
fieldB += 8; \
|
|
} \
|
|
}
|
|
|
|
#define FIELD_A_AC() \
|
|
{ \
|
|
fieldA = FIELDA_AC(state->words[0]); \
|
|
if (fieldA > 3) { \
|
|
fieldA += 8; \
|
|
} \
|
|
}
|
|
|
|
#define IS_SMALL(x) (((field##x) < 256) && ((field##x) > -257))
|
|
#define IS_REG(x) (field##x##isReg)
|
|
#define IS_SIMD_128_REG(x) (usesSimdReg##x == 1)
|
|
#define IS_SIMD_16_REG(x) (usesSimdReg##x == 2)
|
|
#define IS_SIMD_DATA_REG(x) (usesSimdReg##x == 3)
|
|
#define WRITE_FORMAT_LB_Rx_RB(x) WRITE_FORMAT(x, "[", "]", "", "")
|
|
#define WRITE_FORMAT_x_COMMA_LB(x) WRITE_FORMAT(x, "", ", [", "", ",[")
|
|
#define WRITE_FORMAT_COMMA_x_RB(x) WRITE_FORMAT(x, ", ", "]", ", ", "]")
|
|
#define WRITE_FORMAT_x_RB(x) WRITE_FORMAT(x, "", "]", "", "]")
|
|
#define WRITE_FORMAT_COMMA_x(x) WRITE_FORMAT(x, ", ", "", ", ", "")
|
|
#define WRITE_FORMAT_x_COMMA(x) WRITE_FORMAT(x, "", ", ", "", ", ")
|
|
#define WRITE_FORMAT_x(x) WRITE_FORMAT(x, "", "", "", "")
|
|
#define WRITE_FORMAT(x, cb1, ca1, cb, ca) strcat(formatString, \
|
|
(IS_SIMD_128_REG(x) ? cb1 "%S" ca1 : IS_SIMD_16_REG(x) ? cb1 "%I" ca1 \
|
|
: IS_SIMD_DATA_REG(x) ? cb1 "%D" ca1 \
|
|
: IS_REG(x) ? cb1 "%r" ca1 \
|
|
: usesAuxReg ? cb "%a" ca \
|
|
: IS_SMALL(x) ? cb "%d" ca \
|
|
: cb "%h" ca))
|
|
|
|
#define WRITE_FORMAT_LB() strcat(formatString, "[")
|
|
#define WRITE_FORMAT_RB() strcat(formatString, "]")
|
|
#define WRITE_COMMENT(str) (state->comm[state->commNum++] = (str))
|
|
#define WRITE_NOP_COMMENT() \
|
|
if (!fieldAisReg && !flag) \
|
|
WRITE_COMMENT("nop");
|
|
|
|
#define NEXT_WORD(x) (offset += 4, state->words[x])
|
|
|
|
#define NEXT_WORD_AC(x) (offset += 2, state->words[x])
|
|
|
|
#define add_target(x) (state->targets[state->tcnt++] = (x))
|
|
|
|
static short int enable_simd = 0;
|
|
static short int enable_insn_stream = 0;
|
|
|
|
static const char *
|
|
core_reg_name(struct arcDisState *state, int val) {
|
|
if (state->coreRegName) {
|
|
return (*state->coreRegName)(state->_this, val);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const char *
|
|
aux_reg_name(struct arcDisState *state, int val) {
|
|
if (state->auxRegName) {
|
|
return (*state->auxRegName)(state->_this, val);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const char *
|
|
cond_code_name(struct arcDisState *state, int val) {
|
|
if (state->condCodeName) {
|
|
return (*state->condCodeName)(state->_this, val);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const char *
|
|
instruction_name(struct arcDisState *state, int op1, int op2, int *flags) {
|
|
if (state->instName) {
|
|
return (*state->instName)(state->_this, op1, op2, flags);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
mwerror(struct arcDisState *state, const char *msg) {
|
|
if (state->err != 0) {
|
|
(*state->err)(state->_this, (msg));
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
post_address(struct arcDisState *state, int addr) {
|
|
static char id[3 * _NELEM(state->addresses)];
|
|
unsigned int j, i = state->acnt;
|
|
if (i < _NELEM(state->addresses)) {
|
|
state->addresses[i] = addr;
|
|
++state->acnt;
|
|
j = i * 3;
|
|
id[j + 0] = '@';
|
|
id[j + 1] = '0' + i;
|
|
id[j + 2] = 0;
|
|
return id + j;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
static void
|
|
my_sprintf(struct arcDisState *state, char *buf, const char *format, ...) {
|
|
char *bp;
|
|
const char *p;
|
|
int size, leading_zero, regMap[2];
|
|
va_list ap;
|
|
|
|
if (!buf) {
|
|
return;
|
|
}
|
|
va_start(ap, format);
|
|
bp = buf;
|
|
*bp = 0;
|
|
p = format;
|
|
regMap[0] = 0;
|
|
regMap[1] = 0;
|
|
while (1) {
|
|
switch (*p++) {
|
|
case 0: goto DOCOMM; /*(return) */
|
|
default:
|
|
*bp++ = p[-1];
|
|
break;
|
|
case '%':
|
|
size = 0;
|
|
leading_zero = 0;
|
|
RETRY:;
|
|
switch (*p++) {
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9': {
|
|
/* size. */
|
|
size = p[-1] - '0';
|
|
if (size == 0) {
|
|
leading_zero = 1; /* e.g. %08x */
|
|
}
|
|
while (*p >= '0' && *p <= '9') {
|
|
size = size * 10 + *p - '0', p++;
|
|
}
|
|
goto RETRY;
|
|
}
|
|
#define inc_bp() bp = bp + strlen(bp)
|
|
|
|
case 'h': {
|
|
unsigned u = va_arg(ap, int);
|
|
/*
|
|
* Hex. We can change the format to 0x%08x in
|
|
* one place, here, if we wish.
|
|
* We add underscores for easy reading.
|
|
*/
|
|
#define CDT_DEBUG
|
|
if (u > 65536) {
|
|
#ifndef CDT_DEBUG
|
|
sprintf(bp, "0x%x_%04x", u >> 16, u & 0xffff);
|
|
#else
|
|
sprintf(bp, "0x%08x", u);
|
|
#endif // CDT_DEBUG
|
|
} else {
|
|
sprintf(bp, "0x%x", u);
|
|
}
|
|
inc_bp();
|
|
} break;
|
|
case 'X':
|
|
case 'x': {
|
|
int val = va_arg(ap, int);
|
|
if (size != 0) {
|
|
if (leading_zero) {
|
|
sprintf(bp, "%0*x", size, val);
|
|
} else {
|
|
sprintf(bp, "%*x", size, val);
|
|
}
|
|
} else {
|
|
sprintf(bp, "%x", val);
|
|
}
|
|
inc_bp();
|
|
} break;
|
|
case 'd': {
|
|
int val = va_arg(ap, int);
|
|
if (size != 0) {
|
|
sprintf(bp, "%*d", size, val);
|
|
} else {
|
|
sprintf(bp, "%d", val);
|
|
}
|
|
inc_bp();
|
|
} break;
|
|
case 'r': {
|
|
/* Register. */
|
|
int val = va_arg(ap, int);
|
|
|
|
#define REG2NAME(num, name) \
|
|
case num: \
|
|
sprintf(bp, "" name); \
|
|
regMap[((num) < 32) ? 0 : 1] |= 1 << ((num) - (((num) < 32) ? 0 : 32)); \
|
|
break;
|
|
switch (val) {
|
|
REG2NAME(26, "gp");
|
|
REG2NAME(27, "fp");
|
|
REG2NAME(28, "sp");
|
|
REG2NAME(29, "ilink1");
|
|
REG2NAME(30, "ilink2");
|
|
REG2NAME(31, "blink");
|
|
REG2NAME(60, "lp_count");
|
|
REG2NAME(63, "pcl");
|
|
default: {
|
|
const char *ext;
|
|
ext = core_reg_name(state, val);
|
|
if (ext) {
|
|
sprintf(bp, "%s", ext);
|
|
} else {
|
|
sprintf(bp, "r%d", val);
|
|
}
|
|
} break;
|
|
}
|
|
inc_bp();
|
|
} break;
|
|
|
|
case 'a': {
|
|
/* Aux Register. */
|
|
int val = va_arg(ap, int);
|
|
char *ret;
|
|
ret = arc_aux_reg_name(val);
|
|
if (ret) {
|
|
sprintf(bp, "%s", ret);
|
|
} else {
|
|
const char *ext;
|
|
ext = aux_reg_name(state, val);
|
|
if (ext) {
|
|
sprintf(bp, "%s", ext);
|
|
} else {
|
|
my_sprintf(state, bp, "%h", val);
|
|
}
|
|
}
|
|
|
|
inc_bp();
|
|
} break;
|
|
case 's': {
|
|
sprintf(bp, "%s", va_arg(ap, char *));
|
|
inc_bp();
|
|
} break;
|
|
case '*': {
|
|
#if 0
|
|
va_arg(ap,char*);
|
|
inc_bp();
|
|
break;
|
|
#elif 1 /* used for prefetch to skip an argument. */
|
|
va_arg(ap, int);
|
|
break;
|
|
#else
|
|
extern void abort(void);
|
|
|
|
abort();
|
|
#endif
|
|
}
|
|
|
|
/* SIMD operands follow*/
|
|
case 'S': {
|
|
int val = va_arg(ap, int);
|
|
|
|
sprintf(bp, "vr%d", val);
|
|
inc_bp();
|
|
break;
|
|
}
|
|
case 'I': {
|
|
int val = va_arg(ap, int);
|
|
|
|
sprintf(bp, "i%d", val);
|
|
inc_bp();
|
|
break;
|
|
}
|
|
case 'D': {
|
|
int val = va_arg(ap, int);
|
|
|
|
sprintf(bp, "dr%d", val);
|
|
inc_bp();
|
|
break;
|
|
}
|
|
/* SIMD operands end */
|
|
default:
|
|
fprintf(stderr, "?? format %c\n", p[-1]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DOCOMM:
|
|
*bp = 0;
|
|
va_end(ap);
|
|
}
|
|
|
|
static const char *condName[] = {
|
|
/* 0..15. */
|
|
"", "z", "nz", "p", "n", "c", "nc", "v",
|
|
"nv", "gt", "ge", "lt", "le", "hi", "ls", "pnz",
|
|
"ss", "sc"
|
|
|
|
};
|
|
|
|
static void
|
|
write_instr_name_(struct arcDisState *state,
|
|
const char *instrName,
|
|
int cond,
|
|
int condCodeIsPartOfName,
|
|
int flag,
|
|
int signExtend,
|
|
int addrWriteBack,
|
|
int directMem) {
|
|
if (!instrName) {
|
|
return;
|
|
}
|
|
strncpy(state->instrBuffer, instrName, sizeof(state->instrBuffer) - 1);
|
|
if (cond > 0) {
|
|
int condlim = 0; /* condition code limit*/
|
|
const char *cc = 0;
|
|
if (!condCodeIsPartOfName) {
|
|
strcat(state->instrBuffer, ".");
|
|
}
|
|
condlim = 18;
|
|
if (cond < condlim) {
|
|
cc = condName[cond];
|
|
} else {
|
|
cc = cond_code_name(state, cond);
|
|
}
|
|
if (!cc) {
|
|
cc = "???";
|
|
}
|
|
strcat(state->instrBuffer, cc);
|
|
}
|
|
if (flag) {
|
|
strcat(state->instrBuffer, ".f");
|
|
}
|
|
if (state->nullifyMode) {
|
|
if (strstr(state->instrBuffer, ".d") == NULL) {
|
|
strcat(state->instrBuffer, ".d");
|
|
}
|
|
}
|
|
if (signExtend) {
|
|
strcat(state->instrBuffer, ".x");
|
|
}
|
|
switch (addrWriteBack) {
|
|
case 1: strcat(state->instrBuffer, ".a"); break;
|
|
case 2: strcat(state->instrBuffer, ".ab"); break;
|
|
case 3: strcat(state->instrBuffer, ".as"); break;
|
|
}
|
|
if (directMem) {
|
|
strcat(state->instrBuffer, ".di");
|
|
}
|
|
}
|
|
|
|
#define write_instr_name() \
|
|
{ \
|
|
write_instr_name_(state, instrName, cond, condCodeIsPartOfName, flag, signExtend, addrWriteBack, directMem); \
|
|
formatString[0] = '\0'; \
|
|
}
|
|
|
|
enum {
|
|
op_BC = 0,
|
|
op_BLC = 1,
|
|
op_LD = 2,
|
|
op_ST = 3,
|
|
op_MAJOR_4 = 4,
|
|
/* START ARC LOCAL */
|
|
op_MAJOR_5 = 5,
|
|
op_MAJOR_6 = 6,
|
|
op_SIMD = 9,
|
|
op_LD_ADD = 12,
|
|
op_ADD_SUB_SHIFT = 13,
|
|
/* END ARC LOCAL */
|
|
op_ADD_MOV_CMP = 14,
|
|
op_S = 15,
|
|
op_LD_S = 16,
|
|
op_LDB_S = 17,
|
|
op_LDW_S = 18,
|
|
op_LDWX_S = 19,
|
|
op_ST_S = 20,
|
|
op_STB_S = 21,
|
|
op_STW_S = 22,
|
|
op_Su5 = 23,
|
|
op_SP = 24,
|
|
op_GP = 25,
|
|
op_Pcl = 26,
|
|
op_MOV_S = 27,
|
|
op_ADD_CMP = 28,
|
|
op_BR_S = 29,
|
|
op_B_S = 30,
|
|
op_BL_S = 31
|
|
};
|
|
|
|
extern disassemble_info tm_print_insn_info;
|
|
|
|
/*
|
|
* bfd_getm32 - To retrieve the upper 16-bits of the ARCtangent-A5
|
|
* basecase (32-bit) instruction
|
|
*/
|
|
static bfd_vma
|
|
bfd_getm32(data)
|
|
unsigned int data;
|
|
{
|
|
bfd_vma value = 0;
|
|
|
|
value = ((data & 0xff00) | (data & 0xff)) << 16;
|
|
value |= ((data & 0xff0000) | (data & 0xff000000)) >> 16;
|
|
return value;
|
|
}
|
|
|
|
/*
|
|
* bfd_getm32_ac - To retrieve the upper 8-bits of the ARCompact
|
|
* 16-bit instruction
|
|
*/
|
|
static bfd_vma
|
|
bfd_getm32_ac(data)
|
|
unsigned int data;
|
|
{
|
|
bfd_vma value = 0;
|
|
|
|
value = ((data & 0xff) << 8 | (data & 0xff00) >> 8);
|
|
return value;
|
|
}
|
|
|
|
/*
|
|
* sign_extend - Sign Extend the value
|
|
*
|
|
*/
|
|
static int
|
|
sign_extend(int value, int bits) {
|
|
if (BIT(value, (bits - 1))) {
|
|
value |= (0xffffffff << bits);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/* dsmOneArcInst - This module is used to identify the instruction
|
|
* and to decode them based on the ARCtangent-A5
|
|
* instruction set architecture.
|
|
* First, the major opcode is computed. Based on the
|
|
* major opcode and sub opcode, the instruction is
|
|
* identified. The appropriate decoding class is assigned
|
|
* based on the instruction.Further subopcode 2 is used in
|
|
* cases where decoding upto subopcode1 is not possible.
|
|
*
|
|
* The instruction is then decoded accordingly.
|
|
*/
|
|
static int
|
|
dsmOneArcInst(bfd_vma addr, struct arcDisState *state, disassemble_info *info) {
|
|
|
|
int subopcode, mul;
|
|
int condCodeIsPartOfName = 0;
|
|
int decodingClass;
|
|
const char *instrName;
|
|
int fieldAisReg = 1, fieldBisReg = 1, fieldCisReg = 1;
|
|
int fieldA = 0, fieldB = 0, fieldC = 0;
|
|
int flag = 0, cond = 0, is_shimm = 0, is_limm = 0;
|
|
int signExtend = 0, addrWriteBack = 0, directMem = 0;
|
|
int is_linked = 0;
|
|
int offset = 0;
|
|
int usesAuxReg = 0;
|
|
int usesSimdRegA = 0, usesSimdRegB = 0, usesSimdRegC = 0, simd_scale_u8 = -1;
|
|
int flags = !E_ARC_MACH_A4;
|
|
char formatString[60];
|
|
|
|
state->nullifyMode = BR_exec_when_no_jump;
|
|
state->isBranch = 0;
|
|
|
|
state->_mem_load = 0;
|
|
state->_ea_present = 0;
|
|
state->_load_len = 0;
|
|
state->ea_reg1 = no_reg;
|
|
state->ea_reg2 = no_reg;
|
|
state->_offset = 0;
|
|
|
|
state->sourceType = ARC_UNDEFINED;
|
|
|
|
/* ARCtangent-A5 basecase instruction and little-endian mode */
|
|
if ((info->endian == BFD_ENDIAN_LITTLE) && (state->instructionLen == 4)) {
|
|
state->words[0] = bfd_getm32(state->words[0]);
|
|
}
|
|
|
|
if (state->instructionLen == 4) {
|
|
if (!NEXT_WORD(0)) {
|
|
return 0;
|
|
}
|
|
/* Get the major opcode of the ARCtangent-A5 32-bit instruction. */
|
|
state->_opcode = OPCODE(state->words[0]);
|
|
} else {
|
|
/* ARCompact 16-bit instruction */
|
|
if (!NEXT_WORD_AC(0)) {
|
|
return 0;
|
|
}
|
|
/* Get the major opcode of the ARCompact 16-bit instruction. */
|
|
state->_opcode = OPCODE_AC(state->words[0]);
|
|
}
|
|
|
|
instrName = 0;
|
|
decodingClass = 0; /* default! */
|
|
mul = 0;
|
|
condCodeIsPartOfName = 0;
|
|
state->commNum = 0;
|
|
state->tcnt = 0;
|
|
state->acnt = 0;
|
|
state->flow = noflow;
|
|
|
|
/* Find the match for the opcode. Once the major opcode category is
|
|
* identified, get the subopcode to determine the exact instruction.
|
|
* Based on the instruction identified, select the decoding class.
|
|
* If condition code is part of the instruction name, then set the
|
|
* flag 'condCodeIsPartOfName'.
|
|
* For branch, jump instructions set 'isBranch' (state->isBranch).
|
|
*/
|
|
|
|
switch (state->_opcode) {
|
|
case op_BC:
|
|
/* Branch Conditionally */
|
|
instrName = "b";
|
|
decodingClass = 13;
|
|
condCodeIsPartOfName = 1;
|
|
state->isBranch = 1;
|
|
break;
|
|
|
|
case op_BLC:
|
|
/* Branch and Link, Compare and Branch */
|
|
decodingClass = 9;
|
|
state->isBranch = 1;
|
|
switch (BITS(state->words[0], 16, 16)) {
|
|
case 0:
|
|
if (!instrName) {
|
|
instrName = "bl";
|
|
}
|
|
decodingClass = 13;
|
|
condCodeIsPartOfName = 1;
|
|
break;
|
|
case 1:
|
|
switch (BITS(state->words[0], 0, 3)) {
|
|
case 0: instrName = "breq"; break;
|
|
case 1: instrName = "brne"; break;
|
|
case 2: instrName = "brlt"; break;
|
|
case 3: instrName = "brge"; break;
|
|
case 4: instrName = "brlo"; break;
|
|
case 5: instrName = "brhs"; break;
|
|
case 14: instrName = "bbit0"; break;
|
|
case 15: instrName = "bbit1"; break;
|
|
default:
|
|
instrName = "??? (0[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
instrName = "??? (0[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case op_LD:
|
|
/* Load register with offset [major opcode 2] */
|
|
decodingClass = 6;
|
|
switch (BITS(state->words[0], 7, 8)) {
|
|
case 0:
|
|
instrName = "ld";
|
|
state->_load_len = 4;
|
|
break;
|
|
case 1:
|
|
instrName = "ldb";
|
|
state->_load_len = 1;
|
|
break;
|
|
case 2:
|
|
instrName = "ldw";
|
|
state->_load_len = 2;
|
|
break;
|
|
default:
|
|
instrName = "??? (0[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case op_ST:
|
|
/* Store register with offset [major opcode 0x03] */
|
|
decodingClass = 7;
|
|
switch (BITS(state->words[0], 1, 2)) {
|
|
case 0: instrName = "st"; break;
|
|
case 1: instrName = "stb"; break;
|
|
case 2: instrName = "stw"; break;
|
|
default:
|
|
instrName = "??? (2[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case op_MAJOR_4:
|
|
/* ARC 32-bit basecase instructions with 3 Operands */
|
|
decodingClass = 0; /* Default for 3 operand instructions */
|
|
subopcode = BITS(state->words[0], 16, 21);
|
|
switch (subopcode) {
|
|
case 0: instrName = "add"; break;
|
|
case 1: instrName = "adc"; break;
|
|
case 2: instrName = "sub"; break;
|
|
case 3: instrName = "sbc"; break;
|
|
case 4: instrName = "and"; break;
|
|
case 5: instrName = "or"; break;
|
|
case 6: instrName = "bic"; break;
|
|
case 7: instrName = "xor"; break;
|
|
case 8: instrName = "max"; break;
|
|
case 9: instrName = "min"; break;
|
|
case 10: {
|
|
if (state->words[0] == 0x264a7000) {
|
|
instrName = "nop";
|
|
decodingClass = 26;
|
|
} else {
|
|
instrName = "mov";
|
|
decodingClass = 12;
|
|
}
|
|
break;
|
|
}
|
|
case 11:
|
|
instrName = "tst";
|
|
decodingClass = 2;
|
|
break;
|
|
case 12:
|
|
instrName = "cmp";
|
|
decodingClass = 2;
|
|
break;
|
|
case 13:
|
|
instrName = "rcmp";
|
|
decodingClass = 2;
|
|
break;
|
|
case 14: instrName = "rsub"; break;
|
|
case 15: instrName = "bset"; break;
|
|
case 16: instrName = "bclr"; break;
|
|
case 17:
|
|
instrName = "btst";
|
|
decodingClass = 2;
|
|
break;
|
|
case 18: instrName = "bxor"; break;
|
|
case 19: instrName = "bmsk"; break;
|
|
case 20: instrName = "add1"; break;
|
|
case 21: instrName = "add2"; break;
|
|
case 22: instrName = "add3"; break;
|
|
case 23: instrName = "sub1"; break;
|
|
case 24: instrName = "sub2"; break;
|
|
case 25: instrName = "sub3"; break;
|
|
case 30: instrName = "mpyw"; break;
|
|
case 31: instrName = "mpyuw"; break;
|
|
case 32:
|
|
case 33:
|
|
instrName = "j";
|
|
// fallthrough
|
|
case 34:
|
|
case 35:
|
|
if (!instrName) {
|
|
instrName = "jl";
|
|
}
|
|
decodingClass = 4;
|
|
condCodeIsPartOfName = 1;
|
|
state->isBranch = 1;
|
|
break;
|
|
case 40:
|
|
instrName = "lp";
|
|
decodingClass = 11;
|
|
condCodeIsPartOfName = 1;
|
|
state->isBranch = 1;
|
|
break;
|
|
case 41:
|
|
instrName = "flag";
|
|
decodingClass = 3;
|
|
break;
|
|
case 42:
|
|
instrName = "lr";
|
|
decodingClass = 10;
|
|
break;
|
|
case 43:
|
|
instrName = "sr";
|
|
decodingClass = 8;
|
|
break;
|
|
case 47:
|
|
decodingClass = 1;
|
|
switch (BITS(state->words[0], 0, 5)) /* Checking based on Subopcode2 */
|
|
{
|
|
case 0: instrName = "asl"; break;
|
|
case 1: instrName = "asr"; break;
|
|
case 2: instrName = "lsr"; break;
|
|
case 3: instrName = "ror"; break;
|
|
case 4: instrName = "rrc"; break;
|
|
case 5: instrName = "sexb"; break;
|
|
case 6: instrName = "sexw"; break;
|
|
case 7: instrName = "extb"; break;
|
|
case 8: instrName = "extw"; break;
|
|
case 9: instrName = "abs"; break;
|
|
case 10: instrName = "not"; break;
|
|
case 11: instrName = "rlc"; break;
|
|
case 12:
|
|
instrName = "ex";
|
|
|
|
decodingClass = 34;
|
|
break; // ramana adds
|
|
|
|
/* START ARC LOCAL */
|
|
case 16:
|
|
instrName = "llock";
|
|
decodingClass = 34;
|
|
break;
|
|
case 17:
|
|
instrName = "scond";
|
|
decodingClass = 34;
|
|
break;
|
|
/* END ARC LOCAL */
|
|
|
|
case 63:
|
|
decodingClass = 26;
|
|
switch (BITS(state->words[0], 24, 26)) {
|
|
case 1:
|
|
instrName = "sleep";
|
|
decodingClass = 32;
|
|
break;
|
|
case 2:
|
|
if ((info->mach) == ARC_MACH_ARC7) {
|
|
instrName = "trap0";
|
|
} else {
|
|
instrName = "swi";
|
|
}
|
|
break;
|
|
case 3:
|
|
|
|
if (BITS(state->words[0], 22, 23) == 1) {
|
|
instrName = "sync";
|
|
}
|
|
|
|
break;
|
|
case 4: instrName = "rtie"; break;
|
|
case 5: instrName = "brk"; break;
|
|
default:
|
|
|
|
instrName = "???";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!instrName) {
|
|
subopcode = BITS(state->words[0], 17, 21);
|
|
decodingClass = 5;
|
|
switch (subopcode) {
|
|
case 24:
|
|
instrName = "ld";
|
|
state->_load_len = 4;
|
|
break;
|
|
case 25:
|
|
instrName = "ldb";
|
|
state->_load_len = 1;
|
|
break;
|
|
case 26:
|
|
instrName = "ldw";
|
|
state->_load_len = 2;
|
|
break;
|
|
default:
|
|
instrName = "??? (0[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case op_MAJOR_5:
|
|
/* ARC 32-bit extension instructions */
|
|
decodingClass = 0; /* Default for Major opcode 5 ... */
|
|
subopcode = BITS(state->words[0], 16, 21);
|
|
switch (subopcode) {
|
|
case 0: instrName = "asl"; break;
|
|
case 1: instrName = "lsr"; break;
|
|
case 2: instrName = "asr"; break;
|
|
case 3: instrName = "ror"; break;
|
|
case 4:
|
|
instrName = "mul64";
|
|
mul = 1;
|
|
decodingClass = 2;
|
|
break;
|
|
case 5:
|
|
instrName = "mulu64";
|
|
mul = 1;
|
|
decodingClass = 2;
|
|
break;
|
|
|
|
/* ARC A700 */
|
|
case 6: instrName = "adds"; break;
|
|
|
|
case 7: instrName = "subs"; break;
|
|
case 8: instrName = "divaw"; break;
|
|
case 0xA: instrName = "asls"; break;
|
|
case 0xB: instrName = "asrs"; break;
|
|
case 0x28: instrName = "addsdw"; break;
|
|
case 0x29: instrName = "subsdw"; break;
|
|
|
|
case 47:
|
|
switch (BITS(state->words[0], 0, 5)) {
|
|
case 0:
|
|
instrName = "swap";
|
|
decodingClass = 1;
|
|
break;
|
|
case 1:
|
|
instrName = "norm";
|
|
decodingClass = 1;
|
|
break;
|
|
/* ARC A700 DSP Extensions */
|
|
case 2:
|
|
instrName = "sat16";
|
|
decodingClass = 1;
|
|
break;
|
|
case 3:
|
|
instrName = "rnd16";
|
|
decodingClass = 1;
|
|
break;
|
|
case 4:
|
|
instrName = "abssw";
|
|
decodingClass = 1;
|
|
break;
|
|
case 5:
|
|
instrName = "abss";
|
|
decodingClass = 1;
|
|
break;
|
|
case 6:
|
|
instrName = "negsw";
|
|
decodingClass = 1;
|
|
break;
|
|
case 7:
|
|
instrName = "negs";
|
|
decodingClass = 1;
|
|
break;
|
|
|
|
case 8:
|
|
instrName = "normw";
|
|
decodingClass = 1;
|
|
break;
|
|
|
|
/* START ARC LOCAL */
|
|
case 9:
|
|
instrName = "swape";
|
|
decodingClass = 1;
|
|
break;
|
|
/* END ARC LOCAL */
|
|
|
|
default:
|
|
instrName = "???";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
instrName = "??? (2[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
/* START ARC LOCAL */
|
|
case op_MAJOR_6:
|
|
decodingClass = 44; /* Default for Major opcode 6 ... */
|
|
subopcode = BITS(state->words[0], 0, 5);
|
|
switch (subopcode) {
|
|
case 26: /* 0x1a */ instrName = "rtsc"; break;
|
|
default:
|
|
instrName = "??? (2[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
/* END ARC LOCAL */
|
|
|
|
/* Aurora SIMD instruction support*/
|
|
case op_SIMD:
|
|
|
|
if (enable_simd) {
|
|
decodingClass = 42;
|
|
subopcode = BITS(state->words[0], 17, 23);
|
|
|
|
switch (subopcode) {
|
|
|
|
case 68:
|
|
instrName = "vld32";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 2;
|
|
break;
|
|
|
|
case 72:
|
|
instrName = "vld64";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 3;
|
|
break;
|
|
|
|
case 74:
|
|
instrName = "vld64w";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 3;
|
|
break;
|
|
|
|
case 70:
|
|
instrName = "vld32wl";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 2;
|
|
break;
|
|
|
|
case 66:
|
|
instrName = "vld32wh";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 2;
|
|
break;
|
|
|
|
case 76:
|
|
instrName = "vld128";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 4;
|
|
break;
|
|
|
|
case 78: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vld128r";
|
|
decodingClass = 38;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = usesSimdRegC = 0;
|
|
break;
|
|
default:
|
|
instrName = "SIMD";
|
|
state->flow = invalid_instr;
|
|
}
|
|
} break;
|
|
case 71:
|
|
instrName = "vst16_0";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 1;
|
|
break;
|
|
|
|
case 81:
|
|
instrName = "vst16_1";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 1;
|
|
break;
|
|
|
|
case 67:
|
|
instrName = "vst16_2";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 1;
|
|
break;
|
|
|
|
case 75:
|
|
instrName = "vst16_3";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 1;
|
|
break;
|
|
|
|
case 83:
|
|
instrName = "vst16_4";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 1;
|
|
break;
|
|
|
|
case 89:
|
|
instrName = "vst16_5";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 1;
|
|
break;
|
|
|
|
case 91:
|
|
instrName = "vst16_6";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 1;
|
|
break;
|
|
|
|
case 93:
|
|
instrName = "vst16_7";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 1;
|
|
break;
|
|
|
|
case 69:
|
|
instrName = "vst32_0";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 2;
|
|
break;
|
|
|
|
case 82:
|
|
instrName = "vst32_2";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 2;
|
|
break;
|
|
|
|
case 86:
|
|
instrName = "vst32_4";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 2;
|
|
break;
|
|
|
|
case 88:
|
|
instrName = "vst32_6";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 2;
|
|
break;
|
|
|
|
case 73:
|
|
instrName = "vst64";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 3;
|
|
break;
|
|
|
|
case 77:
|
|
instrName = "vst128";
|
|
decodingClass = 37;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = 2;
|
|
usesSimdRegC = 0;
|
|
simd_scale_u8 = 4;
|
|
break;
|
|
|
|
case 79: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vst128r";
|
|
decodingClass = 38;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = usesSimdRegC = 0;
|
|
break;
|
|
|
|
default:
|
|
instrName = "SIMD";
|
|
state->flow = invalid_instr;
|
|
}
|
|
|
|
} break;
|
|
case 80:
|
|
instrName = "vmvw";
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
decodingClass = 39;
|
|
break;
|
|
|
|
case 84:
|
|
instrName = "vmvzw";
|
|
decodingClass = 39;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
|
|
case 90:
|
|
instrName = "vmovw";
|
|
decodingClass = 39;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = usesSimdRegC = 0;
|
|
break;
|
|
|
|
case 94:
|
|
instrName = "vmovzw";
|
|
decodingClass = 39;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = usesSimdRegC = 0;
|
|
break;
|
|
|
|
case 85:
|
|
instrName = "vmvaw";
|
|
decodingClass = 39;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
|
|
case 95:
|
|
instrName = "vmovaw";
|
|
decodingClass = 39;
|
|
usesSimdRegA = 1;
|
|
usesSimdRegB = usesSimdRegC = 0;
|
|
break;
|
|
|
|
case 10: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vaddw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 1:
|
|
instrName = "vaddaw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 2:
|
|
instrName = "vbaddw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 11: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vsubw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 1:
|
|
instrName = "vsubaw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 2:
|
|
instrName = "vbsubw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
}
|
|
} break;
|
|
|
|
case 12: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vmulw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 1:
|
|
instrName = "vmulaw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 2:
|
|
instrName = "vbmulw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
|
|
case 3:
|
|
instrName = "vbmulaw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
}
|
|
} break;
|
|
|
|
case 13: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vmulfw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 1:
|
|
instrName = "vmulfaw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 2:
|
|
instrName = "vbmulfw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
}
|
|
} break;
|
|
|
|
case 15: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vsummw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
case 2:
|
|
instrName = "vbrsubw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
}
|
|
} break;
|
|
|
|
case 23: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vmr7w";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 1:
|
|
instrName = "vmr7aw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 2:
|
|
switch (BITS(state->words[0], 0, 5)) {
|
|
case 0:
|
|
instrName = "vaddsuw";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = 1;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 1:
|
|
instrName = "vabsw";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = 1;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 2:
|
|
instrName = "vsignw";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = 1;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 3:
|
|
instrName = "vupbw";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = 1;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 4:
|
|
instrName = "vexch1";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = 1;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 5:
|
|
instrName = "vexch2";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = 1;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 6:
|
|
instrName = "vexch4";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = 1;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 7:
|
|
instrName = "vupsbw";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = 1;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 8:
|
|
instrName = "vdirun";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 9:
|
|
instrName = "vdorun";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 10:
|
|
instrName = "vdiwr";
|
|
decodingClass = 40;
|
|
usesSimdRegB = 3;
|
|
usesSimdRegA = usesSimdRegC = 0;
|
|
break;
|
|
|
|
case 11:
|
|
instrName = "vdowr";
|
|
decodingClass = 40;
|
|
usesSimdRegB = 3;
|
|
usesSimdRegA = usesSimdRegC = 0;
|
|
break;
|
|
|
|
case 12:
|
|
instrName = "vdird";
|
|
decodingClass = 40;
|
|
usesSimdRegB = 1;
|
|
usesSimdRegC = 3;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 13:
|
|
instrName = "vdord";
|
|
decodingClass = 40;
|
|
usesSimdRegB = 1;
|
|
usesSimdRegC = 3;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 63: {
|
|
switch (BITS(state->words[0], 24, 25)) {
|
|
case 0:
|
|
instrName = "vrec";
|
|
decodingClass = 43;
|
|
usesSimdRegC = 0;
|
|
usesSimdRegB = usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 1:
|
|
instrName = "vrecrun";
|
|
decodingClass = 43;
|
|
usesSimdRegC = 0;
|
|
usesSimdRegA = usesSimdRegB = 0;
|
|
break;
|
|
|
|
case 2:
|
|
instrName = "vrun";
|
|
decodingClass = 43;
|
|
usesSimdRegC = 0;
|
|
usesSimdRegB = usesSimdRegA = 0;
|
|
break;
|
|
|
|
case 3:
|
|
instrName = "vendrec";
|
|
decodingClass = 43;
|
|
usesSimdRegC = 0;
|
|
usesSimdRegB = usesSimdRegA = 0;
|
|
break;
|
|
}
|
|
} break;
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
switch (BITS(state->words[0], 0, 2)) {
|
|
case 1:
|
|
instrName = "vabsaw";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = 1;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
case 3:
|
|
instrName = "vupbaw";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = 1;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
case 7:
|
|
instrName = "vupsbaw";
|
|
decodingClass = 40;
|
|
usesSimdRegC = usesSimdRegB = 1;
|
|
usesSimdRegA = 0;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
} break;
|
|
|
|
case 16:
|
|
instrName = "vasrw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 2;
|
|
break;
|
|
|
|
case 48: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vasrwi";
|
|
decodingClass = 41;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
case 2:
|
|
instrName = "vasrrwi";
|
|
decodingClass = 41;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
}
|
|
} break;
|
|
|
|
case 59:
|
|
instrName = "vasrsrwi";
|
|
decodingClass = 41;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
|
|
case 18: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vmaxw";
|
|
usesSimdRegC = 1;
|
|
break;
|
|
case 1:
|
|
instrName = "vmaxaw";
|
|
usesSimdRegC = 1;
|
|
break;
|
|
case 2:
|
|
instrName = "vbmaxw";
|
|
usesSimdRegC = 0;
|
|
break;
|
|
}
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
break;
|
|
}
|
|
|
|
case 19: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vminw";
|
|
usesSimdRegC = 1;
|
|
break;
|
|
case 1:
|
|
instrName = "vminaw";
|
|
usesSimdRegC = 0;
|
|
break;
|
|
case 2:
|
|
instrName = "vbminw";
|
|
usesSimdRegC = 0;
|
|
break;
|
|
}
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
break;
|
|
}
|
|
|
|
case 14: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vdifw";
|
|
break;
|
|
case 1:
|
|
instrName = "vdifaw";
|
|
break;
|
|
case 2:
|
|
instrName = "vmrb";
|
|
break;
|
|
}
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
}
|
|
|
|
case 24: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vand";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
case 1:
|
|
instrName = "vandaw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 25: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vor";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 26: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vxor";
|
|
break;
|
|
case 1:
|
|
instrName = "vxoraw";
|
|
break;
|
|
}
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
}
|
|
|
|
case 27: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vbic";
|
|
break;
|
|
case 1:
|
|
instrName = "vbicaw";
|
|
break;
|
|
}
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
}
|
|
|
|
case 4: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vavb";
|
|
break;
|
|
case 2:
|
|
instrName = "vavrb";
|
|
break;
|
|
}
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
}
|
|
|
|
case 28:
|
|
instrName = "veqw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 29:
|
|
instrName = "vnew";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 30:
|
|
instrName = "vlew";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 31:
|
|
instrName = "vltw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
|
|
case 49: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vasrpwbi";
|
|
decodingClass = 41;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
case 2:
|
|
instrName = "vasrrpwbi";
|
|
decodingClass = 41;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 5: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vsr8";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 2;
|
|
break;
|
|
|
|
case 1:
|
|
instrName = "vsr8aw";
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 2;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 37: {
|
|
short sub_subopcode = BITS(state->words[0], 15, 16);
|
|
switch (sub_subopcode) {
|
|
case 0:
|
|
instrName = "vsr8i";
|
|
decodingClass = 41;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
|
|
case 1:
|
|
instrName = "vsr8awi";
|
|
decodingClass = 41;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 20:
|
|
case 21:
|
|
case 22: {
|
|
short subopcode2 = BITS(state->words[0], 15, 18);
|
|
switch (subopcode2) {
|
|
case 0:
|
|
instrName = "vmr1w";
|
|
break;
|
|
|
|
case 2:
|
|
instrName = "vmr2w";
|
|
break;
|
|
|
|
case 4:
|
|
instrName = "vmr3w";
|
|
break;
|
|
|
|
case 6:
|
|
instrName = "vmr4w";
|
|
break;
|
|
|
|
case 8:
|
|
instrName = "vmr5w";
|
|
break;
|
|
|
|
case 10:
|
|
instrName = "vmr6w";
|
|
break;
|
|
|
|
case 1:
|
|
instrName = "vmr1aw";
|
|
break;
|
|
|
|
case 3:
|
|
instrName = "vmr2aw";
|
|
break;
|
|
|
|
case 5:
|
|
instrName = "vmr3aw";
|
|
break;
|
|
|
|
case 7:
|
|
instrName = "vmr4aw";
|
|
break;
|
|
|
|
case 9:
|
|
instrName = "vmr5aw";
|
|
break;
|
|
|
|
case 11:
|
|
instrName = "vmr6aw";
|
|
break;
|
|
}
|
|
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
}
|
|
|
|
case 7:
|
|
case 6: {
|
|
switch (BITS(state->words[0], 16, 19)) {
|
|
case 15:
|
|
instrName = "vh264ft";
|
|
break;
|
|
case 14:
|
|
instrName = "vh264f";
|
|
break;
|
|
case 13:
|
|
instrName = "vvc1ft";
|
|
break;
|
|
case 12:
|
|
instrName = "vvc1f";
|
|
break;
|
|
}
|
|
decodingClass = 42;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 1;
|
|
break;
|
|
}
|
|
|
|
case 92:
|
|
instrName = "vd6tapf";
|
|
decodingClass = 39;
|
|
usesSimdRegA = usesSimdRegB = 1;
|
|
usesSimdRegC = 0;
|
|
break;
|
|
|
|
case 55:
|
|
instrName = "vinti";
|
|
decodingClass = 43;
|
|
usesSimdRegA = usesSimdRegB = usesSimdRegC = 0;
|
|
break;
|
|
|
|
default:
|
|
instrName = "SIMD";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
} else {
|
|
instrName = "???_SIMD";
|
|
state->flow = invalid_instr;
|
|
}
|
|
break;
|
|
|
|
case op_LD_ADD:
|
|
/* Load/Add resister-register */
|
|
decodingClass = 15; /* default for Major opcode 12 ... */
|
|
switch (BITS(state->words[0], 3, 4)) {
|
|
case 0: instrName = "ld_s"; break;
|
|
case 1: instrName = "ldb_s"; break;
|
|
case 2: instrName = "ldw_s"; break;
|
|
case 3: instrName = "add_s"; break;
|
|
default:
|
|
instrName = "??? (2[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case op_ADD_SUB_SHIFT:
|
|
/* Add/sub/shift immediate */
|
|
decodingClass = 16; /* default for Major opcode 13 ... */
|
|
switch (BITS(state->words[0], 3, 4)) {
|
|
case 0: instrName = "add_s"; break;
|
|
case 1: instrName = "sub_s"; break;
|
|
case 2: instrName = "asl_s"; break;
|
|
case 3: instrName = "asr_s"; break;
|
|
default:
|
|
instrName = "??? (2[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case op_ADD_MOV_CMP:
|
|
/* One Dest/Source can be any of r0 - r63 */
|
|
decodingClass = 17; /* default for Major opcode 14 ... */
|
|
switch (BITS(state->words[0], 3, 4)) {
|
|
case 0: instrName = "add_s"; break;
|
|
case 1:
|
|
case 3:
|
|
instrName = "mov_s";
|
|
decodingClass = 18;
|
|
break;
|
|
case 2:
|
|
instrName = "cmp_s";
|
|
decodingClass = 18;
|
|
break;
|
|
default:
|
|
instrName = "??? (2[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case op_S:
|
|
/* ARCompact 16-bit instructions, General ops/ single ops */
|
|
decodingClass = 22; /* default for Major opcode 15 ... */
|
|
switch (BITS(state->words[0], 0, 4)) {
|
|
case 0:
|
|
decodingClass = 27;
|
|
switch (BITS(state->words[0], 5, 7)) {
|
|
case 0:
|
|
instrName = "j_s";
|
|
// fallthrough
|
|
case 2:
|
|
if (!instrName) {
|
|
instrName = "jl_s";
|
|
}
|
|
state->isBranch = 1;
|
|
state->nullifyMode = BR_exec_when_no_jump;
|
|
break;
|
|
case 1:
|
|
if (!instrName) {
|
|
instrName = "j_s.d";
|
|
}
|
|
// fallthrough
|
|
case 3:
|
|
if (!instrName) {
|
|
instrName = "jl_s.d";
|
|
}
|
|
state->isBranch = 1;
|
|
state->nullifyMode = BR_exec_always;
|
|
break;
|
|
case 6:
|
|
instrName = "sub_s.ne";
|
|
decodingClass = 35;
|
|
break;
|
|
case 7:
|
|
decodingClass = 26;
|
|
switch (BITS(state->words[0], 8, 10)) {
|
|
case 0:
|
|
instrName = "nop_s";
|
|
break;
|
|
|
|
/* Unimplemented instruction reserved in ARC700 */
|
|
case 1: instrName = "unimp_s"; break;
|
|
|
|
case 4:
|
|
instrName = "jeq_s [blink]";
|
|
// fallthrough
|
|
case 5:
|
|
if (!instrName) {
|
|
instrName = "jne_s [blink]";
|
|
}
|
|
// fallthrough
|
|
case 6:
|
|
if (!instrName) {
|
|
instrName = "j_s [blink]";
|
|
}
|
|
state->isBranch = 1;
|
|
state->nullifyMode = BR_exec_when_no_jump;
|
|
break;
|
|
case 7:
|
|
if (!instrName) {
|
|
instrName = "j_s.d [blink]";
|
|
}
|
|
state->isBranch = 1;
|
|
state->nullifyMode = BR_exec_always;
|
|
break;
|
|
default:
|
|
instrName = "??? (2[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
instrName = "??? (2[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
case 2: instrName = "sub_s"; break;
|
|
case 4: instrName = "and_s"; break;
|
|
case 5: instrName = "or_s"; break;
|
|
case 6: instrName = "bic_s"; break;
|
|
case 7: instrName = "xor_s"; break;
|
|
case 11:
|
|
instrName = "tst_s";
|
|
decodingClass = 14;
|
|
break;
|
|
case 12:
|
|
instrName = "mul64_s";
|
|
mul = 1;
|
|
decodingClass = 14;
|
|
break;
|
|
case 13:
|
|
instrName = "sexb_s";
|
|
decodingClass = 14;
|
|
break;
|
|
case 14:
|
|
instrName = "sexw_s";
|
|
decodingClass = 14;
|
|
break;
|
|
case 15:
|
|
instrName = "extb_s";
|
|
decodingClass = 14;
|
|
break;
|
|
case 16:
|
|
instrName = "extw_s";
|
|
decodingClass = 14;
|
|
break;
|
|
case 17:
|
|
instrName = "abs_s";
|
|
decodingClass = 14;
|
|
break;
|
|
case 18:
|
|
instrName = "not_s";
|
|
decodingClass = 14;
|
|
break;
|
|
case 19:
|
|
instrName = "neg_s";
|
|
decodingClass = 14;
|
|
break;
|
|
case 20: instrName = "add1_s"; break;
|
|
case 21: instrName = "add2_s"; break;
|
|
case 22: instrName = "add3_s"; break;
|
|
case 24: instrName = "asl_s"; break;
|
|
case 25: instrName = "lsr_s"; break;
|
|
case 26: instrName = "asr_s"; break;
|
|
case 27:
|
|
instrName = "asl_s";
|
|
decodingClass = 14;
|
|
break;
|
|
case 28:
|
|
instrName = "asr_s";
|
|
decodingClass = 14;
|
|
break;
|
|
case 29:
|
|
instrName = "lsr_s";
|
|
decodingClass = 14;
|
|
break;
|
|
case 30:
|
|
instrName = "trap_s";
|
|
decodingClass = 33;
|
|
break;
|
|
case 31:
|
|
instrName = "brk_s";
|
|
decodingClass = 26;
|
|
break;
|
|
|
|
default:
|
|
instrName = "??? (2[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case op_LD_S:
|
|
/* ARCompact 16-bit Load with offset, Major Opcode 0x10 */
|
|
instrName = "ld_s";
|
|
decodingClass = 28;
|
|
break;
|
|
|
|
case op_LDB_S:
|
|
/* ARCompact 16-bit Load with offset, Major Opcode 0x11 */
|
|
instrName = "ldb_s";
|
|
decodingClass = 28;
|
|
break;
|
|
|
|
case op_LDW_S:
|
|
/* ARCompact 16-bit Load with offset, Major Opcode 0x12 */
|
|
instrName = "ldw_s";
|
|
decodingClass = 28;
|
|
break;
|
|
|
|
case op_LDWX_S:
|
|
/* ARCompact 16-bit Load with offset, Major Opcode 0x13 */
|
|
instrName = "ldw_s.x";
|
|
decodingClass = 28;
|
|
break;
|
|
|
|
case op_ST_S:
|
|
/* ARCompact 16-bit Store with offset, Major Opcode 0x14 */
|
|
instrName = "st_s";
|
|
decodingClass = 28;
|
|
break;
|
|
|
|
case op_STB_S:
|
|
/* ARCompact 16-bit Store with offset, Major Opcode 0x15 */
|
|
instrName = "stb_s";
|
|
decodingClass = 28;
|
|
break;
|
|
|
|
case op_STW_S:
|
|
/* ARCompact 16-bit Store with offset, Major Opcode 0x16 */
|
|
instrName = "stw_s";
|
|
decodingClass = 28;
|
|
break;
|
|
|
|
case op_Su5:
|
|
/* ARCompact 16-bit involving unsigned 5-bit immediate operand */
|
|
decodingClass = 23; /* default for major opcode 0x17 ... */
|
|
switch (BITS(state->words[0], 5, 7)) {
|
|
case 0: instrName = "asl_s"; break;
|
|
case 1: instrName = "lsr_s"; break;
|
|
case 2: instrName = "asr_s"; break;
|
|
case 3: instrName = "sub_s"; break;
|
|
case 4: instrName = "bset_s"; break;
|
|
case 5: instrName = "bclr_s"; break;
|
|
case 6: instrName = "bmsk_s"; break;
|
|
case 7:
|
|
instrName = "btst_s";
|
|
decodingClass = 21;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case op_SP:
|
|
/* ARCompact 16-bit Stack pointer-based instructions */
|
|
decodingClass = 19; /* default for Stack pointer-based insns ... */
|
|
switch (BITS(state->words[0], 5, 7)) {
|
|
case 0: instrName = "ld_s"; break;
|
|
case 1: instrName = "ldb_s"; break;
|
|
case 2: instrName = "st_s"; break;
|
|
case 3: instrName = "stb_s"; break;
|
|
case 4: instrName = "add_s"; break;
|
|
case 5:
|
|
if (!BITS(state->words[0], 8, 8)) {
|
|
instrName = "add_s";
|
|
} else {
|
|
instrName = "sub_s";
|
|
}
|
|
break;
|
|
case 6:
|
|
instrName = "pop_s";
|
|
decodingClass = 31;
|
|
break;
|
|
case 7:
|
|
instrName = "push_s";
|
|
decodingClass = 31;
|
|
break;
|
|
default:
|
|
instrName = "??? (2[3])";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case op_GP:
|
|
/* ARCompact 16-bit Gp-based ld/add (data aligned offset) */
|
|
decodingClass = 20; /* default for gp-relative insns ... */
|
|
switch (BITS(state->words[0], 9, 10)) {
|
|
case 0: instrName = "ld_s"; break;
|
|
case 1: instrName = "ldb_s"; break;
|
|
case 2: instrName = "ldw_s"; break;
|
|
case 3: instrName = "add_s"; break;
|
|
}
|
|
break;
|
|
|
|
case op_Pcl:
|
|
/* ARCompact 16-bit Pcl-based ld (32-bit aligned offset) */
|
|
instrName = "ld_s";
|
|
decodingClass = 29;
|
|
break;
|
|
|
|
case op_MOV_S:
|
|
/* ARCompact 16-bit Move immediate */
|
|
instrName = "mov_s";
|
|
decodingClass = 30;
|
|
break;
|
|
|
|
case op_ADD_CMP:
|
|
/* ARCompact 16-bit Add/compare immediate */
|
|
decodingClass = 21; /* default for major opcode 0x1c ... */
|
|
if (BIT(state->words[0], 7)) {
|
|
instrName = "cmp_s";
|
|
} else {
|
|
instrName = "add_s";
|
|
}
|
|
break;
|
|
|
|
case op_BR_S:
|
|
/* ARCompact 16-bit Branch conditionally on reg z/nz */
|
|
decodingClass = 25; /* Default for BR_S instruction ... */
|
|
if (BIT(state->words[0], 7)) {
|
|
instrName = "brne_s";
|
|
} else {
|
|
instrName = "breq_s";
|
|
}
|
|
state->isBranch = 1;
|
|
break;
|
|
|
|
case op_B_S:
|
|
/* ARCompact 16-bit Branch conditionally */
|
|
decodingClass = 24; /* Default for B_S instruction ... */
|
|
state->isBranch = 1;
|
|
switch (BITS(state->words[0], 9, 10)) {
|
|
case 0: instrName = "b_s"; break;
|
|
case 1: instrName = "beq_s"; break;
|
|
case 2: instrName = "bne_s"; break;
|
|
case 3:
|
|
switch (BITS(state->words[0], 6, 8)) {
|
|
case 0: instrName = "bgt_s"; break;
|
|
case 1: instrName = "bge_s"; break;
|
|
case 2: instrName = "blt_s"; break;
|
|
case 3: instrName = "ble_s"; break;
|
|
case 4: instrName = "bhi_s"; break;
|
|
case 5: instrName = "bhs_s"; break;
|
|
case 6: instrName = "blo_s"; break;
|
|
case 7: instrName = "bls_s"; break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case op_BL_S:
|
|
/* ARCompact 16-bit Branch and link unconditionally */
|
|
decodingClass = 24; /* Default for B_S instruction ... */
|
|
instrName = "bl_s";
|
|
state->isBranch = 1;
|
|
break;
|
|
|
|
default:
|
|
|
|
instrName = "???";
|
|
state->flow = invalid_instr;
|
|
break;
|
|
}
|
|
|
|
/* Maybe we should be checking for extension instructions over here
|
|
* instead of all over this crazy switch case. */
|
|
if (state->flow == invalid_instr) {
|
|
if (!((state->_opcode == op_SIMD) && enable_simd)) {
|
|
instrName = instruction_name(state, state->_opcode,
|
|
state->words[0],
|
|
&flags);
|
|
}
|
|
|
|
if (state->instructionLen == 2) {
|
|
switch (flags) {
|
|
case AC_SYNTAX_3OP:
|
|
decodingClass = 22;
|
|
break;
|
|
case AC_SYNTAX_2OP:
|
|
decodingClass = 14;
|
|
break;
|
|
case AC_SYNTAX_1OP:
|
|
decodingClass = 36;
|
|
break;
|
|
case AC_SYNTAX_NOP:
|
|
decodingClass = 26;
|
|
break;
|
|
default:
|
|
mwerror(state, "Invalid syntax class\n");
|
|
}
|
|
} else {
|
|
/* Must do the above for this one too */
|
|
switch (flags) {
|
|
case AC_SYNTAX_3OP:
|
|
decodingClass = 0;
|
|
break;
|
|
case AC_SYNTAX_2OP:
|
|
decodingClass = 1;
|
|
break;
|
|
case AC_SYNTAX_1OP:
|
|
decodingClass = 32;
|
|
break;
|
|
case AC_SYNTAX_NOP:
|
|
break;
|
|
case AC_SYNTAX_SIMD:
|
|
break;
|
|
default:
|
|
mwerror(state, "Invalid syntax class\n");
|
|
}
|
|
}
|
|
|
|
if (!instrName) {
|
|
instrName = "???";
|
|
state->flow = invalid_instr;
|
|
}
|
|
}
|
|
|
|
fieldAisReg = fieldBisReg = fieldCisReg = 1; /* assume regs for now */
|
|
flag = cond = is_shimm = is_limm = 0;
|
|
state->nullifyMode = BR_exec_when_no_jump; /* 0 */
|
|
signExtend = addrWriteBack = directMem = 0;
|
|
usesAuxReg = 0;
|
|
|
|
/* The following module decodes the instruction */
|
|
switch (decodingClass) {
|
|
case 0:
|
|
|
|
/* For ARCtangent 32-bit instructions with 3 operands */
|
|
|
|
subopcode = BITS(state->words[0], 22, 23);
|
|
switch (subopcode) {
|
|
case 0:
|
|
|
|
/* Either fieldB or fieldC or both can be a limm value;
|
|
* fieldA can be 0;
|
|
*/
|
|
|
|
CHECK_FIELD_C();
|
|
if (!is_limm) {
|
|
/* If fieldC is not a limm, then fieldB may be a limm value */
|
|
CHECK_FIELD_B();
|
|
} else {
|
|
FIELD_B();
|
|
if (!fieldBisReg) {
|
|
fieldB = fieldC;
|
|
}
|
|
}
|
|
CHECK_FIELD_A();
|
|
CHECK_FLAG();
|
|
break;
|
|
|
|
case 1:
|
|
|
|
/* fieldB may ba a limm value
|
|
* fieldC is a shimm (unsigned 6-bit immediate)
|
|
* fieldA can be 0
|
|
*/
|
|
|
|
CHECK_FIELD_B();
|
|
FIELD_C();
|
|
fieldCisReg = 0;
|
|
/* Say ea is not present, so only one of us will do the
|
|
name lookup. */
|
|
state->_offset += fieldB, state->_ea_present = 0;
|
|
CHECK_FIELD_A();
|
|
CHECK_FLAG();
|
|
break;
|
|
|
|
case 2:
|
|
|
|
/* fieldB may ba a limm value
|
|
* fieldC is a shimm (signed 12-bit immediate)
|
|
* fieldA can be 0
|
|
*/
|
|
|
|
fieldCisReg = 0;
|
|
fieldC = FIELDS(state->words[0]);
|
|
CHECK_FIELD_B();
|
|
/* Say ea is not present, so only one of us will do the
|
|
name lookup. */
|
|
state->_offset += fieldB, state->_ea_present = 0;
|
|
if (is_limm) {
|
|
fieldAisReg = fieldA = 0;
|
|
} else {
|
|
fieldA = fieldB;
|
|
}
|
|
CHECK_FLAG();
|
|
break;
|
|
|
|
case 3:
|
|
|
|
/* fieldB may ba a limm value
|
|
* fieldC may be a limm or a shimm (unsigned 6-bit immediate)
|
|
* fieldA can be 0
|
|
* Conditional instructions
|
|
*/
|
|
|
|
CHECK_FIELD_B();
|
|
/* fieldC is a shimm (unsigned 6-bit immediate) */
|
|
if (is_limm) {
|
|
fieldAisReg = fieldA = 0;
|
|
FIELD_C();
|
|
if (BIT(state->words[0], 5)) {
|
|
fieldCisReg = 0;
|
|
} else if (fieldC == 62) {
|
|
fieldCisReg = 0;
|
|
fieldC = fieldB;
|
|
}
|
|
} else {
|
|
fieldA = fieldB;
|
|
if (BIT(state->words[0], 5)) {
|
|
FIELD_C();
|
|
fieldCisReg = 0;
|
|
} else {
|
|
CHECK_FIELD_C();
|
|
}
|
|
}
|
|
CHECK_FLAG_COND();
|
|
break;
|
|
}
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x(A);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldA, fieldB, fieldC);
|
|
break;
|
|
|
|
case 1:
|
|
|
|
/* For ARCtangent 32-bit instructions with 2 operands */
|
|
|
|
/* field C is either a register or limm (different!) */
|
|
CHECK_FIELD_C();
|
|
FIELD_B();
|
|
CHECK_FLAG();
|
|
|
|
if (BITS(state->words[0], 22, 23) == 1) {
|
|
fieldCisReg = 0;
|
|
}
|
|
if (fieldCisReg) {
|
|
state->ea_reg1 = fieldC;
|
|
/* field C is either a shimm (same as fieldC) or limm (different!) */
|
|
/* Say ea is not present, so only one of us will do the name lookup. */
|
|
} else {
|
|
state->_offset += fieldB, state->_ea_present = 0;
|
|
}
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC);
|
|
break;
|
|
|
|
case 2:
|
|
|
|
/* For BTST, CMP, MUL64, MULU64 instruction */
|
|
|
|
/* field C is either a register or limm (different!) */
|
|
subopcode = BITS(state->words[0], 22, 23);
|
|
if (subopcode == 0 || ((subopcode == 3) && (!BIT(state->words[0], 5)))) {
|
|
CHECK_FIELD_C();
|
|
if (is_limm) {
|
|
FIELD_B();
|
|
if (!fieldBisReg) {
|
|
fieldB = fieldC;
|
|
}
|
|
} else {
|
|
CHECK_FIELD_B();
|
|
}
|
|
} else if (subopcode == 1 || ((subopcode == 3) && (BIT(state->words[0], 5)))) {
|
|
FIELD_C();
|
|
fieldCisReg = 0;
|
|
CHECK_FIELD_B();
|
|
} else if (subopcode == 2) {
|
|
FIELD_B();
|
|
fieldC = FIELDS(state->words[0]);
|
|
fieldCisReg = 0;
|
|
}
|
|
if (subopcode == 3)
|
|
CHECK_COND();
|
|
|
|
if (fieldCisReg) {
|
|
state->ea_reg1 = fieldC;
|
|
/* field C is either a shimm (same as fieldC) or limm (different!) */
|
|
/* Say ea is not present, so only one of us will do the name lookup. */
|
|
} else {
|
|
state->_offset += fieldB, state->_ea_present = 0;
|
|
}
|
|
|
|
write_instr_name();
|
|
if (mul) {
|
|
/* For Multiply instructions, the first operand is 0 */
|
|
WRITE_FORMAT_x(A);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, 0, fieldB, fieldC);
|
|
} else {
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC);
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
/*
|
|
* For FLAG instruction
|
|
*/
|
|
subopcode = BITS(state->words[0], 22, 23);
|
|
|
|
if (subopcode == 0 || ((subopcode == 3) && (!BIT(state->words[0], 5)))) {
|
|
CHECK_FIELD_C();
|
|
} else if (subopcode == 1 || ((subopcode == 3) && (BIT(state->words[0], 5)))) {
|
|
FIELD_C();
|
|
fieldCisReg = 0;
|
|
} else if (subopcode == 2) {
|
|
fieldC = FIELDS(state->words[0]);
|
|
fieldCisReg = 0;
|
|
}
|
|
if (subopcode == 3)
|
|
CHECK_COND();
|
|
flag = 0; /* this is the FLAG instruction -- it's redundant */
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x(C);
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldC);
|
|
break;
|
|
|
|
case 4:
|
|
/*
|
|
* For op_JC -- jump to address specified.
|
|
* Also covers jump and link--bit 9 of the instr. word
|
|
* selects whether linked, thus "is_linked" is set above.
|
|
*/
|
|
subopcode = BITS(state->words[0], 22, 23);
|
|
if (subopcode == 0 || ((subopcode == 3) && (!BIT(state->words[0], 5)))) {
|
|
CHECK_FIELD_C();
|
|
/* ilink registers */
|
|
if (fieldC == 29 || fieldC == 31)
|
|
CHECK_FLAG();
|
|
} else if (subopcode == 1 || ((subopcode == 3) && (BIT(state->words[0], 5)))) {
|
|
FIELD_C();
|
|
fieldCisReg = 0;
|
|
} else if (subopcode == 2) {
|
|
fieldC = FIELDS(state->words[0]);
|
|
fieldCisReg = 0;
|
|
}
|
|
|
|
if (subopcode == 3)
|
|
CHECK_COND();
|
|
|
|
state->nullifyMode = BITS(state->words[0], 16, 16);
|
|
|
|
if (!fieldCisReg) {
|
|
state->flow = is_linked ? direct_call : direct_jump;
|
|
add_target(fieldC);
|
|
} else {
|
|
state->flow = is_linked ? indirect_call : indirect_jump;
|
|
/*
|
|
* We should also treat this as indirect call if NOT linked
|
|
* but the preceding instruction was a "lr blink,[status]"
|
|
* and we have a delay slot with "add blink,blink,2".
|
|
* For now we can't detect such.
|
|
*/
|
|
state->register_for_indirect_jump = fieldC;
|
|
}
|
|
|
|
write_instr_name();
|
|
strcat(formatString,
|
|
IS_REG(C) ? "[%r]" : "%s"); /* address/label name */
|
|
|
|
if (IS_REG(C)) {
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldC);
|
|
} else {
|
|
my_sprintf(state, state->operandBuffer, formatString,
|
|
post_address(state, fieldC));
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
/* LD instruction. B and C can be regs, or one or both can be limm. */
|
|
|
|
CHECK_FIELD_A();
|
|
CHECK_FIELD_B();
|
|
|
|
if (FIELDA(state->words[0]) == 62) {
|
|
instrName = "prefetch";
|
|
}
|
|
|
|
if (is_limm) {
|
|
FIELD_C();
|
|
if (!fieldCisReg) {
|
|
fieldC = fieldB;
|
|
}
|
|
} else {
|
|
CHECK_FIELD_C();
|
|
}
|
|
state->_offset = 0;
|
|
state->_ea_present = 1;
|
|
if (fieldBisReg) {
|
|
state->ea_reg1 = fieldB;
|
|
} else {
|
|
state->_offset += fieldB;
|
|
}
|
|
if (fieldCisReg) {
|
|
state->ea_reg2 = fieldC;
|
|
} else {
|
|
state->_offset += fieldC;
|
|
}
|
|
state->_mem_load = 1;
|
|
|
|
directMem = BIT(state->words[0], 15);
|
|
/* - We should display the instruction as decoded, not some censored
|
|
version of it
|
|
- Scaled index is encoded as 'addrWriteBack', even though it isn't
|
|
actually doing a write back; it is legitimate with a LIMM. */
|
|
#if 0
|
|
/* Check if address writeback is allowed before decoding the
|
|
address writeback field of a load instruction.*/
|
|
if (fieldBisReg && (fieldB != 62))
|
|
#endif
|
|
addrWriteBack = BITS(state->words[0], 22, 23);
|
|
signExtend = BIT(state->words[0], 16);
|
|
|
|
write_instr_name();
|
|
|
|
/* Check for prefetch or ld 0,...*/
|
|
if (IS_REG(A)) {
|
|
WRITE_FORMAT_x_COMMA_LB(A);
|
|
} else {
|
|
strcat(formatString, "%*");
|
|
WRITE_FORMAT_LB();
|
|
}
|
|
|
|
if (fieldBisReg || fieldB != 0) {
|
|
WRITE_FORMAT_x(B);
|
|
} else {
|
|
fieldB = fieldC;
|
|
}
|
|
|
|
WRITE_FORMAT_COMMA_x_RB(C);
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldA, fieldB, fieldC);
|
|
break;
|
|
|
|
case 6:
|
|
/* LD instruction. */
|
|
CHECK_FIELD_B();
|
|
CHECK_FIELD_A();
|
|
/* Support for Prefetch */
|
|
/* Fixme :: Check for A700 within this function */
|
|
|
|
if (FIELDA(state->words[0]) == 62) {
|
|
instrName = "prefetch";
|
|
}
|
|
|
|
fieldC = FIELDD9(state->words[0]);
|
|
fieldCisReg = 0;
|
|
|
|
state->_ea_present = 1;
|
|
state->_offset = fieldC;
|
|
state->_mem_load = 1;
|
|
if (fieldBisReg) {
|
|
state->ea_reg1 = fieldB;
|
|
/* field B is either a shimm (same as fieldC) or limm (different!) */
|
|
/* Say ea is not present, so only one of us will do the name lookup. */
|
|
} else {
|
|
state->_offset += fieldB, state->_ea_present = 0;
|
|
}
|
|
|
|
directMem = BIT(state->words[0], 11);
|
|
/* Check if address writeback is allowed before decoding the
|
|
address writeback field of a load instruction.*/
|
|
if (fieldBisReg && (fieldB != 62)) {
|
|
addrWriteBack = BITS(state->words[0], 9, 10);
|
|
}
|
|
signExtend = BIT(state->words[0], 6);
|
|
|
|
write_instr_name();
|
|
if (IS_REG(A)) {
|
|
WRITE_FORMAT_x_COMMA_LB(A);
|
|
} else {
|
|
strcat(formatString, "%*");
|
|
WRITE_FORMAT_LB();
|
|
}
|
|
if (!fieldBisReg) {
|
|
fieldB = state->_offset;
|
|
WRITE_FORMAT_x_RB(B);
|
|
} else {
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x_RB(C);
|
|
}
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldA, fieldB, fieldC);
|
|
break;
|
|
|
|
case 7:
|
|
/* ST instruction. */
|
|
CHECK_FIELD_B();
|
|
CHECK_FIELD_C();
|
|
state->source_operand.registerNum = fieldC;
|
|
state->sourceType = fieldCisReg ? ARC_REGISTER : ARC_LIMM;
|
|
fieldA = FIELDD9(state->words[0]); /* shimm */
|
|
|
|
/* [B,A offset] */
|
|
state->_ea_present = 1;
|
|
state->_offset = fieldA;
|
|
if (fieldBisReg) {
|
|
state->ea_reg1 = fieldB;
|
|
/*
|
|
* field B is either a shimm (same as fieldA) or limm (different!)
|
|
* Say ea is not present, so only one of us will do the name lookup.
|
|
* (for is_limm we do the name translation here).
|
|
*/
|
|
} else {
|
|
state->_offset += fieldB, state->_ea_present = 0;
|
|
}
|
|
|
|
directMem = BIT(state->words[0], 5);
|
|
addrWriteBack = BITS(state->words[0], 3, 4);
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x_COMMA_LB(C);
|
|
if (fieldA == 0) {
|
|
WRITE_FORMAT_x_RB(B);
|
|
} else {
|
|
WRITE_FORMAT_x(B);
|
|
fieldAisReg = 0;
|
|
WRITE_FORMAT_COMMA_x_RB(A);
|
|
}
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldC, fieldB, fieldA);
|
|
break;
|
|
|
|
case 8:
|
|
/* SR instruction */
|
|
CHECK_FIELD_B();
|
|
switch (BITS(state->words[0], 22, 23)) {
|
|
case 0:
|
|
if (is_limm) {
|
|
FIELD_C();
|
|
if (!fieldCisReg) {
|
|
fieldC = fieldB;
|
|
}
|
|
} else {
|
|
CHECK_FIELD_C();
|
|
}
|
|
break;
|
|
case 1:
|
|
FIELD_C();
|
|
fieldCisReg = 0;
|
|
break;
|
|
case 2:
|
|
fieldC = FIELDS(state->words[0]);
|
|
fieldCisReg = 0;
|
|
break;
|
|
}
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x_COMMA_LB(B);
|
|
/* Try to print B as an aux reg if it is not a core reg. */
|
|
usesAuxReg = 1;
|
|
WRITE_FORMAT_x(C);
|
|
WRITE_FORMAT_RB();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC);
|
|
break;
|
|
|
|
case 9:
|
|
/* BBIT0/BBIT1 Instruction */
|
|
|
|
CHECK_FIELD_C();
|
|
if (is_limm || BIT(state->words[0], 4)) {
|
|
fieldCisReg = 0;
|
|
FIELD_B();
|
|
} else {
|
|
CHECK_FIELD_B();
|
|
}
|
|
fieldAisReg = 0;
|
|
fieldA = FIELDS9(state->words[0]);
|
|
fieldA += (addr & ~0x3);
|
|
CHECK_NULLIFY();
|
|
|
|
write_instr_name();
|
|
|
|
add_target(fieldA);
|
|
state->flow = state->_opcode == op_BLC ? direct_call : direct_jump;
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
strcat(formatString, ",%s"); /* address/label name */
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC, post_address(state, fieldA));
|
|
break;
|
|
|
|
case 10:
|
|
/* LR instruction */
|
|
CHECK_FIELD_B();
|
|
switch (BITS(state->words[0], 22, 23)) {
|
|
case 0:
|
|
CHECK_FIELD_C();
|
|
break;
|
|
case 1:
|
|
FIELD_C();
|
|
fieldCisReg = 0;
|
|
break;
|
|
case 2:
|
|
fieldC = FIELDS(state->words[0]);
|
|
fieldCisReg = 0;
|
|
break;
|
|
}
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x_COMMA_LB(B);
|
|
/* Try to print B as an aux reg if it is not a core reg. */
|
|
usesAuxReg = 1;
|
|
WRITE_FORMAT_x(C);
|
|
WRITE_FORMAT_RB();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC);
|
|
break;
|
|
|
|
case 11:
|
|
/* lp instruction */
|
|
|
|
if (BITS(state->words[0], 22, 23) == 3) {
|
|
FIELD_C();
|
|
CHECK_COND();
|
|
} else {
|
|
fieldC = FIELDS(state->words[0]);
|
|
}
|
|
|
|
fieldC = fieldC << 1;
|
|
fieldC += (addr & ~0x3);
|
|
|
|
write_instr_name();
|
|
|
|
/* This address could be a label we know. Convert it. */
|
|
add_target(fieldC);
|
|
state->flow = state->_opcode == op_BLC ? direct_call : direct_jump;
|
|
|
|
strcat(formatString, "%s"); /* address/label name */
|
|
my_sprintf(state, state->operandBuffer, formatString, post_address(state, fieldC));
|
|
break;
|
|
|
|
case 12:
|
|
/* MOV instruction */
|
|
FIELD_B();
|
|
subopcode = BITS(state->words[0], 22, 23);
|
|
if (subopcode == 0 || ((subopcode == 3) && (!BIT(state->words[0], 5)))) {
|
|
CHECK_FIELD_C();
|
|
} else if (subopcode == 1 || ((subopcode == 3) && (BIT(state->words[0], 5)))) {
|
|
FIELD_C();
|
|
fieldCisReg = 0;
|
|
} else if (subopcode == 2) {
|
|
fieldC = FIELDS(state->words[0]);
|
|
fieldCisReg = 0;
|
|
}
|
|
if (subopcode == 3) {
|
|
CHECK_FLAG_COND();
|
|
} else {
|
|
CHECK_FLAG();
|
|
}
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC);
|
|
break;
|
|
|
|
case 13:
|
|
/* "B", "BL" instruction */
|
|
|
|
fieldA = 0;
|
|
if ((state->_opcode == op_BC && (BIT(state->words[0], 16))) ||
|
|
(state->_opcode == op_BLC && (BIT(state->words[0], 17)))) {
|
|
/* unconditional branch s25 or branch and link d25 */
|
|
fieldA = (BITS(state->words[0], 0, 4)) << 10;
|
|
}
|
|
fieldA |= BITS(state->words[0], 6, 15);
|
|
|
|
if (state->_opcode == op_BLC) {
|
|
/* Fix for Bug #553. A bl unconditional has only 9 bits in the
|
|
* least order bits. */
|
|
fieldA = fieldA << 9;
|
|
fieldA |= BITS(state->words[0], 18, 26);
|
|
fieldA = fieldA << 2;
|
|
} else {
|
|
fieldA = fieldA << 10;
|
|
fieldA |= BITS(state->words[0], 17, 26);
|
|
fieldA = fieldA << 1;
|
|
}
|
|
|
|
if ((state->_opcode == op_BC && (BIT(state->words[0], 16))) ||
|
|
(state->_opcode == op_BLC && (BIT(state->words[0], 17)))) {
|
|
/* unconditional branch s25 or branch and link d25 */
|
|
fieldA = sign_extend(fieldA, 25);
|
|
} else {
|
|
/* conditional branch s21 or branch and link d21 */
|
|
fieldA = sign_extend(fieldA, 21);
|
|
}
|
|
|
|
fieldA += (addr & ~0x3);
|
|
|
|
if (BIT(state->words[0], 16) && state->_opcode == op_BC) {
|
|
CHECK_NULLIFY();
|
|
} else
|
|
/* Checking for bl unconditionally FIX For Bug #553 */
|
|
if ((state->_opcode == op_BLC && BITS(state->words[0], 16, 17) == 2) || (state->_opcode == op_BC && (BIT(state->words[0], 16)))) {
|
|
CHECK_NULLIFY();
|
|
} else {
|
|
CHECK_COND_NULLIFY();
|
|
}
|
|
|
|
write_instr_name();
|
|
/* This address could be a label we know. Convert it. */
|
|
add_target(fieldA); /* For debugger. */
|
|
state->flow = state->_opcode == op_BLC /* BL */
|
|
? direct_call
|
|
: direct_jump;
|
|
/* indirect calls are achieved by "lr blink,[status]; */
|
|
/* lr dest<- func addr; j [dest]" */
|
|
|
|
strcat(formatString, "%s"); /* address/label name */
|
|
my_sprintf(state, state->operandBuffer, formatString, post_address(state, fieldA));
|
|
break;
|
|
|
|
case 14:
|
|
|
|
/* Extension Instructions */
|
|
|
|
FIELD_C_AC();
|
|
FIELD_B_AC();
|
|
|
|
write_instr_name();
|
|
if (mul) {
|
|
fieldA = fieldAisReg = 0;
|
|
WRITE_FORMAT_x(A);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
} else {
|
|
WRITE_FORMAT_x(B);
|
|
}
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
if (mul) {
|
|
my_sprintf(state, state->operandBuffer, formatString, 0, fieldB, fieldC);
|
|
} else {
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC);
|
|
}
|
|
break;
|
|
|
|
case 15:
|
|
|
|
/* ARCompact 16-bit Load/Add resister-register */
|
|
|
|
FIELD_C_AC();
|
|
FIELD_B_AC();
|
|
FIELD_A_AC();
|
|
|
|
write_instr_name();
|
|
|
|
if (BITS(state->words[0], 3, 4) != 3) {
|
|
WRITE_FORMAT_x_COMMA_LB(A);
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x_RB(C);
|
|
} else {
|
|
WRITE_FORMAT_x(A);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
}
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldA, fieldB, fieldC);
|
|
break;
|
|
|
|
case 16:
|
|
|
|
/* ARCompact 16-bit Add/Sub/Shift instructions */
|
|
|
|
FIELD_C_AC();
|
|
FIELD_B_AC();
|
|
fieldA = FIELDA_AC(state->words[0]);
|
|
fieldAisReg = 0;
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x(C);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_FORMAT_COMMA_x(A);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldC, fieldB, fieldA);
|
|
break;
|
|
|
|
case 17:
|
|
|
|
/* add_s instruction, one Dest/Source can be any of r0 - r63 */
|
|
|
|
CHECK_FIELD_H_AC();
|
|
FIELD_B_AC();
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldB, fieldC);
|
|
break;
|
|
|
|
case 18:
|
|
|
|
/* mov_s/cmp_s instruction, one Dest/Source can be any of r0 - r63 */
|
|
|
|
if ((BITS(state->words[0], 3, 4) == 1) || (BITS(state->words[0], 3, 4) == 2)) {
|
|
CHECK_FIELD_H_AC();
|
|
} else if (BITS(state->words[0], 3, 4) == 3) {
|
|
FIELD_H_AC();
|
|
}
|
|
FIELD_B_AC();
|
|
|
|
write_instr_name();
|
|
if (BITS(state->words[0], 3, 4) == 3) {
|
|
WRITE_FORMAT_x(C);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldC, fieldB);
|
|
} else {
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC);
|
|
}
|
|
break;
|
|
|
|
case 19:
|
|
|
|
/* Stack pointer-based instructions [major opcode 0x18] */
|
|
|
|
if (BITS(state->words[0], 5, 7) == 5) {
|
|
fieldA = 28;
|
|
} else {
|
|
FIELD_B_AC();
|
|
fieldA = fieldB;
|
|
}
|
|
fieldB = 28; /* Field B is the stack pointer register */
|
|
fieldC = (FIELDU_AC(state->words[0])) << 2;
|
|
fieldCisReg = 0;
|
|
|
|
write_instr_name();
|
|
|
|
switch (BITS(state->words[0], 5, 7)) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
WRITE_FORMAT_x_COMMA_LB(A);
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x_RB(C);
|
|
break;
|
|
case 4:
|
|
case 5:
|
|
WRITE_FORMAT_x(A);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
break;
|
|
}
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldA, fieldB, fieldC);
|
|
break;
|
|
|
|
case 20:
|
|
|
|
/* gp-relative instructions [major opcode 0x19] */
|
|
|
|
fieldA = 0;
|
|
fieldB = 26; /* Field B is the gp register */
|
|
fieldC = FIELDS_AC(state->words[0]);
|
|
switch (BITS(state->words[0], 9, 10)) {
|
|
case 0:
|
|
case 3:
|
|
fieldC = fieldC << 2;
|
|
break;
|
|
case 2:
|
|
fieldC = fieldC << 1;
|
|
break;
|
|
}
|
|
fieldCisReg = 0;
|
|
|
|
write_instr_name();
|
|
|
|
if (BITS(state->words[0], 9, 10) != 3) {
|
|
WRITE_FORMAT_x_COMMA_LB(A);
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x_RB(C);
|
|
} else {
|
|
WRITE_FORMAT_x(A);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
}
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldA, fieldB, fieldC);
|
|
break;
|
|
|
|
case 21:
|
|
|
|
/* add/cmp/btst instructions [major opcode 28] */
|
|
|
|
FIELD_B_AC();
|
|
if (state->_opcode == op_Su5) {
|
|
fieldC = (BITS(state->words[0], 0, 4));
|
|
} else {
|
|
fieldC = (BITS(state->words[0], 0, 6));
|
|
}
|
|
fieldCisReg = 0;
|
|
write_instr_name();
|
|
|
|
if (!BIT(state->words[0], 7)) {
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldB, fieldC);
|
|
} else {
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC);
|
|
}
|
|
break;
|
|
|
|
case 22:
|
|
|
|
/* ARCompact 16-bit instructions, General ops/ single ops */
|
|
|
|
FIELD_C_AC();
|
|
FIELD_B_AC();
|
|
|
|
write_instr_name();
|
|
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldB, fieldC);
|
|
break;
|
|
|
|
case 23:
|
|
|
|
/* Shift/subtract/bit immediate instructions [major opcode 23] */
|
|
|
|
FIELD_B_AC();
|
|
fieldC = FIELDU_AC(state->words[0]);
|
|
fieldCisReg = 0;
|
|
write_instr_name();
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldB, fieldC);
|
|
break;
|
|
|
|
case 24:
|
|
|
|
/* ARCompact 16-bit Branch conditionally */
|
|
|
|
if (state->_opcode == op_BL_S) {
|
|
fieldA = (BITS(state->words[0], 0, 10)) << 2;
|
|
fieldA = sign_extend(fieldA, 13);
|
|
} else if (BITS(state->words[0], 9, 10) != 3) {
|
|
fieldA = (BITS(state->words[0], 0, 8)) << 1;
|
|
fieldA = sign_extend(fieldA, 10);
|
|
} else {
|
|
fieldA = (BITS(state->words[0], 0, 5)) << 1;
|
|
fieldA = sign_extend(fieldA, 7);
|
|
}
|
|
fieldA += (addr & ~0x3);
|
|
|
|
write_instr_name();
|
|
/* This address could be a label we know. Convert it. */
|
|
add_target(fieldA); /* For debugger. */
|
|
state->flow = state->_opcode == op_BL_S /* BL */
|
|
? direct_call
|
|
: direct_jump;
|
|
/* indirect calls are achieved by "lr blink,[status]; */
|
|
/* lr dest<- func addr; j [dest]" */
|
|
|
|
strcat(formatString, "%s"); /* address/label name */
|
|
my_sprintf(state, state->operandBuffer, formatString, post_address(state, fieldA));
|
|
break;
|
|
|
|
case 25:
|
|
|
|
/* ARCompact 16-bit Branch conditionally on reg z/nz */
|
|
|
|
FIELD_B_AC();
|
|
fieldC = (BITS(state->words[0], 0, 6)) << 1;
|
|
fieldC = sign_extend(fieldC, 8);
|
|
|
|
fieldC += (addr & ~0x3);
|
|
fieldA = fieldAisReg = 0;
|
|
|
|
write_instr_name();
|
|
/* This address could be a label we know. Convert it. */
|
|
add_target(fieldC); /* For debugger. */
|
|
state->flow = direct_jump;
|
|
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(A);
|
|
strcat(formatString, ",%s"); /* address/label name */
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldA, post_address(state, fieldC));
|
|
break;
|
|
|
|
case 26:
|
|
|
|
/* Zero operand Instructions */
|
|
|
|
write_instr_name();
|
|
state->operandBuffer[0] = '\0';
|
|
break;
|
|
|
|
case 27:
|
|
|
|
/* j_s instruction */
|
|
|
|
FIELD_B_AC();
|
|
write_instr_name();
|
|
strcat(formatString, "[%r]");
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB);
|
|
break;
|
|
|
|
case 28:
|
|
|
|
/* Load/Store with offset */
|
|
|
|
FIELD_C_AC();
|
|
FIELD_B_AC();
|
|
switch (state->_opcode) {
|
|
case op_LD_S:
|
|
case op_ST_S:
|
|
fieldA = (FIELDU_AC(state->words[0])) << 2;
|
|
break;
|
|
case op_LDB_S:
|
|
case op_STB_S:
|
|
fieldA = (FIELDU_AC(state->words[0]));
|
|
break;
|
|
case op_LDW_S:
|
|
case op_LDWX_S:
|
|
case op_STW_S:
|
|
fieldA = (FIELDU_AC(state->words[0])) << 1;
|
|
break;
|
|
}
|
|
fieldAisReg = 0;
|
|
|
|
write_instr_name();
|
|
|
|
WRITE_FORMAT_x_COMMA_LB(C);
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(A);
|
|
WRITE_FORMAT_RB();
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldC, fieldB, fieldA);
|
|
break;
|
|
|
|
case 29:
|
|
|
|
/* Load pc-relative */
|
|
|
|
FIELD_B_AC();
|
|
fieldC = 63;
|
|
fieldA = (BITS(state->words[0], 0, 7)) << 2;
|
|
fieldAisReg = 0;
|
|
|
|
write_instr_name();
|
|
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_FORMAT_COMMA_x(A);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC, fieldA);
|
|
break;
|
|
|
|
case 30:
|
|
|
|
/* mov immediate */
|
|
|
|
FIELD_B_AC();
|
|
fieldC = (BITS(state->words[0], 0, 7));
|
|
fieldCisReg = 0;
|
|
|
|
write_instr_name();
|
|
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC);
|
|
break;
|
|
|
|
case 31:
|
|
|
|
/* push/pop instructions */
|
|
|
|
if (BITS(state->words[0], 0, 4) == 1) {
|
|
FIELD_B_AC();
|
|
} else if (BITS(state->words[0], 0, 4) == 17) {
|
|
fieldB = 31;
|
|
}
|
|
|
|
write_instr_name();
|
|
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB);
|
|
break;
|
|
|
|
case 32:
|
|
|
|
/* Single operand instruction */
|
|
|
|
if (!BITS(state->words[0], 22, 23)) {
|
|
CHECK_FIELD_C();
|
|
} else {
|
|
FIELD_C();
|
|
fieldCisReg = 0;
|
|
}
|
|
|
|
write_instr_name();
|
|
|
|
if (!fieldC) {
|
|
state->operandBuffer[0] = '\0';
|
|
} else {
|
|
WRITE_FORMAT_x(C);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldC);
|
|
}
|
|
break;
|
|
|
|
case 33:
|
|
/* For trap_s and the class of instructions that have
|
|
unsigned 6 bits in the fields B and C in A700 16 bit
|
|
instructions */
|
|
fieldC = FIELDC_AC(state->words[0]);
|
|
fieldB = FIELDB_AC(state->words[0]);
|
|
write_instr_name();
|
|
strcat(formatString, "%d");
|
|
my_sprintf(state, state->operandBuffer, formatString, ((fieldB << 3) | fieldC));
|
|
break;
|
|
|
|
case 34:
|
|
/* For ex.di and its class of instructions within op_major_4
|
|
This class is different from the normal set of instructions
|
|
in op_major_4 because this uses bit 15 as .di and the second
|
|
operand is actually a memory operand.
|
|
This is of the class
|
|
<op>.<di> b,[c] and <op>.<di> b,[limm]
|
|
*/
|
|
|
|
/* field C is either a register or limm (different!) */
|
|
|
|
CHECK_FIELD_C();
|
|
FIELD_B();
|
|
directMem = BIT(state->words[0], 15);
|
|
|
|
if (BITS(state->words[0], 22, 23) == 1) {
|
|
fieldCisReg = 0;
|
|
}
|
|
if (fieldCisReg) {
|
|
state->ea_reg1 = fieldC;
|
|
}
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x_COMMA_LB(B);
|
|
|
|
WRITE_FORMAT_x_RB(C);
|
|
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC);
|
|
break;
|
|
|
|
case 35:
|
|
|
|
/* sub_s.ne instruction */
|
|
|
|
FIELD_B_AC();
|
|
write_instr_name();
|
|
strcat(formatString, "%r,%r,%r");
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldB, fieldB);
|
|
break;
|
|
|
|
case 36:
|
|
|
|
FIELD_B_AC();
|
|
|
|
write_instr_name();
|
|
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB);
|
|
|
|
break;
|
|
|
|
/* START ARC LOCAL */
|
|
case 44:
|
|
/* rtsc instruction */
|
|
/* The source operand has no use. */
|
|
fieldB = fieldBisReg = 0;
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x(A);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldA,
|
|
fieldB);
|
|
break;
|
|
/* END ARC LOCAL */
|
|
|
|
/*******SIMD instructions decoding follows*************/
|
|
case 37:
|
|
case 39:
|
|
case 41:
|
|
/*fieldA is vr register
|
|
fieldB is I register
|
|
fieldC is a constant
|
|
%*,[%(,%<]
|
|
or
|
|
%*,%(,%<
|
|
or
|
|
%*,%(,%u
|
|
*/
|
|
|
|
CHECK_FIELD_A();
|
|
|
|
CHECK_FIELD_B();
|
|
if (decodingClass == 41) {
|
|
FIELD_C();
|
|
} else {
|
|
FIELD_U8();
|
|
|
|
if (simd_scale_u8 > 0) {
|
|
fieldC = fieldC << simd_scale_u8;
|
|
}
|
|
}
|
|
|
|
fieldCisReg = 0;
|
|
|
|
write_instr_name();
|
|
(decodingClass == 37 ? WRITE_FORMAT_x_COMMA_LB(A) : WRITE_FORMAT_x_COMMA(A));
|
|
WRITE_FORMAT_x_COMMA(B);
|
|
(decodingClass == 37 ? WRITE_FORMAT_x_RB(C) : WRITE_FORMAT_x(C));
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldA, fieldB, fieldC);
|
|
|
|
break;
|
|
case 38:
|
|
/* fieldA is a vr register
|
|
fieldB is a ARC700 basecase register.
|
|
%*,[%b]
|
|
*/
|
|
CHECK_FIELD_A();
|
|
CHECK_FIELD_B();
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x_COMMA_LB(A);
|
|
WRITE_FORMAT_x_RB(B);
|
|
WRITE_NOP_COMMENT();
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldA, fieldB);
|
|
|
|
break;
|
|
case 40:
|
|
/* fieldB & fieldC are vr registers
|
|
%(,%)
|
|
or
|
|
%B,%C
|
|
or
|
|
%(,%C
|
|
*/
|
|
CHECK_FIELD_B();
|
|
CHECK_FIELD_C();
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldB, fieldC);
|
|
break;
|
|
|
|
case 42:
|
|
/* fieldA, fieldB, fieldC are all vr registers
|
|
%*, %(, %) */
|
|
CHECK_FIELD_A();
|
|
CHECK_FIELD_B();
|
|
FIELD_C();
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x(A);
|
|
WRITE_FORMAT_COMMA_x(B);
|
|
WRITE_FORMAT_COMMA_x(C);
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldA, fieldB, fieldC);
|
|
break;
|
|
|
|
case 43:
|
|
/* Only fieldC is a register
|
|
%C*/
|
|
CHECK_FIELD_C();
|
|
|
|
if (BITS(state->words[0], 17, 23) == 55) {
|
|
fieldCisReg = 0;
|
|
}
|
|
|
|
write_instr_name();
|
|
WRITE_FORMAT_x(C);
|
|
my_sprintf(state, state->operandBuffer, formatString, fieldC);
|
|
break;
|
|
|
|
/***************SIMD decoding ends*********************/
|
|
default:
|
|
mwerror(state, "Bad decoding class in ARC disassembler");
|
|
break;
|
|
}
|
|
|
|
state->_cond = cond;
|
|
return state->instructionLen = offset;
|
|
}
|
|
|
|
/*
|
|
* _coreRegName - Returns the name the user specified core extension
|
|
* register.
|
|
*/
|
|
static const char *
|
|
_coreRegName(
|
|
void *_this ATTRIBUTE_UNUSED, /* C++ this pointer */
|
|
int v /* Register value */
|
|
) {
|
|
return arcExtMap_coreRegName(v);
|
|
}
|
|
|
|
/*
|
|
* _auxRegName - Returns the name the user specified AUX extension
|
|
* register.
|
|
*/
|
|
static const char *
|
|
_auxRegName(void *_this ATTRIBUTE_UNUSED, /* C++ this pointer */
|
|
int v /* Register value */
|
|
) {
|
|
return arcExtMap_auxRegName(v);
|
|
}
|
|
|
|
/*
|
|
* _condCodeName - Returns the name the user specified condition code
|
|
* name.
|
|
*/
|
|
static const char *
|
|
_condCodeName(
|
|
void *_this ATTRIBUTE_UNUSED, /* C++ this pointer */
|
|
int v /* Register value */
|
|
) {
|
|
return arcExtMap_condCodeName(v);
|
|
}
|
|
|
|
/*
|
|
* _instName - Returns the name the user specified extension instruction.
|
|
*/
|
|
static const char *
|
|
_instName(
|
|
void *_this ATTRIBUTE_UNUSED, /* C++ this pointer */
|
|
int op1, /* major opcode value */
|
|
int op2, /* minor opcode value */
|
|
int *flags /* instruction flags */
|
|
) {
|
|
return arcExtMap_instName(op1, op2, flags);
|
|
}
|
|
|
|
static void
|
|
parse_disassembler_options(char *options) {
|
|
const char *p;
|
|
for (p = options; p != NULL;) {
|
|
if (CONST_STRNEQ(p, "simd")) {
|
|
enable_simd = 1;
|
|
}
|
|
if (CONST_STRNEQ(p, "insn-stream")) {
|
|
enable_insn_stream = 1;
|
|
}
|
|
|
|
p = strchr(p, ',');
|
|
|
|
if (p != NULL) {
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ARCompact_decodeInstr - Decode an ARCompact instruction returning the
|
|
size of the instruction in bytes or zero if unrecognized. */
|
|
int ARCompact_decodeInstr(bfd_vma address, /* Address of this instruction. */
|
|
disassemble_info *info) {
|
|
int status;
|
|
bfd_byte buffer[4];
|
|
struct arcDisState s; /* ARC Disassembler state */
|
|
void *stream = info->stream; /* output stream */
|
|
fprintf_ftype func = info->fprintf_func;
|
|
int bytes;
|
|
int lowbyte, highbyte;
|
|
char buf[allOperandsSize + 1];
|
|
|
|
if (info->disassembler_options) {
|
|
parse_disassembler_options(info->disassembler_options);
|
|
|
|
/* To avoid repeated parsing of these options, we remove them here. */
|
|
info->disassembler_options = NULL;
|
|
}
|
|
|
|
lowbyte = ((info->endian == BFD_ENDIAN_LITTLE) ? 1 : 0);
|
|
highbyte = ((info->endian == BFD_ENDIAN_LITTLE) ? 0 : 1);
|
|
|
|
memset(&s, 0, sizeof(struct arcDisState));
|
|
|
|
/* read first instruction */
|
|
status = (*info->read_memory_func)(address, buffer, 2, info);
|
|
|
|
if (status != 0) {
|
|
(*info->memory_error_func)(status, address, info);
|
|
return -1;
|
|
}
|
|
|
|
if (((buffer[lowbyte] & 0xf8) > 0x38) && ((buffer[lowbyte] & 0xf8) != 0x48)) {
|
|
s.instructionLen = 2;
|
|
s.words[0] = (buffer[lowbyte] << 8) | buffer[highbyte];
|
|
(*info->read_memory_func)(address + 2, buffer, 4, info);
|
|
if (info->endian == BFD_ENDIAN_LITTLE) {
|
|
s.words[1] = bfd_getl32(buffer);
|
|
} else {
|
|
s.words[1] = bfd_getb32(buffer);
|
|
}
|
|
} else {
|
|
s.instructionLen = 4;
|
|
status = (*info->read_memory_func)(address + 2, &buffer[2], 2, info);
|
|
if (status != 0) {
|
|
(*info->memory_error_func)(status, address + 2, info);
|
|
return -1;
|
|
}
|
|
if (info->endian == BFD_ENDIAN_LITTLE) {
|
|
s.words[0] = bfd_getl32(buffer);
|
|
} else {
|
|
s.words[0] = bfd_getb32(buffer);
|
|
}
|
|
|
|
/* always read second word in case of limm */
|
|
/* we ignore the result since last insn may not have a limm */
|
|
(*info->read_memory_func)(address + 4, buffer, 4, info);
|
|
if (info->endian == BFD_ENDIAN_LITTLE) {
|
|
s.words[1] = bfd_getl32(buffer);
|
|
} else {
|
|
s.words[1] = bfd_getb32(buffer);
|
|
}
|
|
}
|
|
|
|
s._this = &s;
|
|
s.coreRegName = _coreRegName;
|
|
s.auxRegName = _auxRegName;
|
|
s.condCodeName = _condCodeName;
|
|
s.instName = _instName;
|
|
|
|
/* disassemble */
|
|
bytes = dsmOneArcInst(address, (void *)&s, info);
|
|
|
|
/* display the disassembled instruction */
|
|
{
|
|
char *instr = s.instrBuffer;
|
|
char *operand = s.operandBuffer;
|
|
char *space = strchr(instr, ' ');
|
|
|
|
if (enable_insn_stream) {
|
|
/* Show instruction stream from MSB to LSB*/
|
|
|
|
if (s.instructionLen == 2) {
|
|
(*func)(stream, " %04x ", (unsigned int)s.words[0]);
|
|
} else {
|
|
(*func)(stream, "%08x ", (unsigned int)s.words[0]);
|
|
}
|
|
|
|
(*func)(stream, " ");
|
|
}
|
|
|
|
/* if the operand is actually in the instruction buffer */
|
|
if ((space != NULL) && (operand[0] == '\0')) {
|
|
*space = '\0';
|
|
operand = space + 1;
|
|
}
|
|
|
|
(*func)(stream, "%s ", instr);
|
|
|
|
if (__TRANSLATION_REQUIRED(s)) {
|
|
bfd_vma addr;
|
|
char *tmpBuffer;
|
|
int i = 1;
|
|
|
|
if (operand[0] != '@') {
|
|
/* Branch instruction with 3 operands, Translation is required
|
|
only for the third operand. Print the first 2 operands */
|
|
strncpy(buf, operand, sizeof(buf) - 1);
|
|
buf[sizeof(buf) - 1] = '\0';
|
|
tmpBuffer = strtok(buf, "@");
|
|
(*func)(stream, "%s", tmpBuffer);
|
|
i = strlen(tmpBuffer) + 1;
|
|
}
|
|
|
|
addr = s.addresses[operand[i] - '0'];
|
|
(*info->print_address_func)((bfd_vma)addr, info);
|
|
//(*func) (stream, "\n");
|
|
} else {
|
|
(*func)(stream, "%s", operand);
|
|
}
|
|
}
|
|
|
|
/* We print max bytes for instruction */
|
|
info->bytes_per_line = 8;
|
|
|
|
return bytes; // s.instructionLen;
|
|
}
|
|
|
|
/*
|
|
* This function is the same as decodeInstr except that this function
|
|
* returns a struct arcDisState instead of the instruction length.
|
|
*
|
|
* This struct contains information useful to the debugger.
|
|
*/
|
|
struct arcDisState
|
|
arcAnalyzeInstr(
|
|
bfd_vma address, /* Address of this instruction */
|
|
disassemble_info *info) {
|
|
int status;
|
|
bfd_byte buffer[4];
|
|
struct arcDisState s; /* ARC Disassembler state */
|
|
int bytes;
|
|
int lowbyte, highbyte;
|
|
|
|
lowbyte = ((info->endian == BFD_ENDIAN_LITTLE) ? 1 : 0);
|
|
highbyte = ((info->endian == BFD_ENDIAN_LITTLE) ? 0 : 1);
|
|
|
|
memset(&s, 0, sizeof(struct arcDisState));
|
|
|
|
/* read first instruction */
|
|
status = (*info->read_memory_func)(address, buffer, 2, info);
|
|
|
|
if (status != 0) {
|
|
(*info->memory_error_func)(status, address, info);
|
|
s.instructionLen = -1;
|
|
return s;
|
|
}
|
|
|
|
if (((buffer[lowbyte] & 0xf8) > 0x38) && ((buffer[lowbyte] & 0xf8) != 0x48)) {
|
|
s.instructionLen = 2;
|
|
s.words[0] = (buffer[lowbyte] << 8) | buffer[highbyte];
|
|
(*info->read_memory_func)(address + 2, buffer, 4, info);
|
|
if (info->endian == BFD_ENDIAN_LITTLE) {
|
|
s.words[1] = bfd_getl32(buffer);
|
|
} else {
|
|
s.words[1] = bfd_getb32(buffer);
|
|
}
|
|
} else {
|
|
s.instructionLen = 4;
|
|
status = (*info->read_memory_func)(address + 2, &buffer[2], 2, info);
|
|
if (status != 0) {
|
|
(*info->memory_error_func)(status, address + 2, info);
|
|
s.instructionLen = -1;
|
|
return s;
|
|
}
|
|
if (info->endian == BFD_ENDIAN_LITTLE) {
|
|
s.words[0] = bfd_getl32(buffer);
|
|
} else {
|
|
s.words[0] = bfd_getb32(buffer);
|
|
}
|
|
|
|
/* always read second word in case of limm */
|
|
/* we ignore the result since last insn may not have a limm */
|
|
(*info->read_memory_func)(address + 4, buffer, 4, info);
|
|
if (info->endian == BFD_ENDIAN_LITTLE) {
|
|
s.words[1] = bfd_getl32(buffer);
|
|
} else {
|
|
s.words[1] = bfd_getb32(buffer);
|
|
}
|
|
}
|
|
|
|
s._this = &s;
|
|
s.coreRegName = _coreRegName;
|
|
s.auxRegName = _auxRegName;
|
|
s.condCodeName = _condCodeName;
|
|
s.instName = _instName;
|
|
|
|
/* disassemble */
|
|
bytes = dsmOneArcInst(address, (void *)&s, info);
|
|
/* We print max bytes for instruction */
|
|
info->bytes_per_line = bytes;
|
|
return s;
|
|
}
|
|
|
|
void arc_print_disassembler_options(FILE *stream) {
|
|
fprintf(stream, "\n\
|
|
ARC-specific disassembler options:\n\
|
|
use with the -M switch, with options separated by commas\n\n");
|
|
|
|
fprintf(stream, " insn-stream Show the instruction byte stream from most\n");
|
|
fprintf(stream, " significant byte to least significant byte (excluding LIMM).\n");
|
|
fprintf(stream, " This option is useful for viewing the actual encoding of instructions.\n");
|
|
|
|
fprintf(stream, " simd Enable SIMD instructions disassembly.\n\n");
|
|
}
|