- bfd_boolean ok;
- char relax_char;
-
- gas_assert (strcmp (insn->name, str) == 0);
-
- ok = is_opcode_valid_16 (insn);
- if (! ok)
- {
- if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes]
- && strcmp (insn->name, insn[1].name) == 0)
- {
- ++insn;
- continue;
- }
- else
- {
- if (!insn_error)
- {
- static char buf[100];
- sprintf (buf,
- _("Opcode not supported on this processor: %s (%s)"),
- mips_cpu_info_from_arch (mips_opts.arch)->name,
- mips_cpu_info_from_isa (mips_opts.isa)->name);
- insn_error = buf;
- }
- return;
- }
- }
-
- create_insn (ip, insn);
- imm_expr.X_op = O_absent;
- imm2_expr.X_op = O_absent;
- offset_expr.X_op = O_absent;
- offset_reloc[0] = BFD_RELOC_UNUSED;
- offset_reloc[1] = BFD_RELOC_UNUSED;
- offset_reloc[2] = BFD_RELOC_UNUSED;
- relax_char = 0;
- for (args = insn->args; 1; ++args)
- {
- int c;
-
- if (*s == ' ')
- ++s;
-
- /* In this switch statement we call break if we did not find
- a match, continue if we did find a match, or return if we
- are done. */
-
- c = *args;
- switch (c)
- {
- case '\0':
- if (*s == '\0')
- {
- offsetT value;
-
- /* Stuff the immediate value in now, if we can. */
- if (insn->pinfo == INSN_MACRO)
- {
- gas_assert (relax_char == 0);
- gas_assert (*offset_reloc == BFD_RELOC_UNUSED);
- }
- else if (relax_char
- && offset_expr.X_op == O_constant
- && calculate_reloc (*offset_reloc,
- offset_expr.X_add_number,
- &value))
- {
- mips16_immed (NULL, 0, relax_char, *offset_reloc, value,
- forced_insn_length, &ip->insn_opcode);
- offset_expr.X_op = O_absent;
- *offset_reloc = BFD_RELOC_UNUSED;
- }
- else if (relax_char && *offset_reloc != BFD_RELOC_UNUSED)
- {
- if (forced_insn_length == 2)
- as_bad (_("invalid unextended operand value"));
- forced_insn_length = 4;
- ip->insn_opcode |= MIPS16_EXTEND;
- }
- else if (relax_char)
- *offset_reloc = (int) BFD_RELOC_UNUSED + relax_char;
-
- return;
- }
- break;
-
- case ',':
- if (*s++ == c)
- continue;
- s--;
- switch (*++args)
- {
- case 'v':
- MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
- continue;
- case 'w':
- MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
- continue;
- }
- break;
-
- case '(':
- case ')':
- if (*s++ == c)
- continue;
- break;
-
- case 'v':
- case 'w':
- if (s[0] != '$')
- {
- if (c == 'v')
- MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
- else
- MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
- ++args;
- continue;
- }
- /* Fall through. */
- case 'x':
- case 'y':
- case 'z':
- case 'Z':
- case '0':
- case 'S':
- case 'R':
- case 'X':
- case 'Y':
- s_reset = s;
- if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no))
- {
- if (c == 'v' || c == 'w')
- {
- if (c == 'v')
- MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
- else
- MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
- ++args;
- continue;
- }
- break;
- }
-
- if (*s == ' ')
- ++s;
- if (args[1] != *s)
- {
- if (c == 'v' || c == 'w')
- {
- regno = mips16_to_32_reg_map[lastregno];
- s = s_reset;
- ++args;
- }
- }
-
- switch (c)
- {
- case 'x':
- case 'y':
- case 'z':
- case 'v':
- case 'w':
- case 'Z':
- regno = mips32_to_16_reg_map[regno];
- break;
-
- case '0':
- if (regno != 0)
- regno = ILLEGAL_REG;
- break;
-
- case 'S':
- if (regno != SP)
- regno = ILLEGAL_REG;
- break;
-
- case 'R':
- if (regno != RA)
- regno = ILLEGAL_REG;
- break;
-
- case 'X':
- case 'Y':
- 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);
- }
- break;
-
- default:
- abort ();
- }
-
- if (regno == ILLEGAL_REG)
- break;
-
- switch (c)
- {
- case 'x':
- case 'v':
- MIPS16_INSERT_OPERAND (RX, *ip, regno);
- break;
- case 'y':
- case 'w':
- MIPS16_INSERT_OPERAND (RY, *ip, regno);
- break;
- case 'z':
- MIPS16_INSERT_OPERAND (RZ, *ip, regno);
- break;
- case 'Z':
- MIPS16_INSERT_OPERAND (MOVE32Z, *ip, regno);
- case '0':
- case 'S':
- case 'R':
- break;
- case 'X':
- MIPS16_INSERT_OPERAND (REGR32, *ip, regno);
- break;
- case 'Y':
- regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
- MIPS16_INSERT_OPERAND (REG32R, *ip, regno);
- break;
- default:
- abort ();
- }
-
- lastregno = regno;
- continue;
-
- case 'P':
- if (strncmp (s, "$pc", 3) == 0)
- {
- s += 3;
- continue;
- }
- break;
-
- case '5':
- case 'H':
- case 'W':
- case 'D':
- case 'j':
- case 'V':
- case 'C':
- case 'U':
- case 'k':
- case 'K':
- i = my_getSmallExpression (&offset_expr, offset_reloc, s);
- if (i > 0)
- {
- relax_char = c;
- s = expr_end;
- continue;
- }
- *offset_reloc = BFD_RELOC_UNUSED;
- /* Fall through. */
- case '<':
- case '>':
- case '[':
- case ']':
- case '4':
- case '8':
- my_getExpression (&offset_expr, s);
- if (offset_expr.X_op == O_register)
- {
- /* What we thought was an expression turned out to
- be a register. */
-
- if (s[0] == '(' && args[1] == '(')
- {
- /* It looks like the expression was omitted
- before a register indirection, which means
- that the expression is implicitly zero. We
- still set up offset_expr, so that we handle
- explicit extensions correctly. */
- offset_expr.X_op = O_constant;
- offset_expr.X_add_number = 0;
- relax_char = c;
- continue;
- }
-
- break;
- }
-
- /* We need to relax this instruction. */
- relax_char = c;
- s = expr_end;
- continue;
-
- case 'p':
- case 'q':
- case 'A':
- case 'B':
- case 'E':
- /* We use offset_reloc rather than imm_reloc for the PC
- relative operands. This lets macros with both
- immediate and address operands work correctly. */
- my_getExpression (&offset_expr, s);
-
- if (offset_expr.X_op == O_register)
- break;
-
- /* We need to relax this instruction. */
- relax_char = c;
- s = expr_end;
- continue;
-
- case '6': /* break code */
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > 63)
- as_warn (_("Invalid value for `%s' (%lu)"),
- ip->insn_mo->name,
- (unsigned long) imm_expr.X_add_number);
- MIPS16_INSERT_OPERAND (IMM6, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- case 'I':
- my_getExpression (&imm_expr, s);
- if (imm_expr.X_op != O_big
- && imm_expr.X_op != O_constant)
- insn_error = _("absolute expression required");
- if (HAVE_32BIT_GPRS)
- normalize_constant_expr (&imm_expr);
- s = expr_end;
- continue;
-
- case 'a': /* 26 bit address */
- case 'i':
- my_getExpression (&offset_expr, s);
- s = expr_end;
- *offset_reloc = BFD_RELOC_MIPS16_JMP;
- ip->insn_opcode <<= 16;
- continue;
-
- case 'l': /* register list for entry macro */
- case 'L': /* register list for exit macro */
- {
- int mask;
-
- if (c == 'l')
- mask = 0;
- else
- mask = 7 << 3;
- while (*s != '\0')
- {
- unsigned int freg, reg1, reg2;
-
- while (*s == ' ' || *s == ',')
- ++s;
- if (reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®1))
- freg = 0;
- else if (reg_lookup (&s, RTYPE_FPU, ®1))
- freg = 1;
- else
- {
- as_bad (_("can't parse register list"));
- break;
- }
- if (*s == ' ')
- ++s;
- if (*s != '-')
- reg2 = reg1;
- else
- {
- ++s;
- if (!reg_lookup (&s, freg ? RTYPE_FPU
- : (RTYPE_GP | RTYPE_NUM), ®2))
- {
- as_bad (_("invalid register list"));
- break;
- }
- }
- if (freg && reg1 == 0 && reg2 == 0 && c == 'L')
- {
- mask &= ~ (7 << 3);
- mask |= 5 << 3;
- }
- else if (freg && reg1 == 0 && reg2 == 1 && c == 'L')
- {
- mask &= ~ (7 << 3);
- mask |= 6 << 3;
- }
- else if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
- mask |= (reg2 - 3) << 3;
- else if (reg1 == 16 && reg2 >= 16 && reg2 <= 17)
- mask |= (reg2 - 15) << 1;
- else if (reg1 == RA && reg2 == RA)
- mask |= 1;
- else
- {
- as_bad (_("invalid register list"));
- break;
- }
- }
- /* The mask is filled in in the opcode table for the
- benefit of the disassembler. We remove it before
- applying the actual mask. */
- ip->insn_opcode &= ~ ((7 << 3) << MIPS16OP_SH_IMM6);
- ip->insn_opcode |= mask << MIPS16OP_SH_IMM6;
- }
- continue;
-
- case 'm': /* Register list for save insn. */
- case 'M': /* Register list for restore insn. */
- {
- int opcode = ip->insn_opcode;
- int framesz = 0, seen_framesz = 0;
- int nargs = 0, statics = 0, sregs = 0;
-
- while (*s != '\0')
- {
- unsigned int reg1, reg2;
-
- SKIP_SPACE_TABS (s);
- while (*s == ',')
- ++s;
- SKIP_SPACE_TABS (s);
-
- my_getExpression (&imm_expr, s);
- if (imm_expr.X_op == O_constant)
- {
- /* Handle the frame size. */
- if (seen_framesz)
- {
- as_bad (_("more than one frame size in list"));
- break;
- }
- seen_framesz = 1;
- framesz = imm_expr.X_add_number;
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
- }
-
- if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®1))
- {
- as_bad (_("can't parse register list"));
- break;
- }
-
- while (*s == ' ')
- ++s;
-
- if (*s != '-')
- reg2 = reg1;
- else
- {
- ++s;
- if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®2)
- || reg2 < reg1)
- {
- as_bad (_("can't parse register list"));
- break;
- }
- }
-
- while (reg1 <= reg2)
- {
- if (reg1 >= 4 && reg1 <= 7)
- {
- if (!seen_framesz)
- /* args $a0-$a3 */
- nargs |= 1 << (reg1 - 4);
- else
- /* statics $a0-$a3 */
- statics |= 1 << (reg1 - 4);
- }
- else if ((reg1 >= 16 && reg1 <= 23) || reg1 == 30)
- {
- /* $s0-$s8 */
- sregs |= 1 << ((reg1 == 30) ? 8 : (reg1 - 16));
- }
- else if (reg1 == 31)
- {
- /* Add $ra to insn. */
- opcode |= 0x40;
- }
- else
- {
- as_bad (_("unexpected register in list"));
- break;
- }
- if (++reg1 == 24)
- reg1 = 30;
- }
- }
-
- /* Encode args/statics combination. */
- if (nargs & statics)
- as_bad (_("arg/static registers overlap"));
- else if (nargs == 0xf)
- /* All $a0-$a3 are args. */
- opcode |= MIPS16_ALL_ARGS << 16;
- else if (statics == 0xf)
- /* All $a0-$a3 are statics. */
- opcode |= MIPS16_ALL_STATICS << 16;
- else
- {
- int narg = 0, nstat = 0;
-
- /* Count arg registers. */
- while (nargs & 0x1)
- {
- nargs >>= 1;
- narg++;
- }
- if (nargs != 0)
- as_bad (_("invalid arg register list"));
-
- /* Count static registers. */
- while (statics & 0x8)
- {
- statics = (statics << 1) & 0xf;
- nstat++;
- }
- if (statics != 0)
- as_bad (_("invalid static register list"));
-
- /* Encode args/statics. */
- opcode |= ((narg << 2) | nstat) << 16;
- }
-
- /* Encode $s0/$s1. */
- if (sregs & (1 << 0)) /* $s0 */
- opcode |= 0x20;
- if (sregs & (1 << 1)) /* $s1 */
- opcode |= 0x10;
- sregs >>= 2;
-
- if (sregs != 0)
- {
- /* Count regs $s2-$s8. */
- int nsreg = 0;
- while (sregs & 1)
- {
- sregs >>= 1;
- nsreg++;
- }
- if (sregs != 0)
- as_bad (_("invalid static register list"));
- /* Encode $s2-$s8. */
- opcode |= nsreg << 24;
- }
-
- /* Encode frame size. */
- if (!seen_framesz)
- as_bad (_("missing frame size"));
- else if ((framesz & 7) != 0 || framesz < 0
- || framesz > 0xff * 8)
- as_bad (_("invalid frame size"));
- else if (framesz != 128 || (opcode >> 16) != 0)
- {
- framesz /= 8;
- opcode |= (((framesz & 0xf0) << 16)
- | (framesz & 0x0f));
- }
-
- /* Finally build the instruction. */
- if ((opcode >> 16) != 0 || framesz == 0)
- opcode |= MIPS16_EXTEND;
- ip->insn_opcode = opcode;
- }
- continue;
-
- case 'e': /* extend code */
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > 0x7ff)
- {
- as_warn (_("Invalid value for `%s' (%lu)"),
- ip->insn_mo->name,
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number &= 0x7ff;
- }
- ip->insn_opcode |= imm_expr.X_add_number;
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- default:
- abort ();
- }
- break;
- }
-
- /* Args don't match. */
- if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] &&
- strcmp (insn->name, insn[1].name) == 0)
- {
- ++insn;
- s = argsstart;
- continue;
- }
-
- insn_error = _("illegal operands");
-