+ case 'm': /* Opcode extension character. */
+ gas_assert (mips_opts.micromips);
+ c = *++args;
+ switch (c)
+ {
+ case 'r':
+ if (strncmp (s, "$pc", 3) == 0)
+ {
+ s += 3;
+ continue;
+ }
+ break;
+
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'p':
+ case 'q':
+ case 's':
+ case 't':
+ case 'x':
+ case 'y':
+ case 'z':
+ s_reset = s;
+ ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no);
+ if (regno == AT && mips_opts.at)
+ {
+ if (mips_opts.at == ATREG)
+ as_warn (_("Used $at without \".set noat\""));
+ else
+ as_warn (_("Used $%u with \".set at=$%u\""),
+ regno, mips_opts.at);
+ }
+ if (!ok)
+ {
+ if (c == 'c')
+ {
+ gas_assert (args[1] == ',');
+ regno = lastregno;
+ ++args;
+ }
+ else if (c == 't')
+ {
+ gas_assert (args[1] == ',');
+ ++args;
+ continue; /* Nothing to do. */
+ }
+ else
+ break;
+ }
+
+ if (c == 'j' && !strncmp (ip->insn_mo->name, "jalr", 4))
+ {
+ if (regno == lastregno)
+ {
+ insn_error
+ = _("Source and destination must be different");
+ continue;
+ }
+ if (regno == 31 && lastregno == 0xffffffff)
+ {
+ insn_error
+ = _("A destination register must be supplied");
+ continue;
+ }
+ }
+
+ if (*s == ' ')
+ ++s;
+ if (args[1] != *s)
+ {
+ if (c == 'e')
+ {
+ gas_assert (args[1] == ',');
+ regno = lastregno;
+ s = s_reset;
+ ++args;
+ }
+ else if (c == 't')
+ {
+ gas_assert (args[1] == ',');
+ s = s_reset;
+ ++args;
+ continue; /* Nothing to do. */
+ }
+ }
+
+ /* Make sure regno is the same as lastregno. */
+ if (c == 't' && regno != lastregno)
+ break;
+
+ /* Make sure regno is the same as destregno. */
+ if (c == 'x' && regno != destregno)
+ break;
+
+ /* We need to save regno, before regno maps to the
+ microMIPS register encoding. */
+ lastregno = regno;
+
+ if (c == 'f')
+ destregno = regno;
+
+ switch (c)
+ {
+ case 'a':
+ if (regno != GP)
+ regno = ILLEGAL_REG;
+ break;
+
+ case 'b':
+ regno = mips32_to_micromips_reg_b_map[regno];
+ break;
+
+ case 'c':
+ regno = mips32_to_micromips_reg_c_map[regno];
+ break;
+
+ case 'd':
+ regno = mips32_to_micromips_reg_d_map[regno];
+ break;
+
+ case 'e':
+ regno = mips32_to_micromips_reg_e_map[regno];
+ break;
+
+ case 'f':
+ regno = mips32_to_micromips_reg_f_map[regno];
+ break;
+
+ case 'g':
+ regno = mips32_to_micromips_reg_g_map[regno];
+ break;
+
+ case 'h':
+ regno = mips32_to_micromips_reg_h_map[regno];
+ break;
+
+ case 'i':
+ switch (EXTRACT_OPERAND (1, MI, *ip))
+ {
+ case 4:
+ if (regno == 21)
+ regno = 3;
+ else if (regno == 22)
+ regno = 4;
+ else if (regno == 5)
+ regno = 5;
+ else if (regno == 6)
+ regno = 6;
+ else if (regno == 7)
+ regno = 7;
+ else
+ regno = ILLEGAL_REG;
+ break;
+
+ case 5:
+ if (regno == 6)
+ regno = 0;
+ else if (regno == 7)
+ regno = 1;
+ else
+ regno = ILLEGAL_REG;
+ break;
+
+ case 6:
+ if (regno == 7)
+ regno = 2;
+ else
+ regno = ILLEGAL_REG;
+ break;
+
+ default:
+ regno = ILLEGAL_REG;
+ break;
+ }
+ break;
+
+ case 'l':
+ regno = mips32_to_micromips_reg_l_map[regno];
+ break;
+
+ case 'm':
+ regno = mips32_to_micromips_reg_m_map[regno];
+ break;
+
+ case 'n':
+ regno = mips32_to_micromips_reg_n_map[regno];
+ break;
+
+ case 'q':
+ regno = mips32_to_micromips_reg_q_map[regno];
+ break;
+
+ case 's':
+ if (regno != SP)
+ regno = ILLEGAL_REG;
+ break;
+
+ case 'y':
+ if (regno != 31)
+ regno = ILLEGAL_REG;
+ break;
+
+ case 'z':
+ if (regno != ZERO)
+ regno = ILLEGAL_REG;
+ break;
+
+ case 'j': /* Do nothing. */
+ case 'p':
+ case 't':
+ case 'x':
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (regno == ILLEGAL_REG)
+ break;
+
+ switch (c)
+ {
+ case 'b':
+ INSERT_OPERAND (1, MB, *ip, regno);
+ break;
+
+ case 'c':
+ INSERT_OPERAND (1, MC, *ip, regno);
+ break;
+
+ case 'd':
+ INSERT_OPERAND (1, MD, *ip, regno);
+ break;
+
+ case 'e':
+ INSERT_OPERAND (1, ME, *ip, regno);
+ break;
+
+ case 'f':
+ INSERT_OPERAND (1, MF, *ip, regno);
+ break;
+
+ case 'g':
+ INSERT_OPERAND (1, MG, *ip, regno);
+ break;
+
+ case 'h':
+ INSERT_OPERAND (1, MH, *ip, regno);
+ break;
+
+ case 'i':
+ INSERT_OPERAND (1, MI, *ip, regno);
+ break;
+
+ case 'j':
+ INSERT_OPERAND (1, MJ, *ip, regno);
+ break;
+
+ case 'l':
+ INSERT_OPERAND (1, ML, *ip, regno);
+ break;
+
+ case 'm':
+ INSERT_OPERAND (1, MM, *ip, regno);
+ break;
+
+ case 'n':
+ INSERT_OPERAND (1, MN, *ip, regno);
+ break;
+
+ case 'p':
+ INSERT_OPERAND (1, MP, *ip, regno);
+ break;
+
+ case 'q':
+ INSERT_OPERAND (1, MQ, *ip, regno);
+ break;
+
+ case 'a': /* Do nothing. */
+ case 's': /* Do nothing. */
+ case 't': /* Do nothing. */
+ case 'x': /* Do nothing. */
+ case 'y': /* Do nothing. */
+ case 'z': /* Do nothing. */
+ break;
+
+ default:
+ abort ();
+ }
+ continue;
+
+ case 'A':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ /* Check whether there is only a single bracketed
+ expression left. If so, it must be the base register
+ and the constant must be zero. */
+ if (*s == '(' && strchr (s + 1, '(') == 0)
+ {
+ INSERT_OPERAND (1, IMMA, *ip, 0);
+ continue;
+ }
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, -64, 64, 2))
+ break;
+
+ imm = ep.X_add_number >> 2;
+ INSERT_OPERAND (1, IMMA, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'B':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || ep.X_op != O_constant)
+ break;
+
+ for (imm = 0; imm < 8; imm++)
+ if (micromips_imm_b_map[imm] == ep.X_add_number)
+ break;
+ if (imm >= 8)
+ break;
+
+ INSERT_OPERAND (1, IMMB, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'C':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || ep.X_op != O_constant)
+ break;
+
+ for (imm = 0; imm < 16; imm++)
+ if (micromips_imm_c_map[imm] == ep.X_add_number)
+ break;
+ if (imm >= 16)
+ break;
+
+ INSERT_OPERAND (1, IMMC, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'D': /* pc relative offset */
+ case 'E': /* pc relative offset */
+ my_getExpression (&offset_expr, s);
+ if (offset_expr.X_op == O_register)
+ break;
+
+ if (!forced_insn_length)
+ *offset_reloc = (int) BFD_RELOC_UNUSED + c;
+ else if (c == 'D')
+ *offset_reloc = BFD_RELOC_MICROMIPS_10_PCREL_S1;
+ else
+ *offset_reloc = BFD_RELOC_MICROMIPS_7_PCREL_S1;
+ s = expr_end;
+ continue;
+
+ case 'F':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, 0, 16, 0))
+ break;
+
+ imm = ep.X_add_number;
+ INSERT_OPERAND (1, IMMF, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'G':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ /* Check whether there is only a single bracketed
+ expression left. If so, it must be the base register
+ and the constant must be zero. */
+ if (*s == '(' && strchr (s + 1, '(') == 0)
+ {
+ INSERT_OPERAND (1, IMMG, *ip, 0);
+ continue;
+ }
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, -1, 15, 0))
+ break;
+
+ imm = ep.X_add_number & 15;
+ INSERT_OPERAND (1, IMMG, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'H':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ /* Check whether there is only a single bracketed
+ expression left. If so, it must be the base register
+ and the constant must be zero. */
+ if (*s == '(' && strchr (s + 1, '(') == 0)
+ {
+ INSERT_OPERAND (1, IMMH, *ip, 0);
+ continue;
+ }
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, 0, 16, 1))
+ break;
+
+ imm = ep.X_add_number >> 1;
+ INSERT_OPERAND (1, IMMH, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'I':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, -1, 127, 0))
+ break;
+
+ imm = ep.X_add_number & 127;
+ INSERT_OPERAND (1, IMMI, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'J':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ /* Check whether there is only a single bracketed
+ expression left. If so, it must be the base register
+ and the constant must be zero. */
+ if (*s == '(' && strchr (s + 1, '(') == 0)
+ {
+ INSERT_OPERAND (1, IMMJ, *ip, 0);
+ continue;
+ }
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, 0, 16, 2))
+ break;
+
+ imm = ep.X_add_number >> 2;
+ INSERT_OPERAND (1, IMMJ, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'L':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ /* Check whether there is only a single bracketed
+ expression left. If so, it must be the base register
+ and the constant must be zero. */
+ if (*s == '(' && strchr (s + 1, '(') == 0)
+ {
+ INSERT_OPERAND (1, IMML, *ip, 0);
+ continue;
+ }
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, 0, 16, 0))
+ break;
+
+ imm = ep.X_add_number;
+ INSERT_OPERAND (1, IMML, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'M':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, 1, 9, 0))
+ break;
+
+ imm = ep.X_add_number & 7;
+ INSERT_OPERAND (1, IMMM, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'N': /* Register list for lwm and swm. */
+ {
+ /* A comma-separated list of registers and/or
+ dash-separated contiguous ranges including
+ both ra and a set of one or more registers
+ starting at s0 up to s3 which have to be
+ consecutive, e.g.:
+
+ s0, ra
+ s0, s1, ra, s2, s3
+ s0-s2, ra
+
+ and any permutations of these. */
+ unsigned int reglist;
+ int imm;
+
+ if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, ®list))
+ break;
+
+ if ((reglist & 0xfff1ffff) != 0x80010000)
+ break;
+
+ reglist = (reglist >> 17) & 7;
+ reglist += 1;
+ if ((reglist & -reglist) != reglist)
+ break;
+
+ imm = ffs (reglist) - 1;
+ INSERT_OPERAND (1, IMMN, *ip, imm);
+ }
+ continue;
+
+ case 'O': /* sdbbp 4-bit code. */
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, 0, 16, 0))
+ break;
+
+ imm = ep.X_add_number;
+ INSERT_OPERAND (1, IMMO, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'P':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, 0, 32, 2))
+ break;
+
+ imm = ep.X_add_number >> 2;
+ INSERT_OPERAND (1, IMMP, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'Q':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, -0x400000, 0x400000, 2))
+ break;
+
+ imm = ep.X_add_number >> 2;
+ INSERT_OPERAND (1, IMMQ, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'U':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ /* Check whether there is only a single bracketed
+ expression left. If so, it must be the base register
+ and the constant must be zero. */
+ if (*s == '(' && strchr (s + 1, '(') == 0)
+ {
+ INSERT_OPERAND (1, IMMU, *ip, 0);
+ continue;
+ }
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, 0, 32, 2))
+ break;
+
+ imm = ep.X_add_number >> 2;
+ INSERT_OPERAND (1, IMMU, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'W':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, 0, 64, 2))
+ break;
+
+ imm = ep.X_add_number >> 2;
+ INSERT_OPERAND (1, IMMW, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'X':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, -8, 8, 0))
+ break;
+
+ imm = ep.X_add_number;
+ INSERT_OPERAND (1, IMMX, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'Y':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+ int imm;
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || expr_const_in_range (&ep, -2, 2, 2)
+ || !expr_const_in_range (&ep, -258, 258, 2))
+ break;
+
+ imm = ep.X_add_number >> 2;
+ imm = ((imm >> 1) & ~0xff) | (imm & 0xff);
+ INSERT_OPERAND (1, IMMY, *ip, imm);
+ }
+ s = expr_end;
+ continue;
+
+ case 'Z':
+ {
+ bfd_reloc_code_real_type r[3];
+ expressionS ep;
+
+ if (my_getSmallExpression (&ep, r, s) > 0
+ || !expr_const_in_range (&ep, 0, 1, 0))
+ break;
+ }
+ s = expr_end;
+ continue;
+
+ default:
+ as_bad (_("Internal error: bad microMIPS opcode "
+ "(unknown extension operand type `m%c'): %s %s"),
+ *args, insn->name, insn->args);
+ /* Further processing is fruitless. */
+ return;