Copyright 2004, 2005 Free Software Foundation, Inc.
Contributed by Tomer Levi, NSC, Israel.
Written by Tomer Levi.
This file is part of the GNU binutils and GDB, the GNU debugger.
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, 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 "dis-asm.h"
#include "sysdep.h"
#include "opcode/crx.h"
#define ILLEGAL "illegal"
#define ESCAPE_16_BIT 0xE
#define EXTRACT(a, offs, n_bits) \
(n_bits == 32 ? (((a) >> (offs)) & 0xffffffffL) \
: (((a) >> (offs)) & ((1 << (n_bits)) -1)))
#define SBM(offs) ((((1 << (32 - offs)) -1) << (offs)))
typedef unsigned long dwordU;
typedef unsigned short wordU;
typedef struct
{
dwordU val;
int nbits;
} parameter;
typedef struct
{
char *str;
unsigned int value;
}
cinv_entry;
const cinv_entry crx_cinvs[] =
{
{"[i]", 2}, {"[i,u]", 3}, {"[d]", 4}, {"[d,u]", 5},
{"[d,i]", 6}, {"[d,i,u]", 7}, {"[b]", 8},
{"[b,i]", 10}, {"[b,i,u]", 11}, {"[b,d]", 12},
{"[b,d,u]", 13}, {"[b,d,i]", 14}, {"[b,d,i,u]", 15}
};
typedef enum REG_ARG_TYPE
{
REG_ARG = 0,
USER_REG_ARG,
COP_ARG,
COPS_ARG
}
REG_ARG_TYPE;
int NUMCINVS = ((sizeof crx_cinvs)/(sizeof crx_cinvs[0]));
const inst *instruction;
ins currInsn;
wordU words[3];
ULONGLONG allWords;
int processing_argument_number;
int cst4flag;
incremented (escape sequence is used). */
int size_changed;
static int get_number_of_operands (void);
static argtype getargtype (operand_type);
static int getbits (operand_type);
static char *getregname (reg);
static char *getcopregname (copreg, reg_type);
static char * getprocregname (int);
static char *gettrapstring (unsigned);
static char *getcinvstring (unsigned);
static void getregliststring (int, char *, enum REG_ARG_TYPE);
static wordU get_word_at_PC (bfd_vma, struct disassemble_info *);
static void get_words_at_PC (bfd_vma, struct disassemble_info *);
static unsigned long build_mask (void);
static int powerof2 (int);
static int match_opcode (void);
static void make_instruction (void);
static void print_arguments (ins *, bfd_vma, struct disassemble_info *);
static void print_arg (argument *, bfd_vma, struct disassemble_info *);
static int
get_number_of_operands (void)
{
int i;
for (i = 0; instruction->operands[i].op_type && i < MAX_OPERANDS; i++)
;
return i;
}
static int
getbits (operand_type op)
{
if (op < MAX_OPRD)
return crx_optab[op].bit_size;
else
return 0;
}
static argtype
getargtype (operand_type op)
{
if (op < MAX_OPRD)
return crx_optab[op].arg_type;
else
return nullargs;
}
This routine is used when disassembling the 'excp' instruction. */
static char *
gettrapstring (unsigned int index)
{
const trap_entry *trap;
for (trap = crx_traps; trap < crx_traps + NUMTRAPS; trap++)
if (trap->entry == index)
return trap->name;
return ILLEGAL;
}
This routine is used when disassembling the 'cinv' instruction. */
static char *
getcinvstring (unsigned int num)
{
const cinv_entry *cinv;
for (cinv = crx_cinvs; cinv < (crx_cinvs + NUMCINVS); cinv++)
if (cinv->value == num)
return cinv->str;
return ILLEGAL;
}
char *
getregname (reg r)
{
const reg_entry *reg = &crx_regtab[r];
if (reg->type != CRX_R_REGTYPE)
return ILLEGAL;
else
return reg->name;
}
char *
getcopregname (copreg r, reg_type type)
{
const reg_entry *reg;
if (type == CRX_C_REGTYPE)
reg = &crx_copregtab[r];
else if (type == CRX_CS_REGTYPE)
reg = &crx_copregtab[r+(cs0-c0)];
else
return ILLEGAL;
return reg->name;
}
static char *
getprocregname (int index)
{
const reg_entry *r;
for (r = crx_regtab; r < crx_regtab + NUMREGS; r++)
if (r->image == index)
return r->name;
return "ILLEGAL REGISTER";
}
static int
powerof2 (int x)
{
int product, i;
for (i = 0, product = 1; i < x; i++)
product *= 2;
return product;
}
void
getregliststring (int mask, char *string, enum REG_ARG_TYPE core_cop)
{
char temp_string[5];
int i;
string[0] = '{';
string[1] = '\0';
if (mask == 0)
{
if (core_cop == USER_REG_ARG)
strcat (string, "ulo,uhi");
else
strcat (string, "lo,hi");
}
else
{
for (i = 0; i < 16; i++)
{
if (mask & 0x1)
{
switch (core_cop)
{
case REG_ARG:
sprintf (temp_string, "r%d", i);
break;
case USER_REG_ARG:
sprintf (temp_string, "u%d", i);
break;
case COP_ARG:
sprintf (temp_string, "c%d", i);
break;
case COPS_ARG:
sprintf (temp_string, "cs%d", i);
break;
default:
break;
}
strcat (string, temp_string);
if (mask & 0xfffe)
strcat (string, ",");
}
mask >>= 1;
}
}
strcat (string, "}");
}
START|--------|END
+---------+---------+---------+---------+
| | V | A | L |
+---------+---------+---------+---------+
0 16 32 48
words [0] [1] [2] */
static parameter
makelongparameter (ULONGLONG val, int start, int end)
{
parameter p;
p.val = (dwordU) EXTRACT(val, 48 - end, end - start);
p.nbits = end - start;
return p;
}
based on the instruction's printing flags. */
static unsigned long
build_mask (void)
{
unsigned int print_flags;
unsigned long mask;
print_flags = instruction->flags & FMT_CRX;
switch (print_flags)
{
case FMT_1:
mask = 0xF0F00000;
break;
case FMT_2:
mask = 0xFFF0FF00;
break;
case FMT_3:
mask = 0xFFF00F00;
break;
case FMT_4:
mask = 0xFFF0F000;
break;
case FMT_5:
mask = 0xFFF0FFF0;
break;
default:
mask = SBM(instruction->match_bits);
break;
}
return mask;
}
static int
match_opcode (void)
{
unsigned long mask;
unsigned long doubleWord = words[1] + (words[0] << 16);
instruction = &crx_instruction[NUMOPCODES - 2];
while (instruction >= crx_instruction)
{
mask = build_mask ();
if ((doubleWord & mask) == BIN(instruction->match, instruction->match_bits))
return 1;
else
instruction--;
}
return 0;
}
static void
make_argument (argument * a, int start_bits)
{
int inst_bit_size, total_size;
parameter p;
if ((instruction->size == 3) && a->size >= 16)
inst_bit_size = 48;
else
inst_bit_size = 32;
switch (a->type)
{
case arg_copr:
case arg_copsr:
p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size),
inst_bit_size - start_bits);
a->cr = p.val;
break;
case arg_r:
p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size),
inst_bit_size - start_bits);
a->r = p.val;
break;
case arg_ic:
p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size),
inst_bit_size - start_bits);
if ((p.nbits == 4) && cst4flag)
{
if (IS_INSN_TYPE (CMPBR_INS) && (p.val == ESCAPE_16_BIT))
{
in the last 4 bits. */
p = makelongparameter (allWords, 44, 48);
size_changed = 1;
}
if (p.val == 6)
p.val = -1;
else if (p.val == 13)
p.val = 48;
else if (p.val == 5)
p.val = -4;
else if (p.val == 10)
p.val = 32;
else if (p.val == 11)
p.val = 20;
else if (p.val == 9)
p.val = 16;
}
a->constant = p.val;
break;
case arg_idxr:
a->scale = 0;
total_size = a->size + 10;
p = makelongparameter (allWords, inst_bit_size - total_size,
inst_bit_size - (total_size - 4));
a->r = p.val;
p = makelongparameter (allWords, inst_bit_size - (total_size - 4),
inst_bit_size - (total_size - 8));
a->i_r = p.val;
p = makelongparameter (allWords, inst_bit_size - (total_size - 8),
inst_bit_size - (total_size - 10));
a->scale = p.val;
p = makelongparameter (allWords, inst_bit_size - (total_size - 10),
inst_bit_size);
a->constant = p.val;
break;
case arg_rbase:
p = makelongparameter (allWords, inst_bit_size - (start_bits + 4),
inst_bit_size - start_bits);
a->r = p.val;
break;
case arg_cr:
if (a->size <= 8)
{
p = makelongparameter (allWords, inst_bit_size - (start_bits + 4),
inst_bit_size - start_bits);
a->r = p.val;
p = makelongparameter (allWords, inst_bit_size - (start_bits + 8),
inst_bit_size - (start_bits + 4));
}
else
{
p = makelongparameter (allWords, 32 - (start_bits + 4),
32 - start_bits);
a->r = p.val;
p = makelongparameter (allWords, 32 - start_bits,
inst_bit_size);
}
if ((p.nbits == 4) && cst4flag)
{
if (instruction->flags & DISPUW4)
p.val *= 2;
else if (instruction->flags & DISPUD4)
p.val *= 4;
}
a->constant = p.val;
break;
case arg_c:
p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size),
inst_bit_size - start_bits);
a->constant = p.val;
break;
default:
break;
}
}
static void
print_arg (argument *a, bfd_vma memaddr, struct disassemble_info *info)
{
LONGLONG longdisp, mask;
int sign_flag = 0;
int relative = 0;
bfd_vma number;
int op_index = 0;
char string[200];
PTR stream = info->stream;
fprintf_ftype func = info->fprintf_func;
switch (a->type)
{
case arg_copr:
func (stream, "%s", getcopregname (a->cr, CRX_C_REGTYPE));
break;
case arg_copsr:
func (stream, "%s", getcopregname (a->cr, CRX_CS_REGTYPE));
break;
case arg_r:
if (IS_INSN_MNEMONIC ("mtpr") || IS_INSN_MNEMONIC ("mfpr"))
func (stream, "%s", getprocregname (a->r));
else
func (stream, "%s", getregname (a->r));
break;
case arg_ic:
if (IS_INSN_MNEMONIC ("excp"))
func (stream, "%s", gettrapstring (a->constant));
else if (IS_INSN_MNEMONIC ("cinv"))
func (stream, "%s", getcinvstring (a->constant));
else if (INST_HAS_REG_LIST)
{
REG_ARG_TYPE reg_arg_type = IS_INSN_TYPE (COP_REG_INS) ?
COP_ARG : IS_INSN_TYPE (COPS_REG_INS) ?
COPS_ARG : (instruction->flags & USER_REG) ?
USER_REG_ARG : REG_ARG;
if ((reg_arg_type == COP_ARG) || (reg_arg_type == COPS_ARG))
{
if (processing_argument_number == 2)
{
getregliststring (a->constant, string, reg_arg_type);
func (stream, "%s", string);
}
else
func (stream, "$0x%lx", a->constant);
}
else
{
getregliststring (a->constant, string, reg_arg_type);
func (stream, "%s", string);
}
}
else
func (stream, "$0x%lx", a->constant);
break;
case arg_idxr:
func (stream, "0x%lx(%s,%s,%d)", a->constant, getregname (a->r),
getregname (a->i_r), powerof2 (a->scale));
break;
case arg_rbase:
func (stream, "(%s)", getregname (a->r));
break;
case arg_cr:
func (stream, "0x%lx(%s)", a->constant, getregname (a->r));
if (IS_INSN_TYPE (LD_STOR_INS_INC))
func (stream, "+");
break;
case arg_c:
Have to fix this as this needs a bit of extension in terms of branchins.
Have to add support for cmp and branch instructions. */
if (IS_INSN_TYPE (BRANCH_INS) || IS_INSN_MNEMONIC ("bal")
|| IS_INSN_TYPE (CMPBR_INS) || IS_INSN_TYPE (DCR_BRANCH_INS)
|| IS_INSN_TYPE (COP_BRANCH_INS))
{
relative = 1;
longdisp = a->constant;
longdisp <<= 1;
switch (a->size)
{
case 8:
case 16:
case 24:
case 32:
mask = ((LONGLONG)1 << a->size) - 1;
if (longdisp & ((LONGLONG)1 << a->size))
{
sign_flag = 1;
longdisp = ~(longdisp) + 1;
}
a->constant = (unsigned long int) (longdisp & mask);
break;
default:
func (stream,
"Wrong offset used in branch/bal instruction");
break;
}
}
else if (IS_INSN_TYPE (BRANCH_NEQ_INS))
a->constant = 2 * a->constant + 2;
else if (IS_INSN_TYPE (LD_STOR_INS_INC)
|| IS_INSN_TYPE (LD_STOR_INS)
|| IS_INSN_TYPE (STOR_IMM_INS)
|| IS_INSN_TYPE (CSTBIT_INS))
{
op_index = instruction->flags & REVERSE_MATCH ? 0 : 1;
if (instruction->operands[op_index].op_type == abs16)
a->constant |= 0xFFFF0000;
}
func (stream, "%s", "0x");
number = (relative ? memaddr : 0)
+ (sign_flag ? -a->constant : a->constant);
(*info->print_address_func) (number, info);
break;
default:
break;
}
}
static void
print_arguments (ins *currInsn, bfd_vma memaddr, struct disassemble_info *info)
{
int i;
for (i = 0; i < currInsn->nargs; i++)
{
processing_argument_number = i;
print_arg (&currInsn->arg[i], memaddr, info);
if (i != currInsn->nargs - 1)
info->fprintf_func (info->stream, ", ");
}
}
static void
make_instruction (void)
{
int i;
unsigned int shift;
for (i = 0; i < currInsn.nargs; i++)
{
argument a;
memset (&a, 0, sizeof (a));
a.type = getargtype (instruction->operands[i].op_type);
if (instruction->operands[i].op_type == cst4
|| instruction->operands[i].op_type == rbase_dispu4)
cst4flag = 1;
a.size = getbits (instruction->operands[i].op_type);
shift = instruction->operands[i].shift;
make_argument (&a, shift);
currInsn.arg[i] = a;
}
currInsn.size = instruction->size + (size_changed ? 1 : 0);
currInsn.size *= 2;
}
static wordU
get_word_at_PC (bfd_vma memaddr, struct disassemble_info *info)
{
bfd_byte buffer[4];
int status;
wordU insn = 0;
status = info->read_memory_func (memaddr, buffer, 2, info);
if (status == 0)
insn = (wordU) bfd_getl16 (buffer);
return insn;
}
static void
get_words_at_PC (bfd_vma memaddr, struct disassemble_info *info)
{
int i;
bfd_vma mem;
for (i = 0, mem = memaddr; i < 3; i++, mem += 2)
words[i] = get_word_at_PC (mem, info);
allWords =
((ULONGLONG) words[0] << 32) + ((unsigned long) words[1] << 16) + words[2];
}
int
print_insn_crx (memaddr, info)
bfd_vma memaddr;
struct disassemble_info *info;
{
int is_decoded;
cst4flag = 0;
size_changed = 0;
get_words_at_PC (memaddr, info);
is_decoded = match_opcode ();
if (is_decoded > 0 && (words[0] << 16 || words[1]) != 0)
{
info->fprintf_func (info->stream, "%s", instruction->mnemonic);
if ((currInsn.nargs = get_number_of_operands ()) != 0)
info->fprintf_func (info->stream, "\t");
make_instruction ();
print_arguments (&currInsn, memaddr, info);
return currInsn.size;
}
info->fprintf_func (info->stream,"%s ",ILLEGAL);
return 2;
}