+ if (auxr->subclass != NONE)
+ return NULL;
+
+ if (auxr->address == value)
+ return auxr->name;
+ }
+ return NULL;
+}
+
+/* Convert a value representing an address type to a string used to refer to
+ the address type in assembly code. */
+
+static const char *
+get_addrtype (int value)
+{
+ if (value < 0 || value > addrtypenames_max)
+ return addrtypeunknown;
+
+ return addrtypenames[value];
+}
+
+/* Calculate the instruction length for an instruction starting with MSB
+ and LSB, the most and least significant byte. The ISA_MASK is used to
+ filter the instructions considered to only those that are part of the
+ current architecture.
+
+ The instruction lengths are calculated from the ARC_OPCODE table, and
+ cached for later use. */
+
+static unsigned int
+arc_insn_length (bfd_byte msb, bfd_byte lsb, struct disassemble_info *info)
+{
+ bfd_byte major_opcode = msb >> 3;
+
+ switch (info->mach)
+ {
+ case bfd_mach_arc_arc700:
+ /* The nps400 extension set requires this special casing of the
+ instruction length calculation. Right now this is not causing any
+ problems as none of the known extensions overlap in opcode space,
+ but, if they ever do then we might need to start carrying
+ information around in the elf about which extensions are in use. */
+ if (major_opcode == 0xb)
+ {
+ bfd_byte minor_opcode = lsb & 0x1f;
+
+ if (minor_opcode < 4)
+ return 6;
+ else if (minor_opcode == 0x10 || minor_opcode == 0x11)
+ return 8;
+ }
+ if (major_opcode == 0xa)
+ {
+ return 8;
+ }
+ /* Fall through. */
+ case bfd_mach_arc_arc600:
+ return (major_opcode > 0xb) ? 2 : 4;
+ break;
+
+ case bfd_mach_arc_arcv2:
+ return (major_opcode > 0x7) ? 2 : 4;
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Extract and return the value of OPERAND from the instruction whose value
+ is held in the array INSN. */
+
+static int
+extract_operand_value (const struct arc_operand *operand,
+ unsigned long long insn,
+ unsigned limm)
+{
+ int value;
+
+ /* Read the limm operand, if required. */
+ if (operand->flags & ARC_OPERAND_LIMM)
+ /* The second part of the instruction value will have been loaded as
+ part of the find_format call made earlier. */
+ value = limm;
+ else
+ {
+ if (operand->extract)
+ value = (*operand->extract) (insn, (int *) NULL);
+ else
+ {
+ if (operand->flags & ARC_OPERAND_ALIGNED32)
+ {
+ value = (insn >> operand->shift)
+ & ((1 << (operand->bits - 2)) - 1);
+ value = value << 2;
+ }
+ else
+ {
+ value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
+ }
+ if (operand->flags & ARC_OPERAND_SIGNED)
+ {
+ int signbit = 1 << (operand->bits - 1);
+ value = (value ^ signbit) - signbit;
+ }
+ }
+ }
+
+ return value;
+}
+
+/* Find the next operand, and the operands value from ITER. Return TRUE if
+ there is another operand, otherwise return FALSE. If there is an
+ operand returned then the operand is placed into OPERAND, and the value
+ into VALUE. If there is no operand returned then OPERAND and VALUE are
+ unchanged. */
+
+static bfd_boolean
+operand_iterator_next (struct arc_operand_iterator *iter,
+ const struct arc_operand **operand,
+ int *value)
+{
+ if (*iter->opidx == 0)
+ {
+ *operand = NULL;
+ return FALSE;
+ }
+
+ *operand = &arc_operands[*iter->opidx];
+ *value = extract_operand_value (*operand, iter->insn, iter->limm);
+ iter->opidx++;
+
+ return TRUE;
+}
+
+/* Helper for parsing the options. */
+
+static void
+parse_option (const char *option)
+{
+ if (disassembler_options_cmp (option, "dsp") == 0)
+ add_to_decodelist (DSP, NONE);
+
+ else if (disassembler_options_cmp (option, "spfp") == 0)
+ add_to_decodelist (FLOAT, SPX);
+
+ else if (disassembler_options_cmp (option, "dpfp") == 0)
+ add_to_decodelist (FLOAT, DPX);
+
+ else if (disassembler_options_cmp (option, "quarkse_em") == 0)
+ {
+ add_to_decodelist (FLOAT, DPX);
+ add_to_decodelist (FLOAT, SPX);
+ add_to_decodelist (FLOAT, QUARKSE1);
+ add_to_decodelist (FLOAT, QUARKSE2);
+ }
+
+ else if (disassembler_options_cmp (option, "fpuda") == 0)
+ add_to_decodelist (FLOAT, DPA);
+
+ else if (disassembler_options_cmp (option, "fpus") == 0)
+ {
+ add_to_decodelist (FLOAT, SP);
+ add_to_decodelist (FLOAT, CVT);
+ }
+
+ else if (disassembler_options_cmp (option, "fpud") == 0)
+ {
+ add_to_decodelist (FLOAT, DP);
+ add_to_decodelist (FLOAT, CVT);
+ }
+ else if (CONST_STRNEQ (option, "hex"))
+ print_hex = TRUE;
+ else
+ fprintf (stderr, _("Unrecognised disassembler option: %s\n"), option);
+}
+
+#define ARC_CPU_TYPE_A6xx(NAME,EXTRA) \
+ { #NAME, ARC_OPCODE_ARC600, "ARC600" }
+#define ARC_CPU_TYPE_A7xx(NAME,EXTRA) \
+ { #NAME, ARC_OPCODE_ARC700, "ARC700" }
+#define ARC_CPU_TYPE_AV2EM(NAME,EXTRA) \
+ { #NAME, ARC_OPCODE_ARCv2EM, "ARC EM" }
+#define ARC_CPU_TYPE_AV2HS(NAME,EXTRA) \
+ { #NAME, ARC_OPCODE_ARCv2HS, "ARC HS" }
+#define ARC_CPU_TYPE_NONE \
+ { 0, 0, 0 }
+
+/* A table of CPU names and opcode sets. */
+static const struct cpu_type
+{
+ const char *name;
+ unsigned flags;
+ const char *isa;
+}
+ cpu_types[] =
+{
+ #include "elf/arc-cpu.def"
+};
+
+/* Helper for parsing the CPU options. Accept any of the ARC architectures
+ values. OPTION should be a value passed to cpu=. */
+
+static unsigned
+parse_cpu_option (const char *option)
+{
+ int i;
+
+ for (i = 0; cpu_types[i].name; ++i)
+ {
+ if (!disassembler_options_cmp (cpu_types[i].name, option))
+ {
+ return cpu_types[i].flags;
+ }
+ }
+
+ fprintf (stderr, _("Unrecognised disassembler CPU option: %s\n"), option);
+ return ARC_OPCODE_NONE;
+}
+
+/* Go over the options list and parse it. */
+
+static void
+parse_disassembler_options (const char *options)
+{
+ const char *option;
+
+ if (options == NULL)
+ return;
+
+ /* Disassembler might be reused for difference CPU's, and cpu option set for
+ the first one shouldn't be applied to second (which might not have
+ explicit cpu in its options. Therefore it is required to reset enforced
+ CPU when new options are being parsed. */
+ enforced_isa_mask = ARC_OPCODE_NONE;
+
+ FOR_EACH_DISASSEMBLER_OPTION (option, options)
+ {
+ /* A CPU option? Cannot use STRING_COMMA_LEN because strncmp is also a
+ preprocessor macro. */
+ if (strncmp (option, "cpu=", 4) == 0)
+ /* Strip leading `cpu=`. */
+ enforced_isa_mask = parse_cpu_option (option + 4);
+ else
+ parse_option (option);
+ }
+}
+
+/* Return the instruction type for an instruction described by OPCODE. */
+
+static enum dis_insn_type
+arc_opcode_to_insn_type (const struct arc_opcode *opcode)
+{
+ enum dis_insn_type insn_type;
+
+ switch (opcode->insn_class)
+ {
+ case BRANCH:
+ case BBIT0:
+ case BBIT1:
+ case BI:
+ case BIH:
+ case BRCC:
+ case EI:
+ case JLI:
+ case JUMP:
+ case LOOP:
+ if (!strncmp (opcode->name, "bl", 2)
+ || !strncmp (opcode->name, "jl", 2))
+ {
+ if (opcode->subclass == COND)
+ insn_type = dis_condjsr;
+ else
+ insn_type = dis_jsr;
+ }
+ else
+ {
+ if (opcode->subclass == COND)
+ insn_type = dis_condbranch;
+ else
+ insn_type = dis_branch;
+ }
+ break;
+ case LOAD:
+ case STORE:
+ case MEMORY:
+ case ENTER:
+ case PUSH:
+ case POP:
+ insn_type = dis_dref;
+ break;
+ case LEAVE:
+ insn_type = dis_branch;
+ break;
+ default:
+ insn_type = dis_nonbranch;
+ break;
+ }
+
+ return insn_type;
+}
+
+/* Disassemble ARC instructions. */
+
+static int
+print_insn_arc (bfd_vma memaddr,
+ struct disassemble_info *info)
+{
+ bfd_byte buffer[8];
+ unsigned int highbyte, lowbyte;
+ int status;
+ unsigned int insn_len;
+ unsigned long long insn = 0;
+ unsigned isa_mask = ARC_OPCODE_NONE;
+ const struct arc_opcode *opcode;
+ bfd_boolean need_comma;
+ bfd_boolean open_braket;
+ int size;
+ const struct arc_operand *operand;
+ int value, vpcl;
+ struct arc_operand_iterator iter;
+ struct arc_disassemble_info *arc_infop;
+ bfd_boolean rpcl = FALSE, rset = FALSE;
+
+ if (info->disassembler_options)
+ {
+ parse_disassembler_options (info->disassembler_options);
+
+ /* Avoid repeated parsing of the options. */
+ info->disassembler_options = NULL;
+ }
+
+ if (info->private_data == NULL && !init_arc_disasm_info (info))
+ return -1;
+
+ memset (&iter, 0, sizeof (iter));
+ highbyte = ((info->endian == BFD_ENDIAN_LITTLE) ? 1 : 0);
+ lowbyte = ((info->endian == BFD_ENDIAN_LITTLE) ? 0 : 1);
+
+ /* Figure out CPU type, unless it was enforced via disassembler options. */
+ if (enforced_isa_mask == ARC_OPCODE_NONE)
+ {
+ Elf_Internal_Ehdr *header = NULL;
+
+ if (info->section && info->section->owner)
+ header = elf_elfheader (info->section->owner);
+
+ switch (info->mach)
+ {
+ case bfd_mach_arc_arc700:
+ isa_mask = ARC_OPCODE_ARC700;
+ break;
+
+ case bfd_mach_arc_arc600:
+ isa_mask = ARC_OPCODE_ARC600;
+ break;
+
+ case bfd_mach_arc_arcv2:
+ default:
+ isa_mask = ARC_OPCODE_ARCv2EM;
+ /* TODO: Perhaps remove definition of header since it is only used at
+ this location. */
+ if (header != NULL
+ && (header->e_flags & EF_ARC_MACH_MSK) == EF_ARC_CPU_ARCV2HS)
+ isa_mask = ARC_OPCODE_ARCv2HS;
+ break;
+ }
+ }
+ else
+ isa_mask = enforced_isa_mask;
+
+ if (isa_mask == ARC_OPCODE_ARCv2HS)
+ {
+ /* FPU instructions are not extensions for HS. */
+ add_to_decodelist (FLOAT, SP);
+ add_to_decodelist (FLOAT, DP);
+ add_to_decodelist (FLOAT, CVT);
+ }
+
+ /* This variable may be set by the instruction decoder. It suggests
+ the number of bytes objdump should display on a single line. If
+ the instruction decoder sets this, it should always set it to
+ the same value in order to get reasonable looking output. */
+
+ info->bytes_per_line = 8;
+
+ /* In the next lines, we set two info variables control the way
+ objdump displays the raw data. For example, if bytes_per_line is
+ 8 and bytes_per_chunk is 4, the output will look like this:
+ 00: 00000000 00000000
+ with the chunks displayed according to "display_endian". */
+
+ if (info->section
+ && !(info->section->flags & SEC_CODE))
+ {
+ /* This is not a CODE section. */
+ switch (info->section->size)
+ {
+ case 1:
+ case 2:
+ case 4:
+ size = info->section->size;
+ break;
+ default:
+ size = (info->section->size & 0x01) ? 1 : 4;
+ break;
+ }
+ info->bytes_per_chunk = 1;
+ info->display_endian = info->endian;
+ }
+ else
+ {
+ size = 2;
+ info->bytes_per_chunk = 2;
+ info->display_endian = info->endian;
+ }
+
+ /* Read the insn into a host word. */
+ status = (*info->read_memory_func) (memaddr, buffer, size, info);
+
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+
+ if (info->section
+ && !(info->section->flags & SEC_CODE))
+ {
+ /* Data section. */
+ unsigned long data;
+
+ data = bfd_get_bits (buffer, size * 8,
+ info->display_endian == BFD_ENDIAN_BIG);
+ switch (size)
+ {
+ case 1:
+ (*info->fprintf_func) (info->stream, ".byte\t0x%02lx", data);
+ break;
+ case 2:
+ (*info->fprintf_func) (info->stream, ".short\t0x%04lx", data);
+ break;
+ case 4:
+ (*info->fprintf_func) (info->stream, ".word\t0x%08lx", data);
+ break;
+ default:
+ abort ();
+ }
+ return size;
+ }
+
+ insn_len = arc_insn_length (buffer[highbyte], buffer[lowbyte], info);
+ pr_debug ("instruction length = %d bytes\n", insn_len);
+ arc_infop = info->private_data;
+ arc_infop->insn_len = insn_len;
+
+ switch (insn_len)
+ {
+ case 2:
+ insn = (buffer[highbyte] << 8) | buffer[lowbyte];
+ break;
+
+ case 4:
+ {
+ /* This is a long instruction: Read the remaning 2 bytes. */
+ status = (*info->read_memory_func) (memaddr + 2, &buffer[2], 2, info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr + 2, info);
+ return -1;
+ }
+ insn = (unsigned long long) ARRANGE_ENDIAN (info, buffer);
+ }
+ break;
+
+ case 6:
+ {
+ status = (*info->read_memory_func) (memaddr + 2, &buffer[2], 4, info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr + 2, info);
+ return -1;
+ }
+ insn = (unsigned long long) ARRANGE_ENDIAN (info, &buffer[2]);
+ insn |= ((unsigned long long) buffer[highbyte] << 40)
+ | ((unsigned long long) buffer[lowbyte] << 32);
+ }
+ break;
+
+ case 8:
+ {
+ status = (*info->read_memory_func) (memaddr + 2, &buffer[2], 6, info);
+ if (status != 0)
+ {
+ (*info->memory_error_func) (status, memaddr + 2, info);
+ return -1;
+ }
+ insn =
+ ((((unsigned long long) ARRANGE_ENDIAN (info, buffer)) << 32)
+ | ((unsigned long long) ARRANGE_ENDIAN (info, &buffer[4])));
+ }
+ break;
+
+ default:
+ /* There is no instruction whose length is not 2, 4, 6, or 8. */
+ abort ();
+ }
+
+ pr_debug ("instruction value = %llx\n", insn);
+
+ /* Set some defaults for the insn info. */
+ info->insn_info_valid = 1;
+ info->branch_delay_insns = 0;
+ info->data_size = 4;
+ info->insn_type = dis_nonbranch;
+ info->target = 0;
+ info->target2 = 0;
+
+ /* FIXME to be moved in dissasemble_init_for_target. */
+ info->disassembler_needs_relocs = TRUE;
+
+ /* Find the first match in the opcode table. */
+ if (!find_format (memaddr, insn, &insn_len, isa_mask, info, &opcode, &iter))
+ return -1;
+
+ if (!opcode)
+ {
+ switch (insn_len)
+ {
+ case 2:
+ (*info->fprintf_func) (info->stream, ".shor\t%#04llx",
+ insn & 0xffff);
+ break;
+ case 4:
+ (*info->fprintf_func) (info->stream, ".word\t%#08llx",
+ insn & 0xffffffff);
+ break;
+ case 6:
+ (*info->fprintf_func) (info->stream, ".long\t%#08llx",
+ insn & 0xffffffff);
+ (*info->fprintf_func) (info->stream, ".long\t%#04llx",
+ (insn >> 32) & 0xffff);
+ break;
+ case 8:
+ (*info->fprintf_func) (info->stream, ".long\t%#08llx",
+ insn & 0xffffffff);
+ (*info->fprintf_func) (info->stream, ".long\t%#08llx",
+ insn >> 32);
+ break;
+ default:
+ abort ();
+ }
+
+ info->insn_type = dis_noninsn;
+ return insn_len;
+ }
+
+ /* Print the mnemonic. */
+ (*info->fprintf_func) (info->stream, "%s", opcode->name);
+
+ /* Preselect the insn class. */
+ info->insn_type = arc_opcode_to_insn_type (opcode);
+
+ pr_debug ("%s: 0x%08llx\n", opcode->name, opcode->opcode);
+
+ print_flags (opcode, &insn, info);
+
+ if (opcode->operands[0] != 0)
+ (*info->fprintf_func) (info->stream, "\t");
+
+ need_comma = FALSE;
+ open_braket = FALSE;
+ arc_infop->operands_count = 0;
+
+ /* Now extract and print the operands. */
+ operand = NULL;
+ vpcl = 0;
+ while (operand_iterator_next (&iter, &operand, &value))
+ {