+ 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;
+ }
+
+ 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:
+ return -1;
+ }
+ return size;
+ }
+
+ insn_len = arc_insn_length (buffer[highbyte], buffer[lowbyte], info);
+ pr_debug ("instruction length = %d bytes\n", insn_len);
+ if (insn_len == 0)
+ return -1;
+
+ 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. */
+ return -1;
+ }
+
+ 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:
+ return -1;