X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=opcodes%2Fmips-dis.c;h=5bf33d9f8fddaa45cf389990f2d3ab1bc9ef13f2;hb=HEAD;hp=0bd5fef00d8a57e0928e5bcab2a7db71a06ea07c;hpb=e407c74b5b6020cd4f5de18ba74f3959fd9ac190;p=thirdparty%2Fbinutils-gdb.git diff --git a/opcodes/mips-dis.c b/opcodes/mips-dis.c index 0bd5fef00d8..97318529c87 100644 --- a/opcodes/mips-dis.c +++ b/opcodes/mips-dis.c @@ -1,7 +1,5 @@ /* Print mips instructions for GDB, the GNU debugger, or for objdump. - Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003, 2005, 2006, 2007, 2008, 2009, 2012 - Free Software Foundation, Inc. + Copyright (C) 1989-2024 Free Software Foundation, Inc. Contributed by Nobuyuki Hikichi(hikichi@sra.co.jp). This file is part of the GNU opcodes library. @@ -22,10 +20,13 @@ MA 02110-1301, USA. */ #include "sysdep.h" -#include "dis-asm.h" +#include "disassemble.h" #include "libiberty.h" #include "opcode/mips.h" #include "opintl.h" +#include "elf-bfd.h" +#include "elf/mips.h" +#include "elfxx-mips.h" /* FIXME: These are needed to figure out if the code is mips16 or not. The low bit of the address is often a good indicator. No @@ -34,8 +35,6 @@ #if !defined(EMBEDDED_ENV) #define SYMTAB_AVAILABLE 1 -#include "elf-bfd.h" -#include "elf/mips.h" #endif /* Mips instructions are at maximum this many bytes long. */ @@ -51,100 +50,6 @@ struct mips_cp0sel_name const char * const name; }; -/* The mips16 registers. */ -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_map[] = -{ - 5, 5, 6, 4, 4, 4, 4, 4 -}; - -/* The microMIPS registers with type i. */ -static const unsigned int micromips_to_32_reg_i_map[] = -{ - 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]] - - static const char * const mips_gpr_names_numeric[32] = { "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", @@ -209,6 +114,26 @@ static const char * const mips_cp0_names_numeric[32] = "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31" }; +static const char * const mips_cp1_names_numeric[32] = +{ + "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", + "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", + "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31" +}; + +static const char * const mips_cp0_names_r3900[32] = +{ + "$0", "$1", "$2", "c0_config", + "$4", "$5", "$6", "c0_cache", + "c0_badvaddr", "$9", "$10", "$11", + "c0_sr", "c0_cause", "c0_epc", "c0_prid", + "c0_debug", "c0_depc", "$18", "$19", + "$20", "$21", "$22", "$23", + "$24", "$25", "$26", "$27", + "$28", "$29", "$30", "$31", +}; + static const char * const mips_cp0_names_r3000[32] = { "c0_index", "c0_random", "c0_entrylo", "$3", @@ -245,18 +170,6 @@ static const char * const mips_cp0_names_r5900[32] = "c0_taglo", "c0_taghi", "c0_errorepc", "$31" }; -static const struct mips_cp0sel_name mips_cp0sel_names_mipsr5900[] = -{ - { 24, 2, "c0_iab" }, - { 24, 3, "c0_iabm" }, - { 24, 4, "c0_dab" }, - { 24, 5, "c0_dabm" }, - { 24, 6, "c0_dvb" }, - { 24, 7, "c0_dvbm" }, - { 25, 1, "c0_perfcnt,1" }, - { 25, 2, "c0_perfcnt,2" } -}; - static const char * const mips_cp0_names_mips3264[32] = { "c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1", @@ -269,6 +182,30 @@ static const char * const mips_cp0_names_mips3264[32] = "c0_taglo", "c0_taghi", "c0_errorepc", "c0_desave", }; +static const char * const mips_cp1_names_mips[32] = +{ + "c1_fir", "$1", "$2", "$3", + "$4", "$5", "$6", "$7", + "$8", "$9", "$10", "$11", + "$12", "$13", "$14", "$15", + "$16", "$17", "$18", "$19", + "$20", "$21", "$22", "$23", + "$24", "$25", "$26", "$27", + "$28", "$29", "$30", "c1_fcsr" +}; + +static const char * const mips_cp1_names_mips3264[32] = +{ + "c1_fir", "c1_ufr", "$2", "$3", + "c1_unfr", "$5", "$6", "$7", + "$8", "$9", "$10", "$11", + "$12", "$13", "$14", "$15", + "$16", "$17", "$18", "$19", + "$20", "$21", "$22", "$23", + "$24", "c1_fccr", "c1_fexr", "$27", + "c1_fenr", "$29", "$30", "c1_fcsr" +}; + static const struct mips_cp0sel_name mips_cp0sel_names_mips3264[] = { { 16, 1, "c0_config1" }, @@ -495,6 +432,15 @@ static const char * const mips_hwr_names_mips3264r2[32] = "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31" }; +static const char * const msa_control_names[32] = +{ + "msa_ir", "msa_csr", "msa_access", "msa_save", + "msa_modify", "msa_request", "msa_map", "msa_unmap", + "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23", + "$24", "$25", "$26", "$27", "$28", "$29", "$30", "$31" +}; + struct mips_abi_choice { const char * name; @@ -517,65 +463,95 @@ struct mips_arch_choice unsigned long bfd_mach; int processor; int isa; + int ase; const char * const *cp0_names; const struct mips_cp0sel_name *cp0sel_names; unsigned int cp0sel_names_len; + const char * const *cp1_names; const char * const *hwr_names; }; const struct mips_arch_choice mips_arch_choices[] = { - { "numeric", 0, 0, 0, 0, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - - { "r3000", 1, bfd_mach_mips3000, CPU_R3000, ISA_MIPS1, - mips_cp0_names_r3000, NULL, 0, mips_hwr_names_numeric }, - { "r3900", 1, bfd_mach_mips3900, CPU_R3900, ISA_MIPS1, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "r4000", 1, bfd_mach_mips4000, CPU_R4000, ISA_MIPS3, - mips_cp0_names_r4000, NULL, 0, mips_hwr_names_numeric }, - { "r4010", 1, bfd_mach_mips4010, CPU_R4010, ISA_MIPS2, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "vr4100", 1, bfd_mach_mips4100, CPU_VR4100, ISA_MIPS3, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "vr4111", 1, bfd_mach_mips4111, CPU_R4111, ISA_MIPS3, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "vr4120", 1, bfd_mach_mips4120, CPU_VR4120, ISA_MIPS3, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "r4300", 1, bfd_mach_mips4300, CPU_R4300, ISA_MIPS3, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "r4400", 1, bfd_mach_mips4400, CPU_R4400, ISA_MIPS3, - mips_cp0_names_r4000, NULL, 0, mips_hwr_names_numeric }, - { "r4600", 1, bfd_mach_mips4600, CPU_R4600, ISA_MIPS3, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "r4650", 1, bfd_mach_mips4650, CPU_R4650, ISA_MIPS3, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "r5000", 1, bfd_mach_mips5000, CPU_R5000, ISA_MIPS4, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "vr5400", 1, bfd_mach_mips5400, CPU_VR5400, ISA_MIPS4, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "vr5500", 1, bfd_mach_mips5500, CPU_VR5500, ISA_MIPS4, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "r5900", 1, bfd_mach_mips5900, CPU_R5900, ISA_MIPS3, - mips_cp0_names_r5900, NULL, 0, mips_hwr_names_numeric }, - { "r6000", 1, bfd_mach_mips6000, CPU_R6000, ISA_MIPS2, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "rm7000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "rm9000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "r8000", 1, bfd_mach_mips8000, CPU_R8000, ISA_MIPS4, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "r10000", 1, bfd_mach_mips10000, CPU_R10000, ISA_MIPS4, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "r12000", 1, bfd_mach_mips12000, CPU_R12000, ISA_MIPS4, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "r14000", 1, bfd_mach_mips14000, CPU_R14000, ISA_MIPS4, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "r16000", 1, bfd_mach_mips16000, CPU_R16000, ISA_MIPS4, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, - { "mips5", 1, bfd_mach_mips5, CPU_MIPS5, ISA_MIPS5, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "numeric", 0, 0, 0, 0, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_numeric, + mips_hwr_names_numeric }, + + { "r3000", 1, bfd_mach_mips3000, CPU_R3000, ISA_MIPS1, 0, + mips_cp0_names_r3000, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r3900", 1, bfd_mach_mips3900, CPU_R3900, ISA_MIPS1, 0, + mips_cp0_names_r3900, NULL, 0, mips_cp1_names_numeric, + mips_hwr_names_numeric }, + { "r4000", 1, bfd_mach_mips4000, CPU_R4000, ISA_MIPS3, 0, + mips_cp0_names_r4000, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r4010", 1, bfd_mach_mips4010, CPU_R4010, ISA_MIPS2, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "allegrex", 1, bfd_mach_mips_allegrex, CPU_ALLEGREX, ISA_MIPS2, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_numeric, + mips_hwr_names_numeric }, + { "vr4100", 1, bfd_mach_mips4100, CPU_VR4100, ISA_MIPS3, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "vr4111", 1, bfd_mach_mips4111, CPU_R4111, ISA_MIPS3, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "vr4120", 1, bfd_mach_mips4120, CPU_VR4120, ISA_MIPS3, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r4300", 1, bfd_mach_mips4300, CPU_R4300, ISA_MIPS3, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r4400", 1, bfd_mach_mips4400, CPU_R4400, ISA_MIPS3, 0, + mips_cp0_names_r4000, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r4600", 1, bfd_mach_mips4600, CPU_R4600, ISA_MIPS3, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r4650", 1, bfd_mach_mips4650, CPU_R4650, ISA_MIPS3, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r5000", 1, bfd_mach_mips5000, CPU_R5000, ISA_MIPS4, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "vr5400", 1, bfd_mach_mips5400, CPU_VR5400, ISA_MIPS4, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "vr5500", 1, bfd_mach_mips5500, CPU_VR5500, ISA_MIPS4, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r5900", 1, bfd_mach_mips5900, CPU_R5900, ISA_MIPS3, 0, + mips_cp0_names_r5900, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r6000", 1, bfd_mach_mips6000, CPU_R6000, ISA_MIPS2, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "rm7000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "rm9000", 1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r8000", 1, bfd_mach_mips8000, CPU_R8000, ISA_MIPS4, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r10000", 1, bfd_mach_mips10000, CPU_R10000, ISA_MIPS4, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r12000", 1, bfd_mach_mips12000, CPU_R12000, ISA_MIPS4, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r14000", 1, bfd_mach_mips14000, CPU_R14000, ISA_MIPS4, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "r16000", 1, bfd_mach_mips16000, CPU_R16000, ISA_MIPS4, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, + { "mips5", 1, bfd_mach_mips5, CPU_MIPS5, ISA_MIPS5, 0, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips, + mips_hwr_names_numeric }, /* For stock MIPS32, disassemble all applicable MIPS-specified ASEs. Note that MIPS-3D and MDMX are not applicable to MIPS32. (See @@ -583,80 +559,162 @@ const struct mips_arch_choice mips_arch_choices[] = MIPS32 Architecture_ (MIPS Document Number MD00082, Revision 0.95), page 1. */ { "mips32", 1, bfd_mach_mipsisa32, CPU_MIPS32, - ISA_MIPS32 | INSN_SMARTMIPS, + ISA_MIPS32, ASE_SMARTMIPS, mips_cp0_names_mips3264, mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264), - mips_hwr_names_numeric }, + mips_cp1_names_mips3264, mips_hwr_names_numeric }, { "mips32r2", 1, bfd_mach_mipsisa32r2, CPU_MIPS32R2, - (ISA_MIPS32R2 | INSN_SMARTMIPS | INSN_DSP | INSN_DSPR2 - | INSN_MIPS3D | INSN_MT | INSN_MCU), + ISA_MIPS32R2, + (ASE_SMARTMIPS | ASE_DSP | ASE_DSPR2 | ASE_EVA | ASE_MIPS3D + | ASE_MT | ASE_MCU | ASE_VIRT | ASE_MSA | ASE_XPA), + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, + + { "mips32r3", 1, bfd_mach_mipsisa32r3, CPU_MIPS32R3, + ISA_MIPS32R3, + (ASE_SMARTMIPS | ASE_DSP | ASE_DSPR2 | ASE_EVA | ASE_MIPS3D + | ASE_MT | ASE_MCU | ASE_VIRT | ASE_MSA | ASE_XPA), + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, + + { "mips32r5", 1, bfd_mach_mipsisa32r5, CPU_MIPS32R5, + ISA_MIPS32R5, + (ASE_SMARTMIPS | ASE_DSP | ASE_DSPR2 | ASE_EVA | ASE_MIPS3D + | ASE_MT | ASE_MCU | ASE_VIRT | ASE_MSA | ASE_XPA), + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, + + { "mips32r6", 1, bfd_mach_mipsisa32r6, CPU_MIPS32R6, + ISA_MIPS32R6, + (ASE_EVA | ASE_MSA | ASE_VIRT | ASE_XPA | ASE_MCU | ASE_MT | ASE_DSP + | ASE_DSPR2 | ASE_DSPR3 | ASE_CRC | ASE_GINV), mips_cp0_names_mips3264r2, mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), - mips_hwr_names_mips3264r2 }, + mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, /* For stock MIPS64, disassemble all applicable MIPS-specified ASEs. */ { "mips64", 1, bfd_mach_mipsisa64, CPU_MIPS64, - ISA_MIPS64 | INSN_MIPS3D | INSN_MDMX, + ISA_MIPS64, ASE_MIPS3D | ASE_MDMX, mips_cp0_names_mips3264, mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264), - mips_hwr_names_numeric }, + mips_cp1_names_mips3264, mips_hwr_names_numeric }, { "mips64r2", 1, bfd_mach_mipsisa64r2, CPU_MIPS64R2, - (ISA_MIPS64R2 | INSN_MIPS3D | INSN_DSP | INSN_DSPR2 - | INSN_DSP64 | INSN_MT | INSN_MDMX | INSN_MCU), + ISA_MIPS64R2, + (ASE_MIPS3D | ASE_DSP | ASE_DSPR2 | ASE_DSP64 | ASE_EVA | ASE_MT + | ASE_MCU | ASE_VIRT | ASE_VIRT64 | ASE_MSA | ASE_MSA64 | ASE_XPA), + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, + + { "mips64r3", 1, bfd_mach_mipsisa64r3, CPU_MIPS64R3, + ISA_MIPS64R3, + (ASE_MIPS3D | ASE_DSP | ASE_DSPR2 | ASE_DSP64 | ASE_EVA | ASE_MT + | ASE_MCU | ASE_VIRT | ASE_VIRT64 | ASE_MSA | ASE_MSA64 | ASE_XPA), + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, + + { "mips64r5", 1, bfd_mach_mipsisa64r5, CPU_MIPS64R5, + ISA_MIPS64R5, + (ASE_MIPS3D | ASE_DSP | ASE_DSPR2 | ASE_DSP64 | ASE_EVA | ASE_MT + | ASE_MCU | ASE_VIRT | ASE_VIRT64 | ASE_MSA | ASE_MSA64 | ASE_XPA), + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, + + { "mips64r6", 1, bfd_mach_mipsisa64r6, CPU_MIPS64R6, + ISA_MIPS64R6, + (ASE_EVA | ASE_MSA | ASE_MSA64 | ASE_XPA | ASE_VIRT | ASE_VIRT64 + | ASE_MCU | ASE_MT | ASE_DSP | ASE_DSPR2 | ASE_DSPR3 | ASE_CRC + | ASE_CRC64 | ASE_GINV), mips_cp0_names_mips3264r2, mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), - mips_hwr_names_mips3264r2 }, + mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, + + { "interaptiv-mr2", 1, bfd_mach_mips_interaptiv_mr2, CPU_INTERAPTIV_MR2, + ISA_MIPS32R3, + ASE_MT | ASE_EVA | ASE_DSP | ASE_DSPR2 | ASE_MIPS16E2 | ASE_MIPS16E2_MT, + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, { "sb1", 1, bfd_mach_mips_sb1, CPU_SB1, - ISA_MIPS64 | INSN_MIPS3D | INSN_SB1, + ISA_MIPS64 | INSN_SB1, ASE_MIPS3D, mips_cp0_names_sb1, mips_cp0sel_names_sb1, ARRAY_SIZE (mips_cp0sel_names_sb1), - mips_hwr_names_numeric }, + mips_cp1_names_mips3264, mips_hwr_names_numeric }, { "loongson2e", 1, bfd_mach_mips_loongson_2e, CPU_LOONGSON_2E, - ISA_MIPS3 | INSN_LOONGSON_2E, mips_cp0_names_numeric, - NULL, 0, mips_hwr_names_numeric }, + ISA_MIPS3 | INSN_LOONGSON_2E, 0, mips_cp0_names_numeric, + NULL, 0, mips_cp1_names_mips, mips_hwr_names_numeric }, { "loongson2f", 1, bfd_mach_mips_loongson_2f, CPU_LOONGSON_2F, - ISA_MIPS3 | INSN_LOONGSON_2F, mips_cp0_names_numeric, - NULL, 0, mips_hwr_names_numeric }, + ISA_MIPS3 | INSN_LOONGSON_2F, ASE_LOONGSON_MMI, mips_cp0_names_numeric, + NULL, 0, mips_cp1_names_mips, mips_hwr_names_numeric }, - { "loongson3a", 1, bfd_mach_mips_loongson_3a, CPU_LOONGSON_3A, - ISA_MIPS64 | INSN_LOONGSON_3A, mips_cp0_names_numeric, - NULL, 0, mips_hwr_names_numeric }, + /* The loongson3a is an alias of gs464 for compatibility */ + { "loongson3a", 1, bfd_mach_mips_gs464, CPU_GS464, + ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips3264, + mips_hwr_names_numeric }, - { "octeon", 1, bfd_mach_mips_octeon, CPU_OCTEON, - ISA_MIPS64R2 | INSN_OCTEON, mips_cp0_names_numeric, NULL, 0, + { "gs464", 1, bfd_mach_mips_gs464, CPU_GS464, + ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips3264, + mips_hwr_names_numeric }, + + { "gs464e", 1, bfd_mach_mips_gs464e, CPU_GS464E, + ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT + | ASE_LOONGSON_EXT2, mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric }, + { "gs264e", 1, bfd_mach_mips_gs264e, CPU_GS264E, + ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT + | ASE_LOONGSON_EXT2 | ASE_MSA | ASE_MSA64, mips_cp0_names_numeric, NULL, + 0, mips_cp1_names_mips3264, mips_hwr_names_numeric }, + + { "octeon", 1, bfd_mach_mips_octeon, CPU_OCTEON, + ISA_MIPS64R2 | INSN_OCTEON, 0, mips_cp0_names_numeric, NULL, 0, + mips_cp1_names_mips3264, mips_hwr_names_numeric }, + { "octeon+", 1, bfd_mach_mips_octeonp, CPU_OCTEONP, - ISA_MIPS64R2 | INSN_OCTEONP, mips_cp0_names_numeric, - NULL, 0, mips_hwr_names_numeric }, + ISA_MIPS64R2 | INSN_OCTEONP, 0, mips_cp0_names_numeric, + NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric }, { "octeon2", 1, bfd_mach_mips_octeon2, CPU_OCTEON2, - ISA_MIPS64R2 | INSN_OCTEON2, mips_cp0_names_numeric, - NULL, 0, mips_hwr_names_numeric }, + ISA_MIPS64R2 | INSN_OCTEON2, 0, mips_cp0_names_numeric, + NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric }, + + { "octeon3", 1, bfd_mach_mips_octeon3, CPU_OCTEON3, + ISA_MIPS64R5 | INSN_OCTEON3, ASE_VIRT | ASE_VIRT64, + mips_cp0_names_numeric, + NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric }, { "xlr", 1, bfd_mach_mips_xlr, CPU_XLR, - ISA_MIPS64 | INSN_XLR, + ISA_MIPS64 | INSN_XLR, 0, mips_cp0_names_xlr, mips_cp0sel_names_xlr, ARRAY_SIZE (mips_cp0sel_names_xlr), - mips_hwr_names_numeric }, + mips_cp1_names_mips3264, mips_hwr_names_numeric }, /* XLP is mostly like XLR, with the prominent exception it is being MIPS64R2. */ { "xlp", 1, bfd_mach_mips_xlr, CPU_XLR, - ISA_MIPS64R2 | INSN_XLR, + ISA_MIPS64R2 | INSN_XLR, 0, mips_cp0_names_xlr, mips_cp0sel_names_xlr, ARRAY_SIZE (mips_cp0sel_names_xlr), - mips_hwr_names_numeric }, + mips_cp1_names_mips3264, mips_hwr_names_numeric }, /* This entry, mips16, is here only for ISA/processor selection; do not print its name. */ - { "", 1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS3, - mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, + { "", 1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS64, + ASE_MIPS16E2 | ASE_MIPS16E2_MT, + mips_cp0_names_numeric, NULL, 0, mips_cp1_names_numeric, + mips_hwr_names_numeric }, }; /* ISA and processor type to disassemble for, and register names to use. @@ -664,12 +722,14 @@ const struct mips_arch_choice mips_arch_choices[] = values. */ static int mips_processor; static int mips_isa; +static int mips_ase; static int micromips_ase; static const char * const *mips_gpr_names; static const char * const *mips_fpr_names; static const char * const *mips_cp0_names; static const struct mips_cp0sel_name *mips_cp0sel_names; static int mips_cp0sel_names_len; +static const char * const *mips_cp1_names; static const char * const *mips_hwr_names; /* Other options */ @@ -758,6 +818,60 @@ is_micromips (Elf_Internal_Ehdr *header) return 0; } +/* Convert ASE flags from .MIPS.abiflags to internal values. */ + +static unsigned long +mips_convert_abiflags_ases (unsigned long afl_ases) +{ + unsigned long opcode_ases = 0; + + if (afl_ases & AFL_ASE_DSP) + opcode_ases |= ASE_DSP; + if (afl_ases & AFL_ASE_DSPR2) + opcode_ases |= ASE_DSPR2; + if (afl_ases & AFL_ASE_EVA) + opcode_ases |= ASE_EVA; + if (afl_ases & AFL_ASE_MCU) + opcode_ases |= ASE_MCU; + if (afl_ases & AFL_ASE_MDMX) + opcode_ases |= ASE_MDMX; + if (afl_ases & AFL_ASE_MIPS3D) + opcode_ases |= ASE_MIPS3D; + if (afl_ases & AFL_ASE_MT) + opcode_ases |= ASE_MT; + if (afl_ases & AFL_ASE_SMARTMIPS) + opcode_ases |= ASE_SMARTMIPS; + if (afl_ases & AFL_ASE_VIRT) + opcode_ases |= ASE_VIRT; + if (afl_ases & AFL_ASE_MSA) + opcode_ases |= ASE_MSA; + if (afl_ases & AFL_ASE_XPA) + opcode_ases |= ASE_XPA; + if (afl_ases & AFL_ASE_DSPR3) + opcode_ases |= ASE_DSPR3; + if (afl_ases & AFL_ASE_MIPS16E2) + opcode_ases |= ASE_MIPS16E2; + return opcode_ases; +} + +/* Calculate combination ASE flags from regular ASE flags. */ + +static unsigned long +mips_calculate_combination_ases (int opcode_isa, unsigned long opcode_ases) +{ + unsigned long combination_ases = 0; + + if ((opcode_ases & (ASE_XPA | ASE_VIRT)) == (ASE_XPA | ASE_VIRT)) + combination_ases |= ASE_XPA_VIRT; + if ((opcode_ases & (ASE_MIPS16E2 | ASE_MT)) == (ASE_MIPS16E2 | ASE_MT)) + combination_ases |= ASE_MIPS16E2_MT; + if ((opcode_ases & ASE_EVA) + && ((opcode_isa & INSN_ISA_MASK) == ISA_MIPS64R6 + || (opcode_isa & INSN_ISA_MASK) == ISA_MIPS32R6)) + combination_ases |= ASE_EVA_R6; + return combination_ases; +} + static void set_default_mips_dis_options (struct disassemble_info *info) { @@ -769,45 +883,133 @@ set_default_mips_dis_options (struct disassemble_info *info) mips_isa = ISA_MIPS3; mips_processor = CPU_R3000; micromips_ase = 0; + mips_ase = 0; mips_gpr_names = mips_gpr_names_oldabi; mips_fpr_names = mips_fpr_names_numeric; mips_cp0_names = mips_cp0_names_numeric; mips_cp0sel_names = NULL; mips_cp0sel_names_len = 0; + mips_cp1_names = mips_cp1_names_numeric; mips_hwr_names = mips_hwr_names_numeric; no_aliases = 0; - /* Update settings according to the ELF file header flags. */ - if (info->flavour == bfd_target_elf_flavour && info->section != NULL) - { - Elf_Internal_Ehdr *header; - - header = elf_elfheader (info->section->owner); - /* If an ELF "newabi" binary, use the n32/(n)64 GPR names. */ - if (is_newabi (header)) - mips_gpr_names = mips_gpr_names_newabi; - /* If a microMIPS binary, then don't use MIPS16 bindings. */ - micromips_ase = is_micromips (header); - } - /* Set ISA, architecture, and cp0 register names as best we can. */ #if ! SYMTAB_AVAILABLE /* This is running out on a target machine, not in a host tool. FIXME: Where does mips_target_info come from? */ target_processor = mips_target_info.processor; mips_isa = mips_target_info.isa; + mips_ase = mips_target_info.ase; #else chosen_arch = choose_arch_by_number (info->mach); if (chosen_arch != NULL) { mips_processor = chosen_arch->processor; mips_isa = chosen_arch->isa; + mips_ase = chosen_arch->ase; mips_cp0_names = chosen_arch->cp0_names; mips_cp0sel_names = chosen_arch->cp0sel_names; mips_cp0sel_names_len = chosen_arch->cp0sel_names_len; + mips_cp1_names = chosen_arch->cp1_names; mips_hwr_names = chosen_arch->hwr_names; } + + /* Update settings according to the ELF file header flags. */ + if (info->flavour == bfd_target_elf_flavour && info->section != NULL) + { + struct bfd *abfd = info->section->owner; + Elf_Internal_Ehdr *header = elf_elfheader (abfd); + Elf_Internal_ABIFlags_v0 *abiflags = NULL; + + /* We won't ever get here if !HAVE_BFD_MIPS_ELF_GET_ABIFLAGS, + because we won't then have a MIPS/ELF BFD, however we need + to guard against a link error in a `--enable-targets=...' + configuration with a 32-bit host where the MIPS target is + a secondary, or with MIPS/ECOFF configurations. */ +#ifdef HAVE_BFD_MIPS_ELF_GET_ABIFLAGS + abiflags = bfd_mips_elf_get_abiflags (abfd); +#endif + /* If an ELF "newabi" binary, use the n32/(n)64 GPR names. */ + if (is_newabi (header)) + mips_gpr_names = mips_gpr_names_newabi; + /* If a microMIPS binary, then don't use MIPS16 bindings. */ + micromips_ase = is_micromips (header); + /* OR in any extra ASE flags set in ELF file structures. */ + if (abiflags) + mips_ase |= mips_convert_abiflags_ases (abiflags->ases); + else if (header->e_flags & EF_MIPS_ARCH_ASE_MDMX) + mips_ase |= ASE_MDMX; + } #endif + mips_ase |= mips_calculate_combination_ases (mips_isa, mips_ase); +} + +/* Parse an ASE disassembler option and set the corresponding global + ASE flag(s). Return TRUE if successful, FALSE otherwise. */ + +static bool +parse_mips_ase_option (const char *option) +{ + if (startswith (option, "msa")) + { + mips_ase |= ASE_MSA; + if ((mips_isa & INSN_ISA_MASK) == ISA_MIPS64R2 + || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R3 + || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R5 + || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R6) + mips_ase |= ASE_MSA64; + return true; + } + + if (startswith (option, "virt")) + { + mips_ase |= ASE_VIRT; + if (mips_isa & ISA_MIPS64R2 + || mips_isa & ISA_MIPS64R3 + || mips_isa & ISA_MIPS64R5 + || mips_isa & ISA_MIPS64R6) + mips_ase |= ASE_VIRT64; + return true; + } + + if (startswith (option, "xpa")) + { + mips_ase |= ASE_XPA; + return true; + } + + if (startswith (option, "ginv")) + { + mips_ase |= ASE_GINV; + return true; + } + + if (startswith (option, "loongson-mmi")) + { + mips_ase |= ASE_LOONGSON_MMI; + return true; + } + + if (startswith (option, "loongson-cam")) + { + mips_ase |= ASE_LOONGSON_CAM; + return true; + } + + /* Put here for match ext2 frist */ + if (startswith (option, "loongson-ext2")) + { + mips_ase |= ASE_LOONGSON_EXT2; + return true; + } + + if (startswith (option, "loongson-ext")) + { + mips_ase |= ASE_LOONGSON_EXT; + return true; + } + + return false; } static void @@ -819,12 +1021,18 @@ parse_mips_dis_option (const char *option, unsigned int len) const struct mips_arch_choice *chosen_arch; /* Try to match options that are simple flags */ - if (CONST_STRNEQ (option, "no-aliases")) + if (startswith (option, "no-aliases")) { no_aliases = 1; return; } - + + if (parse_mips_ase_option (option)) + { + mips_ase |= mips_calculate_combination_ases (mips_isa, mips_ase); + return; + } + /* Look for the = that delimits the end of the option name. */ for (i = 0; i < len; i++) if (option[i] == '=') @@ -872,6 +1080,15 @@ parse_mips_dis_option (const char *option, unsigned int len) return; } + if (strncmp ("cp1-names", option, optionlen) == 0 + && strlen ("cp1-names") == optionlen) + { + chosen_arch = choose_arch_by_name (val, vallen); + if (chosen_arch != NULL) + mips_cp1_names = chosen_arch->cp1_names; + return; + } + if (strncmp ("hwr-names", option, optionlen) == 0 && strlen ("hwr-names") == optionlen) { @@ -900,6 +1117,7 @@ parse_mips_dis_option (const char *option, unsigned int len) mips_cp0_names = chosen_arch->cp0_names; mips_cp0sel_names = chosen_arch->cp0sel_names; mips_cp0sel_names_len = chosen_arch->cp0sel_names_len; + mips_cp1_names = chosen_arch->cp1_names; mips_hwr_names = chosen_arch->hwr_names; } return; @@ -951,513 +1169,838 @@ lookup_mips_cp0sel_name (const struct mips_cp0sel_name *names, return &names[i]; return NULL; } - -/* Print insn arguments for 32/64-bit code. */ -static void -print_insn_args (const char *d, - int l, - bfd_vma pc, - struct disassemble_info *info, - const struct mips_opcode *opp) -{ - const fprintf_ftype infprintf = info->fprintf_func; - unsigned int lsb, msb, msbd; - void *is = info->stream; - int op; +/* Print register REGNO, of type TYPE, for instruction OPCODE. */ - lsb = 0; +static void +print_reg (struct disassemble_info *info, const struct mips_opcode *opcode, + enum mips_reg_operand_type type, int regno) +{ + const fprintf_styled_ftype infprintf = info->fprintf_styled_func; -#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 (type) { - switch (*d) - { - case ',': - case '(': - case ')': - case '[': - case ']': - infprintf (is, "%c", *d); - break; + case OP_REG_GP: + infprintf (info->stream, dis_style_register, "%s", + mips_gpr_names[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_FP: + infprintf (info->stream, dis_style_register, "%s", + mips_fpr_names[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 'D': - { - const struct mips_cp0sel_name *n; - unsigned int cp0reg, sel; - - cp0reg = GET_OP (l, RD); - 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, cp0reg, sel); - if (n != NULL) - infprintf (is, "%s", n->name); - else - infprintf (is, "$%d,%d", cp0reg, sel); - break; - } + case OP_REG_CCC: + if (opcode->pinfo & (FP_D | FP_S)) + infprintf (info->stream, dis_style_register, "$fcc%d", regno); + else + infprintf (info->stream, dis_style_register, "$cc%d", regno); + 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 't': /* Coprocessor 0 reg name */ - infprintf (is, "%s", mips_cp0_names[GET_OP (l, RT)]); - break; - - case 'T': /* Coprocessor 0 reg name */ - { - const struct mips_cp0sel_name *n; - unsigned int cp0reg, sel; - - cp0reg = GET_OP (l, RT); - sel = GET_OP (l, SEL); - - /* 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, cp0reg, sel); - if (n != NULL) - infprintf (is, "%s", n->name); - else - infprintf (is, "$%d,%d", cp0reg, sel); - break; - } + case OP_REG_VEC: + if (opcode->membership & INSN_5400) + infprintf (info->stream, dis_style_register, "$f%d", regno); + else + infprintf (info->stream, dis_style_register, "$v%d", regno); + break; - case 'x': /* bbit bit index */ - infprintf (is, "0x%x", GET_OP (l, BBITIND)); - break; + case OP_REG_ACC: + infprintf (info->stream, dis_style_register, "$ac%d", regno); + break; - case 'p': /* cins, cins32, exts and exts32 position */ - infprintf (is, "0x%x", GET_OP (l, CINSPOS)); - break; + case OP_REG_COPRO: + if (opcode->name[strlen (opcode->name) - 1] == '0') + infprintf (info->stream, dis_style_register, "%s", mips_cp0_names[regno]); + else + infprintf (info->stream, dis_style_register, "$%d", regno); + break; - case 's': /* cins and exts length-minus-one */ - infprintf (is, "0x%x", GET_OP (l, CINSLM1)); - break; + case OP_REG_CONTROL: + if (opcode->name[strlen (opcode->name) - 1] == '1') + infprintf (info->stream, dis_style_register, "%s", mips_cp1_names[regno]); + else + infprintf (info->stream, dis_style_register, "$%d", regno); + break; - case 'S': /* cins32 and exts32 length-minus-one field */ - infprintf (is, "0x%x", GET_OP (l, CINSLM1)); - break; + case OP_REG_HW: + infprintf (info->stream, dis_style_register, "%s", mips_hwr_names[regno]); + break; - case 'Q': /* seqi/snei immediate field */ - infprintf (is, "%d", GET_OP_S (l, SEQI)); - break; + case OP_REG_VF: + infprintf (info->stream, dis_style_register, "$vf%d", regno); + break; - case 'a': /* 8-bit signed offset in bit 6 */ - infprintf (is, "%d", GET_OP_S (l, OFFSET_A)); - break; + case OP_REG_VI: + infprintf (info->stream, dis_style_register, "$vi%d", regno); + break; - case 'b': /* 8-bit signed offset in bit 3 */ - infprintf (is, "%d", GET_OP_S (l, OFFSET_B)); - break; + case OP_REG_R5900_I: + infprintf (info->stream, dis_style_register, "$I"); + 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 OP_REG_R5900_Q: + infprintf (info->stream, dis_style_register, "$Q"); + break; - case 'z': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RZ)]); - break; + case OP_REG_R5900_R: + infprintf (info->stream, dis_style_register, "$R"); + break; - case 'Z': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FZ)]); - break; + case OP_REG_R5900_ACC: + infprintf (info->stream, dis_style_register, "$ACC"); + break; - default: - /* xgettext:c-format */ - infprintf (is, - _("# internal error, " - "undefined extension sequence (+%c)"), - *d); - return; - } - break; + case OP_REG_MSA: + infprintf (info->stream, dis_style_register, "$w%d", regno); + break; - case '2': - infprintf (is, "0x%x", GET_OP (l, BP)); - break; + case OP_REG_MSA_CTRL: + infprintf (info->stream, dis_style_register, "%s", + msa_control_names[regno]); + break; - case '3': - infprintf (is, "0x%x", GET_OP (l, SA3)); - 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; + unsigned int dest_regno; + unsigned int seen_dest; +}; - 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 OP_VU0_SUFFIX or OP_VU0_MATCH_SUFFIX operand OPERAND, + whose value is given by UVAL. */ - case '7': - infprintf (is, "$ac%d", GET_OP (l, DSPACC)); - break; +static void +print_vu0_channel (struct disassemble_info *info, + const struct mips_operand *operand, unsigned int uval, + enum disassembler_style style) +{ + const fprintf_styled_ftype infprintf = info->fprintf_styled_func; + + if (operand->size == 4) + infprintf (info->stream, style, "%s%s%s%s", + uval & 8 ? "x" : "", + uval & 4 ? "y" : "", + uval & 2 ? "z" : "", + uval & 1 ? "w" : ""); + else if (operand->size == 2) + infprintf (info->stream, style, "%c", "xyzw"[uval]); + else + abort (); +} - case '8': - infprintf (is, "0x%x", GET_OP (l, WRDSP)); - break; +/* Record information about a register operand. */ - case '9': - infprintf (is, "$ac%d", GET_OP (l, DSPACC_S)); - break; +static void +mips_seen_register (struct mips_print_arg_state *state, + unsigned int regno, + enum mips_reg_operand_type reg_type) +{ + state->last_reg_type = reg_type; + state->last_regno = regno; - case '0': /* dsp 6-bit signed immediate in bit 20 */ - infprintf (is, "%d", GET_OP_S (l, DSPSFT)); - break; + if (!state->seen_dest) + { + state->seen_dest = 1; + state->dest_regno = regno; + } +} - case ':': /* dsp 7-bit signed immediate in bit 19 */ - infprintf (is, "%d", GET_OP_S (l, DSPSFT_7)); - break; +/* Print SAVE/RESTORE instruction operands according to the argument + register mask AMASK, the number of static registers saved NSREG, + the $ra, $s0 and $s1 register specifiers RA, S0 and S1 respectively, + and the frame size FRAME_SIZE. */ - case '~': - infprintf (is, "%d", GET_OP_S (l, OFFSET12)); - break; +static void +mips_print_save_restore (struct disassemble_info *info, unsigned int amask, + unsigned int nsreg, unsigned int ra, + unsigned int s0, unsigned int s1, + unsigned int frame_size) +{ + const fprintf_styled_ftype infprintf = info->fprintf_styled_func; + unsigned int nargs, nstatics, smask, i, j; + void *is = info->stream; + const char *sep; - case '\\': - infprintf (is, "0x%x", GET_OP (l, 3BITPOS)); - break; + if (amask == MIPS_SVRS_ALL_ARGS) + { + nargs = 4; + nstatics = 0; + } + else if (amask == MIPS_SVRS_ALL_STATICS) + { + nargs = 0; + nstatics = 4; + } + else + { + nargs = amask >> 2; + nstatics = amask & 3; + } - case '\'': - infprintf (is, "0x%x", GET_OP (l, RDDSP)); - break; + sep = ""; + if (nargs > 0) + { + infprintf (is, dis_style_register, "%s", mips_gpr_names[4]); + if (nargs > 1) + infprintf (is, dis_style_register, "-%s", mips_gpr_names[4 + nargs - 1]); + sep = ","; + } - case '@': /* dsp 10-bit signed immediate in bit 16 */ - infprintf (is, "%d", GET_OP_S (l, IMM10)); - break; + infprintf (is, dis_style_text, "%s", sep); + infprintf (is, dis_style_immediate, "%d", frame_size); - case '!': - infprintf (is, "%d", GET_OP (l, MT_U)); - break; + if (ra) /* $ra */ + { + infprintf (is, dis_style_text, ","); + infprintf (is, dis_style_register, "%s", mips_gpr_names[31]); + } - case '$': - infprintf (is, "%d", GET_OP (l, MT_H)); - break; + smask = 0; + if (s0) /* $s0 */ + smask |= 1 << 0; + if (s1) /* $s1 */ + smask |= 1 << 1; + if (nsreg > 0) /* $s2-$s8 */ + smask |= ((1 << nsreg) - 1) << 2; - case '*': - infprintf (is, "$ac%d", GET_OP (l, MTACC_T)); - break; + for (i = 0; i < 9; i++) + if (smask & (1 << i)) + { + infprintf (is, dis_style_text, ","); + infprintf (is, dis_style_register, "%s", + mips_gpr_names[i == 8 ? 30 : (16 + i)]); + /* Skip over string of set bits. */ + for (j = i; smask & (2 << j); j++) + continue; + if (j > i) + { + infprintf (is, dis_style_text, "-"); + infprintf (is, dis_style_register, "%s", + mips_gpr_names[j == 8 ? 30 : (16 + j)]); + } + i = j + 1; + } + /* Statics $ax - $a3. */ + if (nstatics == 1) + { + infprintf (is, dis_style_text, ","); + infprintf (is, dis_style_register, "%s", mips_gpr_names[7]); + } + else if (nstatics > 0) + { + infprintf (is, dis_style_text, ","); + infprintf (is, dis_style_register, "%s", + mips_gpr_names[7 - nstatics + 1]); + infprintf (is, dis_style_text, "-"); + infprintf (is, dis_style_register, "%s", mips_gpr_names[7]); + } +} - case '&': - infprintf (is, "$ac%d", GET_OP (l, MTACC_D)); - break; - case 'g': - /* Coprocessor register for CTTC1, MTTC2, MTHC2, CTTC2. */ - infprintf (is, "$%d", GET_OP (l, RD)); - 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 's': - case 'b': - case 'r': - case 'v': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RS)]); - 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_styled_ftype infprintf = info->fprintf_styled_func; + void *is = info->stream; - case 't': - case 'w': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RT)]); - break; + switch (operand->type) + { + case OP_INT: + { + const struct mips_int_operand *int_op; - case 'i': - case 'u': - infprintf (is, "0x%x", GET_OP (l, IMMEDIATE)); - 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, dis_style_immediate, "0x%x", uval); + else + infprintf (is, dis_style_immediate, "%d", uval); + } + break; - case 'j': /* Same as i, but sign-extended. */ - case 'o': - infprintf (is, "%d", GET_OP_S (l, DELTA)); - break; + case OP_MAPPED_INT: + { + const struct mips_mapped_int_operand *mint_op; - case 'h': - infprintf (is, "0x%x", GET_OP (l, PREFX)); - 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, dis_style_immediate, "0x%x", uval); + else + infprintf (is, dis_style_immediate, "%d", uval); + } + break; - case 'k': - infprintf (is, "0x%x", GET_OP (l, CACHE)); - break; + case OP_MSB: + { + const struct mips_msb_operand *msb_op; - case 'a': - 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 - && strcmp (opp->name, "jalx") == 0) - info->target |= 1; - (*info->print_address_func) (info->target, info); - break; + msb_op = (const struct mips_msb_operand *) operand; + uval += msb_op->bias; + if (msb_op->add_lsb) + uval -= state->last_int; + infprintf (is, dis_style_immediate, "0x%x", uval); + } + break; - case 'p': - /* Sign extend the displacement. */ - info->target = (GET_OP_S (l, DELTA) << 2) + pc + INSNLEN; - (*info->print_address_func) (info->target, info); - break; + case OP_REG: + case OP_OPTIONAL_REG: + { + const struct mips_reg_operand *reg_op; - case 'd': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, RD)]); - break; + reg_op = (const struct mips_reg_operand *) operand; + uval = mips_decode_reg_operand (reg_op, uval); + print_reg (info, opcode, reg_op->reg_type, uval); + + mips_seen_register (state, uval, reg_op->reg_type); + } + break; + + case OP_REG_PAIR: + { + const struct mips_reg_pair_operand *pair_op; + + pair_op = (const struct mips_reg_pair_operand *) operand; + print_reg (info, opcode, pair_op->reg_type, + pair_op->reg1_map[uval]); + infprintf (is, dis_style_text, ","); + print_reg (info, opcode, pair_op->reg_type, + pair_op->reg2_map[uval]); + } + break; + + case OP_PCREL: + { + const struct mips_pcrel_operand *pcrel_op; + + pcrel_op = (const struct mips_pcrel_operand *) operand; + info->target = mips_decode_pcrel_operand (pcrel_op, base_pc, uval); + + /* For jumps and branches clear the ISA bit except for + the GDB disassembler. */ + if (pcrel_op->include_isa_bit + && info->flavour != bfd_target_unknown_flavour) + info->target &= -2; + + (*info->print_address_func) (info->target, info); + } + break; + + case OP_PERF_REG: + infprintf (is, dis_style_register, "%d", uval); + break; + + case OP_ADDIUSP_INT: + { + int sval; + + sval = mips_signed_operand (operand, uval) * 4; + if (sval >= -8 && sval < 8) + sval ^= 0x400; + infprintf (is, dis_style_immediate, "%d", sval); + break; + } + + case OP_CLO_CLZ_DEST: + { + unsigned int reg1, reg2; + + reg1 = uval & 31; + reg2 = uval >> 5; + /* If one is zero use the other. */ + if (reg1 == reg2 || reg2 == 0) + infprintf (is, dis_style_register, "%s", mips_gpr_names[reg1]); + else if (reg1 == 0) + infprintf (is, dis_style_register, "%s", mips_gpr_names[reg2]); + else + { + /* Bogus, result depends on processor. */ + infprintf (is, dis_style_register, "%s", mips_gpr_names[reg1]); + infprintf (is, dis_style_text, " or "); + infprintf (is, dis_style_register, "%s", mips_gpr_names[reg2]); + } + } + break; + + case OP_SAME_RS_RT: + case OP_CHECK_PREV: + case OP_NON_ZERO_REG: + { + print_reg (info, opcode, OP_REG_GP, uval & 31); + mips_seen_register (state, uval, OP_REG_GP); + } + break; + + case OP_LWM_SWM_LIST: + if (operand->size == 2) + { + if (uval == 0) + { + infprintf (is, dis_style_register, "%s", + mips_gpr_names[16]); + infprintf (is, dis_style_text, ","); + infprintf (is, dis_style_register, "%s", + mips_gpr_names[31]); + } + else + { + infprintf (is, dis_style_register, "%s", + mips_gpr_names[16]); + infprintf (is, dis_style_text, "-"); + infprintf (is, dis_style_register, "%s", + mips_gpr_names[16 + uval]); + infprintf (is, dis_style_text, ","); + infprintf (is, dis_style_register, "%s", + mips_gpr_names[31]); + } + } + else + { + int s_reg_encode; + + s_reg_encode = uval & 0xf; + if (s_reg_encode != 0) + { + if (s_reg_encode == 1) + infprintf (is, dis_style_register, "%s", mips_gpr_names[16]); + else if (s_reg_encode < 9) + { + infprintf (is, dis_style_register, "%s", + mips_gpr_names[16]); + infprintf (is, dis_style_text, "-"); + infprintf (is, dis_style_register, "%s", + mips_gpr_names[15 + s_reg_encode]); + } + else if (s_reg_encode == 9) + { + infprintf (is, dis_style_register, "%s", + mips_gpr_names[16]); + infprintf (is, dis_style_text, "-"); + infprintf (is, dis_style_register, "%s", + mips_gpr_names[23]); + infprintf (is, dis_style_text, ","); + infprintf (is, dis_style_register, "%s", + mips_gpr_names[30]); + } + else + infprintf (is, dis_style_text, "UNKNOWN"); + } + + if (uval & 0x10) /* For ra. */ + { + if (s_reg_encode == 0) + infprintf (is, dis_style_register, "%s", mips_gpr_names[31]); + else + { + infprintf (is, dis_style_text, ","); + infprintf (is, dis_style_register, "%s", + mips_gpr_names[31]); + } + } + } + break; + + case OP_ENTRY_EXIT_LIST: + { + const char *sep; + unsigned int amask, smask; - case 'U': + sep = ""; + amask = (uval >> 3) & 7; + if (amask > 0 && amask < 5) { - /* First check for both rd and rt being equal. */ - unsigned int reg = GET_OP (l, RD); - if (reg == GET_OP (l, RT)) - infprintf (is, "%s", mips_gpr_names[reg]); - else + infprintf (is, dis_style_register, "%s", mips_gpr_names[4]); + if (amask > 1) { - /* 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)]); + infprintf (is, dis_style_text, "-"); + infprintf (is, dis_style_register, "%s", + mips_gpr_names[amask + 3]); } + sep = ","; } - break; - case 'z': - infprintf (is, "%s", mips_gpr_names[0]); - break; + smask = (uval >> 1) & 3; + if (smask == 3) + { + infprintf (is, dis_style_text, "%s??", sep); + sep = ","; + } + else if (smask > 0) + { + infprintf (is, dis_style_text, "%s", sep); + infprintf (is, dis_style_register, "%s", mips_gpr_names[16]); + if (smask > 1) + { + infprintf (is, dis_style_text, "-"); + infprintf (is, dis_style_register, "%s", + mips_gpr_names[smask + 15]); + } + sep = ","; + } - case '<': - case '1': - infprintf (is, "0x%x", GET_OP (l, SHAMT)); - break; + if (uval & 1) + { + infprintf (is, dis_style_text, "%s", sep); + infprintf (is, dis_style_register, "%s", mips_gpr_names[31]); + sep = ","; + } - case 'c': - infprintf (is, "0x%x", GET_OP (l, CODE)); - break; + if (amask == 5 || amask == 6) + { + infprintf (is, dis_style_text, "%s", sep); + infprintf (is, dis_style_register, "%s", mips_fpr_names[0]); + if (amask == 6) + { + infprintf (is, dis_style_text, "-"); + infprintf (is, dis_style_register, "%s", mips_fpr_names[1]); + } + } + } + break; - case 'q': - infprintf (is, "0x%x", GET_OP (l, CODE2)); - break; + case OP_SAVE_RESTORE_LIST: + /* Should be handled by the caller due to complex behavior. */ + abort (); - case 'C': - infprintf (is, "0x%x", GET_OP (l, COPZ)); - break; + case OP_MDMX_IMM_REG: + { + unsigned int vsel; - case 'B': - infprintf (is, "0x%x", GET_OP (l, CODE20)); - break; + vsel = uval >> 5; + uval &= 31; + if ((vsel & 0x10) == 0) + { + int fmt; - case 'J': - infprintf (is, "0x%x", GET_OP (l, CODE19)); - break; + 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, dis_style_text, "["); + infprintf (is, dis_style_immediate, "%d", vsel >> 1); + infprintf (is, dis_style_text, "]"); + } + else if ((vsel & 0x08) == 0) + print_reg (info, opcode, OP_REG_VEC, uval); + else + infprintf (is, dis_style_immediate, "0x%x", uval); + } + break; - case 'S': - case 'V': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FS)]); - break; + case OP_REPEAT_PREV_REG: + print_reg (info, opcode, state->last_reg_type, state->last_regno); + break; - case 'T': - case 'W': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FT)]); - break; + case OP_REPEAT_DEST_REG: + print_reg (info, opcode, state->last_reg_type, state->dest_regno); + break; - case 'D': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FD)]); - break; + case OP_PC: + infprintf (is, dis_style_register, "$pc"); + break; - case 'R': - infprintf (is, "%s", mips_fpr_names[GET_OP (l, FR)]); - break; + case OP_REG28: + print_reg (info, opcode, OP_REG_GP, 28); + break; - case 'E': - /* Coprocessor register for lwcN instructions, et al. + case OP_VU0_SUFFIX: + case OP_VU0_MATCH_SUFFIX: + print_vu0_channel (info, operand, uval, dis_style_register); + break; - 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 (l, RT)); - break; + case OP_IMM_INDEX: + infprintf (is, dis_style_text, "["); + infprintf (is, dis_style_immediate, "%d", uval); + infprintf (is, dis_style_text, "]"); + 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. */ - op = GET_OP (l, OP); - if (op == OP_OP_COP0) - infprintf (is, "%s", mips_cp0_names[GET_OP (l, RD)]); - else - infprintf (is, "$%d", GET_OP (l, RD)); - break; + case OP_REG_INDEX: + infprintf (is, dis_style_text, "["); + print_reg (info, opcode, OP_REG_GP, uval); + infprintf (is, dis_style_text, "]"); + break; + } +} - case 'K': - infprintf (is, "%s", mips_hwr_names[GET_OP (l, RD)]); - break; +/* Validate the arguments for INSN, which is described by OPCODE. + Use DECODE_OPERAND to get the encoding of each operand. */ - case 'N': - infprintf (is, - (opp->pinfo & (FP_D | FP_S)) != 0 ? "$fcc%d" : "$cc%d", - GET_OP (l, BCC)); - break; +static bool +validate_insn_args (const struct mips_opcode *opcode, + const struct mips_operand *(*decode_operand) (const char *), + unsigned int insn) +{ + struct mips_print_arg_state state; + const struct mips_operand *operand; + const char *s; + unsigned int uval; - case 'M': - infprintf (is, "$fcc%d", GET_OP (l, CCC)); + init_print_arg_state (&state); + for (s = opcode->args; *s; ++s) + { + switch (*s) + { + case ',': + case '(': + case ')': break; - case 'P': - infprintf (is, "%d", GET_OP (l, PERFREG)); + case '#': + ++s; break; - case 'e': - infprintf (is, "%d", GET_OP (l, VECBYTE)); - break; + default: + operand = decode_operand (s); - case '%': - infprintf (is, "%d", GET_OP (l, VECALIGN)); - break; + if (operand) + { + uval = mips_extract_operand (operand, insn); + switch (operand->type) + { + case OP_REG: + case OP_OPTIONAL_REG: + { + const struct mips_reg_operand *reg_op; - case 'H': - infprintf (is, "%d", GET_OP (l, SEL)); - break; + reg_op = (const struct mips_reg_operand *) operand; + uval = mips_decode_reg_operand (reg_op, uval); + mips_seen_register (&state, uval, reg_op->reg_type); + } + break; - case 'O': - infprintf (is, "%d", GET_OP (l, ALN)); - break; + case OP_SAME_RS_RT: + { + unsigned int reg1, reg2; - case 'Q': - { - unsigned int vsel = GET_OP (l, VSEL); + reg1 = uval & 31; + reg2 = uval >> 5; - if ((vsel & 0x10) == 0) - { - int fmt; + if (reg1 != reg2 || reg1 == 0) + return false; + } + break; - vsel &= 0x0f; - for (fmt = 0; fmt < 3; fmt++, vsel >>= 1) - if ((vsel & 1) == 0) - break; - infprintf (is, "$v%d[%d]", GET_OP (l, FT), vsel >> 1); - } - else if ((vsel & 0x08) == 0) - { - infprintf (is, "$v%d", GET_OP (l, FT)); - } - else - { - infprintf (is, "0x%x", GET_OP (l, FT)); - } - } - break; + case OP_CHECK_PREV: + { + const struct mips_check_prev_operand *prev_op; - case 'X': - infprintf (is, "$v%d", GET_OP (l, FD)); - break; + prev_op = (const struct mips_check_prev_operand *) operand; - case 'Y': - infprintf (is, "$v%d", GET_OP (l, FS)); - break; + if (!prev_op->zero_ok && uval == 0) + return false; - case 'Z': - infprintf (is, "$v%d", GET_OP (l, FT)); - break; + if (((prev_op->less_than_ok && uval < state.last_regno) + || (prev_op->greater_than_ok && uval > state.last_regno) + || (prev_op->equal_ok && uval == state.last_regno))) + break; - default: - /* xgettext:c-format */ - infprintf (is, _("# internal error, undefined modifier (%c)"), *d); - return; + return false; + } + + case OP_NON_ZERO_REG: + { + if (uval == 0) + return false; + } + break; + + case OP_INT: + case OP_MAPPED_INT: + case OP_MSB: + case OP_REG_PAIR: + case OP_PCREL: + case OP_PERF_REG: + case OP_ADDIUSP_INT: + case OP_CLO_CLZ_DEST: + case OP_LWM_SWM_LIST: + case OP_ENTRY_EXIT_LIST: + case OP_MDMX_IMM_REG: + case OP_REPEAT_PREV_REG: + case OP_REPEAT_DEST_REG: + case OP_PC: + case OP_REG28: + case OP_VU0_SUFFIX: + case OP_VU0_MATCH_SUFFIX: + case OP_IMM_INDEX: + case OP_REG_INDEX: + case OP_SAVE_RESTORE_LIST: + break; + } + } + if (*s == 'm' || *s == '+' || *s == '-') + ++s; } } + return true; } - -/* Print the mips instruction at address MEMADDR in debugged memory, - on using INFO. Returns length of the instruction, in bytes, which is - always INSNLEN. BIGENDIAN must be 1 if this is big-endian code, 0 if - this is little-endian code. */ -static int -print_insn_mips (bfd_vma memaddr, - int word, - struct disassemble_info *info) +/* Print the arguments for INSN, which is described by OPCODE. + Use DECODE_OPERAND to get the encoding of each operand. Use BASE_PC + as the base of OP_PCREL operands, adjusting by LENGTH if the OP_PCREL + operand is for a branch or jump. */ + +static void +print_insn_args (struct disassemble_info *info, + const struct mips_opcode *opcode, + const struct mips_operand *(*decode_operand) (const char *), + unsigned int insn, bfd_vma insn_pc, unsigned int length) { - static const struct mips_opcode *mips_hash[OP_MASK_OP + 1]; - const fprintf_ftype infprintf = info->fprintf_func; - const struct mips_opcode *op; - static bfd_boolean init = 0; + const fprintf_styled_ftype infprintf = info->fprintf_styled_func; void *is = info->stream; + struct mips_print_arg_state state; + const struct mips_operand *operand; + const char *s; - /* Build a hash table to shorten the search time. */ - if (! init) + init_print_arg_state (&state); + for (s = opcode->args; *s; ++s) + { + switch (*s) + { + case ',': + case '(': + case ')': + infprintf (is, dis_style_text, "%c", *s); + break; + + case '#': + ++s; + infprintf (is, dis_style_text, "%c%c", *s, *s); + break; + + default: + operand = decode_operand (s); + if (!operand) + { + /* xgettext:c-format */ + infprintf (is, dis_style_text, + _("# internal error, undefined operand in `%s %s'"), + opcode->name, opcode->args); + return; + } + + if (operand->type == OP_SAVE_RESTORE_LIST) + { + /* Handle this case here because of the complex behavior. */ + unsigned int amask = (insn >> 15) & 0xf; + unsigned int nsreg = (insn >> 23) & 0x7; + unsigned int ra = insn & 0x1000; /* $ra */ + unsigned int s0 = insn & 0x800; /* $s0 */ + unsigned int s1 = insn & 0x400; /* $s1 */ + unsigned int frame_size = (((insn >> 15) & 0xf0) + | ((insn >> 6) & 0x0f)) * 8; + mips_print_save_restore (info, amask, nsreg, ra, s0, s1, + frame_size); + } + else if (operand->type == OP_REG + && s[1] == ',' + && s[2] == 'H' + && opcode->name[strlen (opcode->name) - 1] == '0') + { + /* Coprocessor register 0 with sel field. */ + const struct mips_cp0sel_name *n; + unsigned int reg, sel; + + reg = mips_extract_operand (operand, insn); + s += 2; + operand = decode_operand (s); + sel = mips_extract_operand (operand, insn); + + /* CP0 register including 'sel' code for mftc0, to be + printed textually if known. If not known, print both + CP0 register name and sel numerically since CP0 register + with sel 0 may have a name unrelated to register being + printed. */ + n = lookup_mips_cp0sel_name (mips_cp0sel_names, + mips_cp0sel_names_len, + reg, sel); + if (n != NULL) + infprintf (is, dis_style_register, "%s", n->name); + else + { + infprintf (is, dis_style_register, "$%d", reg); + infprintf (is, dis_style_text, ","); + infprintf (is, dis_style_immediate, "%d", sel); + } + } + else + { + bfd_vma base_pc = insn_pc; + + /* Adjust the PC relative base so that branch/jump insns use + the following PC as the base but genuinely PC relative + operands use the current PC. */ + if (operand->type == OP_PCREL) + { + const struct mips_pcrel_operand *pcrel_op; + + pcrel_op = (const struct mips_pcrel_operand *) operand; + /* The include_isa_bit flag is sufficient to distinguish + branch/jump from other PC relative operands. */ + if (pcrel_op->include_isa_bit) + base_pc += length; + } + + print_insn_arg (info, &state, opcode, operand, base_pc, + mips_extract_operand (operand, insn)); + } + if (*s == 'm' || *s == '+' || *s == '-') + ++s; + break; + } + } +} + +/* Print the mips instruction at address MEMADDR in debugged memory, + on using INFO. Returns length of the instruction, in bytes, which is + always INSNLEN. BIGENDIAN must be 1 if this is big-endian code, 0 if + this is little-endian code. */ + +static int +print_insn_mips (bfd_vma memaddr, + int word, + struct disassemble_info *info) +{ +#define GET_OP(insn, field) \ + (((insn) >> OP_SH_##field) & OP_MASK_##field) + static const struct mips_opcode *mips_hash[OP_MASK_OP + 1]; + const fprintf_styled_ftype infprintf = info->fprintf_styled_func; + const struct mips_opcode *op; + static bool init = 0; + void *is = info->stream; + + /* Build a hash table to shorten the search time. */ + if (! init) { unsigned int i; @@ -1493,22 +2036,21 @@ print_insn_mips (bfd_vma memaddr, { for (; op < &mips_opcodes[NUMOPCODES]; op++) { - if (op->pinfo != INSN_MACRO + if (op->pinfo != INSN_MACRO && !(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_processor) - && strcmp (op->name, "jalx")) + /* We always disassemble the jalx instruction, except for MIPS r6. */ + if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor) + && (strcmp (op->name, "jalx") + || (mips_isa & INSN_ISA_MASK) == ISA_MIPS32R6 + || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R6)) continue; /* Figure out instruction type and branch delay information. */ if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0) { - if ((op->pinfo & (INSN_WRITE_GPR_31 - | INSN_WRITE_GPR_D)) != 0) + if ((op->pinfo & (INSN_WRITE_GPR_31 | INSN_WRITE_1)) != 0) info->insn_type = dis_jsr; else info->insn_type = dis_branch; @@ -1524,1362 +2066,513 @@ print_insn_mips (bfd_vma memaddr, info->branch_delay_insns = 1; } else if ((op->pinfo & (INSN_STORE_MEMORY - | INSN_LOAD_MEMORY_DELAY)) != 0) + | INSN_LOAD_MEMORY)) != 0) info->insn_type = dis_dref; - infprintf (is, "%s", op->name); + if (!validate_insn_args (op, decode_mips_operand, word)) + continue; - d = op->args; - if (d != NULL && *d != '\0') + infprintf (is, dis_style_mnemonic, "%s", op->name); + if (op->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX) { - infprintf (is, "\t"); - print_insn_args (d, word, memaddr, info, op); + unsigned int uval; + + infprintf (is, dis_style_mnemonic, "."); + uval = mips_extract_operand (&mips_vu0_channel_mask, word); + print_vu0_channel (info, &mips_vu0_channel_mask, uval, + dis_style_mnemonic); + } + + if (op->args[0]) + { + infprintf (is, dis_style_text, "\t"); + print_insn_args (info, op, decode_mips_operand, word, + memaddr, 4); } return INSNLEN; } } } -#undef GET_OP_S #undef GET_OP /* Handle undefined instructions. */ info->insn_type = dis_noninsn; - infprintf (is, "0x%x", word); + infprintf (is, dis_style_assembler_directive, ".word"); + infprintf (is, dis_style_text, "\t"); + infprintf (is, dis_style_immediate, "0x%x", word); return INSNLEN; } /* Disassemble an operand for a mips16 instruction. */ static void -print_mips16_insn_arg (char type, - const struct mips_opcode *op, - int l, - bfd_boolean use_extend, - int extend, - bfd_vma memaddr, - struct disassemble_info *info) -{ - const fprintf_ftype infprintf = info->fprintf_func; +print_mips16_insn_arg (struct disassemble_info *info, + struct mips_print_arg_state *state, + const struct mips_opcode *opcode, + char type, bfd_vma memaddr, + unsigned insn, bool use_extend, + unsigned extend, bool is_offset) +{ + const fprintf_styled_ftype infprintf = info->fprintf_styled_func; void *is = info->stream; + const struct mips_operand *operand, *ext_operand; + unsigned short ext_size; + unsigned int uval; + bfd_vma baseaddr; + + if (!use_extend) + extend = 0; -#define GET_OP(insn, field) \ - (((insn) >> MIPS16OP_SH_##field) & MIPS16OP_MASK_##field) -#define GET_OP_S(insn, field) \ - ((GET_OP (insn, field) ^ ((MIPS16OP_MASK_##field >> 1) + 1)) \ - - ((MIPS16OP_MASK_##field >> 1) + 1)) switch (type) { case ',': case '(': case ')': - infprintf (is, "%c", type); - break; - - case 'y': - case 'w': - infprintf (is, "%s", mips16_reg_names (GET_OP (l, RY))); - break; - - case 'x': - case 'v': - infprintf (is, "%s", mips16_reg_names (GET_OP (l, RX))); - break; - - case 'z': - infprintf (is, "%s", mips16_reg_names (GET_OP (l, RZ))); - break; - - case 'Z': - infprintf (is, "%s", mips16_reg_names (GET_OP (l, MOVE32Z))); - break; - - case '0': - infprintf (is, "%s", mips_gpr_names[0]); - break; - - case 'S': - infprintf (is, "%s", mips_gpr_names[29]); - break; - - case 'P': - infprintf (is, "$pc"); - break; - - case 'R': - infprintf (is, "%s", mips_gpr_names[31]); - break; - - case 'X': - infprintf (is, "%s", mips_gpr_names[GET_OP (l, REGR32)]); - break; - - case 'Y': - infprintf (is, "%s", mips_gpr_names[MIPS16OP_EXTRACT_REG32R (l)]); - break; - - case '<': - case '>': - case '[': - case ']': - case '4': - case '5': - case 'H': - case 'W': - case 'D': - case 'j': - case '6': - case '8': - case 'V': - case 'C': - case 'U': - case 'k': - case 'K': - case 'p': - case 'q': - case 'A': - case 'B': - case 'E': - { - int immed, nbits, shift, signedp, extbits, pcrel, extu, branch; - - shift = 0; - signedp = 0; - extbits = 16; - pcrel = 0; - extu = 0; - branch = 0; - switch (type) - { - case '<': - nbits = 3; - immed = GET_OP (l, RZ); - extbits = 5; - extu = 1; - break; - case '>': - nbits = 3; - immed = GET_OP (l, RX); - extbits = 5; - extu = 1; - break; - case '[': - nbits = 3; - immed = GET_OP (l, RZ); - extbits = 6; - extu = 1; - break; - case ']': - nbits = 3; - immed = GET_OP (l, RX); - extbits = 6; - extu = 1; - break; - case '4': - nbits = 4; - immed = GET_OP (l, IMM4); - signedp = 1; - extbits = 15; - break; - case '5': - nbits = 5; - immed = GET_OP (l, IMM5); - info->insn_type = dis_dref; - info->data_size = 1; - break; - case 'H': - nbits = 5; - shift = 1; - immed = GET_OP (l, IMM5); - info->insn_type = dis_dref; - info->data_size = 2; - break; - case 'W': - nbits = 5; - shift = 2; - immed = GET_OP (l, IMM5); - if ((op->pinfo & MIPS16_INSN_READ_PC) == 0 - && (op->pinfo & MIPS16_INSN_READ_SP) == 0) - { - info->insn_type = dis_dref; - info->data_size = 4; - } - break; - case 'D': - nbits = 5; - shift = 3; - immed = GET_OP (l, IMM5); - info->insn_type = dis_dref; - info->data_size = 8; - break; - case 'j': - nbits = 5; - immed = GET_OP (l, IMM5); - signedp = 1; - break; - case '6': - nbits = 6; - immed = GET_OP (l, IMM6); - break; - case '8': - nbits = 8; - immed = GET_OP (l, IMM8); - break; - case 'V': - nbits = 8; - shift = 2; - immed = GET_OP (l, IMM8); - /* FIXME: This might be lw, or it might be addiu to $sp or - $pc. We assume it's load. */ - info->insn_type = dis_dref; - info->data_size = 4; - break; - case 'C': - nbits = 8; - shift = 3; - immed = GET_OP (l, IMM8); - info->insn_type = dis_dref; - info->data_size = 8; - break; - case 'U': - nbits = 8; - immed = GET_OP (l, IMM8); - extu = 1; - break; - case 'k': - nbits = 8; - immed = GET_OP (l, IMM8); - signedp = 1; - break; - case 'K': - nbits = 8; - shift = 3; - immed = GET_OP (l, IMM8); - signedp = 1; - break; - case 'p': - nbits = 8; - immed = GET_OP (l, IMM8); - signedp = 1; - pcrel = 1; - branch = 1; - break; - case 'q': - nbits = 11; - immed = GET_OP (l, IMM11); - signedp = 1; - pcrel = 1; - branch = 1; - break; - case 'A': - nbits = 8; - shift = 2; - immed = GET_OP (l, IMM8); - pcrel = 1; - /* FIXME: This can be lw or la. We assume it is lw. */ - info->insn_type = dis_dref; - info->data_size = 4; - break; - case 'B': - nbits = 5; - shift = 3; - immed = GET_OP (l, IMM5); - pcrel = 1; - info->insn_type = dis_dref; - info->data_size = 8; - break; - case 'E': - nbits = 5; - shift = 2; - immed = GET_OP (l, IMM5); - pcrel = 1; - break; - default: - abort (); - } - - if (! use_extend) - { - if (signedp && immed >= (1 << (nbits - 1))) - immed -= 1 << nbits; - immed <<= shift; - if ((type == '<' || type == '>' || type == '[' || type == ']') - && immed == 0) - immed = 8; - } - else - { - if (extbits == 16) - immed |= ((extend & 0x1f) << 11) | (extend & 0x7e0); - else if (extbits == 15) - immed |= ((extend & 0xf) << 11) | (extend & 0x7f0); - else - immed = ((extend >> 6) & 0x1f) | (extend & 0x20); - immed &= (1 << extbits) - 1; - if (! extu && immed >= (1 << (extbits - 1))) - immed -= 1 << extbits; - } - - if (! pcrel) - infprintf (is, "%d", immed); - else - { - bfd_vma baseaddr; - - if (branch) - { - immed *= 2; - baseaddr = memaddr + 2; - } - else if (use_extend) - baseaddr = memaddr - 2; - else - { - int status; - bfd_byte buffer[2]; - - baseaddr = memaddr; - - /* If this instruction is in the delay slot of a jr - instruction, the base address is the address of the - jr instruction. If it is in the delay slot of jalr - instruction, the base address is the address of the - jalr instruction. This test is unreliable: we have - no way of knowing whether the previous word is - instruction or data. */ - status = (*info->read_memory_func) (memaddr - 4, buffer, 2, - info); - if (status == 0 - && (((info->endian == BFD_ENDIAN_BIG - ? bfd_getb16 (buffer) - : bfd_getl16 (buffer)) - & 0xf800) == 0x1800)) - baseaddr = memaddr - 4; - else - { - status = (*info->read_memory_func) (memaddr - 2, buffer, - 2, info); - if (status == 0 - && (((info->endian == BFD_ENDIAN_BIG - ? bfd_getb16 (buffer) - : bfd_getl16 (buffer)) - & 0xf81f) == 0xe800)) - baseaddr = memaddr - 2; - } - } - info->target = (baseaddr & ~((1 << shift) - 1)) + immed; - if (pcrel && branch - && info->flavour == bfd_target_unknown_flavour) - /* For gdb disassembler, maintain odd address. */ - info->target |= 1; - (*info->print_address_func) (info->target, info); - } - } - break; - - case 'a': - { - int jalx = l & 0x400; - - if (! use_extend) - extend = 0; - l = ((l & 0x1f) << 23) | ((l & 0x3e0) << 13) | (extend << 2); - if (!jalx && info->flavour == bfd_target_unknown_flavour) - /* For gdb disassembler, maintain odd address. */ - l |= 1; - } - info->target = ((memaddr + 4) & ~(bfd_vma) 0x0fffffff) | l; - (*info->print_address_func) (info->target, info); - break; - - case 'l': - case 'L': - { - int need_comma, amask, smask; - - need_comma = 0; - - l = GET_OP (l, IMM6); - - amask = (l >> 3) & 7; - - if (amask > 0 && amask < 5) - { - infprintf (is, "%s", mips_gpr_names[4]); - if (amask > 1) - infprintf (is, "-%s", mips_gpr_names[amask + 3]); - need_comma = 1; - } - - smask = (l >> 1) & 3; - if (smask == 3) - { - infprintf (is, "%s??", need_comma ? "," : ""); - need_comma = 1; - } - else if (smask > 0) - { - infprintf (is, "%s%s", need_comma ? "," : "", mips_gpr_names[16]); - if (smask > 1) - infprintf (is, "-%s", mips_gpr_names[smask + 15]); - need_comma = 1; - } - - if (l & 1) - { - infprintf (is, "%s%s", need_comma ? "," : "", mips_gpr_names[31]); - need_comma = 1; - } - - if (amask == 5 || amask == 6) - { - infprintf (is, "%s$f0", need_comma ? "," : ""); - if (amask == 6) - infprintf (is, "-$f1"); - } - } - break; - - case 'm': - case 'M': - /* MIPS16e save/restore. */ - { - int need_comma = 0; - int amask, args, statics; - int nsreg, smask; - int framesz; - int i, j; - - l = l & 0x7f; - if (use_extend) - l |= extend << 16; - - amask = (l >> 16) & 0xf; - if (amask == MIPS16_ALL_ARGS) - { - args = 4; - statics = 0; - } - else if (amask == MIPS16_ALL_STATICS) - { - args = 0; - statics = 4; - } - else - { - args = amask >> 2; - statics = amask & 3; - } - - if (args > 0) { - infprintf (is, "%s", mips_gpr_names[4]); - if (args > 1) - infprintf (is, "-%s", mips_gpr_names[4 + args - 1]); - need_comma = 1; - } - - framesz = (((l >> 16) & 0xf0) | (l & 0x0f)) * 8; - if (framesz == 0 && !use_extend) - framesz = 128; - - infprintf (is, "%s%d", need_comma ? "," : "", framesz); - - if (l & 0x40) /* $ra */ - infprintf (is, ",%s", mips_gpr_names[31]); - - nsreg = (l >> 24) & 0x7; - smask = 0; - if (l & 0x20) /* $s0 */ - smask |= 1 << 0; - if (l & 0x10) /* $s1 */ - smask |= 1 << 1; - if (nsreg > 0) /* $s2-$s8 */ - smask |= ((1 << nsreg) - 1) << 2; - - /* Find first set static reg bit. */ - for (i = 0; i < 9; i++) - { - if (smask & (1 << i)) - { - infprintf (is, ",%s", mips_gpr_names[i == 8 ? 30 : (16 + i)]); - /* Skip over string of set bits. */ - for (j = i; smask & (2 << j); j++) - continue; - if (j > i) - infprintf (is, "-%s", mips_gpr_names[j == 8 ? 30 : (16 + j)]); - i = j + 1; - } - } - - /* Statics $ax - $a3. */ - if (statics == 1) - infprintf (is, ",%s", mips_gpr_names[7]); - else if (statics > 0) - infprintf (is, ",%s-%s", - mips_gpr_names[7 - statics + 1], - mips_gpr_names[7]); - } + infprintf (is, dis_style_text, "%c", type); break; default: - /* xgettext:c-format */ - infprintf (is, - _("# internal disassembler error, " - "unrecognised modifier (%c)"), - type); - abort (); - } -} - -/* Disassemble mips16 instructions. */ - -static int -print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info) -{ - const fprintf_ftype infprintf = info->fprintf_func; - int status; - bfd_byte buffer[2]; - int length; - int insn; - bfd_boolean use_extend; - int extend = 0; - const struct mips_opcode *op, *opend; - void *is = info->stream; - - info->bytes_per_chunk = 2; - info->display_endian = info->endian; - info->insn_info_valid = 1; - info->branch_delay_insns = 0; - info->data_size = 0; - info->insn_type = dis_nonbranch; - info->target = 0; - info->target2 = 0; - - status = (*info->read_memory_func) (memaddr, buffer, 2, info); - if (status != 0) - { - (*info->memory_error_func) (status, memaddr, info); - return -1; - } - - length = 2; - - if (info->endian == BFD_ENDIAN_BIG) - insn = bfd_getb16 (buffer); - else - insn = bfd_getl16 (buffer); - - /* Handle the extend opcode specially. */ - use_extend = FALSE; - if ((insn & 0xf800) == 0xf000) - { - use_extend = TRUE; - extend = insn & 0x7ff; - - memaddr += 2; - - status = (*info->read_memory_func) (memaddr, buffer, 2, info); - if (status != 0) + operand = decode_mips16_operand (type, false); + if (!operand) { - infprintf (is, "extend 0x%x", (unsigned int) extend); - (*info->memory_error_func) (status, memaddr, info); - return -1; + /* xgettext:c-format */ + infprintf (is, dis_style_text, _("# internal error, undefined operand in `%s %s'"), + opcode->name, opcode->args); + return; } - if (info->endian == BFD_ENDIAN_BIG) - insn = bfd_getb16 (buffer); - else - insn = bfd_getl16 (buffer); - - /* Check for an extend opcode followed by an extend opcode. */ - if ((insn & 0xf800) == 0xf000) + if (operand->type == OP_SAVE_RESTORE_LIST) { - infprintf (is, "extend 0x%x", (unsigned int) extend); - info->insn_type = dis_noninsn; - return length; + /* Handle this case here because of the complex interaction + with the EXTEND opcode. */ + unsigned int amask = extend & 0xf; + unsigned int nsreg = (extend >> 8) & 0x7; + unsigned int ra = insn & 0x40; /* $ra */ + unsigned int s0 = insn & 0x20; /* $s0 */ + unsigned int s1 = insn & 0x10; /* $s1 */ + unsigned int frame_size = ((extend & 0xf0) | (insn & 0x0f)) * 8; + if (frame_size == 0 && !use_extend) + frame_size = 128; + mips_print_save_restore (info, amask, nsreg, ra, s0, s1, frame_size); + break; } - length += 2; - } + if (is_offset && operand->type == OP_INT) + { + const struct mips_int_operand *int_op; - /* FIXME: Should probably use a hash table on the major opcode here. */ + int_op = (const struct mips_int_operand *) operand; + info->insn_type = dis_dref; + info->data_size = 1 << int_op->shift; + } - opend = mips16_opcodes + bfd_mips16_num_opcodes; - for (op = mips16_opcodes; op < opend; op++) - { - if (op->pinfo != INSN_MACRO - && !(no_aliases && (op->pinfo2 & INSN2_ALIAS)) - && (insn & op->mask) == op->match) + ext_size = 0; + if (use_extend) { - const char *s; - - if (strchr (op->args, 'a') != NULL) + ext_operand = decode_mips16_operand (type, true); + if (ext_operand != operand + || (operand->type == OP_INT && operand->lsb == 0 + && mips_opcode_32bit_p (opcode))) { - if (use_extend) - { - infprintf (is, "extend 0x%x", (unsigned int) extend); - info->insn_type = dis_noninsn; - return length - 2; - } - - use_extend = FALSE; - - memaddr += 2; - - status = (*info->read_memory_func) (memaddr, buffer, 2, - info); - if (status == 0) - { - use_extend = TRUE; - if (info->endian == BFD_ENDIAN_BIG) - extend = bfd_getb16 (buffer); - else - extend = bfd_getl16 (buffer); - length += 2; - } + ext_size = ext_operand->size; + operand = ext_operand; } + } + if (operand->size == 26) + uval = ((extend & 0x1f) << 21) | ((extend & 0x3e0) << 11) | insn; + else if (ext_size == 16 || ext_size == 9) + uval = ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f); + else if (ext_size == 15) + uval = ((extend & 0xf) << 11) | (extend & 0x7f0) | (insn & 0xf); + else if (ext_size == 6) + uval = ((extend >> 6) & 0x1f) | (extend & 0x20); + else + uval = mips_extract_operand (operand, (extend << 16) | insn); + if (ext_size == 9) + uval &= (1U << ext_size) - 1; - infprintf (is, "%s", op->name); - if (op->args[0] != '\0') - infprintf (is, "\t"); - - for (s = op->args; *s != '\0'; s++) - { - if (*s == ',' - && s[1] == 'w' - && GET_OP (insn, RX) == GET_OP (insn, RY)) - { - /* Skip the register and the comma. */ - ++s; - continue; - } - if (*s == ',' - && s[1] == 'v' - && GET_OP (insn, RZ) == GET_OP (insn, RX)) - { - /* Skip the register and the comma. */ - ++s; - continue; - } - print_mips16_insn_arg (*s, op, insn, use_extend, extend, memaddr, - info); - } + baseaddr = memaddr + 2; + if (operand->type == OP_PCREL) + { + const struct mips_pcrel_operand *pcrel_op; - /* Figure out branch instruction type and delay slot information. */ - if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0) - info->branch_delay_insns = 1; - if ((op->pinfo & (INSN_UNCOND_BRANCH_DELAY - | MIPS16_INSN_UNCOND_BRANCH)) != 0) + pcrel_op = (const struct mips_pcrel_operand *) operand; + if (!pcrel_op->include_isa_bit && use_extend) + baseaddr = memaddr - 2; + else if (!pcrel_op->include_isa_bit) { - if ((op->pinfo & INSN_WRITE_GPR_31) != 0) - info->insn_type = dis_jsr; + bfd_byte buffer[2]; + + /* If this instruction is in the delay slot of a JAL/JALX + instruction, the base address is the address of the + JAL/JALX instruction. If it is in the delay slot of + a JR/JALR instruction, the base address is the address + of the JR/JALR instruction. This test is unreliable: + we have no way of knowing whether the previous word is + instruction or data. */ + if (info->read_memory_func (memaddr - 4, buffer, 2, info) == 0 + && (((info->endian == BFD_ENDIAN_BIG + ? bfd_getb16 (buffer) + : bfd_getl16 (buffer)) + & 0xf800) == 0x1800)) + baseaddr = memaddr - 4; + else if (info->read_memory_func (memaddr - 2, buffer, 2, + info) == 0 + && (((info->endian == BFD_ENDIAN_BIG + ? bfd_getb16 (buffer) + : bfd_getl16 (buffer)) + & 0xf89f) == 0xe800) + && (((info->endian == BFD_ENDIAN_BIG + ? bfd_getb16 (buffer) + : bfd_getl16 (buffer)) + & 0x0060) != 0x0060)) + baseaddr = memaddr - 2; else - info->insn_type = dis_branch; + baseaddr = memaddr; } - else if ((op->pinfo & MIPS16_INSN_COND_BRANCH) != 0) - info->insn_type = dis_condbranch; - - return length; } - } -#undef GET_OP_S -#undef GET_OP - if (use_extend) - infprintf (is, "0x%x", extend | 0xf000); - infprintf (is, "0x%x", insn); - info->insn_type = dis_noninsn; - - return length; + print_insn_arg (info, state, opcode, operand, baseaddr + 1, uval); + break; + } } -/* Disassemble microMIPS instructions. */ -static int -print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) +/* Check if the given address is the last word of a MIPS16 PLT entry. + This word is data and depending on the value it may interfere with + disassembly of further PLT entries. We make use of the fact PLT + symbols are marked BSF_SYNTHETIC. */ +static bool +is_mips16_plt_tail (struct disassemble_info *info, bfd_vma addr) { - 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; - int status; - int delta; - int immed; - int insn; - - lsb = 0; - - info->bytes_per_chunk = 2; - info->display_endian = info->endian; - info->insn_info_valid = 1; - info->branch_delay_insns = 0; - info->data_size = 0; - info->insn_type = dis_nonbranch; - info->target = 0; - info->target2 = 0; - - status = (*info->read_memory_func) (memaddr, buffer, 2, info); - if (status != 0) - { - (*info->memory_error_func) (status, memaddr, info); - return -1; - } - - length = 2; - - if (info->endian == BFD_ENDIAN_BIG) - insn = bfd_getb16 (buffer); - else - insn = bfd_getl16 (buffer); - - if ((insn & 0xfc00) == 0x7c00) - { - /* This is a 48-bit microMIPS instruction. */ - higher = insn; - - status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info); - if (status != 0) - { - infprintf (is, "micromips 0x%x", higher); - (*info->memory_error_func) (status, memaddr + 2, info); - return -1; - } - if (info->endian == BFD_ENDIAN_BIG) - insn = bfd_getb16 (buffer); - else - insn = bfd_getl16 (buffer); - higher = (higher << 16) | insn; - - status = (*info->read_memory_func) (memaddr + 4, buffer, 2, info); - if (status != 0) - { - infprintf (is, "micromips 0x%x", higher); - (*info->memory_error_func) (status, memaddr + 4, info); - return -1; - } - if (info->endian == BFD_ENDIAN_BIG) - insn = bfd_getb16 (buffer); - else - insn = bfd_getl16 (buffer); - infprintf (is, "0x%x%04x (48-bit insn)", higher, insn); - - info->insn_type = dis_noninsn; - return 6; - } - else if ((insn & 0x1c00) == 0x0000 || (insn & 0x1000) == 0x1000) - { - /* This is a 32-bit microMIPS instruction. */ - higher = insn; - - status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info); - if (status != 0) - { - infprintf (is, "micromips 0x%x", higher); - (*info->memory_error_func) (status, memaddr + 2, info); - return -1; - } - - if (info->endian == BFD_ENDIAN_BIG) - insn = bfd_getb16 (buffer); - else - insn = bfd_getl16 (buffer); - - insn = insn | (higher << 16); - - length += 2; - } - - /* 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++) - { - if (op->pinfo != INSN_MACRO - && !(no_aliases && (op->pinfo2 & INSN2_ALIAS)) - && (insn & op->mask) == op->match - && ((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++) - { - 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': - if (strcmp (op->name, "jalx") == 0) - info->target = (((memaddr + 4) & ~(bfd_vma) 0x0fffffff) - | (GET_OP (insn, TARGET) << 2)); - else - info->target = (((memaddr + 4) & ~(bfd_vma) 0x07ffffff) - | (GET_OP (insn, TARGET) << 1)); - /* For gdb disassembler, force odd address on jalx. */ - if (info->flavour == bfd_target_unknown_flavour - && strcmp (op->name, "jalx") == 0) - 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. - The microMIPS encoding does not have a coprocessor - identifier field as such, so we must work out the - coprocessor number by looking at the opcode. */ - switch (insn - & ~((MICROMIPSOP_MASK_RT << MICROMIPSOP_SH_RT) - | (MICROMIPSOP_MASK_RS << MICROMIPSOP_SH_RS))) - { - case 0x000000fc: /* mfc0 */ - case 0x000002fc: /* mtc0 */ - case 0x580000fc: /* dmfc0 */ - case 0x580002fc: /* dmtc0 */ - infprintf (is, "%s", mips_cp0_names[GET_OP (insn, RS)]); - break; - default: - infprintf (is, "$%d", GET_OP (insn, RS)); - break; - } - break; - - case 'H': - infprintf (is, "%d", GET_OP (insn, SEL)); - break; - - case 'K': - infprintf (is, "%s", mips_hwr_names[GET_OP (insn, RS)]); - break; + if (info->symbols + && info->symbols[0] + && (info->symbols[0]->flags & BSF_SYNTHETIC) + && addr == bfd_asymbol_value (info->symbols[0]) + 12) + return true; - 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 'D': - { - 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); - 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; - - 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; + return false; +} - case 'c': - regno = micromips_to_32_reg_c_map[GET_OP (insn, MC)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; +/* Whether none, a 32-bit or a 16-bit instruction match has been done. */ - case 'd': - regno = micromips_to_32_reg_d_map[GET_OP (insn, MD)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; +enum match_kind +{ + MATCH_NONE, + MATCH_FULL, + MATCH_SHORT +}; - case 'e': - regno = micromips_to_32_reg_e_map[GET_OP (insn, ME)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; +/* Disassemble mips16 instructions. */ - 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; +static int +print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info) +{ + const fprintf_styled_ftype infprintf = info->fprintf_styled_func; + int status; + bfd_byte buffer[4]; + const struct mips_opcode *op, *opend; + struct mips_print_arg_state state; + void *is = info->stream; + bool have_second; + bool extend_only; + unsigned int second; + unsigned int first; + unsigned int full; - case 'g': - regno = micromips_to_32_reg_g_map[GET_OP (insn, MG)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; + info->bytes_per_chunk = 2; + info->display_endian = info->endian; + info->insn_info_valid = 1; + info->branch_delay_insns = 0; + info->data_size = 0; + info->target = 0; + info->target2 = 0; - case 'h': - regno = micromips_to_32_reg_h_map[GET_OP (insn, MH)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; +#define GET_OP(insn, field) \ + (((insn) >> MIPS16OP_SH_##field) & MIPS16OP_MASK_##field) + /* Decode PLT entry's GOT slot address word. */ + if (is_mips16_plt_tail (info, memaddr)) + { + info->insn_type = dis_noninsn; + status = (*info->read_memory_func) (memaddr, buffer, 4, info); + if (status == 0) + { + unsigned int gotslot; - case 'i': - regno = micromips_to_32_reg_i_map[GET_OP (insn, MI)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; + if (info->endian == BFD_ENDIAN_BIG) + gotslot = bfd_getb32 (buffer); + else + gotslot = bfd_getl32 (buffer); + infprintf (is, dis_style_assembler_directive, ".word"); + infprintf (is, dis_style_text, "\t"); + infprintf (is, dis_style_immediate, "0x%x", gotslot); - case 'j': - infprintf (is, "%s", mips_gpr_names[GET_OP (insn, MJ)]); - break; + return 4; + } + } + else + { + info->insn_type = dis_nonbranch; + status = (*info->read_memory_func) (memaddr, buffer, 2, info); + } + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } - case 'l': - regno = micromips_to_32_reg_l_map[GET_OP (insn, ML)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; + extend_only = false; - case 'm': - regno = micromips_to_32_reg_m_map[GET_OP (insn, MM)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; + if (info->endian == BFD_ENDIAN_BIG) + first = bfd_getb16 (buffer); + else + first = bfd_getl16 (buffer); - case 'n': - regno = micromips_to_32_reg_n_map[GET_OP (insn, MN)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; + status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info); + if (status == 0) + { + have_second = true; + if (info->endian == BFD_ENDIAN_BIG) + second = bfd_getb16 (buffer); + else + second = bfd_getl16 (buffer); + full = (first << 16) | second; + } + else + { + have_second = false; + second = 0; + full = first; + } - case 'p': - /* Save lastregno for "mt" to print out later. */ - lastregno = GET_OP (insn, MP); - infprintf (is, "%s", mips_gpr_names[lastregno]); - break; + /* FIXME: Should probably use a hash table on the major opcode here. */ - case 'q': - regno = micromips_to_32_reg_q_map[GET_OP (insn, MQ)]; - infprintf (is, "%s", mips_gpr_names[regno]); - break; + opend = mips16_opcodes + bfd_mips16_num_opcodes; + for (op = mips16_opcodes; op < opend; op++) + { + enum match_kind match; - case 'r': /* program counter. */ - infprintf (is, "$pc"); - break; + if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor)) + continue; - case 's': /* stack pointer. */ - lastregno = 29; - infprintf (is, "%s", mips_gpr_names[29]); - break; + if (op->pinfo == INSN_MACRO + || (no_aliases && (op->pinfo2 & INSN2_ALIAS))) + match = MATCH_NONE; + else if (mips_opcode_32bit_p (op)) + { + if (have_second + && (full & op->mask) == op->match) + match = MATCH_FULL; + else + match = MATCH_NONE; + } + else if ((first & op->mask) == op->match) + { + match = MATCH_SHORT; + second = 0; + full = first; + } + else if ((first & 0xf800) == 0xf000 + && have_second + && !extend_only + && (second & op->mask) == op->match) + { + if (op->pinfo2 & INSN2_SHORT_ONLY) + { + match = MATCH_NONE; + extend_only = true; + } + else + match = MATCH_FULL; + } + else + match = MATCH_NONE; - case 't': - infprintf (is, "%s", mips_gpr_names[lastregno]); - break; + if (match != MATCH_NONE) + { + const char *s; - case 'z': /* $0. */ - infprintf (is, "%s", mips_gpr_names[0]); - break; + infprintf (is, dis_style_mnemonic, "%s", op->name); + if (op->args[0] != '\0') + infprintf (is, dis_style_text, "\t"); - case 'A': - /* Sign-extend the immediate. */ - immed = GET_OP_S (insn, IMMA) << 2; - infprintf (is, "%d", immed); + init_print_arg_state (&state); + for (s = op->args; *s != '\0'; s++) + { + if (*s == ',' + && s[1] == 'w' + && GET_OP (full, RX) == GET_OP (full, RY)) + { + /* Skip the register and the comma. */ + ++s; + continue; + } + if (*s == ',' + && s[1] == 'v' + && GET_OP (full, RZ) == GET_OP (full, RX)) + { + /* Skip the register and the comma. */ + ++s; + continue; + } + if (s[0] == 'N' + && s[1] == ',' + && s[2] == 'O' + && op->name[strlen (op->name) - 1] == '0') + { + /* Coprocessor register 0 with sel field. */ + const struct mips_cp0sel_name *n; + const struct mips_operand *operand; + unsigned int reg, sel; + + operand = decode_mips16_operand (*s, true); + reg = mips_extract_operand (operand, (first << 16) | second); + s += 2; + operand = decode_mips16_operand (*s, true); + sel = mips_extract_operand (operand, (first << 16) | second); + + /* 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, dis_style_register, "%s", n->name); + else + { + infprintf (is, dis_style_register, "$%d", reg); + infprintf (is, dis_style_text, ","); + infprintf (is, dis_style_immediate, "%d", sel); + } + } + else + switch (match) + { + case MATCH_FULL: + print_mips16_insn_arg (info, &state, op, *s, memaddr + 2, + second, true, first, s[1] == '('); break; - - case 'B': - immed = micromips_imm_b_map[GET_OP (insn, IMMB)]; - infprintf (is, "%d", immed); + case MATCH_SHORT: + print_mips16_insn_arg (info, &state, op, *s, memaddr, + first, false, 0, s[1] == '('); break; - - case 'C': - immed = micromips_imm_c_map[GET_OP (insn, IMMC)]; - infprintf (is, "0x%x", immed); + case MATCH_NONE: /* Stop the compiler complaining. */ 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; + /* Figure out branch instruction type and delay slot information. */ + if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0) + info->branch_delay_insns = 1; + if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0 + || (op->pinfo2 & INSN2_UNCOND_BRANCH) != 0) + { + if ((op->pinfo & INSN_WRITE_GPR_31) != 0) + info->insn_type = dis_jsr; + else + info->insn_type = dis_branch; + } + else if ((op->pinfo2 & INSN2_COND_BRANCH) != 0) + info->insn_type = dis_condbranch; - 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; + return match == MATCH_FULL ? 4 : 2; + } + } +#undef GET_OP - case 'F': - immed = GET_OP (insn, IMMF); - infprintf (is, "0x%x", immed); - break; + infprintf (is, dis_style_assembler_directive, ".short"); + infprintf (is, dis_style_text, "\t"); + infprintf (is, dis_style_immediate, "0x%x", first); + info->insn_type = dis_noninsn; - case 'G': - immed = (insn >> MICROMIPSOP_SH_IMMG) + 1; - immed = (immed & MICROMIPSOP_MASK_IMMG) - 1; - infprintf (is, "%d", immed); - break; + return 2; +} - case 'H': - immed = GET_OP (insn, IMMH) << 1; - infprintf (is, "%d", immed); - break; +/* Disassemble microMIPS instructions. */ - case 'I': - immed = (insn >> MICROMIPSOP_SH_IMMI) + 1; - immed = (immed & MICROMIPSOP_MASK_IMMI) - 1; - infprintf (is, "%d", immed); - break; +static int +print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) +{ + const fprintf_styled_ftype infprintf = info->fprintf_styled_func; + const struct mips_opcode *op, *opend; + void *is = info->stream; + bfd_byte buffer[2]; + unsigned int higher; + unsigned int length; + int status; + unsigned int insn; - case 'J': - immed = GET_OP (insn, IMMJ) << 2; - infprintf (is, "%d", immed); - break; + info->bytes_per_chunk = 2; + info->display_endian = info->endian; + info->insn_info_valid = 1; + info->branch_delay_insns = 0; + info->data_size = 0; + info->insn_type = dis_nonbranch; + info->target = 0; + info->target2 = 0; - case 'L': - immed = GET_OP (insn, IMML); - infprintf (is, "%d", immed); - break; + status = (*info->read_memory_func) (memaddr, buffer, 2, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } - case 'M': - immed = (insn >> MICROMIPSOP_SH_IMMM) - 1; - immed = (immed & MICROMIPSOP_MASK_IMMM) + 1; - infprintf (is, "%d", immed); - break; + length = 2; - 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; + if (info->endian == BFD_ENDIAN_BIG) + insn = bfd_getb16 (buffer); + else + insn = bfd_getl16 (buffer); - case 'O': - immed = GET_OP (insn, IMMO); - infprintf (is, "0x%x", immed); - break; + if ((insn & 0x1c00) == 0x0000 || (insn & 0x1000) == 0x1000) + { + /* This is a 32-bit microMIPS instruction. */ + higher = insn; - case 'P': - immed = GET_OP (insn, IMMP) << 2; - infprintf (is, "%d", immed); - break; + status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info); + if (status != 0) + { + infprintf (is, dis_style_text, "micromips 0x%x", higher); + (*info->memory_error_func) (status, memaddr + 2, info); + return -1; + } - case 'Q': - /* Sign-extend the immediate. */ - immed = GET_OP_S (insn, IMMQ) << 2; - infprintf (is, "%d", immed); - break; + if (info->endian == BFD_ENDIAN_BIG) + insn = bfd_getb16 (buffer); + else + insn = bfd_getl16 (buffer); - case 'U': - immed = GET_OP (insn, IMMU) << 2; - infprintf (is, "%d", immed); - break; + insn = insn | (higher << 16); - case 'W': - immed = GET_OP (insn, IMMW) << 2; - infprintf (is, "%d", immed); - break; + length += 2; + } - case 'X': - /* Sign-extend the immediate. */ - immed = GET_OP_S (insn, IMMX); - infprintf (is, "%d", immed); - break; + /* FIXME: Should probably use a hash table on the major opcode here. */ - 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; + opend = micromips_opcodes + bfd_micromips_num_opcodes; + for (op = micromips_opcodes; op < opend; op++) + { + if (op->pinfo != INSN_MACRO + && !(no_aliases && (op->pinfo2 & INSN2_ALIAS)) + && (insn & op->mask) == op->match + && ((length == 2 && (op->mask & 0xffff0000) == 0) + || (length == 4 && (op->mask & 0xffff0000) != 0))) + { + if (!validate_insn_args (op, decode_micromips_operand, insn)) + continue; - default: - /* xgettext:c-format */ - infprintf (is, - _("# internal disassembler error, " - "unrecognized modifier (m%c)"), - *s); - abort (); - } - break; + infprintf (is, dis_style_mnemonic, "%s", op->name); - default: - /* xgettext:c-format */ - infprintf (is, - _("# internal disassembler error, " - "unrecognized modifier (%c)"), - *s); - abort (); - } + if (op->args[0]) + { + infprintf (is, dis_style_text, "\t"); + print_insn_args (info, op, decode_micromips_operand, insn, + memaddr + 1, length); } /* Figure out instruction type and branch delay information. */ @@ -2889,7 +2582,7 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) if (((op->pinfo & INSN_UNCOND_BRANCH_DELAY) | (op->pinfo2 & INSN2_UNCOND_BRANCH)) != 0) { - if ((op->pinfo & (INSN_WRITE_GPR_31 | INSN_WRITE_GPR_T)) != 0) + if ((op->pinfo & (INSN_WRITE_GPR_31 | INSN_WRITE_1)) != 0) info->insn_type = dis_jsr; else info->insn_type = dis_branch; @@ -2903,50 +2596,58 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) info->insn_type = dis_condbranch; } else if ((op->pinfo - & (INSN_STORE_MEMORY | INSN_LOAD_MEMORY_DELAY)) != 0) + & (INSN_STORE_MEMORY | INSN_LOAD_MEMORY)) != 0) info->insn_type = dis_dref; return length; } } -#undef GET_OP_S -#undef GET_OP - infprintf (is, "0x%x", insn); + infprintf (is, dis_style_assembler_directive, ".short"); + infprintf (is, dis_style_text, "\t"); + if (length != 2) + { + infprintf (is, dis_style_immediate, "0x%x", (insn >> 16) & 0xffff); + infprintf (is, dis_style_text, ", "); + } + infprintf (is, dis_style_immediate, "0x%x", (insn & 0xffff)); + info->insn_type = dis_noninsn; return length; } /* Return 1 if a symbol associated with the location being disassembled - indicates a compressed (MIPS16 or microMIPS) mode. We iterate over - all the symbols at the address being considered assuming if at least - one of them indicates code compression, then such code has been - genuinely produced here (other symbols could have been derived from - function symbols defined elsewhere or could define data). Otherwise, - return 0. */ - -static bfd_boolean -is_compressed_mode_p (struct disassemble_info *info) -{ - elf_symbol_type *symbol; - int pos; + indicates a compressed mode, either MIPS16 or microMIPS, according to + MICROMIPS_P. We iterate over all the symbols at the address being + considered assuming if at least one of them indicates code compression, + then such code has been genuinely produced here (other symbols could + have been derived from function symbols defined elsewhere or could + define data). Otherwise, return 0. */ + +static bool +is_compressed_mode_p (struct disassemble_info *info, bool micromips_p) +{ int i; - - for (i = 0; i < info->num_symbols; i++) - { - pos = info->symtab_pos + i; - - if (bfd_asymbol_flavour (info->symtab[pos]) != bfd_target_elf_flavour) - continue; - - symbol = (elf_symbol_type *) info->symtab[pos]; - if ((!micromips_ase - && ELF_ST_IS_MIPS16 (symbol->internal_elf_sym.st_other)) - || (micromips_ase - && ELF_ST_IS_MICROMIPS (symbol->internal_elf_sym.st_other))) - return 1; - } + int l; + + for (i = info->symtab_pos, l = i + info->num_symbols; i < l; i++) + if (((info->symtab[i])->flags & BSF_SYNTHETIC) != 0 + && ((!micromips_p + && ELF_ST_IS_MIPS16 ((*info->symbols)->udata.i)) + || (micromips_p + && ELF_ST_IS_MICROMIPS ((*info->symbols)->udata.i)))) + return 1; + else if (bfd_asymbol_flavour (info->symtab[i]) == bfd_target_elf_flavour + && info->symtab[i]->section == info->section) + { + elf_symbol_type *symbol = (elf_symbol_type *) info->symtab[i]; + if ((!micromips_p + && ELF_ST_IS_MIPS16 (symbol->internal_elf_sym.st_other)) + || (micromips_p + && ELF_ST_IS_MICROMIPS (symbol->internal_elf_sym.st_other))) + return 1; + } return 0; } @@ -2962,7 +2663,6 @@ _print_insn_mips (bfd_vma memaddr, struct disassemble_info *info, enum bfd_endian endianness) { - int (*print_insn_compr) (bfd_vma, struct disassemble_info *); bfd_byte buffer[INSNLEN]; int status; @@ -2974,18 +2674,23 @@ _print_insn_mips (bfd_vma memaddr, if (info->mach == bfd_mach_mips_micromips) return print_insn_micromips (memaddr, info); - print_insn_compr = !micromips_ase ? print_insn_mips16 : print_insn_micromips; - #if 1 /* FIXME: If odd address, this is CLEARLY a compressed instruction. */ /* Only a few tools will work this way. */ if (memaddr & 0x01) - return print_insn_compr (memaddr, info); + { + if (micromips_ase) + return print_insn_micromips (memaddr, info); + else + return print_insn_mips16 (memaddr, info); + } #endif #if SYMTAB_AVAILABLE - if (is_compressed_mode_p (info)) - return print_insn_compr (memaddr, info); + if (is_compressed_mode_p (info, true)) + return print_insn_micromips (memaddr, info); + if (is_compressed_mode_p (info, false)) + return print_insn_mips16 (memaddr, info); #endif status = (*info->read_memory_func) (memaddr, buffer, INSNLEN, info); @@ -3019,55 +2724,196 @@ print_insn_little_mips (bfd_vma memaddr, struct disassemble_info *info) return _print_insn_mips (memaddr, info, BFD_ENDIAN_LITTLE); } -void -print_mips_disassembler_options (FILE *stream) +/* Indices into option argument vector for options accepting an argument. + Use MIPS_OPTION_ARG_NONE for options accepting no argument. */ +typedef enum { - unsigned int i; + MIPS_OPTION_ARG_NONE = -1, + MIPS_OPTION_ARG_ABI, + MIPS_OPTION_ARG_ARCH, + MIPS_OPTION_ARG_SIZE +} mips_option_arg_t; + +/* Valid MIPS disassembler options. */ +static struct +{ + const char *name; + const char *description; + mips_option_arg_t arg; +} mips_options[] = +{ + { "no-aliases", N_("Use canonical instruction forms.\n"), + MIPS_OPTION_ARG_NONE }, + { "msa", N_("Recognize MSA instructions.\n"), + MIPS_OPTION_ARG_NONE }, + { "virt", N_("Recognize the virtualization ASE instructions.\n"), + MIPS_OPTION_ARG_NONE }, + { "xpa", N_("Recognize the eXtended Physical Address (XPA) ASE\n\ + instructions.\n"), + MIPS_OPTION_ARG_NONE }, + { "ginv", N_("Recognize the Global INValidate (GINV) ASE " + "instructions.\n"), + MIPS_OPTION_ARG_NONE }, + { "loongson-mmi", + N_("Recognize the Loongson MultiMedia extensions " + "Instructions (MMI) ASE instructions.\n"), + MIPS_OPTION_ARG_NONE }, + { "loongson-cam", + N_("Recognize the Loongson Content Address Memory (CAM) " + " instructions.\n"), + MIPS_OPTION_ARG_NONE }, + { "loongson-ext", + N_("Recognize the Loongson EXTensions (EXT) " + " instructions.\n"), + MIPS_OPTION_ARG_NONE }, + { "loongson-ext2", + N_("Recognize the Loongson EXTensions R2 (EXT2) " + " instructions.\n"), + MIPS_OPTION_ARG_NONE }, + { "gpr-names=", N_("Print GPR names according to specified ABI.\n\ + Default: based on binary being disassembled.\n"), + MIPS_OPTION_ARG_ABI }, + { "fpr-names=", N_("Print FPR names according to specified ABI.\n\ + Default: numeric.\n"), + MIPS_OPTION_ARG_ABI }, + { "cp0-names=", N_("Print CP0 register names according to specified " + "architecture.\n\ + Default: based on binary being disassembled.\n"), + MIPS_OPTION_ARG_ARCH }, + { "hwr-names=", N_("Print HWR names according to specified architecture.\n\ + Default: based on binary being disassembled.\n"), + MIPS_OPTION_ARG_ARCH }, + { "reg-names=", N_("Print GPR and FPR names according to specified ABI.\n"), + MIPS_OPTION_ARG_ABI }, + { "reg-names=", N_("Print CP0 register and HWR names according to " + "specified\n\ + architecture."), + MIPS_OPTION_ARG_ARCH } +}; - fprintf (stream, _("\n\ -The following MIPS specific disassembler options are supported for use\n\ -with the -M switch (multiple options should be separated by commas):\n")); +/* Build the structure representing valid MIPS disassembler options. + This is done dynamically for maintenance ease purpose; a static + initializer would be unreadable. */ - fprintf (stream, _("\n\ - gpr-names=ABI Print GPR names according to specified ABI.\n\ - Default: based on binary being disassembled.\n")); +const disasm_options_and_args_t * +disassembler_options_mips (void) +{ + static disasm_options_and_args_t *opts_and_args; - fprintf (stream, _("\n\ - fpr-names=ABI Print FPR names according to specified ABI.\n\ - Default: numeric.\n")); + if (opts_and_args == NULL) + { + size_t num_options = ARRAY_SIZE (mips_options); + size_t num_args = MIPS_OPTION_ARG_SIZE; + disasm_option_arg_t *args; + disasm_options_t *opts; + size_t i; + size_t j; + + args = XNEWVEC (disasm_option_arg_t, num_args + 1); + + args[MIPS_OPTION_ARG_ABI].name = "ABI"; + args[MIPS_OPTION_ARG_ABI].values + = XNEWVEC (const char *, ARRAY_SIZE (mips_abi_choices) + 1); + for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++) + args[MIPS_OPTION_ARG_ABI].values[i] = mips_abi_choices[i].name; + /* The array we return must be NULL terminated. */ + args[MIPS_OPTION_ARG_ABI].values[i] = NULL; + + args[MIPS_OPTION_ARG_ARCH].name = "ARCH"; + args[MIPS_OPTION_ARG_ARCH].values + = XNEWVEC (const char *, ARRAY_SIZE (mips_arch_choices) + 1); + for (i = 0, j = 0; i < ARRAY_SIZE (mips_arch_choices); i++) + if (*mips_arch_choices[i].name != '\0') + args[MIPS_OPTION_ARG_ARCH].values[j++] = mips_arch_choices[i].name; + /* The array we return must be NULL terminated. */ + args[MIPS_OPTION_ARG_ARCH].values[j] = NULL; + + /* The array we return must be NULL terminated. */ + args[MIPS_OPTION_ARG_SIZE].name = NULL; + args[MIPS_OPTION_ARG_SIZE].values = NULL; + + opts_and_args = XNEW (disasm_options_and_args_t); + opts_and_args->args = args; + + opts = &opts_and_args->options; + opts->name = XNEWVEC (const char *, num_options + 1); + opts->description = XNEWVEC (const char *, num_options + 1); + opts->arg = XNEWVEC (const disasm_option_arg_t *, num_options + 1); + for (i = 0; i < num_options; i++) + { + opts->name[i] = mips_options[i].name; + opts->description[i] = _(mips_options[i].description); + if (mips_options[i].arg != MIPS_OPTION_ARG_NONE) + opts->arg[i] = &args[mips_options[i].arg]; + else + opts->arg[i] = NULL; + } + /* The array we return must be NULL terminated. */ + opts->name[i] = NULL; + opts->description[i] = NULL; + opts->arg[i] = NULL; + } - fprintf (stream, _("\n\ - cp0-names=ARCH Print CP0 register names according to\n\ - specified architecture.\n\ - Default: based on binary being disassembled.\n")); + return opts_and_args; +} - fprintf (stream, _("\n\ - hwr-names=ARCH Print HWR names according to specified \n\ - architecture.\n\ - Default: based on binary being disassembled.\n")); +void +print_mips_disassembler_options (FILE *stream) +{ + const disasm_options_and_args_t *opts_and_args; + const disasm_option_arg_t *args; + const disasm_options_t *opts; + size_t max_len = 0; + size_t i; + size_t j; - fprintf (stream, _("\n\ - reg-names=ABI Print GPR and FPR names according to\n\ - specified ABI.\n")); + opts_and_args = disassembler_options_mips (); + opts = &opts_and_args->options; + args = opts_and_args->args; fprintf (stream, _("\n\ - reg-names=ARCH Print CP0 register and HWR names according to\n\ - specified architecture.\n")); +The following MIPS specific disassembler options are supported for use\n\ +with the -M switch (multiple options should be separated by commas):\n\n")); - fprintf (stream, _("\n\ - For the options above, the following values are supported for \"ABI\":\n\ - ")); - for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++) - fprintf (stream, " %s", mips_abi_choices[i].name); - fprintf (stream, _("\n")); + /* Compute the length of the longest option name. */ + for (i = 0; opts->name[i] != NULL; i++) + { + size_t len = strlen (opts->name[i]); - fprintf (stream, _("\n\ - For the options above, The following values are supported for \"ARCH\":\n\ - ")); - for (i = 0; i < ARRAY_SIZE (mips_arch_choices); i++) - if (*mips_arch_choices[i].name != '\0') - fprintf (stream, " %s", mips_arch_choices[i].name); - fprintf (stream, _("\n")); + if (opts->arg[i] != NULL) + len += strlen (opts->arg[i]->name); + if (max_len < len) + max_len = len; + } + + for (i = 0, max_len++; opts->name[i] != NULL; i++) + { + fprintf (stream, " %s", opts->name[i]); + if (opts->arg[i] != NULL) + fprintf (stream, "%s", opts->arg[i]->name); + if (opts->description[i] != NULL) + { + size_t len = strlen (opts->name[i]); + + if (opts->arg[i] != NULL) + len += strlen (opts->arg[i]->name); + fprintf (stream, + "%*c %s", (int) (max_len - len), ' ', opts->description[i]); + } + fprintf (stream, _("\n")); + } + + for (i = 0; args[i].name != NULL; i++) + { + if (args[i].values == NULL) + continue; + fprintf (stream, _("\n\ + For the options above, the following values are supported for \"%s\":\n "), + args[i].name); + for (j = 0; args[i].values[j] != NULL; j++) + fprintf (stream, " %s", args[i].values[j]); + fprintf (stream, _("\n")); + } fprintf (stream, _("\n")); }