X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=opcodes%2Fcris-dis.c;h=8f2a77700c48a065dd26d4304ed7625ff507372f;hb=3cd7f3e3bdab2b9f5fa0005ce2b8333b46843c3e;hp=44a4b56bd981279d56c82301179248486f0ce928;hpb=b6b0b32c895842eff432aae033ed437db8af403f;p=deliverable%2Fbinutils-gdb.git diff --git a/opcodes/cris-dis.c b/opcodes/cris-dis.c index 44a4b56bd9..8f2a77700c 100644 --- a/opcodes/cris-dis.c +++ b/opcodes/cris-dis.c @@ -1,26 +1,27 @@ /* Disassembler code for CRIS. - Copyright (C) 2000 Free Software Foundation, Inc. + Copyright (C) 2000-2020 Free Software Foundation, Inc. Contributed by Axis Communications AB, Lund, Sweden. Written by Hans-Peter Nilsson. -This file is part of the GNU binutils and GDB, the GNU debugger. + This file is part of the GNU opcodes library. -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 library 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 3, 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. + It 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + 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 "dis-asm.h" #include "opcode/cris.h" #include "libiberty.h" @@ -62,9 +63,24 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ FIXME: Make this optional later. */ #ifndef TRACE_CASE -#define TRACE_CASE 1 +#define TRACE_CASE (disdata->trace_case) #endif +enum cris_disass_family + { cris_dis_v0_v10, cris_dis_common_v10_v32, cris_dis_v32 }; + +/* Stored in the disasm_info->private_data member. */ +struct cris_disasm_data +{ + /* Whether to print something less confusing if we find something + matching a switch-construct. */ + bfd_boolean trace_case; + + /* Whether this code is flagged as crisv32. FIXME: Should be an enum + that includes "compatible". */ + enum cris_disass_family distype; +}; + /* Value of first element in switch. */ static long case_offset = 0; @@ -77,40 +93,59 @@ static long no_of_case_offsets = 0; /* Candidate for next case_offset. */ static long last_immediate = 0; -static int number_of_bits PARAMS ((unsigned int)); -static char *format_hex PARAMS ((unsigned long, char *)); -static char *format_dec PARAMS ((long, char *, int)); -static char *format_reg PARAMS ((int, char *, boolean)); -static int cris_constraint PARAMS ((const char *, unsigned int, - unsigned int)); -static unsigned bytes_to_skip PARAMS ((unsigned int, - const struct cris_opcode *)); -static char *print_flags PARAMS ((unsigned int, char *)); -static void print_with_operands - PARAMS ((const struct cris_opcode *, unsigned int, unsigned char *, - bfd_vma, disassemble_info *, const struct cris_opcode *, - unsigned int, unsigned char *, boolean)); -static const struct cris_spec_reg *spec_reg_info PARAMS ((unsigned int)); -static int print_insn_cris_generic - PARAMS ((bfd_vma, disassemble_info *, boolean)); -static int print_insn_cris_with_register_prefix - PARAMS ((bfd_vma, disassemble_info *)); -static int print_insn_cris_without_register_prefix - PARAMS ((bfd_vma, disassemble_info *)); - -/* Return the descriptor of a special register. - FIXME: Depend on a CPU-version specific argument when all machinery - is in place. */ +static int cris_constraint + (const char *, unsigned, unsigned, struct cris_disasm_data *); + +/* Parse disassembler options and store state in info. FIXME: For the + time being, we abuse static variables. */ + +static bfd_boolean +cris_parse_disassembler_options (disassemble_info *info, + enum cris_disass_family distype) +{ + struct cris_disasm_data *disdata; + + info->private_data = calloc (1, sizeof (struct cris_disasm_data)); + disdata = (struct cris_disasm_data *) info->private_data; + if (disdata == NULL) + return FALSE; + + /* Default true. */ + disdata->trace_case + = (info->disassembler_options == NULL + || (strcmp (info->disassembler_options, "nocase") != 0)); + + disdata->distype = distype; + return TRUE; +} static const struct cris_spec_reg * -spec_reg_info (sreg) - unsigned int sreg; +spec_reg_info (unsigned int sreg, enum cris_disass_family distype) { int i; + for (i = 0; cris_spec_regs[i].name != NULL; i++) { if (cris_spec_regs[i].number == sreg) - return &cris_spec_regs[i]; + { + if (distype == cris_dis_v32) + switch (cris_spec_regs[i].applicable_version) + { + case cris_ver_warning: + case cris_ver_version_all: + case cris_ver_v3p: + case cris_ver_v8p: + case cris_ver_v10p: + case cris_ver_v32p: + /* No ambiguous sizes or register names with CRISv32. */ + if (cris_spec_regs[i].warning == NULL) + return &cris_spec_regs[i]; + default: + ; + } + else if (cris_spec_regs[i].applicable_version != cris_ver_v32p) + return &cris_spec_regs[i]; + } } return NULL; @@ -119,12 +154,11 @@ spec_reg_info (sreg) /* Return the number of bits in the argument. */ static int -number_of_bits (val) - unsigned int val; +number_of_bits (unsigned int val) { int bits; - for (bits = 0; val != 0; val &= val-1) + for (bits = 0; val != 0; val &= val - 1) bits++; return bits; @@ -133,9 +167,9 @@ number_of_bits (val) /* Get an entry in the opcode-table. */ static const struct cris_opcode * -get_opcode_entry (insn, prefix_insn) - unsigned int insn; - unsigned int prefix_insn; +get_opcode_entry (unsigned int insn, + unsigned int prefix_insn, + struct cris_disasm_data *disdata) { /* For non-prefixed insns, we keep a table of pointers, indexed by the insn code. Each entry is initialized when found to be NULL. */ @@ -154,27 +188,45 @@ get_opcode_entry (insn, prefix_insn) /* Allocate and clear the opcode-table. */ if (opc_table == NULL) { - opc_table = xmalloc (65536 * sizeof (opc_table[0])); + opc_table = malloc (65536 * sizeof (opc_table[0])); + if (opc_table == NULL) + return NULL; + memset (opc_table, 0, 65536 * sizeof (const struct cris_opcode *)); dip_prefixes - = xmalloc (65536 * sizeof (const struct cris_opcode **)); + = malloc (65536 * sizeof (const struct cris_opcode **)); + if (dip_prefixes == NULL) + return NULL; + memset (dip_prefixes, 0, 65536 * sizeof (dip_prefixes[0])); bdapq_m1_prefixes - = xmalloc (65536 * sizeof (const struct cris_opcode **)); + = malloc (65536 * sizeof (const struct cris_opcode **)); + if (bdapq_m1_prefixes == NULL) + return NULL; + memset (bdapq_m1_prefixes, 0, 65536 * sizeof (bdapq_m1_prefixes[0])); bdapq_m2_prefixes - = xmalloc (65536 * sizeof (const struct cris_opcode **)); + = malloc (65536 * sizeof (const struct cris_opcode **)); + if (bdapq_m2_prefixes == NULL) + return NULL; + memset (bdapq_m2_prefixes, 0, 65536 * sizeof (bdapq_m2_prefixes[0])); bdapq_m4_prefixes - = xmalloc (65536 * sizeof (const struct cris_opcode **)); + = malloc (65536 * sizeof (const struct cris_opcode **)); + if (bdapq_m4_prefixes == NULL) + return NULL; + memset (bdapq_m4_prefixes, 0, 65536 * sizeof (bdapq_m4_prefixes[0])); rest_prefixes - = xmalloc (65536 * sizeof (const struct cris_opcode **)); + = malloc (65536 * sizeof (const struct cris_opcode **)); + if (rest_prefixes == NULL) + return NULL; + memset (rest_prefixes, 0, 65536 * sizeof (rest_prefixes[0])); } @@ -189,7 +241,7 @@ get_opcode_entry (insn, prefix_insn) const struct cris_opcode *popcodep = (opc_table[prefix_insn] != NULL ? opc_table[prefix_insn] - : get_opcode_entry (prefix_insn, NO_CRIS_PREFIX)); + : get_opcode_entry (prefix_insn, NO_CRIS_PREFIX, disdata)); if (popcodep == NULL) return NULL; @@ -246,6 +298,60 @@ get_opcode_entry (insn, prefix_insn) { int level_of_match; + if (disdata->distype == cris_dis_v32) + { + switch (opcodep->applicable_version) + { + case cris_ver_version_all: + break; + + case cris_ver_v0_3: + case cris_ver_v0_10: + case cris_ver_v3_10: + case cris_ver_sim_v0_10: + case cris_ver_v8_10: + case cris_ver_v10: + case cris_ver_warning: + continue; + + case cris_ver_v3p: + case cris_ver_v8p: + case cris_ver_v10p: + case cris_ver_v32p: + break; + + case cris_ver_v8: + abort (); + default: + abort (); + } + } + else + { + switch (opcodep->applicable_version) + { + case cris_ver_version_all: + case cris_ver_v0_3: + case cris_ver_v3p: + case cris_ver_v0_10: + case cris_ver_v8p: + case cris_ver_v8_10: + case cris_ver_v10: + case cris_ver_sim_v0_10: + case cris_ver_v10p: + case cris_ver_warning: + break; + + case cris_ver_v32p: + continue; + + case cris_ver_v8: + abort (); + default: + abort (); + } + } + /* We give a double lead for bits matching the template in cris_opcodes. Not even, because then "move p8,r10" would be given 2 bits lead over "clear.d r10". When there's a @@ -257,7 +363,8 @@ get_opcode_entry (insn, prefix_insn) && ((level_of_match = cris_constraint (opcodep->args, insn, - prefix_insn)) + prefix_insn, + disdata)) >= 0) && ((level_of_match += 2 * number_of_bits (opcodep->match @@ -289,98 +396,36 @@ get_opcode_entry (insn, prefix_insn) return max_matchedp; } -/* Format number as hex with a leading "0x" into outbuffer. */ - -static char * -format_hex (number, outbuffer) - unsigned long number; - char *outbuffer; -{ - /* Obfuscate to avoid warning on 32-bit host, but properly truncate - negative numbers on >32-bit hosts. */ - if (sizeof (number) > 4) - number &= (1 << (sizeof (number) > 4 ? 32 : 1)) - 1; - - sprintf (outbuffer, "0x%lx", number); - - /* Save this value for the "case" support. */ - if (TRACE_CASE) - last_immediate = number; - - return outbuffer + strlen (outbuffer); -} - -/* Format number as decimal into outbuffer. Parameter signedp says - whether the number should be formatted as signed (!= 0) or - unsigned (== 0). */ - -static char * -format_dec (number, outbuffer, signedp) - long number; - char *outbuffer; - int signedp; -{ - last_immediate = number; - sprintf (outbuffer, signedp ? "%ld" : "%lu", number); - - return outbuffer + strlen (outbuffer); -} - -/* Format the name of the general register regno into outbuffer. */ - -static char * -format_reg (regno, outbuffer_start, with_reg_prefix) - int regno; - char *outbuffer_start; - boolean with_reg_prefix; -{ - char *outbuffer = outbuffer_start; - - if (with_reg_prefix) - *outbuffer++ = REGISTER_PREFIX_CHAR; - - switch (regno) - { - case 15: - strcpy (outbuffer, "pc"); - break; - - case 14: - strcpy (outbuffer, "sp"); - break; - - default: - sprintf (outbuffer, "r%d", regno); - break; - } - - return outbuffer_start + strlen (outbuffer_start); -} - /* Return -1 if the constraints of a bitwise-matched instruction say that there is no match. Otherwise return a nonnegative number indicating the confidence in the match (higher is better). */ static int -cris_constraint (cs, insn, prefix_insn) - const char *cs; - unsigned int insn; - unsigned int prefix_insn; +cris_constraint (const char *cs, + unsigned int insn, + unsigned int prefix_insn, + struct cris_disasm_data *disdata) { int retval = 0; int tmp; int prefix_ok = 0; - const char *s; - for (s = cs; *s; s++) + + for (s = cs; *s; s++) switch (*s) { case '!': - /* Do not recognize "pop" if there's a prefix. */ - if (prefix_insn != NO_CRIS_PREFIX) + /* Do not recognize "pop" if there's a prefix and then only for + v0..v10. */ + if (prefix_insn != NO_CRIS_PREFIX + || disdata->distype != cris_dis_v0_v10) return -1; break; + case 'U': + /* Not recognized at disassembly. */ + return -1; + case 'M': /* Size modifier for "clear", i.e. special register 0, 4 or 8. Check that it is one of them. Only special register 12 could @@ -408,6 +453,7 @@ cris_constraint (cs, insn, prefix_insn) case 's': case 'y': + case 'Y': /* If this is a prefixed insn with postincrement (side-effect), the prefix must not be DIP. */ if (prefix_insn != NO_CRIS_PREFIX) @@ -415,7 +461,7 @@ cris_constraint (cs, insn, prefix_insn) if (insn & 0x400) { const struct cris_opcode *prefix_opcodep - = get_opcode_entry (prefix_insn, NO_CRIS_PREFIX); + = get_opcode_entry (prefix_insn, NO_CRIS_PREFIX, disdata); if (prefix_opcodep->match == DIP_OPCODE) return -1; @@ -435,7 +481,7 @@ cris_constraint (cs, insn, prefix_insn) { /* Match the prefix insn to BDAPQ. */ const struct cris_opcode *prefix_opcodep - = get_opcode_entry (prefix_insn, NO_CRIS_PREFIX); + = get_opcode_entry (prefix_insn, NO_CRIS_PREFIX, disdata); if (prefix_opcodep->match == BDAP_QUICK_OPCODE) { @@ -448,7 +494,7 @@ cris_constraint (cs, insn, prefix_insn) { unsigned int spec_reg = (insn >> 12) & 15; const struct cris_spec_reg *sregp - = spec_reg_info (spec_reg); + = spec_reg_info (spec_reg, disdata->distype); /* For a special-register, the "prefix size" must match the size of the register. */ @@ -477,7 +523,7 @@ cris_constraint (cs, insn, prefix_insn) case 'P': { const struct cris_spec_reg *sregp - = spec_reg_info ((insn >> 12) & 15); + = spec_reg_info ((insn >> 12) & 15, disdata->distype); /* Since we match four bits, we will give a value of 4-1 = 3 in a match. If there is a corresponding exact match of a @@ -507,43 +553,147 @@ cris_constraint (cs, insn, prefix_insn) return retval; } +/* Format number as hex with a leading "0x" into outbuffer. */ + +static char * +format_hex (unsigned long number, + char *outbuffer, + struct cris_disasm_data *disdata) +{ + /* Truncate negative numbers on >32-bit hosts. */ + number &= 0xffffffff; + + sprintf (outbuffer, "0x%lx", number); + + /* Save this value for the "case" support. */ + if (TRACE_CASE) + last_immediate = number; + + return outbuffer + strlen (outbuffer); +} + +/* Format number as decimal into outbuffer. Parameter signedp says + whether the number should be formatted as signed (!= 0) or + unsigned (== 0). */ + +static char * +format_dec (long number, char *outbuffer, int signedp) +{ + last_immediate = number; + if (signedp) + sprintf (outbuffer, "%ld", number); + else + sprintf (outbuffer, "%lu", (unsigned long) number); + + return outbuffer + strlen (outbuffer); +} + +/* Format the name of the general register regno into outbuffer. */ + +static char * +format_reg (struct cris_disasm_data *disdata, + int regno, + char *outbuffer_start, + bfd_boolean with_reg_prefix) +{ + char *outbuffer = outbuffer_start; + + if (with_reg_prefix) + *outbuffer++ = REGISTER_PREFIX_CHAR; + + switch (regno) + { + case 15: + /* For v32, there is no context in which we output PC. */ + if (disdata->distype == cris_dis_v32) + strcpy (outbuffer, "acr"); + else + strcpy (outbuffer, "pc"); + break; + + case 14: + strcpy (outbuffer, "sp"); + break; + + default: + sprintf (outbuffer, "r%d", regno); + break; + } + + return outbuffer_start + strlen (outbuffer_start); +} + +/* Format the name of a support register into outbuffer. */ + +static char * +format_sup_reg (unsigned int regno, + char *outbuffer_start, + bfd_boolean with_reg_prefix) +{ + char *outbuffer = outbuffer_start; + int i; + + if (with_reg_prefix) + *outbuffer++ = REGISTER_PREFIX_CHAR; + + for (i = 0; cris_support_regs[i].name != NULL; i++) + if (cris_support_regs[i].number == regno) + { + sprintf (outbuffer, "%s", cris_support_regs[i].name); + return outbuffer_start + strlen (outbuffer_start); + } + + /* There's supposed to be register names covering all numbers, though + some may be generic names. */ + sprintf (outbuffer, "format_sup_reg-BUG"); + return outbuffer_start + strlen (outbuffer_start); +} + /* Return the length of an instruction. */ static unsigned -bytes_to_skip (insn, matchedp) - unsigned int insn; - const struct cris_opcode *matchedp; +bytes_to_skip (unsigned int insn, + const struct cris_opcode *matchedp, + enum cris_disass_family distype, + const struct cris_opcode *prefix_matchedp) { /* Each insn is a word plus "immediate" operands. */ unsigned to_skip = 2; - const char *template = matchedp->args; + const char *template_name = (const char *) matchedp->args; const char *s; - for (s = template; *s; s++) - if (*s == 's' && (insn & 0x400) && (insn & 15) == 15) + for (s = template_name; *s; s++) + if ((*s == 's' || *s == 'N' || *s == 'Y') + && (insn & 0x400) && (insn & 15) == 15 + && prefix_matchedp == NULL) { /* Immediate via [pc+], so we have to check the size of the operand. */ - int mode_size = 1 << ((insn >> 4) & (*template == 'z' ? 1 : 3)); + int mode_size = 1 << ((insn >> 4) & (*template_name == 'z' ? 1 : 3)); if (matchedp->imm_oprnd_size == SIZE_FIX_32) to_skip += 4; else if (matchedp->imm_oprnd_size == SIZE_SPEC_REG) { const struct cris_spec_reg *sregp - = spec_reg_info ((insn >> 12) & 15); + = spec_reg_info ((insn >> 12) & 15, distype); /* FIXME: Improve error handling; should have been caught earlier. */ if (sregp == NULL) return 2; - /* PC is incremented by two, not one, for a byte. */ - to_skip += (sregp->reg_size + 1) & ~1; + /* PC is incremented by two, not one, for a byte. Except on + CRISv32, where constants are always DWORD-size for + special registers. */ + to_skip += + distype == cris_dis_v32 ? 4 : (sregp->reg_size + 1) & ~1; } else to_skip += (mode_size + 1) & ~1; } + else if (*s == 'n') + to_skip += 4; else if (*s == 'b') to_skip += 2; @@ -553,15 +703,17 @@ bytes_to_skip (insn, matchedp) /* Print condition code flags. */ static char * -print_flags (insn, cp) - unsigned int insn; - char *cp; +print_flags (struct cris_disasm_data *disdata, unsigned int insn, char *cp) { /* Use the v8 (Etrax 100) flag definitions for disassembly. The differences with v0 (Etrax 1..4) vs. Svinto are: - v0 'd' <=> v8 'm' - v0 'e' <=> v8 'b'. */ - static const char fnames[] = "cvznxibm"; + v0 'd' <=> v8 'm' + v0 'e' <=> v8 'b'. + FIXME: Emit v0..v3 flag names somehow. */ + static const char v8_fnames[] = "cvznxibm"; + static const char v32_fnames[] = "cvznxiup"; + const char *fnames + = disdata->distype == cris_dis_v32 ? v32_fnames : v8_fnames; unsigned char flagbits = (((insn >> 8) & 0xf0) | (insn & 15)); int i; @@ -578,20 +730,18 @@ print_flags (insn, cp) supposed to be output as an address mode. */ static void -print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, - prefix_insn, prefix_buffer, with_reg_prefix) - const struct cris_opcode *opcodep; - unsigned int insn; - unsigned char *buffer; - bfd_vma addr; - disassemble_info *info; - - /* If a prefix insn was before this insn (and is supposed to be - output as an address), here is a description of it. */ - const struct cris_opcode *prefix_opcodep; - unsigned int prefix_insn; - unsigned char *prefix_buffer; - boolean with_reg_prefix; +print_with_operands (const struct cris_opcode *opcodep, + unsigned int insn, + unsigned char *buffer, + bfd_vma addr, + disassemble_info *info, + /* If a prefix insn was before this insn (and is supposed + to be output as an address), here is a description of + it. */ + const struct cris_opcode *prefix_opcodep, + unsigned int prefix_insn, + unsigned char *prefix_buffer, + bfd_boolean with_reg_prefix) { /* Get a buffer of somewhat reasonable size where we store intermediate parts of the insn. */ @@ -600,6 +750,8 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, static const char mode_char[] = "bwd?"; const char *s; const char *cs; + struct cris_disasm_data *disdata + = (struct cris_disasm_data *) info->private_data; /* Print out the name first thing we do. */ (*info->fprintf_func) (info->stream, "%s", opcodep->name); @@ -634,7 +786,7 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, better way). */ if (opcodep->name[0] == 'j') { - if (strncmp (opcodep->name, "jsr", 3) == 0) + if (CONST_STRNEQ (opcodep->name, "jsr")) /* It's "jsr" or "jsrc". */ info->insn_type = dis_jsr; else @@ -650,14 +802,33 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, { switch (*s) { + case 'T': + tp = format_sup_reg ((insn >> 12) & 15, tp, with_reg_prefix); + break; + + case 'A': + if (with_reg_prefix) + *tp++ = REGISTER_PREFIX_CHAR; + *tp++ = 'a'; + *tp++ = 'c'; + *tp++ = 'r'; + break; + + case '[': + case ']': case ',': *tp++ = *s; break; case '!': - /* Ignore at this point; used at earlier stages to avoid recognition - if there's a prefixes at something that in other ways looks like - a "pop". */ + /* Ignore at this point; used at earlier stages to avoid + recognition if there's a prefix at something that in other + ways looks like a "pop". */ + break; + + case 'd': + /* Ignore. This is an optional ".d " on the large one of + relaxable insns. */ break; case 'B': @@ -669,22 +840,55 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, case 'D': case 'r': - tp = format_reg (insn & 15, tp, with_reg_prefix); + tp = format_reg (disdata, insn & 15, tp, with_reg_prefix); break; case 'R': - tp = format_reg ((insn >> 12) & 15, tp, with_reg_prefix); + tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix); + break; + + case 'n': + { + /* Like N but pc-relative to the start of the insn. */ + int32_t number = (buffer[2] + buffer[3] * 256 + buffer[4] * 65536 + + buffer[5] * 0x1000000u); + + /* Finish off and output previous formatted bytes. */ + *tp = 0; + if (temp[0]) + (*info->fprintf_func) (info->stream, "%s", temp); + tp = temp; + + (*info->print_address_func) (addr + number, info); + } + break; + + case 'u': + { + /* Like n but the offset is bits <3:0> in the instruction. */ + unsigned int number = (buffer[0] & 0xf) * 2; + + /* Finish off and output previous formatted bytes. */ + *tp = 0; + if (temp[0]) + (*info->fprintf_func) (info->stream, "%s", temp); + tp = temp; + + (*info->print_address_func) (addr + number, info); + } break; + case 'N': case 'y': + case 'Y': case 'S': case 's': /* Any "normal" memory operand. */ - if ((insn & 0x400) && (insn & 15) == 15) + if ((insn & 0x400) && (insn & 15) == 15 && prefix_opcodep == NULL) { /* We're looking at [pc+], i.e. we need to output an immediate number, where the size can depend on different things. */ - long number; + int32_t number; int signedp = ((*cs == 'z' && (insn & 0x20)) || opcodep->match == BDAP_QUICK_OPCODE); @@ -695,7 +899,7 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, else if (opcodep->imm_oprnd_size == SIZE_SPEC_REG) { const struct cris_spec_reg *sregp - = spec_reg_info ((insn >> 12) & 15); + = spec_reg_info ((insn >> 12) & 15, disdata->distype); /* A NULL return should have been as a non-match earlier, so catch it as an internal error in the error-case @@ -704,8 +908,11 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, /* Whatever non-valid size. */ nbytes = 42; else - /* PC is always incremented by a multiple of two. */ - nbytes = (sregp->reg_size + 1) & ~1; + /* PC is always incremented by a multiple of two. + For CRISv32, immediates are always 4 bytes for + special registers. */ + nbytes = disdata->distype == cris_dis_v32 + ? 4 : (sregp->reg_size + 1) & ~1; } else { @@ -732,9 +939,8 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, break; case 4: - number - = buffer[2] + buffer[3] * 256 + buffer[4] * 65536 - + buffer[5] * 0x1000000; + number = (buffer[2] + buffer[3] * 256 + buffer[4] * 65536 + + buffer[5] * 0x1000000u); break; default: @@ -773,7 +979,7 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, info->target = number; } else - tp = format_hex (number, tp); + tp = format_hex (number, tp, disdata); } } else @@ -794,7 +1000,7 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, else if (opcodep->imm_oprnd_size == SIZE_SPEC_REG) { const struct cris_spec_reg *sregp - = spec_reg_info ((insn >> 12) & 15); + = spec_reg_info ((insn >> 12) & 15, disdata->distype); /* FIXME: Improve error handling; should have been caught earlier. */ @@ -819,7 +1025,7 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, { if (insn & 0x400) { - tp = format_reg (insn & 15, tp, with_reg_prefix); + tp = format_reg (disdata, insn & 15, tp, with_reg_prefix); *tp++ = '='; } @@ -834,10 +1040,10 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, { /* It's [pc+]. This cannot possibly be anything but an address. */ - unsigned long number - = prefix_buffer[2] + prefix_buffer[3] * 256 - + prefix_buffer[4] * 65536 - + prefix_buffer[5] * 0x1000000; + int32_t number = (prefix_buffer[2] + + prefix_buffer[3] * 256 + + prefix_buffer[4] * 65536 + + prefix_buffer[5] * 0x1000000u); info->target = (bfd_vma) number; @@ -861,7 +1067,7 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, info->target2 = prefix_insn & 15; *tp++ = '['; - tp = format_reg (prefix_insn & 15, tp, + tp = format_reg (disdata, prefix_insn & 15, tp, with_reg_prefix); if (prefix_insn & 0x400) *tp++ = '+'; @@ -878,7 +1084,7 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, number -= 256; /* Output "reg+num" or, if num < 0, "reg-num". */ - tp = format_reg ((prefix_insn >> 12) & 15, tp, + tp = format_reg (disdata, (prefix_insn >> 12) & 15, tp, with_reg_prefix); if (number >= 0) *tp++ = '+'; @@ -892,9 +1098,10 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, case BIAP_OPCODE: /* Output "r+R.m". */ - tp = format_reg (prefix_insn & 15, tp, with_reg_prefix); + tp = format_reg (disdata, prefix_insn & 15, tp, + with_reg_prefix); *tp++ = '+'; - tp = format_reg ((prefix_insn >> 12) & 15, tp, + tp = format_reg (disdata, (prefix_insn >> 12) & 15, tp, with_reg_prefix); *tp++ = '.'; *tp++ = mode_char[(prefix_insn >> 4) & 3]; @@ -917,12 +1124,12 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, case BDAP_INDIR_OPCODE: /* Output "r+s.m", or, if "s" is [pc+], "r+s" or "r-s". */ - tp = format_reg ((prefix_insn >> 12) & 15, tp, + tp = format_reg (disdata, (prefix_insn >> 12) & 15, tp, with_reg_prefix); if ((prefix_insn & 0x400) && (prefix_insn & 15) == 15) { - long number; + int32_t number; unsigned int nbytes; /* It's a value. Get its size. */ @@ -948,10 +1155,9 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, break; case 4: - number - = prefix_buffer[2] + prefix_buffer[3] * 256 - + prefix_buffer[4] * 65536 - + prefix_buffer[5] * 0x1000000; + number = (prefix_buffer[2] + prefix_buffer[3] * 256 + + prefix_buffer[4] * 65536 + + prefix_buffer[5] * 0x1000000u); break; default: @@ -988,7 +1194,7 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, /* Output "r+[R].m" or "r+[R+].m". */ *tp++ = '+'; *tp++ = '['; - tp = format_reg (prefix_insn & 15, tp, + tp = format_reg (disdata, prefix_insn & 15, tp, with_reg_prefix); if (prefix_insn & 0x400) *tp++ = '+'; @@ -1018,7 +1224,7 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, } else { - tp = format_reg (insn & 15, tp, with_reg_prefix); + tp = format_reg (disdata, insn & 15, tp, with_reg_prefix); info->flags |= CRIS_DIS_FLAG_MEM_TARGET_IS_REG; info->target = insn & 15; @@ -1031,7 +1237,7 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, break; case 'x': - tp = format_reg ((insn >> 12) & 15, tp, with_reg_prefix); + tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix); *tp++ = '.'; *tp++ = mode_char[(insn >> 4) & 3]; break; @@ -1047,7 +1253,7 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, if (where > 32767) where -= 65536; - where += addr + 4; + where += addr + ((disdata->distype == cris_dis_v32) ? 0 : 4); if (insn == BA_PC_INCR_OPCODE) info->insn_type = dis_branch; @@ -1076,6 +1282,7 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, case 'o': { long offset = insn & 0xfe; + bfd_vma target; if (insn & 1) offset |= ~0xff; @@ -1085,15 +1292,16 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, else info->insn_type = dis_condbranch; - info->target = (bfd_vma) (addr + 2 + offset); + target = addr + ((disdata->distype == cris_dis_v32) ? 0 : 2) + offset; + info->target = target; *tp = 0; tp = temp; (*info->fprintf_func) (info->stream, "%s", temp); - - (*info->print_address_func) ((bfd_vma) (addr + 2 + offset), info); + (*info->print_address_func) (target, info); } break; + case 'Q': case 'O': { long number = buffer[0]; @@ -1103,22 +1311,22 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, tp = format_dec (number, tp, 1); *tp++ = ','; - tp = format_reg ((insn >> 12) & 15, tp, with_reg_prefix); + tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix); } break; case 'f': - tp = print_flags (insn, tp); + tp = print_flags (disdata, insn, tp); break; case 'i': - tp = format_dec ((insn & 32) ? (insn & 31) | ~31 : insn & 31, tp, 1); + tp = format_dec ((insn & 32) ? (insn & 31) | ~31L : insn & 31, tp, 1); break; case 'P': { const struct cris_spec_reg *sregp - = spec_reg_info ((insn >> 12) & 15); + = spec_reg_info ((insn >> 12) & 15, disdata->distype); if (sregp->name == NULL) /* Should have been caught as a non-match eariler. */ @@ -1152,20 +1360,18 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, itself or in a "move.d const,rN, sub.d rN,rM"-like sequence. */ if (TRACE_CASE && case_offset_counter == 0) { - if (strncmp (opcodep->name, "sub", 3) == 0) + if (CONST_STRNEQ (opcodep->name, "sub")) case_offset = last_immediate; /* It could also be an "add", if there are negative case-values. */ - else if (strncmp (opcodep->name, "add", 3) == 0) - { - /* The first case is the negated operand to the add. */ - case_offset = -last_immediate; - } + else if (CONST_STRNEQ (opcodep->name, "add")) + /* The first case is the negated operand to the add. */ + case_offset = -last_immediate; + /* A bound insn will tell us the number of cases. */ - else if (strncmp (opcodep->name, "bound", 5) == 0) - { - no_of_case_offsets = last_immediate + 1; - } + else if (CONST_STRNEQ (opcodep->name, "bound")) + no_of_case_offsets = last_immediate + 1; + /* A jump or jsr or branch breaks the chain of insns for a case-table, so assume default first-case again. */ else if (info->insn_type == dis_jsr @@ -1181,21 +1387,22 @@ print_with_operands (opcodep, insn, buffer, addr, info, prefix_opcodep, WITH_REG_PREFIX. */ static int -print_insn_cris_generic (memaddr, info, with_reg_prefix) - bfd_vma memaddr; - disassemble_info *info; - boolean with_reg_prefix; +print_insn_cris_generic (bfd_vma memaddr, + disassemble_info *info, + bfd_boolean with_reg_prefix) { int nbytes; unsigned int insn; const struct cris_opcode *matchedp; int advance = 0; + struct cris_disasm_data *disdata + = (struct cris_disasm_data *) info->private_data; /* No instruction will be disassembled as longer than this number of bytes; stacked prefixes will not be expanded. */ unsigned char buffer[MAX_BYTES_PER_CRIS_INSN]; unsigned char *bufp; - int status; + int status = 0; bfd_vma addr; /* There will be an "out of range" error after the last instruction. @@ -1243,7 +1450,7 @@ print_insn_cris_generic (memaddr, info, with_reg_prefix) advance += 2; /* If to print data as offsets, then shortcut here. */ - (*info->fprintf_func) (info->stream, "case %d%s: -> ", + (*info->fprintf_func) (info->stream, "case %ld%s: -> ", case_offset + no_of_case_offsets - case_offset_counter, case_offset_counter == 1 ? "/default" : @@ -1268,7 +1475,9 @@ print_insn_cris_generic (memaddr, info, with_reg_prefix) valid "bcc .+2" insn, it is also useless enough and enough of a nuiscance that we will just output "bcc .+2" for it and signal it as a noninsn. */ - (*info->fprintf_func) (info->stream, "bcc .+2"); + (*info->fprintf_func) (info->stream, + disdata->distype == cris_dis_v32 + ? "bcc ." : "bcc .+2"); info->insn_type = dis_noninsn; advance += 2; } @@ -1279,7 +1488,7 @@ print_insn_cris_generic (memaddr, info, with_reg_prefix) unsigned int prefix_insn = insn; int prefix_size = 0; - matchedp = get_opcode_entry (insn, NO_CRIS_PREFIX); + matchedp = get_opcode_entry (insn, NO_CRIS_PREFIX, disdata); /* Check if we're supposed to write out prefixes as address modes and if this was a prefix. */ @@ -1287,11 +1496,12 @@ print_insn_cris_generic (memaddr, info, with_reg_prefix) { /* If it's a prefix, put it into the prefix vars and get the main insn. */ - prefix_size = bytes_to_skip (prefix_insn, matchedp); + prefix_size = bytes_to_skip (prefix_insn, matchedp, + disdata->distype, NULL); prefix_opcodep = matchedp; insn = bufp[prefix_size] + bufp[prefix_size + 1] * 256; - matchedp = get_opcode_entry (insn, prefix_insn); + matchedp = get_opcode_entry (insn, prefix_insn, disdata); if (matchedp != NULL) { @@ -1312,14 +1522,16 @@ print_insn_cris_generic (memaddr, info, with_reg_prefix) if (matchedp == NULL) { - (*info->fprintf_func) (info->stream, "??0x%lx", insn); + (*info->fprintf_func) (info->stream, "??0x%x", insn); advance += 2; info->insn_type = dis_noninsn; } else { - advance += bytes_to_skip (insn, matchedp); + advance + += bytes_to_skip (insn, matchedp, disdata->distype, + prefix_opcodep); /* The info_type and assorted fields will be set according to the operands. */ @@ -1360,45 +1572,114 @@ print_insn_cris_generic (memaddr, info, with_reg_prefix) return advance; } -/* Disassemble, prefixing register names with `$'. */ +/* Disassemble, prefixing register names with `$'. CRIS v0..v10. */ + +static int +print_insn_cris_with_register_prefix (bfd_vma vma, + disassemble_info *info) +{ + if (info->private_data == NULL + && !cris_parse_disassembler_options (info, cris_dis_v0_v10)) + return -1; + return print_insn_cris_generic (vma, info, TRUE); +} + +/* Disassemble, prefixing register names with `$'. CRIS v32. */ + +static int +print_insn_crisv32_with_register_prefix (bfd_vma vma, + disassemble_info *info) +{ + if (info->private_data == NULL + && !cris_parse_disassembler_options (info, cris_dis_v32)) + return -1; + return print_insn_cris_generic (vma, info, TRUE); +} + +/* Disassemble, prefixing register names with `$'. + Common v10 and v32 subset. */ + +static int +print_insn_crisv10_v32_with_register_prefix (bfd_vma vma, + disassemble_info *info) +{ + if (info->private_data == NULL + && !cris_parse_disassembler_options (info, cris_dis_common_v10_v32)) + return -1; + return print_insn_cris_generic (vma, info, TRUE); +} + +/* Disassemble, no prefixes on register names. CRIS v0..v10. */ + +static int +print_insn_cris_without_register_prefix (bfd_vma vma, + disassemble_info *info) +{ + if (info->private_data == NULL + && !cris_parse_disassembler_options (info, cris_dis_v0_v10)) + return -1; + return print_insn_cris_generic (vma, info, FALSE); +} + +/* Disassemble, no prefixes on register names. CRIS v32. */ static int -print_insn_cris_with_register_prefix (vma, info) - bfd_vma vma; - disassemble_info *info; +print_insn_crisv32_without_register_prefix (bfd_vma vma, + disassemble_info *info) { - return print_insn_cris_generic (vma, info, true); + if (info->private_data == NULL + && !cris_parse_disassembler_options (info, cris_dis_v32)) + return -1; + return print_insn_cris_generic (vma, info, FALSE); } -/* Disassemble, no prefixes on register names. */ +/* Disassemble, no prefixes on register names. + Common v10 and v32 subset. */ static int -print_insn_cris_without_register_prefix (vma, info) - bfd_vma vma; - disassemble_info *info; +print_insn_crisv10_v32_without_register_prefix (bfd_vma vma, + disassemble_info *info) { - return print_insn_cris_generic (vma, info, false); + if (info->private_data == NULL + && !cris_parse_disassembler_options (info, cris_dis_common_v10_v32)) + return -1; + return print_insn_cris_generic (vma, info, FALSE); } /* Return a disassembler-function that prints registers with a `$' prefix, - or one that prints registers without a prefix. */ + or one that prints registers without a prefix. + FIXME: We should improve the solution to avoid the multitude of + functions seen above. */ disassembler_ftype -cris_get_disassembler (abfd) - bfd *abfd; +cris_get_disassembler (bfd *abfd) { /* If there's no bfd in sight, we return what is valid as input in all contexts if fed back to the assembler: disassembly *with* register - prefix. */ - if (abfd == NULL || bfd_get_symbol_leading_char (abfd) == 0) + prefix. Unfortunately this will be totally wrong for v32. */ + if (abfd == NULL) return print_insn_cris_with_register_prefix; + if (bfd_get_symbol_leading_char (abfd) == 0) + { + if (bfd_get_mach (abfd) == bfd_mach_cris_v32) + return print_insn_crisv32_with_register_prefix; + if (bfd_get_mach (abfd) == bfd_mach_cris_v10_v32) + return print_insn_crisv10_v32_with_register_prefix; + + /* We default to v10. This may be specifically specified in the + bfd mach, but is also the default setting. */ + return print_insn_cris_with_register_prefix; + } + + if (bfd_get_mach (abfd) == bfd_mach_cris_v32) + return print_insn_crisv32_without_register_prefix; + if (bfd_get_mach (abfd) == bfd_mach_cris_v10_v32) + return print_insn_crisv10_v32_without_register_prefix; return print_insn_cris_without_register_prefix; } -/* - * Local variables: - * eval: (c-set-style "gnu") - * indent-tabs-mode: t - * End: - */ +/* Local variables: + eval: (c-set-style "gnu") + indent-tabs-mode: t + End: */