+/* Used to track the state carried over from previous operands in
+ an instruction. */
+struct mips_print_arg_state {
+ /* The value of the last OP_INT seen. We only use this for OP_MSB,
+ where the value is known to be unsigned and small. */
+ unsigned int last_int;
+
+ /* The type and number of the last OP_REG seen. We only use this for
+ OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG. */
+ enum mips_reg_operand_type last_reg_type;
+ unsigned int last_regno;
+ unsigned int dest_regno;
+ unsigned int seen_dest;
+};
+
+/* Initialize STATE for the start of an instruction. */
+
+static inline void
+init_print_arg_state (struct mips_print_arg_state *state)
+{
+ memset (state, 0, sizeof (*state));
+}
+
+/* Print OP_VU0_SUFFIX or OP_VU0_MATCH_SUFFIX operand OPERAND,
+ whose value is given by UVAL. */
+
+static void
+print_vu0_channel (struct disassemble_info *info,
+ const struct mips_operand *operand, unsigned int uval)
+{
+ if (operand->size == 4)
+ info->fprintf_func (info->stream, "%s%s%s%s",
+ uval & 8 ? "x" : "",
+ uval & 4 ? "y" : "",
+ uval & 2 ? "z" : "",
+ uval & 1 ? "w" : "");
+ else if (operand->size == 2)
+ info->fprintf_func (info->stream, "%c", "xyzw"[uval]);
+ else
+ abort ();
+}
+
+/* Record information about a register operand. */
+
+static void
+mips_seen_register (struct mips_print_arg_state *state,
+ unsigned int regno,
+ enum mips_reg_operand_type reg_type)
+{
+ state->last_reg_type = reg_type;
+ state->last_regno = regno;
+
+ if (!state->seen_dest)
+ {
+ state->seen_dest = 1;
+ state->dest_regno = regno;
+ }
+}
+
+/* Print SAVE/RESTORE instruction operands according to the argument
+ register mask AMASK, the number of static registers saved NSREG,
+ the $ra, $s0 and $s1 register specifiers RA, S0 and S1 respectively,
+ and the frame size FRAME_SIZE. */
+
+static void
+mips_print_save_restore (struct disassemble_info *info, unsigned int amask,
+ unsigned int nsreg, unsigned int ra,
+ unsigned int s0, unsigned int s1,
+ unsigned int frame_size)
+{
+ const fprintf_ftype infprintf = info->fprintf_func;
+ unsigned int nargs, nstatics, smask, i, j;
+ void *is = info->stream;
+ const char *sep;
+
+ if (amask == MIPS_SVRS_ALL_ARGS)
+ {
+ nargs = 4;
+ nstatics = 0;
+ }
+ else if (amask == MIPS_SVRS_ALL_STATICS)
+ {
+ nargs = 0;
+ nstatics = 4;
+ }
+ else
+ {
+ nargs = amask >> 2;
+ nstatics = amask & 3;
+ }
+
+ sep = "";
+ if (nargs > 0)
+ {
+ infprintf (is, "%s", mips_gpr_names[4]);
+ if (nargs > 1)
+ infprintf (is, "-%s", mips_gpr_names[4 + nargs - 1]);
+ sep = ",";
+ }
+
+ infprintf (is, "%s%d", sep, frame_size);
+
+ if (ra) /* $ra */
+ infprintf (is, ",%s", mips_gpr_names[31]);
+
+ smask = 0;
+ if (s0) /* $s0 */
+ smask |= 1 << 0;
+ if (s1) /* $s1 */
+ smask |= 1 << 1;
+ if (nsreg > 0) /* $s2-$s8 */
+ smask |= ((1 << nsreg) - 1) << 2;
+
+ for (i = 0; i < 9; i++)
+ if (smask & (1 << i))
+ {
+ infprintf (is, ",%s", mips_gpr_names[i == 8 ? 30 : (16 + i)]);
+ /* Skip over string of set bits. */
+ for (j = i; smask & (2 << j); j++)
+ continue;
+ if (j > i)
+ infprintf (is, "-%s", mips_gpr_names[j == 8 ? 30 : (16 + j)]);
+ i = j + 1;
+ }
+ /* Statics $ax - $a3. */
+ if (nstatics == 1)
+ infprintf (is, ",%s", mips_gpr_names[7]);
+ else if (nstatics > 0)
+ infprintf (is, ",%s-%s",
+ mips_gpr_names[7 - nstatics + 1],
+ mips_gpr_names[7]);
+}
+
+
+/* Print operand OPERAND of OPCODE, using STATE to track inter-operand state.
+ UVAL is the encoding of the operand (shifted into bit 0) and BASE_PC is
+ the base address for OP_PCREL operands. */
+
+static void
+print_insn_arg (struct disassemble_info *info,
+ struct mips_print_arg_state *state,
+ const struct mips_opcode *opcode,
+ const struct mips_operand *operand,
+ bfd_vma base_pc,
+ unsigned int uval)
+{
+ const fprintf_ftype infprintf = info->fprintf_func;
+ void *is = info->stream;
+
+ switch (operand->type)
+ {
+ case OP_INT:
+ {
+ const struct mips_int_operand *int_op;
+
+ int_op = (const struct mips_int_operand *) operand;
+ uval = mips_decode_int_operand (int_op, uval);
+ state->last_int = uval;
+ if (int_op->print_hex)
+ infprintf (is, "0x%x", uval);
+ else
+ infprintf (is, "%d", uval);
+ }
+ break;
+
+ case OP_MAPPED_INT:
+ {
+ const struct mips_mapped_int_operand *mint_op;
+
+ mint_op = (const struct mips_mapped_int_operand *) operand;
+ uval = mint_op->int_map[uval];
+ state->last_int = uval;
+ if (mint_op->print_hex)
+ infprintf (is, "0x%x", uval);
+ else
+ infprintf (is, "%d", uval);
+ }
+ break;
+
+ case OP_MSB:
+ {
+ const struct mips_msb_operand *msb_op;
+
+ msb_op = (const struct mips_msb_operand *) operand;
+ uval += msb_op->bias;
+ if (msb_op->add_lsb)
+ uval -= state->last_int;
+ infprintf (is, "0x%x", uval);
+ }
+ break;
+
+ case OP_REG:
+ case OP_OPTIONAL_REG:
+ {
+ const struct mips_reg_operand *reg_op;
+
+ reg_op = (const struct mips_reg_operand *) operand;
+ uval = mips_decode_reg_operand (reg_op, uval);
+ print_reg (info, opcode, reg_op->reg_type, uval);
+
+ mips_seen_register (state, uval, reg_op->reg_type);
+ }
+ break;
+
+ case OP_REG_PAIR:
+ {
+ const struct mips_reg_pair_operand *pair_op;
+
+ pair_op = (const struct mips_reg_pair_operand *) operand;
+ print_reg (info, opcode, pair_op->reg_type,
+ pair_op->reg1_map[uval]);
+ infprintf (is, ",");
+ print_reg (info, opcode, pair_op->reg_type,
+ pair_op->reg2_map[uval]);
+ }
+ break;
+
+ case OP_PCREL:
+ {
+ const struct mips_pcrel_operand *pcrel_op;
+
+ pcrel_op = (const struct mips_pcrel_operand *) operand;
+ info->target = mips_decode_pcrel_operand (pcrel_op, base_pc, uval);
+
+ /* For jumps and branches clear the ISA bit except for
+ the GDB disassembler. */
+ if (pcrel_op->include_isa_bit
+ && info->flavour != bfd_target_unknown_flavour)
+ info->target &= -2;
+
+ (*info->print_address_func) (info->target, info);
+ }
+ break;
+
+ case OP_PERF_REG:
+ infprintf (is, "%d", uval);
+ break;
+
+ case OP_ADDIUSP_INT:
+ {
+ int sval;
+
+ sval = mips_signed_operand (operand, uval) * 4;
+ if (sval >= -8 && sval < 8)
+ sval ^= 0x400;
+ infprintf (is, "%d", sval);
+ break;
+ }
+
+ case OP_CLO_CLZ_DEST:
+ {
+ unsigned int reg1, reg2;
+
+ reg1 = uval & 31;
+ reg2 = uval >> 5;
+ /* If one is zero use the other. */
+ if (reg1 == reg2 || reg2 == 0)
+ infprintf (is, "%s", mips_gpr_names[reg1]);
+ else if (reg1 == 0)
+ infprintf (is, "%s", mips_gpr_names[reg2]);
+ else
+ /* Bogus, result depends on processor. */
+ infprintf (is, "%s or %s", mips_gpr_names[reg1],
+ mips_gpr_names[reg2]);
+ }
+ break;
+
+ case OP_SAME_RS_RT:
+ case OP_CHECK_PREV:
+ case OP_NON_ZERO_REG:
+ {
+ print_reg (info, opcode, OP_REG_GP, uval & 31);
+ mips_seen_register (state, uval, OP_REG_GP);
+ }
+ break;
+
+ case OP_LWM_SWM_LIST:
+ if (operand->size == 2)
+ {
+ if (uval == 0)
+ infprintf (is, "%s,%s",
+ mips_gpr_names[16],
+ mips_gpr_names[31]);
+ else
+ infprintf (is, "%s-%s,%s",
+ mips_gpr_names[16],
+ mips_gpr_names[16 + uval],
+ mips_gpr_names[31]);
+ }
+ else
+ {
+ int s_reg_encode;
+
+ s_reg_encode = uval & 0xf;
+ if (s_reg_encode != 0)
+ {
+ if (s_reg_encode == 1)
+ infprintf (is, "%s", mips_gpr_names[16]);
+ else if (s_reg_encode < 9)
+ infprintf (is, "%s-%s",
+ mips_gpr_names[16],
+ mips_gpr_names[15 + s_reg_encode]);
+ else if (s_reg_encode == 9)
+ infprintf (is, "%s-%s,%s",
+ mips_gpr_names[16],
+ mips_gpr_names[23],
+ mips_gpr_names[30]);
+ else
+ infprintf (is, "UNKNOWN");
+ }
+
+ if (uval & 0x10) /* For ra. */
+ {
+ if (s_reg_encode == 0)
+ infprintf (is, "%s", mips_gpr_names[31]);
+ else
+ infprintf (is, ",%s", mips_gpr_names[31]);
+ }
+ }
+ break;
+
+ case OP_ENTRY_EXIT_LIST:
+ {
+ const char *sep;
+ unsigned int amask, smask;
+
+ sep = "";
+ amask = (uval >> 3) & 7;
+ if (amask > 0 && amask < 5)
+ {
+ infprintf (is, "%s", mips_gpr_names[4]);
+ if (amask > 1)
+ infprintf (is, "-%s", mips_gpr_names[amask + 3]);
+ sep = ",";
+ }
+
+ smask = (uval >> 1) & 3;
+ if (smask == 3)
+ {
+ infprintf (is, "%s??", sep);
+ sep = ",";
+ }
+ else if (smask > 0)
+ {
+ infprintf (is, "%s%s", sep, mips_gpr_names[16]);
+ if (smask > 1)
+ infprintf (is, "-%s", mips_gpr_names[smask + 15]);
+ sep = ",";
+ }
+
+ if (uval & 1)
+ {
+ infprintf (is, "%s%s", sep, mips_gpr_names[31]);
+ sep = ",";
+ }
+
+ if (amask == 5 || amask == 6)
+ {
+ infprintf (is, "%s%s", sep, mips_fpr_names[0]);
+ if (amask == 6)
+ infprintf (is, "-%s", mips_fpr_names[1]);
+ }
+ }
+ break;
+
+ case OP_SAVE_RESTORE_LIST:
+ /* Should be handled by the caller due to complex behavior. */
+ abort ();
+
+ case OP_MDMX_IMM_REG:
+ {
+ unsigned int vsel;
+
+ vsel = uval >> 5;
+ uval &= 31;
+ if ((vsel & 0x10) == 0)
+ {
+ int fmt;
+
+ vsel &= 0x0f;
+ for (fmt = 0; fmt < 3; fmt++, vsel >>= 1)
+ if ((vsel & 1) == 0)
+ break;
+ print_reg (info, opcode, OP_REG_VEC, uval);
+ infprintf (is, "[%d]", vsel >> 1);
+ }
+ else if ((vsel & 0x08) == 0)
+ print_reg (info, opcode, OP_REG_VEC, uval);
+ else
+ infprintf (is, "0x%x", uval);
+ }
+ break;
+
+ case OP_REPEAT_PREV_REG:
+ print_reg (info, opcode, state->last_reg_type, state->last_regno);
+ break;
+
+ case OP_REPEAT_DEST_REG:
+ print_reg (info, opcode, state->last_reg_type, state->dest_regno);
+ break;
+
+ case OP_PC:
+ infprintf (is, "$pc");
+ break;
+
+ case OP_REG28:
+ print_reg (info, opcode, OP_REG_GP, 28);
+ break;
+
+ case OP_VU0_SUFFIX:
+ case OP_VU0_MATCH_SUFFIX:
+ print_vu0_channel (info, operand, uval);
+ break;
+
+ case OP_IMM_INDEX:
+ infprintf (is, "[%d]", uval);
+ break;
+
+ case OP_REG_INDEX:
+ infprintf (is, "[");
+ print_reg (info, opcode, OP_REG_GP, uval);
+ infprintf (is, "]");
+ break;
+ }
+}
+
+/* Validate the arguments for INSN, which is described by OPCODE.
+ Use DECODE_OPERAND to get the encoding of each operand. */
+
+static bfd_boolean
+validate_insn_args (const struct mips_opcode *opcode,
+ const struct mips_operand *(*decode_operand) (const char *),
+ unsigned int insn)
+{
+ struct mips_print_arg_state state;
+ const struct mips_operand *operand;
+ const char *s;
+ unsigned int uval;
+
+ init_print_arg_state (&state);
+ for (s = opcode->args; *s; ++s)
+ {
+ switch (*s)
+ {
+ case ',':
+ case '(':
+ case ')':
+ break;
+
+ case '#':
+ ++s;
+ break;
+
+ default:
+ operand = decode_operand (s);
+
+ if (operand)
+ {
+ uval = mips_extract_operand (operand, insn);
+ switch (operand->type)
+ {
+ case OP_REG:
+ case OP_OPTIONAL_REG:
+ {
+ const struct mips_reg_operand *reg_op;
+
+ reg_op = (const struct mips_reg_operand *) operand;
+ uval = mips_decode_reg_operand (reg_op, uval);
+ mips_seen_register (&state, uval, reg_op->reg_type);
+ }
+ break;
+
+ case OP_SAME_RS_RT:
+ {
+ unsigned int reg1, reg2;
+
+ reg1 = uval & 31;
+ reg2 = uval >> 5;
+
+ if (reg1 != reg2 || reg1 == 0)
+ return FALSE;
+ }
+ break;
+
+ case OP_CHECK_PREV:
+ {
+ const struct mips_check_prev_operand *prev_op;
+
+ prev_op = (const struct mips_check_prev_operand *) operand;
+
+ if (!prev_op->zero_ok && uval == 0)
+ return FALSE;
+
+ if (((prev_op->less_than_ok && uval < state.last_regno)
+ || (prev_op->greater_than_ok && uval > state.last_regno)
+ || (prev_op->equal_ok && uval == state.last_regno)))
+ break;
+
+ return FALSE;
+ }
+
+ case OP_NON_ZERO_REG:
+ {
+ if (uval == 0)
+ return FALSE;
+ }
+ break;
+
+ case OP_INT:
+ case OP_MAPPED_INT:
+ case OP_MSB:
+ case OP_REG_PAIR:
+ case OP_PCREL:
+ case OP_PERF_REG:
+ case OP_ADDIUSP_INT:
+ case OP_CLO_CLZ_DEST:
+ case OP_LWM_SWM_LIST:
+ case OP_ENTRY_EXIT_LIST:
+ case OP_MDMX_IMM_REG:
+ case OP_REPEAT_PREV_REG:
+ case OP_REPEAT_DEST_REG:
+ case OP_PC:
+ case OP_REG28:
+ case OP_VU0_SUFFIX:
+ case OP_VU0_MATCH_SUFFIX:
+ case OP_IMM_INDEX:
+ case OP_REG_INDEX:
+ case OP_SAVE_RESTORE_LIST:
+ break;
+ }
+ }
+ if (*s == 'm' || *s == '+' || *s == '-')
+ ++s;
+ }
+ }
+ return TRUE;
+}
+
+/* Print the arguments for INSN, which is described by OPCODE.
+ Use DECODE_OPERAND to get the encoding of each operand. Use BASE_PC
+ as the base of OP_PCREL operands, adjusting by LENGTH if the OP_PCREL
+ operand is for a branch or jump. */
+
+static void
+print_insn_args (struct disassemble_info *info,
+ const struct mips_opcode *opcode,
+ const struct mips_operand *(*decode_operand) (const char *),
+ unsigned int insn, bfd_vma insn_pc, unsigned int length)
+{
+ const fprintf_ftype infprintf = info->fprintf_func;
+ void *is = info->stream;
+ struct mips_print_arg_state state;
+ const struct mips_operand *operand;
+ const char *s;
+
+ init_print_arg_state (&state);
+ for (s = opcode->args; *s; ++s)
+ {
+ switch (*s)
+ {
+ case ',':
+ case '(':
+ case ')':
+ infprintf (is, "%c", *s);
+ break;
+
+ case '#':
+ ++s;
+ infprintf (is, "%c%c", *s, *s);
+ break;
+
+ default:
+ operand = decode_operand (s);
+ if (!operand)
+ {
+ /* xgettext:c-format */
+ infprintf (is,
+ _("# internal error, undefined operand in `%s %s'"),
+ opcode->name, opcode->args);
+ return;
+ }
+
+ if (operand->type == OP_SAVE_RESTORE_LIST)
+ {
+ /* Handle this case here because of the complex behavior. */
+ unsigned int amask = (insn >> 15) & 0xf;
+ unsigned int nsreg = (insn >> 23) & 0x7;
+ unsigned int ra = insn & 0x1000; /* $ra */
+ unsigned int s0 = insn & 0x800; /* $s0 */
+ unsigned int s1 = insn & 0x400; /* $s1 */
+ unsigned int frame_size = (((insn >> 15) & 0xf0)
+ | ((insn >> 6) & 0x0f)) * 8;
+ mips_print_save_restore (info, amask, nsreg, ra, s0, s1,
+ frame_size);
+ }
+ else if (operand->type == OP_REG
+ && s[1] == ','
+ && s[2] == 'H'
+ && opcode->name[strlen (opcode->name) - 1] == '0')
+ {
+ /* Coprocessor register 0 with sel field. */
+ const struct mips_cp0sel_name *n;
+ unsigned int reg, sel;
+
+ reg = mips_extract_operand (operand, insn);
+ s += 2;
+ operand = decode_operand (s);
+ sel = mips_extract_operand (operand, insn);
+
+ /* CP0 register including 'sel' code for mftc0, to be
+ printed textually if known. If not known, print both
+ CP0 register name and sel numerically since CP0 register
+ with sel 0 may have a name unrelated to register being
+ printed. */
+ n = lookup_mips_cp0sel_name (mips_cp0sel_names,
+ mips_cp0sel_names_len,
+ reg, sel);
+ if (n != NULL)
+ infprintf (is, "%s", n->name);
+ else
+ infprintf (is, "$%d,%d", reg, sel);
+ }
+ else
+ {
+ bfd_vma base_pc = insn_pc;
+
+ /* Adjust the PC relative base so that branch/jump insns use
+ the following PC as the base but genuinely PC relative
+ operands use the current PC. */
+ if (operand->type == OP_PCREL)
+ {
+ const struct mips_pcrel_operand *pcrel_op;
+
+ pcrel_op = (const struct mips_pcrel_operand *) operand;
+ /* The include_isa_bit flag is sufficient to distinguish
+ branch/jump from other PC relative operands. */
+ if (pcrel_op->include_isa_bit)
+ base_pc += length;
+ }
+
+ print_insn_arg (info, &state, opcode, operand, base_pc,
+ mips_extract_operand (operand, insn));
+ }
+ if (*s == 'm' || *s == '+' || *s == '-')
+ ++s;
+ break;
+ }
+ }
+}
+\f
+/* Print the mips instruction at address MEMADDR in debugged memory,
+ on using INFO. Returns length of the instruction, in bytes, which is
+ always INSNLEN. BIGENDIAN must be 1 if this is big-endian code, 0 if
+ this is little-endian code. */
+
+static int
+print_insn_mips (bfd_vma memaddr,
+ int word,
+ struct disassemble_info *info)
+{
+#define GET_OP(insn, field) \
+ (((insn) >> OP_SH_##field) & OP_MASK_##field)
+ static const struct mips_opcode *mips_hash[OP_MASK_OP + 1];
+ const fprintf_ftype infprintf = info->fprintf_func;
+ const struct mips_opcode *op;
+ static bfd_boolean init = 0;
+ void *is = info->stream;
+
+ /* Build a hash table to shorten the search time. */
+ if (! init)
+ {
+ unsigned int i;
+
+ for (i = 0; i <= OP_MASK_OP; i++)
+ {
+ for (op = mips_opcodes; op < &mips_opcodes[NUMOPCODES]; op++)
+ {
+ if (op->pinfo == INSN_MACRO
+ || (no_aliases && (op->pinfo2 & INSN2_ALIAS)))
+ continue;
+ if (i == GET_OP (op->match, OP))
+ {
+ mips_hash[i] = op;
+ break;
+ }
+ }
+ }
+
+ init = 1;
+ }
+
+ info->bytes_per_chunk = INSNLEN;
+ info->display_endian = info->endian;
+ info->insn_info_valid = 1;
+ info->branch_delay_insns = 0;
+ info->data_size = 0;
+ info->insn_type = dis_nonbranch;
+ info->target = 0;
+ info->target2 = 0;
+
+ op = mips_hash[GET_OP (word, OP)];
+ if (op != NULL)
+ {
+ for (; op < &mips_opcodes[NUMOPCODES]; op++)
+ {
+ if (op->pinfo != INSN_MACRO
+ && !(no_aliases && (op->pinfo2 & INSN2_ALIAS))
+ && (word & op->mask) == op->match)
+ {
+ /* We always disassemble the jalx instruction, except for MIPS r6. */
+ if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor)
+ && (strcmp (op->name, "jalx")
+ || (mips_isa & INSN_ISA_MASK) == ISA_MIPS32R6
+ || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R6))
+ continue;
+
+ /* Figure out instruction type and branch delay information. */
+ if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+ {
+ if ((op->pinfo & (INSN_WRITE_GPR_31 | INSN_WRITE_1)) != 0)
+ info->insn_type = dis_jsr;
+ else
+ info->insn_type = dis_branch;
+ info->branch_delay_insns = 1;
+ }
+ else if ((op->pinfo & (INSN_COND_BRANCH_DELAY
+ | INSN_COND_BRANCH_LIKELY)) != 0)
+ {
+ if ((op->pinfo & INSN_WRITE_GPR_31) != 0)
+ info->insn_type = dis_condjsr;
+ else
+ info->insn_type = dis_condbranch;
+ info->branch_delay_insns = 1;
+ }
+ else if ((op->pinfo & (INSN_STORE_MEMORY
+ | INSN_LOAD_MEMORY)) != 0)
+ info->insn_type = dis_dref;
+
+ if (!validate_insn_args (op, decode_mips_operand, word))
+ continue;
+
+ infprintf (is, "%s", op->name);
+ if (op->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX)
+ {
+ unsigned int uval;
+
+ infprintf (is, ".");
+ uval = mips_extract_operand (&mips_vu0_channel_mask, word);
+ print_vu0_channel (info, &mips_vu0_channel_mask, uval);
+ }
+
+ if (op->args[0])
+ {
+ infprintf (is, "\t");
+ print_insn_args (info, op, decode_mips_operand, word,
+ memaddr, 4);
+ }
+
+ return INSNLEN;
+ }
+ }
+ }
+#undef GET_OP
+
+ /* Handle undefined instructions. */
+ info->insn_type = dis_noninsn;
+ infprintf (is, "0x%x", word);
+ return INSNLEN;
+}
+\f
+/* Disassemble an operand for a mips16 instruction. */
+
+static void
+print_mips16_insn_arg (struct disassemble_info *info,
+ struct mips_print_arg_state *state,
+ const struct mips_opcode *opcode,
+ char type, bfd_vma memaddr,
+ unsigned insn, bfd_boolean use_extend,
+ unsigned extend, bfd_boolean is_offset)
+{
+ const fprintf_ftype infprintf = info->fprintf_func;
+ void *is = info->stream;
+ const struct mips_operand *operand, *ext_operand;
+ unsigned short ext_size;
+ unsigned int uval;
+ bfd_vma baseaddr;
+
+ if (!use_extend)
+ extend = 0;
+
+ switch (type)
+ {
+ case ',':
+ case '(':
+ case ')':
+ infprintf (is, "%c", type);
+ break;
+
+ default:
+ operand = decode_mips16_operand (type, FALSE);
+ if (!operand)
+ {
+ /* xgettext:c-format */
+ infprintf (is, _("# internal error, undefined operand in `%s %s'"),
+ opcode->name, opcode->args);
+ return;
+ }
+
+ if (operand->type == OP_SAVE_RESTORE_LIST)
+ {
+ /* Handle this case here because of the complex interaction
+ with the EXTEND opcode. */
+ unsigned int amask = extend & 0xf;
+ unsigned int nsreg = (extend >> 8) & 0x7;
+ unsigned int ra = insn & 0x40; /* $ra */
+ unsigned int s0 = insn & 0x20; /* $s0 */
+ unsigned int s1 = insn & 0x10; /* $s1 */
+ unsigned int frame_size = ((extend & 0xf0) | (insn & 0x0f)) * 8;
+ if (frame_size == 0 && !use_extend)
+ frame_size = 128;
+ mips_print_save_restore (info, amask, nsreg, ra, s0, s1, frame_size);
+ break;
+ }
+
+ if (is_offset && operand->type == OP_INT)
+ {
+ const struct mips_int_operand *int_op;
+
+ int_op = (const struct mips_int_operand *) operand;
+ info->insn_type = dis_dref;
+ info->data_size = 1 << int_op->shift;
+ }
+
+ ext_size = 0;
+ if (use_extend)
+ {
+ ext_operand = decode_mips16_operand (type, TRUE);
+ if (ext_operand != operand
+ || (operand->type == OP_INT && operand->lsb == 0
+ && mips_opcode_32bit_p (opcode)))
+ {
+ ext_size = ext_operand->size;
+ operand = ext_operand;
+ }
+ }
+ if (operand->size == 26)
+ uval = ((extend & 0x1f) << 21) | ((extend & 0x3e0) << 11) | insn;
+ else if (ext_size == 16 || ext_size == 9)
+ uval = ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
+ else if (ext_size == 15)
+ uval = ((extend & 0xf) << 11) | (extend & 0x7f0) | (insn & 0xf);
+ else if (ext_size == 6)
+ uval = ((extend >> 6) & 0x1f) | (extend & 0x20);
+ else
+ uval = mips_extract_operand (operand, (extend << 16) | insn);
+ if (ext_size == 9)
+ uval &= (1U << ext_size) - 1;
+
+ baseaddr = memaddr + 2;
+ if (operand->type == OP_PCREL)
+ {
+ const struct mips_pcrel_operand *pcrel_op;
+
+ pcrel_op = (const struct mips_pcrel_operand *) operand;
+ if (!pcrel_op->include_isa_bit && use_extend)
+ baseaddr = memaddr - 2;
+ else if (!pcrel_op->include_isa_bit)
+ {
+ bfd_byte buffer[2];
+
+ /* If this instruction is in the delay slot of a JAL/JALX
+ instruction, the base address is the address of the
+ JAL/JALX instruction. If it is in the delay slot of
+ a JR/JALR instruction, the base address is the address
+ of the JR/JALR instruction. This test is unreliable:
+ we have no way of knowing whether the previous word is
+ instruction or data. */
+ if (info->read_memory_func (memaddr - 4, buffer, 2, info) == 0
+ && (((info->endian == BFD_ENDIAN_BIG
+ ? bfd_getb16 (buffer)
+ : bfd_getl16 (buffer))
+ & 0xf800) == 0x1800))
+ baseaddr = memaddr - 4;
+ else if (info->read_memory_func (memaddr - 2, buffer, 2,
+ info) == 0
+ && (((info->endian == BFD_ENDIAN_BIG
+ ? bfd_getb16 (buffer)
+ : bfd_getl16 (buffer))
+ & 0xf89f) == 0xe800)
+ && (((info->endian == BFD_ENDIAN_BIG
+ ? bfd_getb16 (buffer)
+ : bfd_getl16 (buffer))
+ & 0x0060) != 0x0060))
+ baseaddr = memaddr - 2;
+ else
+ baseaddr = memaddr;
+ }
+ }
+
+ print_insn_arg (info, state, opcode, operand, baseaddr + 1, uval);
+ break;
+ }
+}
+
+
+/* Check if the given address is the last word of a MIPS16 PLT entry.
+ This word is data and depending on the value it may interfere with
+ disassembly of further PLT entries. We make use of the fact PLT
+ symbols are marked BSF_SYNTHETIC. */
+static bfd_boolean
+is_mips16_plt_tail (struct disassemble_info *info, bfd_vma addr)
+{
+ if (info->symbols
+ && info->symbols[0]
+ && (info->symbols[0]->flags & BSF_SYNTHETIC)
+ && addr == bfd_asymbol_value (info->symbols[0]) + 12)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Whether none, a 32-bit or a 16-bit instruction match has been done. */
+
+enum match_kind
+{
+ MATCH_NONE,
+ MATCH_FULL,
+ MATCH_SHORT
+};
+