diff options
author | Richard Sandiford <rdsandiford@googlemail.com> | 2013-07-14 13:28:56 +0000 |
---|---|---|
committer | Richard Sandiford <rdsandiford@googlemail.com> | 2013-07-14 13:28:56 +0000 |
commit | ab90248154ba05dc800c480712c3cb03eb33b135 (patch) | |
tree | 111a36595f3d3ad10b07a5fc1b7c6a443ac757a0 /opcodes/mips-dis.c | |
parent | 2f8b73cc2b2dd77e0f4cc772262c9ae0092d4e14 (diff) | |
download | binutils-gdb-ab90248154ba05dc800c480712c3cb03eb33b135.tar.gz |
include/opcode/
* mips.h (mips_operand_type, mips_reg_operand_type): New enums.
(mips_operand, mips_int_operand, mips_mapped_int_operand)
(mips_msb_operand, mips_reg_operand, mips_reg_pair_operand)
(mips_pcrel_operand): New structures.
(mips_insert_operand, mips_extract_operand, mips_signed_operand)
(mips_decode_int_operand, mips_decode_pcrel_operand): New functions.
(decode_mips_operand, decode_micromips_operand): Declare.
opcodes/
* mips-formats.h: New file.
* mips-opc.c: Include mips-formats.h.
(reg_0_map): New static array.
(decode_mips_operand): New function.
* micromips-opc.c: Remove <stdio.h> include. Include mips-formats.h.
(reg_0_map, reg_28_map, reg_29_map, reg_31_map, reg_m16_map)
(reg_mn_map, reg_q_map, reg_h_map1, reg_h_map2, int_b_map)
(int_c_map): New static arrays.
(decode_micromips_operand): New function.
* mips-dis.c (micromips_to_32_reg_b_map, micromips_to_32_reg_c_map)
(micromips_to_32_reg_d_map, micromips_to_32_reg_e_map)
(micromips_to_32_reg_f_map, micromips_to_32_reg_g_map)
(micromips_to_32_reg_h_map1, micromips_to_32_reg_h_map2)
(micromips_to_32_reg_l_map, micromips_to_32_reg_m_map)
(micromips_to_32_reg_n_map, micromips_to_32_reg_q_map)
(micromips_imm_b_map, micromips_imm_c_map): Delete.
(print_reg): New function.
(mips_print_arg_state): New structure.
(init_print_arg_state, print_insn_arg): New functions.
(print_insn_args): Change interface and use mips_operand structures.
Delete GET_OP_S. Move GET_OP definition to...
(print_insn_mips): ...here. Update the call to print_insn_args.
(print_insn_micromips): Use print_insn_args.
gas/
* config/tc-mips.c (validate_mips_insn): Move further up file.
Add insn_bits and decode_operand arguments. Use the mips_operand
fields to work out which bits an operand occupies. Detect double
definitions.
(validate_micromips_insn): Move further up file. Call into
validate_mips_insn.
Diffstat (limited to 'opcodes/mips-dis.c')
-rw-r--r-- | opcodes/mips-dis.c | 1401 |
1 files changed, 316 insertions, 1085 deletions
diff --git a/opcodes/mips-dis.c b/opcodes/mips-dis.c index 7e3d1232325..cd7c63387d5 100644 --- a/opcodes/mips-dis.c +++ b/opcodes/mips-dis.c @@ -57,89 +57,6 @@ static const unsigned int mips16_to_32_reg_map[] = 16, 17, 2, 3, 4, 5, 6, 7 }; -/* The microMIPS registers with type b. */ -#define micromips_to_32_reg_b_map mips16_to_32_reg_map - -/* The microMIPS registers with type c. */ -#define micromips_to_32_reg_c_map mips16_to_32_reg_map - -/* The microMIPS registers with type d. */ -#define micromips_to_32_reg_d_map mips16_to_32_reg_map - -/* The microMIPS registers with type e. */ -#define micromips_to_32_reg_e_map mips16_to_32_reg_map - -/* The microMIPS registers with type f. */ -#define micromips_to_32_reg_f_map mips16_to_32_reg_map - -/* The microMIPS registers with type g. */ -#define micromips_to_32_reg_g_map mips16_to_32_reg_map - -/* The microMIPS registers with type h. */ -static const unsigned int micromips_to_32_reg_h_map1[] = -{ - 5, 5, 6, 4, 4, 4, 4, 4 -}; -static const unsigned int micromips_to_32_reg_h_map2[] = -{ - 6, 7, 7, 21, 22, 5, 6, 7 -}; - -/* The microMIPS registers with type j: 32 registers. */ - -/* The microMIPS registers with type l. */ -#define micromips_to_32_reg_l_map mips16_to_32_reg_map - -/* The microMIPS registers with type m. */ -static const unsigned int micromips_to_32_reg_m_map[] = -{ - 0, 17, 2, 3, 16, 18, 19, 20 -}; - -/* The microMIPS registers with type n. */ -#define micromips_to_32_reg_n_map micromips_to_32_reg_m_map - -/* The microMIPS registers with type p: 32 registers. */ - -/* The microMIPS registers with type q. */ -static const unsigned int micromips_to_32_reg_q_map[] = -{ - 0, 17, 2, 3, 4, 5, 6, 7 -}; - -/* reg type s is $29. */ - -/* reg type t is the same as the last register. */ - -/* reg type y is $31. */ - -/* reg type z is $0. */ - -/* micromips imm B type. */ -static const int micromips_imm_b_map[8] = -{ - 1, 4, 8, 12, 16, 20, 24, -1 -}; - -/* micromips imm C type. */ -static const int micromips_imm_c_map[16] = -{ - 128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535 -}; - -/* micromips imm D type: (-512..511)<<1. */ -/* micromips imm E type: (-64..63)<<1. */ -/* micromips imm F type: (0..63). */ -/* micromips imm G type: (-1..14). */ -/* micromips imm H type: (0..15)<<1. */ -/* micromips imm I type: (-1..126). */ -/* micromips imm J type: (0..15)<<2. */ -/* micromips imm L type: (0..15). */ -/* micromips imm M type: (1..8). */ -/* micromips imm W type: (0..63)<<2. */ -/* micromips imm X type: (-8..7). */ -/* micromips imm Y type: (-258..-3, 2..257)<<2. */ - #define mips16_reg_names(rn) mips_gpr_names[mips16_to_32_reg_map[rn]] @@ -964,476 +881,361 @@ lookup_mips_cp0sel_name (const struct mips_cp0sel_name *names, return &names[i]; return NULL; } - -/* Print insn arguments for 32/64-bit code. */ + +/* Print register REGNO, of type TYPE, for instruction OPCODE. */ static void -print_insn_args (const char *d, - int l, - bfd_vma pc, - struct disassemble_info *info, - const struct mips_opcode *opp) +print_reg (struct disassemble_info *info, const struct mips_opcode *opcode, + enum mips_reg_operand_type type, int regno) { - const fprintf_ftype infprintf = info->fprintf_func; - unsigned int lsb, msb, msbd, cpreg; - void *is = info->stream; + switch (type) + { + case OP_REG_GP: + info->fprintf_func (info->stream, "%s", mips_gpr_names[regno]); + break; - lsb = 0; + case OP_REG_FP: + info->fprintf_func (info->stream, "%s", mips_fpr_names[regno]); + break; -#define GET_OP(insn, field) \ - (((insn) >> OP_SH_##field) & OP_MASK_##field) -#define GET_OP_S(insn, field) \ - ((GET_OP (insn, field) ^ ((OP_MASK_##field >> 1) + 1)) \ - - ((OP_MASK_##field >> 1) + 1)) - for (; *d != '\0'; d++) - { - switch (*d) - { - case ',': - case '(': - case ')': - infprintf (is, "%c", *d); - break; + case OP_REG_CCC: + if (opcode->pinfo & (FP_D | FP_S)) + info->fprintf_func (info->stream, "$fcc%d", regno); + else + info->fprintf_func (info->stream, "$cc%d", regno); + break; - case '+': - /* Extension character; switch for second char. */ - d++; - switch (*d) - { - case '\0': - /* xgettext:c-format */ - infprintf (is, - _("# internal error, " - "incomplete extension sequence (+)")); - return; + case OP_REG_VEC: + if (opcode->membership & INSN_5400) + info->fprintf_func (info->stream, "$f%d", regno); + else + info->fprintf_func (info->stream, "$v%d", regno); + break; - case 'A': - lsb = GET_OP (l, SHAMT); - infprintf (is, "0x%x", lsb); - break; - - case 'B': - msb = GET_OP (l, INSMSB); - infprintf (is, "0x%x", msb - lsb + 1); - break; - - case '1': - infprintf (is, "0x%x", GET_OP (l, UDI1)); - break; - - case '2': - infprintf (is, "0x%x", GET_OP (l, UDI2)); - break; - - case '3': - infprintf (is, "0x%x", GET_OP (l, UDI3)); - break; - - case '4': - infprintf (is, "0x%x", GET_OP (l, UDI4)); - break; - - case 'C': - case 'H': - msbd = GET_OP (l, EXTMSBD); - infprintf (is, "0x%x", msbd + 1); - break; - - case 'E': - lsb = GET_OP (l, SHAMT) + 32; - infprintf (is, "0x%x", lsb); - break; - - case 'F': - msb = GET_OP (l, INSMSB) + 32; - infprintf (is, "0x%x", msb - lsb + 1); - break; - - case 'G': - msbd = GET_OP (l, EXTMSBD) + 32; - infprintf (is, "0x%x", msbd + 1); - break; - - case 'J': /* hypcall operand */ - infprintf (is, "0x%x", GET_OP (l, CODE10)); - break; - - case 't': /* Coprocessor 0 reg name */ - infprintf (is, "%s", mips_cp0_names[GET_OP (l, RT)]); - break; - - case 'x': /* bbit bit index */ - infprintf (is, "0x%x", GET_OP (l, BBITIND)); - break; - - case 'p': /* cins, cins32, exts and exts32 position */ - infprintf (is, "0x%x", GET_OP (l, CINSPOS)); - break; - - case 's': /* cins32 and exts32 length-minus-one */ - case 'S': /* cins and exts length-minus-one field */ - infprintf (is, "0x%x", GET_OP (l, CINSLM1)); - break; - - case 'Q': /* seqi/snei immediate field */ - infprintf (is, "%d", GET_OP_S (l, SEQI)); - break; - - case 'a': /* 8-bit signed offset in bit 6 */ - infprintf (is, "%d", GET_OP_S (l, OFFSET_A)); - break; - - case 'b': /* 8-bit signed offset in bit 3 */ - infprintf (is, "%d", GET_OP_S (l, OFFSET_B)); - break; - - case 'c': /* 9-bit signed offset in bit 6 */ - /* Left shift 4 bits to print the real offset. */ - infprintf (is, "%d", GET_OP_S (l, OFFSET_C) << 4); - break; - - case 'z': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RZ)]); - break; - - case 'Z': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FZ)]); - break; - - case 'i': /* JALX destination */ - info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff) - | (GET_OP (l, TARGET) << 2)); - /* For gdb disassembler, force odd address on jalx. */ - if (info->flavour == bfd_target_unknown_flavour) - info->target |= 1; - (*info->print_address_func) (info->target, info); - break; - - case 'j': /* 9-bit signed offset in bit 7. */ - infprintf (is, "%d", GET_OP_S (l, EVAOFFSET)); - break; - - default: - /* xgettext:c-format */ - infprintf (is, - _("# internal error, " - "undefined extension sequence (+%c)"), - *d); - return; - } - break; + case OP_REG_ACC: + info->fprintf_func (info->stream, "$ac%d", regno); + break; - case '2': - infprintf (is, "0x%x", GET_OP (l, BP)); - break; + case OP_REG_COPRO: + if (opcode->name[strlen (opcode->name) - 1] == '0') + info->fprintf_func (info->stream, "%s", mips_cp0_names[regno]); + else + info->fprintf_func (info->stream, "$%d", regno); + break; - case '3': - infprintf (is, "0x%x", GET_OP (l, SA3)); - break; + case OP_REG_HW: + info->fprintf_func (info->stream, "%s", mips_hwr_names[regno]); + break; + } +} + +/* 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; +}; - case '4': - infprintf (is, "0x%x", GET_OP (l, SA4)); - break; +/* Initialize STATE for the start of an instruction. */ - case '5': - infprintf (is, "0x%x", GET_OP (l, IMM8)); - break; +static inline void +init_print_arg_state (struct mips_print_arg_state *state) +{ + memset (state, 0, sizeof (*state)); +} - case '6': - infprintf (is, "0x%x", GET_OP (l, RS)); - break; +/* 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. */ - case '7': - infprintf (is, "$ac%d", GET_OP (l, DSPACC)); - break; +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; - case '8': - infprintf (is, "0x%x", GET_OP (l, WRDSP)); - break; + switch (operand->type) + { + case OP_INT: + { + const struct mips_int_operand *int_op; - case '9': - infprintf (is, "$ac%d", GET_OP (l, DSPACC_S)); - break; + 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 '0': /* dsp 6-bit signed immediate in bit 20 */ - infprintf (is, "%d", GET_OP_S (l, DSPSFT)); - break; + case OP_MAPPED_INT: + { + const struct mips_mapped_int_operand *mint_op; - case ':': /* dsp 7-bit signed immediate in bit 19 */ - infprintf (is, "%d", GET_OP_S (l, DSPSFT_7)); - break; + 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 '~': - infprintf (is, "%d", GET_OP_S (l, OFFSET12)); - break; + case OP_MSB: + { + const struct mips_msb_operand *msb_op; - case '\\': - infprintf (is, "0x%x", GET_OP (l, 3BITPOS)); - break; + 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 '\'': - infprintf (is, "0x%x", GET_OP (l, RDDSP)); - break; + case OP_REG: + { + const struct mips_reg_operand *reg_op; - case '@': /* dsp 10-bit signed immediate in bit 16 */ - infprintf (is, "%d", GET_OP_S (l, IMM10)); - break; + reg_op = (const struct mips_reg_operand *) operand; + if (reg_op->reg_map) + uval = reg_op->reg_map[uval]; + print_reg (info, opcode, reg_op->reg_type, uval); - case '!': - infprintf (is, "%d", GET_OP (l, MT_U)); - break; + state->last_reg_type = reg_op->reg_type; + state->last_regno = uval; + } + break; - case '$': - infprintf (is, "%d", GET_OP (l, MT_H)); - 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 '*': - infprintf (is, "$ac%d", GET_OP (l, MTACC_T)); - break; + case OP_PCREL: + { + const struct mips_pcrel_operand *pcrel_op; - case '&': - infprintf (is, "$ac%d", GET_OP (l, MTACC_D)); - break; + pcrel_op = (const struct mips_pcrel_operand *) operand; + info->target = mips_decode_pcrel_operand (pcrel_op, base_pc, uval); - case 'g': - /* Coprocessor register for CTTC1, MTTC2, MTHC2, CTTC2. */ - infprintf (is, "$%d", GET_OP (l, RD)); - break; + /* Preserve the ISA bit for the GDB disassembler, + otherwise clear it. */ + if (info->flavour != bfd_target_unknown_flavour) + info->target &= -2; - case 's': - case 'b': - case 'r': - case 'v': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RS)]); - break; + (*info->print_address_func) (info->target, info); + } + break; - case 't': - case 'w': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RT)]); - break; + case OP_PERF_REG: + infprintf (is, "%d", uval); + break; - case 'i': - case 'u': - infprintf (is, "0x%x", GET_OP (l, IMMEDIATE)); - break; + case OP_ADDIUSP_INT: + { + int sval; - case 'j': /* Same as i, but sign-extended. */ - case 'o': - infprintf (is, "%d", GET_OP_S (l, DELTA)); - break; + sval = mips_signed_operand (operand, uval) * 4; + if (sval >= -8 && sval < 8) + sval ^= 0x400; + infprintf (is, "%d", sval); + break; + } - case 'h': - infprintf (is, "0x%x", GET_OP (l, PREFX)); - 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 'k': - infprintf (is, "0x%x", GET_OP (l, CACHE)); - 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; - case 'a': - info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff) - | (GET_OP (l, TARGET) << 2)); - (*info->print_address_func) (info->target, info); - break; + 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"); + } - case 'p': - /* Sign extend the displacement. */ - info->target = (GET_OP_S (l, DELTA) << 2) + pc + INSNLEN; - (*info->print_address_func) (info->target, info); - break; + 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 'd': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RD)]); - break; + case OP_MDMX_IMM_REG: + { + unsigned int vsel; - case 'U': + vsel = uval >> 5; + uval &= 31; + if ((vsel & 0x10) == 0) { - /* First check for both rd and rt being equal. */ - unsigned int reg; - - reg = GET_OP (l, RD); - if (reg == GET_OP (l, RT)) - infprintf (is, "%s", mips_gpr_names[reg]); - else - { - /* If one is zero use the other. */ - if (reg == 0) - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RT)]); - else if (GET_OP (l, RT) == 0) - infprintf (is, "%s", mips_gpr_names[reg]); - else /* Bogus, result depends on processor. */ - infprintf (is, "%s or %s", - mips_gpr_names[reg], - mips_gpr_names[GET_OP (l, RT)]); - } + 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); } - break; - - case 'z': - infprintf (is, "%s", mips_gpr_names[0]); - break; - - case '<': - case '1': - infprintf (is, "0x%x", GET_OP (l, SHAMT)); - break; - - case 'c': - infprintf (is, "0x%x", GET_OP (l, CODE)); - break; - - case 'q': - infprintf (is, "0x%x", GET_OP (l, CODE2)); - break; - - case 'C': - infprintf (is, "0x%x", GET_OP (l, COPZ)); - break; + else if ((vsel & 0x08) == 0) + print_reg (info, opcode, OP_REG_VEC, uval); + else + infprintf (is, "0x%x", uval); + } + break; - case 'B': - infprintf (is, "0x%x", GET_OP (l, CODE20)); - break; + case OP_REPEAT_PREV_REG: + print_reg (info, opcode, state->last_reg_type, state->last_regno); + break; - case 'J': - infprintf (is, "0x%x", GET_OP (l, CODE19)); - break; + case OP_REPEAT_DEST_REG: + /* Should always match OP_REPEAT_PREV_REG first. */ + abort (); - case 'S': - case 'V': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FS)]); - break; + case OP_PC: + infprintf (is, "$pc"); + break; + } +} - case 'T': - case 'W': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FT)]); - break; +/* 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. */ - case 'D': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FD)]); - break; +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 base_pc) +{ + 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; - case 'R': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FR)]); + init_print_arg_state (&state); + for (s = opcode->args; *s; ++s) + { + switch (*s) + { + case ',': + case '(': + case ')': + infprintf (is, "%c", *s); break; - case 'E': - cpreg = GET_OP (l, RT); - goto copro; - - case 'G': - cpreg = GET_OP (l, RD); - copro: - /* Coprocessor register for mtcN instructions, et al. Note - that FPU (cp1) instructions disassemble this field using - 'S' format. Therefore, we only need to worry about cp0, - cp2, and cp3. */ - if (opp->name[strlen (opp->name) - 1] == '0') + default: + operand = decode_operand (s); + if (!operand) { - if (d[1] == ',' && d[2] == 'H') - { - const struct mips_cp0sel_name *n; - unsigned int sel; - - sel = GET_OP (l, SEL); - - /* CP0 register including 'sel' code for mtcN (et al.), 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, - cpreg, sel); - if (n != NULL) - infprintf (is, "%s", n->name); - else - infprintf (is, "$%d,%d", cpreg, sel); - d += 2; - } + /* xgettext:c-format */ + infprintf (is, + _("# internal error, undefined operand in `%s %s'"), + opcode->name, opcode->args); + return; + } + if (operand->type == OP_REG + && s[1] == ',' + && s[2] == 'H' + && opcode->name[strlen (opcode->name) - 1] == '0') + { + /* Coprocessor register 0 with sel field (MT ASE). */ + 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, "%s", mips_cp0_names[cpreg]); + infprintf (is, "$%d,%d", reg, sel); } else - infprintf (is, "$%d", cpreg); - break; - - case 'K': - infprintf (is, "%s", mips_hwr_names[GET_OP (l, RD)]); - break; - - case 'N': - infprintf (is, - (opp->pinfo & (FP_D | FP_S)) != 0 ? "$fcc%d" : "$cc%d", - GET_OP (l, BCC)); - break; - - case 'M': - infprintf (is, "$fcc%d", GET_OP (l, CCC)); - break; - - case 'P': - infprintf (is, "%d", GET_OP (l, PERFREG)); - break; - - case 'e': - infprintf (is, "%d", GET_OP (l, VECBYTE)); + print_insn_arg (info, &state, opcode, operand, base_pc, + mips_extract_operand (operand, insn)); + if (*s == 'm' || *s == '+') + ++s; break; - - case '%': - infprintf (is, "%d", GET_OP (l, VECALIGN)); - break; - - case 'H': - infprintf (is, "%d", GET_OP (l, SEL)); - break; - - case 'O': - infprintf (is, "%d", GET_OP (l, ALN)); - break; - - case 'Q': - { - unsigned int vsel = GET_OP (l, VSEL); - char prefix; - - prefix = opp->membership & INSN_5400 ? 'f' : 'v'; - if ((vsel & 0x10) == 0) - { - int fmt; - - vsel &= 0x0f; - for (fmt = 0; fmt < 3; fmt++, vsel >>= 1) - if ((vsel & 1) == 0) - break; - infprintf (is, "$%c%d[%d]", prefix, GET_OP (l, FT), vsel >> 1); - } - else if ((vsel & 0x08) == 0) - { - infprintf (is, "$%c%d", prefix, GET_OP (l, FT)); - } - else - { - infprintf (is, "0x%x", GET_OP (l, FT)); - } - } - break; - - case 'X': - infprintf (is, "$v%d", GET_OP (l, FD)); - break; - - case 'Y': - infprintf (is, "$v%d", GET_OP (l, FS)); - break; - - case 'Z': - infprintf (is, "$v%d", GET_OP (l, FT)); - break; - - default: - /* xgettext:c-format */ - infprintf (is, _("# internal error, undefined modifier (%c)"), *d); - return; } } } @@ -1448,6 +1250,8 @@ 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; @@ -1495,8 +1299,6 @@ print_insn_mips (bfd_vma memaddr, && !(no_aliases && (op->pinfo2 & INSN2_ALIAS)) && (word & op->mask) == op->match) { - const char *d; - /* We always allow to disassemble the jalx instruction. */ if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor) && strcmp (op->name, "jalx")) @@ -1527,18 +1329,17 @@ print_insn_mips (bfd_vma memaddr, infprintf (is, "%s", op->name); - d = op->args; - if (d != NULL && *d != '\0') + if (op->args[0]) { infprintf (is, "\t"); - print_insn_args (d, word, memaddr, info, op); + print_insn_args (info, op, decode_mips_operand, word, + memaddr + 4); } return INSNLEN; } } } -#undef GET_OP_S #undef GET_OP /* Handle undefined instructions. */ @@ -2235,19 +2036,12 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) { const fprintf_ftype infprintf = info->fprintf_func; const struct mips_opcode *op, *opend; - unsigned int lsb, msbd, msb; void *is = info->stream; - unsigned int regno; bfd_byte buffer[2]; - int lastregno = 0; - int higher; - int length; + unsigned int higher; + unsigned int length; int status; - int delta; - int immed; - int insn; - - lsb = 0; + unsigned int insn; info->bytes_per_chunk = 2; info->display_endian = info->endian; @@ -2331,11 +2125,6 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) /* FIXME: Should probably use a hash table on the major opcode here. */ -#define GET_OP(insn, field) \ - (((insn) >> MICROMIPSOP_SH_##field) & MICROMIPSOP_MASK_##field) -#define GET_OP_S(insn, field) \ - ((GET_OP (insn, field) ^ ((MICROMIPSOP_MASK_##field >> 1) + 1)) \ - - ((MICROMIPSOP_MASK_##field >> 1) + 1)) opend = micromips_opcodes + bfd_micromips_num_opcodes; for (op = micromips_opcodes; op < opend; op++) { @@ -2345,569 +2134,13 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) && ((length == 2 && (op->mask & 0xffff0000) == 0) || (length == 4 && (op->mask & 0xffff0000) != 0))) { - const char *s; - infprintf (is, "%s", op->name); - if (op->args[0] != '\0') - infprintf (is, "\t"); - for (s = op->args; *s != '\0'; s++) + if (op->args[0]) { - switch (*s) - { - case ',': - case '(': - case ')': - infprintf (is, "%c", *s); - break; - - case '.': - infprintf (is, "%d", GET_OP_S (insn, OFFSET10)); - break; - - case '1': - infprintf (is, "0x%x", GET_OP (insn, STYPE)); - break; - - case '2': - infprintf (is, "0x%x", GET_OP (insn, BP)); - break; - - case '3': - infprintf (is, "0x%x", GET_OP (insn, SA3)); - break; - - case '4': - infprintf (is, "0x%x", GET_OP (insn, SA4)); - break; - - case '5': - infprintf (is, "0x%x", GET_OP (insn, IMM8)); - break; - - case '6': - infprintf (is, "0x%x", GET_OP (insn, RS)); - break; - - case '7': - infprintf (is, "$ac%d", GET_OP (insn, DSPACC)); - break; - - case '8': - infprintf (is, "0x%x", GET_OP (insn, WRDSP)); - break; - - case '0': /* DSP 6-bit signed immediate in bit 16. */ - delta = (GET_OP (insn, DSPSFT) ^ 0x20) - 0x20; - infprintf (is, "%d", delta); - break; - - case '<': - infprintf (is, "0x%x", GET_OP (insn, SHAMT)); - break; - - case '\\': - infprintf (is, "0x%x", GET_OP (insn, 3BITPOS)); - break; - - case '^': - infprintf (is, "0x%x", GET_OP (insn, RD)); - break; - - case '|': - infprintf (is, "0x%x", GET_OP (insn, TRAP)); - break; - - case '~': - infprintf (is, "%d", GET_OP_S (insn, OFFSET12)); - break; - - case 'a': - info->target = (((memaddr + 4) & ~(bfd_vma) 0x07ffffff) - | (GET_OP (insn, TARGET) << 1)); - /* For gdb disassembler, maintain odd address. */ - if (info->flavour == bfd_target_unknown_flavour) - info->target |= 1; - (*info->print_address_func) (info->target, info); - break; - - case 'b': - case 'r': - case 's': - case 'v': - infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RS)]); - break; - - case 'c': - infprintf (is, "0x%x", GET_OP (insn, CODE)); - break; - - case 'd': - infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RD)]); - break; - - case 'h': - infprintf (is, "0x%x", GET_OP (insn, PREFX)); - break; - - case 'i': - case 'u': - infprintf (is, "0x%x", GET_OP (insn, IMMEDIATE)); - break; - - case 'j': /* Same as i, but sign-extended. */ - case 'o': - infprintf (is, "%d", GET_OP_S (insn, DELTA)); - break; - - case 'k': - infprintf (is, "0x%x", GET_OP (insn, CACHE)); - break; - - case 'n': - { - int s_reg_encode; - - immed = GET_OP (insn, RT); - s_reg_encode = immed & 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 (immed & 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 'p': - /* Sign-extend the displacement. */ - delta = GET_OP_S (insn, DELTA); - info->target = (delta << 1) + memaddr + length; - (*info->print_address_func) (info->target, info); - break; - - case 'q': - infprintf (is, "0x%x", GET_OP (insn, CODE2)); - break; - - case 't': - case 'w': - infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RT)]); - break; - - case 'y': - infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RS3)]); - break; - - case 'z': - infprintf (is, "%s", mips_gpr_names[0]); - break; - - case '@': /* DSP 10-bit signed immediate in bit 16. */ - delta = (GET_OP (insn, IMM10) ^ 0x200) - 0x200; - infprintf (is, "%d", delta); - break; - - case 'B': - infprintf (is, "0x%x", GET_OP (insn, CODE10)); - break; - - case 'C': - infprintf (is, "0x%x", GET_OP (insn, COPZ)); - break; - - case 'D': - infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FD)]); - break; - - case 'E': - /* Coprocessor register for lwcN instructions, et al. - - Note that there is no load/store cp0 instructions, and - that FPU (cp1) instructions disassemble this field using - 'T' format. Therefore, until we gain understanding of - cp2 register names, we can simply print the register - numbers. */ - infprintf (is, "$%d", GET_OP (insn, RT)); - break; - - case 'G': - /* Coprocessor register for mtcN instructions, et al. Note - that FPU (cp1) instructions disassemble this field using - 'S' format. Therefore, we only need to worry about cp0, - cp2, and cp3. */ - if (op->name[strlen (op->name) - 1] == '0') - { - if (s[1] == ',' && s[2] == 'H') - { - const struct mips_cp0sel_name *n; - unsigned int cp0reg, sel; - - cp0reg = GET_OP (insn, RS); - sel = GET_OP (insn, SEL); - - /* CP0 register including 'sel' code for mtcN - (et al.), 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, - cp0reg, sel); - if (n != NULL) - infprintf (is, "%s", n->name); - else - infprintf (is, "$%d,%d", cp0reg, sel); - s += 2; - } - else - infprintf (is, "%s", mips_cp0_names[GET_OP (insn, RS)]); - } - else - infprintf (is, "$%d", GET_OP (insn, RS)); - break; - - case 'H': - infprintf (is, "%d", GET_OP (insn, SEL)); - break; - - case 'K': - infprintf (is, "%s", mips_hwr_names[GET_OP (insn, RS)]); - break; - - case 'M': - infprintf (is, "$fcc%d", GET_OP (insn, CCC)); - break; - - case 'N': - infprintf (is, - (op->pinfo & (FP_D | FP_S)) != 0 - ? "$fcc%d" : "$cc%d", - GET_OP (insn, BCC)); - break; - - case 'R': - infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FR)]); - break; - - case 'S': - case 'V': - infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FS)]); - break; - - case 'T': - infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FT)]); - break; - - case '+': - /* Extension character; switch for second char. */ - s++; - switch (*s) - { - case 'A': - lsb = GET_OP (insn, EXTLSB); - infprintf (is, "0x%x", lsb); - break; - - case 'B': - msb = GET_OP (insn, INSMSB); - infprintf (is, "0x%x", msb - lsb + 1); - break; - - case 'C': - case 'H': - msbd = GET_OP (insn, EXTMSBD); - infprintf (is, "0x%x", msbd + 1); - break; - - case 'E': - lsb = GET_OP (insn, EXTLSB) + 32; - infprintf (is, "0x%x", lsb); - break; - - case 'F': - msb = GET_OP (insn, INSMSB) + 32; - infprintf (is, "0x%x", msb - lsb + 1); - break; - - case 'G': - msbd = GET_OP (insn, EXTMSBD) + 32; - infprintf (is, "0x%x", msbd + 1); - break; - - case 'i': - info->target = (((memaddr + 4) & ~(bfd_vma) 0x0fffffff) - | (GET_OP (insn, TARGET) << 2)); - (*info->print_address_func) (info->target, info); - break; - - case 'j': /* 9-bit signed offset in bit 0. */ - delta = GET_OP_S (insn, EVAOFFSET); - infprintf (is, "%d", delta); - break; - - default: - /* xgettext:c-format */ - infprintf (is, - _("# internal disassembler error, " - "unrecognized modifier (+%c)"), - *s); - abort (); - } - break; - - case 'm': - /* Extension character; switch for second char. */ - s++; - switch (*s) - { - case 'a': /* global pointer. */ - infprintf (is, "%s", mips_gpr_names[28]); - break; - - case 'b': - regno = micromips_to_32_reg_b_map[GET_OP (insn, MB)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'c': - regno = micromips_to_32_reg_c_map[GET_OP (insn, MC)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'd': - regno = micromips_to_32_reg_d_map[GET_OP (insn, MD)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'e': - regno = micromips_to_32_reg_e_map[GET_OP (insn, ME)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'f': - /* Save lastregno for "mt" to print out later. */ - lastregno = micromips_to_32_reg_f_map[GET_OP (insn, MF)]; - infprintf (is, "%s", mips_gpr_names[lastregno]); - break; - - case 'g': - regno = micromips_to_32_reg_g_map[GET_OP (insn, MG)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'h': - regno = micromips_to_32_reg_h_map1[GET_OP (insn, MH)]; - infprintf (is, "%s", mips_gpr_names[regno]); - regno = micromips_to_32_reg_h_map2[GET_OP (insn, MH)]; - infprintf (is, ",%s", mips_gpr_names[regno]); - break; - - case 'j': - infprintf (is, "%s", mips_gpr_names[GET_OP (insn, MJ)]); - break; - - case 'l': - regno = micromips_to_32_reg_l_map[GET_OP (insn, ML)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'm': - regno = micromips_to_32_reg_m_map[GET_OP (insn, MM)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'n': - regno = micromips_to_32_reg_n_map[GET_OP (insn, MN)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'p': - /* Save lastregno for "mt" to print out later. */ - lastregno = GET_OP (insn, MP); - infprintf (is, "%s", mips_gpr_names[lastregno]); - break; - - case 'q': - regno = micromips_to_32_reg_q_map[GET_OP (insn, MQ)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; - - case 'r': /* program counter. */ - infprintf (is, "$pc"); - break; - - case 's': /* stack pointer. */ - lastregno = 29; - infprintf (is, "%s", mips_gpr_names[29]); - break; - - case 't': - infprintf (is, "%s", mips_gpr_names[lastregno]); - break; - - case 'z': /* $0. */ - infprintf (is, "%s", mips_gpr_names[0]); - break; - - case 'A': - /* Sign-extend the immediate. */ - immed = GET_OP_S (insn, IMMA) << 2; - infprintf (is, "%d", immed); - break; - - case 'B': - immed = micromips_imm_b_map[GET_OP (insn, IMMB)]; - infprintf (is, "%d", immed); - break; - - case 'C': - immed = micromips_imm_c_map[GET_OP (insn, IMMC)]; - infprintf (is, "0x%x", immed); - break; - - case 'D': - /* Sign-extend the displacement. */ - delta = GET_OP_S (insn, IMMD); - info->target = (delta << 1) + memaddr + length; - (*info->print_address_func) (info->target, info); - break; - - case 'E': - /* Sign-extend the displacement. */ - delta = GET_OP_S (insn, IMME); - info->target = (delta << 1) + memaddr + length; - (*info->print_address_func) (info->target, info); - break; - - case 'F': - immed = GET_OP (insn, IMMF); - infprintf (is, "0x%x", immed); - break; - - case 'G': - immed = (insn >> MICROMIPSOP_SH_IMMG) + 1; - immed = (immed & MICROMIPSOP_MASK_IMMG) - 1; - infprintf (is, "%d", immed); - break; - - case 'H': - immed = GET_OP (insn, IMMH) << 1; - infprintf (is, "%d", immed); - break; - - case 'I': - immed = (insn >> MICROMIPSOP_SH_IMMI) + 1; - immed = (immed & MICROMIPSOP_MASK_IMMI) - 1; - infprintf (is, "%d", immed); - break; - - case 'J': - immed = GET_OP (insn, IMMJ) << 2; - infprintf (is, "%d", immed); - break; - - case 'L': - immed = GET_OP (insn, IMML); - infprintf (is, "%d", immed); - break; - - case 'M': - immed = (insn >> MICROMIPSOP_SH_IMMM) - 1; - immed = (immed & MICROMIPSOP_MASK_IMMM) + 1; - infprintf (is, "%d", immed); - break; - - case 'N': - immed = GET_OP (insn, IMMN); - if (immed == 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 + immed], - mips_gpr_names[31]); - break; - - case 'O': - immed = GET_OP (insn, IMMO); - infprintf (is, "0x%x", immed); - break; - - case 'P': - immed = GET_OP (insn, IMMP) << 2; - infprintf (is, "%d", immed); - break; - - case 'Q': - /* Sign-extend the immediate. */ - immed = GET_OP_S (insn, IMMQ) << 2; - infprintf (is, "%d", immed); - break; - - case 'U': - immed = GET_OP (insn, IMMU) << 2; - infprintf (is, "%d", immed); - break; - - case 'W': - immed = GET_OP (insn, IMMW) << 2; - infprintf (is, "%d", immed); - break; - - case 'X': - /* Sign-extend the immediate. */ - immed = GET_OP_S (insn, IMMX); - infprintf (is, "%d", immed); - break; - - case 'Y': - /* Sign-extend the immediate. */ - immed = GET_OP_S (insn, IMMY) << 2; - if ((unsigned int) (immed + 8) < 16) - immed ^= 0x400; - infprintf (is, "%d", immed); - break; - - default: - /* xgettext:c-format */ - infprintf (is, - _("# internal disassembler error, " - "unrecognized modifier (m%c)"), - *s); - abort (); - } - break; - - default: - /* xgettext:c-format */ - infprintf (is, - _("# internal disassembler error, " - "unrecognized modifier (%c)"), - *s); - abort (); - } + infprintf (is, "\t"); + print_insn_args (info, op, decode_micromips_operand, insn, + memaddr + length + 1); } /* Figure out instruction type and branch delay information. */ @@ -2937,8 +2170,6 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) return length; } } -#undef GET_OP_S -#undef GET_OP infprintf (is, "0x%x", insn); info->insn_type = dis_noninsn; |