From cfe97b909f1132567841a9b52c11497622b85fee Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Sun, 14 Jul 2013 13:28:56 +0000 Subject: 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 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. --- opcodes/ChangeLog | 26 + opcodes/micromips-opc.c | 149 ++++- opcodes/mips-dis.c | 1401 +++++++++++------------------------------------ opcodes/mips-formats.h | 113 ++++ opcodes/mips-opc.c | 117 ++++ 5 files changed, 720 insertions(+), 1086 deletions(-) create mode 100644 opcodes/mips-formats.h (limited to 'opcodes') diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog index aa4d0f46065..17cc1c6d49c 100644 --- a/opcodes/ChangeLog +++ b/opcodes/ChangeLog @@ -1,3 +1,29 @@ +2013-07-14 Richard Sandiford + + * 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 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. + 2013-07-14 Richard Sandiford * mips16-opc.c (mips16_opcodes): Use "I" for immediate operands diff --git a/opcodes/micromips-opc.c b/opcodes/micromips-opc.c index de8053c5ed9..8630769d3e0 100644 --- a/opcodes/micromips-opc.c +++ b/opcodes/micromips-opc.c @@ -20,8 +20,155 @@ MA 02110-1301, USA. */ #include "sysdep.h" -#include #include "opcode/mips.h" +#include "mips-formats.h" + +static unsigned char reg_0_map[] = { 0 }; +static unsigned char reg_28_map[] = { 28 }; +static unsigned char reg_29_map[] = { 29 }; +static unsigned char reg_31_map[] = { 31 }; +static unsigned char reg_m16_map[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; +static unsigned char reg_mn_map[] = { 0, 17, 2, 3, 16, 18, 19, 20 }; +static unsigned char reg_q_map[] = { 0, 17, 2, 3, 4, 5, 6, 7 }; + +static unsigned char reg_h_map1[] = { 5, 5, 6, 4, 4, 4, 4, 4 }; +static unsigned char reg_h_map2[] = { 6, 7, 7, 21, 22, 5, 6, 7 }; + +static int int_b_map[] = { + 1, 4, 8, 12, 16, 20, 24, -1 +}; +static int int_c_map[] = { + 128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535 +}; + +/* Return the mips_operand structure for the operand at the beginning of P. */ + +const struct mips_operand * +decode_micromips_operand (const char *p) +{ + switch (p[0]) + { + case 'm': + switch (p[1]) + { + case 'a': MAPPED_REG (0, 0, GP, reg_28_map); + case 'b': MAPPED_REG (3, 23, GP, reg_m16_map); + case 'c': MAPPED_REG (3, 4, GP, reg_m16_map); + case 'd': MAPPED_REG (3, 7, GP, reg_m16_map); + case 'e': MAPPED_REG (3, 1, GP, reg_m16_map); + case 'f': MAPPED_REG (3, 3, GP, reg_m16_map); + case 'g': MAPPED_REG (3, 0, GP, reg_m16_map); + case 'h': REG_PAIR (3, 7, GP, reg_h_map); + case 'j': REG (5, 0, GP); + case 'l': MAPPED_REG (3, 4, GP, reg_m16_map); + case 'm': MAPPED_REG (3, 1, GP, reg_mn_map); + case 'n': MAPPED_REG (3, 4, GP, reg_mn_map); + case 'p': REG (5, 5, GP); + case 'q': MAPPED_REG (3, 7, GP, reg_q_map); + case 'r': SPECIAL (0, 0, PC); + case 's': MAPPED_REG (0, 0, GP, reg_29_map); + case 't': SPECIAL (0, 0, REPEAT_PREV_REG); + case 'x': SPECIAL (0, 0, REPEAT_DEST_REG); + case 'y': MAPPED_REG (0, 0, GP, reg_31_map); + case 'z': MAPPED_REG (0, 0, GP, reg_0_map); + + case 'A': INT_ADJ (7, 0, 63, 2, FALSE); /* (-64 .. 63) << 2 */ + case 'B': MAPPED_INT (3, 1, int_b_map, FALSE); + case 'C': MAPPED_INT (4, 0, int_c_map, TRUE); + case 'D': BRANCH (10, 0, 1); + case 'E': BRANCH (7, 0, 1); + case 'F': HINT (4, 0); + case 'G': INT_ADJ (4, 0, 14, 0, FALSE); /* (-1 .. 14) */ + case 'H': INT_ADJ (4, 0, 15, 1, FALSE); /* (0 .. 15) << 1 */ + case 'I': INT_ADJ (7, 0, 126, 0, FALSE); /* (-1 .. 126) */ + case 'J': INT_ADJ (4, 0, 15, 2, FALSE); /* (0 .. 15) << 2 */ + case 'L': INT_ADJ (4, 0, 15, 0, FALSE); /* (0 .. 15) */ + case 'M': INT_ADJ (3, 1, 8, 0, FALSE); /* (1 .. 8) */ + case 'N': SPECIAL (2, 4, LWM_SWM_LIST); + case 'O': HINT (4, 0); + case 'P': INT_ADJ (5, 0, 31, 2, FALSE); /* (0 .. 31) << 2 */ + case 'Q': INT_ADJ (23, 0, 4194303, 2, FALSE); + /* (-4194304 .. 4194303) */ + case 'U': INT_ADJ (5, 0, 31, 2, FALSE); /* (0 .. 31) << 2 */ + case 'W': INT_ADJ (6, 1, 63, 2, FALSE); /* (0 .. 63) << 2 */ + case 'X': SINT (4, 1); + case 'Y': SPECIAL (9, 1, ADDIUSP_INT); + case 'Z': UINT (0, 0); /* 0 only */ + } + break; + + case '+': + switch (p[1]) + { + case 'A': BIT (5, 6, 0); /* (0 .. 31) */ + case 'B': MSB (5, 11, 1, TRUE, 32); /* (1 .. 32), 32-bit op */ + case 'C': MSB (5, 11, 1, FALSE, 32); /* (1 .. 32), 32-bit op */ + case 'E': BIT (5, 6, 32); /* (32 .. 63) */ + case 'F': MSB (5, 11, 33, TRUE, 64); /* (33 .. 64), 64-bit op */ + case 'G': MSB (5, 11, 33, FALSE, 64); /* (33 .. 64), 64-bit op */ + case 'H': MSB (5, 11, 1, FALSE, 64); /* (1 .. 32), 64-bit op */ + + case 'i': JALX (26, 0, 2); + case 'j': SINT (9, 0); + } + break; + + case '.': SINT (10, 6); + case '<': BIT (5, 11, 0); /* (0 .. 31) */ + case '>': BIT (5, 11, 32); /* (32 .. 63) */ + case '\\': BIT (3, 21, 0); /* (0 .. 7) */ + case '|': HINT (4, 12); + case '~': SINT (12, 0); + case '@': SINT (10, 16); + case '^': HINT (5, 11); + + case '0': SINT (6, 16); + case '1': HINT (5, 16); + case '2': HINT (2, 14); + case '3': HINT (3, 13); + case '4': HINT (4, 12); + case '5': HINT (8, 13); + case '6': HINT (5, 16); + case '7': REG (2, 14, ACC); + case '8': HINT (6, 14); + + case 'B': HINT (10, 16); + case 'C': HINT (23, 3); + case 'D': REG (5, 11, FP); + case 'E': REG (5, 21, COPRO); + case 'G': REG (5, 16, COPRO); + case 'K': REG (5, 16, HW); + case 'H': UINT (3, 11); + case 'M': REG (3, 13, CCC); + case 'N': REG (3, 18, CCC); + case 'R': REG (5, 6, FP); + case 'S': REG (5, 16, FP); + case 'T': REG (5, 21, FP); + case 'V': REG (5, 16, FP); + + case 'a': JUMP (26, 0, 1); + case 'b': REG (5, 16, GP); + case 'c': HINT (10, 16); + case 'd': REG (5, 11, GP); + case 'h': HINT (5, 11); + case 'i': HINT (16, 0); + case 'j': SINT (16, 0); + case 'k': HINT (5, 21); + case 'n': SPECIAL (5, 21, LWM_SWM_LIST); + case 'o': SINT (16, 0); + case 'p': BRANCH (16, 0, 1); + case 'q': HINT (10, 6); + case 'r': REG (5, 16, GP); + case 's': REG (5, 16, GP); + case 't': REG (5, 21, GP); + case 'u': HINT (16, 0); + case 'v': REG (5, 16, GP); + case 'w': REG (5, 21, GP); + case 'y': REG (5, 6, GP); + case 'z': MAPPED_REG (0, 0, GP, reg_0_map); + } + return 0; +} #define UBD INSN_UNCOND_BRANCH_DELAY #define CBD INSN_COND_BRANCH_DELAY 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; diff --git a/opcodes/mips-formats.h b/opcodes/mips-formats.h new file mode 100644 index 00000000000..7deba5e0ebc --- /dev/null +++ b/opcodes/mips-formats.h @@ -0,0 +1,113 @@ +/* mips-formats.h + Copyright 2013 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + It is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING3. If not, + see . */ + +/* For ARRAY_SIZE. */ +#include "libiberty.h" + +#define INT_ADJ(SIZE, LSB, MAX_VAL, SHIFT, PRINT_HEX) \ + { \ + static const struct mips_int_operand op = { \ + { OP_INT, SIZE, LSB }, MAX_VAL, 0, SHIFT, PRINT_HEX \ + }; \ + return &op.root; \ + } + +#define UINT(SIZE, LSB) \ + INT_ADJ(SIZE, LSB, (1 << (SIZE)) - 1, 0, FALSE) + +#define SINT(SIZE, LSB) \ + INT_ADJ(SIZE, LSB, (1 << ((SIZE) - 1)) - 1, 0, FALSE) + +#define HINT(SIZE, LSB) \ + INT_ADJ(SIZE, LSB, (1 << (SIZE)) - 1, 0, TRUE) + +#define BIT(SIZE, LSB, BIAS) \ + { \ + static const struct mips_int_operand op = { \ + { OP_INT, SIZE, LSB }, (1 << (SIZE)) - 1, BIAS, 0, TRUE \ + }; \ + return &op.root; \ + } + +#define MAPPED_INT(SIZE, LSB, MAP, PRINT_HEX) \ + { \ + typedef char static_assert[(1 << (SIZE)) == ARRAY_SIZE (MAP)]; \ + static const struct mips_mapped_int_operand op = { \ + { OP_MAPPED_INT, SIZE, LSB }, MAP, PRINT_HEX \ + }; \ + return &op.root; \ + } + +#define MSB(SIZE, LSB, BIAS, ADD_LSB, OPSIZE) \ + { \ + static const struct mips_msb_operand op = { \ + { OP_MSB, SIZE, LSB }, BIAS, ADD_LSB, OPSIZE \ + }; \ + return &op.root; \ + } + +#define REG(SIZE, LSB, BANK) \ + { \ + static const struct mips_reg_operand op = { \ + { OP_REG, SIZE, LSB }, OP_REG_##BANK, 0 \ + }; \ + return &op.root; \ + } + +#define MAPPED_REG(SIZE, LSB, BANK, MAP) \ + { \ + typedef char static_assert[(1 << (SIZE)) == ARRAY_SIZE (MAP)]; \ + static const struct mips_reg_operand op = { \ + { OP_REG, SIZE, LSB }, OP_REG_##BANK, MAP \ + }; \ + return &op.root; \ + } + +#define REG_PAIR(SIZE, LSB, BANK, MAP) \ + { \ + typedef char static_assert1[(1 << (SIZE)) == ARRAY_SIZE (MAP##1)]; \ + typedef char static_assert2[(1 << (SIZE)) == ARRAY_SIZE (MAP##2)]; \ + static const struct mips_reg_pair_operand op = { \ + { OP_REG_PAIR, SIZE, LSB }, OP_REG_##BANK, MAP##1, MAP##2 \ + }; \ + return &op.root; \ + } + +#define PCREL(SIZE, LSB, ALIGN_LOG2, SHIFT, IS_SIGNED, INCLUDE_ISA_BIT, \ + FLIP_ISA_BIT) \ + { \ + static const struct mips_pcrel_operand op = { \ + { OP_PCREL, SIZE, LSB }, ALIGN_LOG2, SHIFT, IS_SIGNED, \ + INCLUDE_ISA_BIT, FLIP_ISA_BIT \ + }; \ + return &op.root; \ + } + +#define JUMP(SIZE, LSB, SHIFT) \ + PCREL (SIZE, LSB, SIZE + SHIFT, SHIFT, FALSE, TRUE, FALSE) + +#define JALX(SIZE, LSB, SHIFT) \ + PCREL (SIZE, LSB, SIZE + SHIFT, SHIFT, FALSE, TRUE, TRUE) + +#define BRANCH(SIZE, LSB, SHIFT) \ + PCREL (SIZE, LSB, 0, SHIFT, TRUE, TRUE, FALSE) + +#define SPECIAL(SIZE, LSB, TYPE) \ + { \ + static const struct mips_operand op = { OP_##TYPE, SIZE, LSB }; \ + return &op; \ + } diff --git a/opcodes/mips-opc.c b/opcodes/mips-opc.c index e6833f74386..702016b2c3c 100644 --- a/opcodes/mips-opc.c +++ b/opcodes/mips-opc.c @@ -28,6 +28,123 @@ #include "sysdep.h" #include #include "opcode/mips.h" +#include "mips-formats.h" + +static unsigned char reg_0_map[] = { 0 }; + +/* Return the mips_operand structure for the operand at the beginning of P. */ + +const struct mips_operand * +decode_mips_operand (const char *p) +{ + switch (p[0]) + { + case '+': + switch (p[1]) + { + case '1': HINT (5, 6); + case '2': HINT (10, 6); + case '3': HINT (15, 6); + case '4': HINT (20, 6); + + case 'A': BIT (5, 6, 0); /* (0 .. 31) */ + case 'B': MSB (5, 11, 1, TRUE, 32); /* (1 .. 32), 32-bit op */ + case 'C': MSB (5, 11, 1, FALSE, 32); /* (1 .. 32), 32-bit op */ + case 'E': BIT (5, 6, 32); /* (32 .. 63) */ + case 'F': MSB (5, 11, 33, TRUE, 64); /* (33 .. 64), 64-bit op */ + case 'G': MSB (5, 11, 33, FALSE, 64); /* (33 .. 64), 64-bit op */ + case 'H': MSB (5, 11, 1, FALSE, 64); /* (1 .. 32), 64-bit op */ + case 'J': HINT (10, 11); + case 'P': BIT (5, 6, 32); /* (32 .. 63) */ + case 'Q': SINT (10, 6); + case 'S': MSB (5, 11, 0, FALSE, 63); /* (0 .. 31), 64-bit op */ + case 'X': BIT (5, 16, 32); /* (32 .. 63) */ + case 'Z': REG (5, 0, FP); + + case 'a': SINT (8, 6); + case 'b': SINT (8, 3); + case 'c': INT_ADJ (9, 6, 255, 4, FALSE); /* (-256 .. 255) << 4 */ + case 'i': JALX (26, 0, 2); + case 'j': SINT (9, 7); + case 'p': BIT (5, 6, 0); /* (0 .. 31), 32-bit op */ + case 's': MSB (5, 11, 0, FALSE, 31); /* (0 .. 31) */ + case 't': REG (5, 16, COPRO); + case 'x': BIT (5, 16, 0); /* (0 .. 31) */ + case 'z': REG (5, 0, GP); + } + break; + + case '<': BIT (5, 6, 0); /* (0 .. 31) */ + case '>': BIT (5, 6, 32); /* (32 .. 63) */ + case '%': UINT (3, 21); + case ':': SINT (7, 19); + case '\'': HINT (6, 16); + case '@': SINT (10, 16); + case '!': UINT (1, 5); + case '$': UINT (1, 4); + case '*': REG (2, 18, ACC); + case '&': REG (2, 13, ACC); + case '~': SINT (12, 0); + case '\\': BIT (3, 12, 0); /* (0 .. 7) */ + + case '0': SINT (6, 20); + case '1': HINT (5, 6); + case '2': HINT (2, 11); + case '3': HINT (3, 21); + case '4': HINT (4, 21); + case '5': HINT (8, 16); + case '6': HINT (5, 21); + case '7': REG (2, 11, ACC); + case '8': HINT (6, 11); + case '9': REG (2, 21, ACC); + + case 'B': HINT (20, 6); + case 'C': HINT (25, 0); + case 'D': REG (5, 6, FP); + case 'E': REG (5, 16, COPRO); + case 'G': REG (5, 11, COPRO); + case 'H': UINT (3, 0); + case 'J': HINT (19, 6); + case 'K': REG (5, 11, HW); + case 'M': REG (3, 8, CCC); + case 'N': REG (3, 18, CCC); + case 'O': UINT (3, 21); + case 'P': SPECIAL (5, 1, PERF_REG); + case 'Q': SPECIAL (10, 16, MDMX_IMM_REG); + case 'R': REG (5, 21, FP); + case 'S': REG (5, 11, FP); + case 'T': REG (5, 16, FP); + case 'U': SPECIAL (10, 11, CLO_CLZ_DEST); + case 'V': REG (5, 11, FP); + case 'W': REG (5, 16, FP); + case 'X': REG (5, 6, VEC); + case 'Y': REG (5, 11, VEC); + case 'Z': REG (5, 16, VEC); + + case 'a': JUMP (26, 0, 2); + case 'b': REG (5, 21, GP); + case 'c': HINT (10, 16); + case 'd': REG (5, 11, GP); + case 'e': UINT (3, 22) + case 'g': REG (5, 11, COPRO); + case 'h': HINT (5, 11); + case 'i': HINT (16, 0); + case 'j': SINT (16, 0); + case 'k': HINT (5, 16); + case 'o': SINT (16, 0); + case 'p': BRANCH (16, 0, 2); + case 'q': HINT (10, 6); + case 'r': REG (5, 21, GP); + case 's': REG (5, 21, GP); + case 't': REG (5, 16, GP); + case 'u': HINT (16, 0); + case 'v': REG (5, 21, GP); + case 'w': REG (5, 16, GP); + case 'x': REG (0, 0, GP); + case 'z': MAPPED_REG (0, 0, GP, reg_0_map); + } + return 0; +} /* Short hand so the lines aren't too long. */ -- cgit v1.2.1