+ return 0;
+ }
+}
+
+/* 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, "nps400") == 0)
+ {
+ add_to_decodelist (ACL, NPS400);
+ add_to_decodelist (ARITH, NPS400);
+ add_to_decodelist (BITOP, NPS400);
+ add_to_decodelist (BMU, NPS400);
+ add_to_decodelist (CONTROL, NPS400);
+ add_to_decodelist (DMA, NPS400);
+ add_to_decodelist (DPI, NPS400);
+ add_to_decodelist (MEMORY, NPS400);
+ add_to_decodelist (MISC, NPS400);
+ add_to_decodelist (NET, NPS400);
+ add_to_decodelist (PMU, NPS400);
+ add_to_decodelist (PROTOCOL_DECODE, NPS400);
+ add_to_decodelist (ULTRAIP, NPS400);
+ }
+
+ 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
+ /* xgettext:c-format */
+ opcodes_error_handler (_("unrecognised disassembler option: %s"), 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;
+ }
+ }
+
+ /* xgettext:c-format */
+ opcodes_error_handler (_("unrecognised disassembler CPU option: %s"), 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;