#include "sysdep.h"
#include <stdio.h>
#include "ansidecl.h"
#include "dis-asm.h"
#include "bfd.h"
#include "symcat.h"
#include "fr30-desc.h"
#include "fr30-opc.h"
#include "opintl.h"
#define UNKNOWN_INSN_MSG _("*unknown*")
static void print_normal
PARAMS ((CGEN_CPU_DESC, PTR, long, unsigned int, bfd_vma, int));
static void print_address
PARAMS ((CGEN_CPU_DESC, PTR, bfd_vma, unsigned int, bfd_vma, int));
static void print_keyword
PARAMS ((CGEN_CPU_DESC, PTR, CGEN_KEYWORD *, long, unsigned int));
static void print_insn_normal
PARAMS ((CGEN_CPU_DESC, PTR, const CGEN_INSN *, CGEN_FIELDS *,
bfd_vma, int));
static int print_insn PARAMS ((CGEN_CPU_DESC, bfd_vma,
disassemble_info *, char *, int));
static int default_print_insn
PARAMS ((CGEN_CPU_DESC, bfd_vma, disassemble_info *));
static void
print_register_list (dis_info, value, offset, load_store)
PTR dis_info;
long value;
long offset;
int load_store;
{
disassemble_info *info = dis_info;
int mask;
int index = 0;
char* comma = "";
if (load_store)
mask = 0x80;
else
mask = 1;
if (value & mask)
{
(*info->fprintf_func) (info->stream, "r%i", index + offset);
comma = ",";
}
for (index = 1; index <= 7; ++index)
{
if (load_store)
mask >>= 1;
else
mask <<= 1;
if (value & mask)
{
(*info->fprintf_func) (info->stream, "%sr%i", comma, index + offset);
comma = ",";
}
}
}
static void
print_hi_register_list_ld (cd, dis_info, value, attrs, pc, length)
CGEN_CPU_DESC cd;
PTR dis_info;
long value;
unsigned int attrs;
bfd_vma pc;
int length;
{
print_register_list (dis_info, value, 8, 0);
}
static void
print_low_register_list_ld (cd, dis_info, value, attrs, pc, length)
CGEN_CPU_DESC cd;
PTR dis_info;
long value;
unsigned int attrs;
bfd_vma pc;
int length;
{
print_register_list (dis_info, value, 0, 0);
}
static void
print_hi_register_list_st (cd, dis_info, value, attrs, pc, length)
CGEN_CPU_DESC cd;
PTR dis_info;
long value;
unsigned int attrs;
bfd_vma pc;
int length;
{
print_register_list (dis_info, value, 8, 1);
}
static void
print_low_register_list_st (cd, dis_info, value, attrs, pc, length)
CGEN_CPU_DESC cd;
PTR dis_info;
long value;
unsigned int attrs;
bfd_vma pc;
int length;
{
print_register_list (dis_info, value, 0, 1);
}
static void
print_m4 (cd, dis_info, value, attrs, pc, length)
CGEN_CPU_DESC cd;
PTR dis_info;
long value;
unsigned int attrs;
bfd_vma pc;
int length;
{
disassemble_info *info = (disassemble_info *) dis_info;
(*info->fprintf_func) (info->stream, "%ld", value);
}
void
fr30_cgen_print_operand (cd, opindex, xinfo, fields, attrs, pc, length)
CGEN_CPU_DESC cd;
int opindex;
PTR xinfo;
CGEN_FIELDS *fields;
void const *attrs;
bfd_vma pc;
int length;
{
disassemble_info *info = (disassemble_info *) xinfo;
switch (opindex)
{
case FR30_OPERAND_CRI :
print_keyword (cd, info, & fr30_cgen_opval_cr_names, fields->f_CRi, 0);
break;
case FR30_OPERAND_CRJ :
print_keyword (cd, info, & fr30_cgen_opval_cr_names, fields->f_CRj, 0);
break;
case FR30_OPERAND_R13 :
print_keyword (cd, info, & fr30_cgen_opval_h_r13, 0, 0);
break;
case FR30_OPERAND_R14 :
print_keyword (cd, info, & fr30_cgen_opval_h_r14, 0, 0);
break;
case FR30_OPERAND_R15 :
print_keyword (cd, info, & fr30_cgen_opval_h_r15, 0, 0);
break;
case FR30_OPERAND_RI :
print_keyword (cd, info, & fr30_cgen_opval_gr_names, fields->f_Ri, 0);
break;
case FR30_OPERAND_RIC :
print_keyword (cd, info, & fr30_cgen_opval_gr_names, fields->f_Ric, 0);
break;
case FR30_OPERAND_RJ :
print_keyword (cd, info, & fr30_cgen_opval_gr_names, fields->f_Rj, 0);
break;
case FR30_OPERAND_RJC :
print_keyword (cd, info, & fr30_cgen_opval_gr_names, fields->f_Rjc, 0);
break;
case FR30_OPERAND_RS1 :
print_keyword (cd, info, & fr30_cgen_opval_dr_names, fields->f_Rs1, 0);
break;
case FR30_OPERAND_RS2 :
print_keyword (cd, info, & fr30_cgen_opval_dr_names, fields->f_Rs2, 0);
break;
case FR30_OPERAND_CC :
print_normal (cd, info, fields->f_cc, 0, pc, length);
break;
case FR30_OPERAND_CCC :
print_normal (cd, info, fields->f_ccc, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
break;
case FR30_OPERAND_DIR10 :
print_normal (cd, info, fields->f_dir10, 0, pc, length);
break;
case FR30_OPERAND_DIR8 :
print_normal (cd, info, fields->f_dir8, 0, pc, length);
break;
case FR30_OPERAND_DIR9 :
print_normal (cd, info, fields->f_dir9, 0, pc, length);
break;
case FR30_OPERAND_DISP10 :
print_normal (cd, info, fields->f_disp10, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
break;
case FR30_OPERAND_DISP8 :
print_normal (cd, info, fields->f_disp8, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
break;
case FR30_OPERAND_DISP9 :
print_normal (cd, info, fields->f_disp9, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
break;
case FR30_OPERAND_I20 :
print_normal (cd, info, fields->f_i20, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_VIRTUAL), pc, length);
break;
case FR30_OPERAND_I32 :
print_normal (cd, info, fields->f_i32, 0|(1<<CGEN_OPERAND_HASH_PREFIX)|(1<<CGEN_OPERAND_SIGN_OPT), pc, length);
break;
case FR30_OPERAND_I8 :
print_normal (cd, info, fields->f_i8, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
break;
case FR30_OPERAND_LABEL12 :
print_address (cd, info, fields->f_rel12, 0|(1<<CGEN_OPERAND_PCREL_ADDR), pc, length);
break;
case FR30_OPERAND_LABEL9 :
print_address (cd, info, fields->f_rel9, 0|(1<<CGEN_OPERAND_PCREL_ADDR), pc, length);
break;
case FR30_OPERAND_M4 :
print_m4 (cd, info, fields->f_m4, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
break;
case FR30_OPERAND_PS :
print_keyword (cd, info, & fr30_cgen_opval_h_ps, 0, 0);
break;
case FR30_OPERAND_REGLIST_HI_LD :
print_hi_register_list_ld (cd, info, fields->f_reglist_hi_ld, 0, pc, length);
break;
case FR30_OPERAND_REGLIST_HI_ST :
print_hi_register_list_st (cd, info, fields->f_reglist_hi_st, 0, pc, length);
break;
case FR30_OPERAND_REGLIST_LOW_LD :
print_low_register_list_ld (cd, info, fields->f_reglist_low_ld, 0, pc, length);
break;
case FR30_OPERAND_REGLIST_LOW_ST :
print_low_register_list_st (cd, info, fields->f_reglist_low_st, 0, pc, length);
break;
case FR30_OPERAND_S10 :
print_normal (cd, info, fields->f_s10, 0|(1<<CGEN_OPERAND_SIGNED)|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
break;
case FR30_OPERAND_U10 :
print_normal (cd, info, fields->f_u10, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
break;
case FR30_OPERAND_U4 :
print_normal (cd, info, fields->f_u4, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
break;
case FR30_OPERAND_U4C :
print_normal (cd, info, fields->f_u4c, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
break;
case FR30_OPERAND_U8 :
print_normal (cd, info, fields->f_u8, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
break;
case FR30_OPERAND_UDISP6 :
print_normal (cd, info, fields->f_udisp6, 0|(1<<CGEN_OPERAND_HASH_PREFIX), pc, length);
break;
default :
fprintf (stderr, _("Unrecognized field %d while printing insn.\n"),
opindex);
abort ();
}
}
cgen_print_fn * const fr30_cgen_print_handlers[] =
{
print_insn_normal,
};
void
fr30_cgen_init_dis (cd)
CGEN_CPU_DESC cd;
{
fr30_cgen_init_opcode_table (cd);
fr30_cgen_init_ibld_table (cd);
cd->print_handlers = & fr30_cgen_print_handlers[0];
cd->print_operand = fr30_cgen_print_operand;
}
static void
print_normal (cd, dis_info, value, attrs, pc, length)
#ifdef CGEN_PRINT_NORMAL
CGEN_CPU_DESC cd;
#else
CGEN_CPU_DESC cd ATTRIBUTE_UNUSED;
#endif
PTR dis_info;
long value;
unsigned int attrs;
#ifdef CGEN_PRINT_NORMAL
bfd_vma pc;
int length;
#else
bfd_vma pc ATTRIBUTE_UNUSED;
int length ATTRIBUTE_UNUSED;
#endif
{
disassemble_info *info = (disassemble_info *) dis_info;
#ifdef CGEN_PRINT_NORMAL
CGEN_PRINT_NORMAL (cd, info, value, attrs, pc, length);
#endif
if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
;
else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
(*info->fprintf_func) (info->stream, "%ld", value);
else
(*info->fprintf_func) (info->stream, "0x%lx", value);
}
static void
print_address (cd, dis_info, value, attrs, pc, length)
#ifdef CGEN_PRINT_NORMAL
CGEN_CPU_DESC cd;
#else
CGEN_CPU_DESC cd ATTRIBUTE_UNUSED;
#endif
PTR dis_info;
bfd_vma value;
unsigned int attrs;
#ifdef CGEN_PRINT_NORMAL
bfd_vma pc;
int length;
#else
bfd_vma pc ATTRIBUTE_UNUSED;
int length ATTRIBUTE_UNUSED;
#endif
{
disassemble_info *info = (disassemble_info *) dis_info;
#ifdef CGEN_PRINT_ADDRESS
CGEN_PRINT_ADDRESS (cd, info, value, attrs, pc, length);
#endif
if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
;
else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR))
(*info->print_address_func) (value, info);
else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR))
(*info->print_address_func) (value, info);
else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
(*info->fprintf_func) (info->stream, "%ld", (long) value);
else
(*info->fprintf_func) (info->stream, "0x%lx", (long) value);
}
static void
print_keyword (cd, dis_info, keyword_table, value, attrs)
CGEN_CPU_DESC cd ATTRIBUTE_UNUSED;
PTR dis_info;
CGEN_KEYWORD *keyword_table;
long value;
unsigned int attrs ATTRIBUTE_UNUSED;
{
disassemble_info *info = (disassemble_info *) dis_info;
const CGEN_KEYWORD_ENTRY *ke;
ke = cgen_keyword_lookup_value (keyword_table, value);
if (ke != NULL)
(*info->fprintf_func) (info->stream, "%s", ke->name);
else
(*info->fprintf_func) (info->stream, "???");
}
static void
print_insn_normal (cd, dis_info, insn, fields, pc, length)
CGEN_CPU_DESC cd;
PTR dis_info;
const CGEN_INSN *insn;
CGEN_FIELDS *fields;
bfd_vma pc;
int length;
{
const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
disassemble_info *info = (disassemble_info *) dis_info;
const unsigned char *syn;
CGEN_INIT_PRINT (cd);
for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
{
if (CGEN_SYNTAX_MNEMONIC_P (*syn))
{
(*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
continue;
}
if (CGEN_SYNTAX_CHAR_P (*syn))
{
(*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn));
continue;
}
fr30_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info,
fields, CGEN_INSN_ATTRS (insn), pc, length);
}
}
static int
read_insn (cd, pc, info, buf, buflen, ex_info, insn_value)
CGEN_CPU_DESC cd;
bfd_vma pc;
disassemble_info *info;
char *buf;
int buflen;
CGEN_EXTRACT_INFO *ex_info;
unsigned long *insn_value;
{
int status = (*info->read_memory_func) (pc, buf, buflen, info);
if (status != 0)
{
(*info->memory_error_func) (status, pc, info);
return -1;
}
ex_info->dis_info = info;
ex_info->valid = (1 << buflen) - 1;
ex_info->insn_bytes = buf;
switch (buflen)
{
case 1:
*insn_value = buf[0];
break;
case 2:
*insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb16 (buf) : bfd_getl16 (buf);
break;
case 4:
*insn_value = info->endian == BFD_ENDIAN_BIG ? bfd_getb32 (buf) : bfd_getl32 (buf);
break;
default:
abort ();
}
return 0;
}
static int
print_insn (cd, pc, info, buf, buflen)
CGEN_CPU_DESC cd;
bfd_vma pc;
disassemble_info *info;
char *buf;
int buflen;
{
unsigned long insn_value;
const CGEN_INSN_LIST *insn_list;
CGEN_EXTRACT_INFO ex_info;
int rc = read_insn (cd, pc, info, buf, buflen, & ex_info, & insn_value);
if (rc != 0)
return rc;
insn_list = CGEN_DIS_LOOKUP_INSN (cd, buf, insn_value);
while (insn_list != NULL)
{
const CGEN_INSN *insn = insn_list->insn;
CGEN_FIELDS fields;
int length;
#ifdef CGEN_VALIDATE_INSN_SUPPORTED
if (! fr30_cgen_insn_supported (cd, insn))
{
insn_list = CGEN_DIS_NEXT_INSN (insn_list);
continue;
}
#endif
if ((insn_value & CGEN_INSN_BASE_MASK (insn))
== CGEN_INSN_BASE_VALUE (insn))
{
#if CGEN_INT_INSN_P
if (CGEN_INSN_BITSIZE (insn) > cd->base_insn_bitsize)
{
unsigned long full_insn_value;
int rc = read_insn (cd, pc, info, buf,
CGEN_INSN_BITSIZE (insn) / 8,
& ex_info, & full_insn_value);
if (rc != 0)
return rc;
length = CGEN_EXTRACT_FN (cd, insn)
(cd, insn, &ex_info, full_insn_value, &fields, pc);
}
else
#endif
length = CGEN_EXTRACT_FN (cd, insn)
(cd, insn, &ex_info, insn_value, &fields, pc);
if (length < 0)
return length;
if (length > 0)
{
CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length);
return length / 8;
}
}
insn_list = CGEN_DIS_NEXT_INSN (insn_list);
}
return 0;
}
#ifndef CGEN_PRINT_INSN
#define CGEN_PRINT_INSN default_print_insn
#endif
static int
default_print_insn (cd, pc, info)
CGEN_CPU_DESC cd;
bfd_vma pc;
disassemble_info *info;
{
char buf[CGEN_MAX_INSN_SIZE];
int status;
status = (*info->read_memory_func) (pc, buf, cd->base_insn_bitsize / 8, info);
if (status != 0)
{
(*info->memory_error_func) (status, pc, info);
return -1;
}
return print_insn (cd, pc, info, buf, cd->base_insn_bitsize / 8);
}
int
print_insn_fr30 (pc, info)
bfd_vma pc;
disassemble_info *info;
{
static CGEN_CPU_DESC cd = 0;
static int prev_isa;
static int prev_mach;
static int prev_endian;
int length;
int isa,mach;
int endian = (info->endian == BFD_ENDIAN_BIG
? CGEN_ENDIAN_BIG
: CGEN_ENDIAN_LITTLE);
enum bfd_architecture arch;
#ifndef CGEN_BFD_ARCH
#define CGEN_BFD_ARCH bfd_arch_fr30
#endif
arch = info->arch;
if (arch == bfd_arch_unknown)
arch = CGEN_BFD_ARCH;
#ifdef CGEN_COMPUTE_ISA
isa = CGEN_COMPUTE_ISA (info);
#else
isa = 0;
#endif
mach = info->mach;
if (cd
&& (isa != prev_isa
|| mach != prev_mach
|| endian != prev_endian))
{
fr30_cgen_cpu_close (cd);
cd = 0;
}
if (! cd)
{
const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach);
const char *mach_name;
if (!arch_type)
abort ();
mach_name = arch_type->printable_name;
prev_isa = isa;
prev_mach = mach;
prev_endian = endian;
cd = fr30_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa,
CGEN_CPU_OPEN_BFDMACH, mach_name,
CGEN_CPU_OPEN_ENDIAN, prev_endian,
CGEN_CPU_OPEN_END);
if (!cd)
abort ();
fr30_cgen_init_dis (cd);
}
length = CGEN_PRINT_INSN (cd, pc, info);
if (length > 0)
return length;
if (length < 0)
return -1;
(*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
return cd->default_insn_bitsize / 8;
}