summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gas/ChangeLog9
-rw-r--r--gas/config/tc-mips.c455
-rw-r--r--include/opcode/ChangeLog10
-rw-r--r--include/opcode/mips.h288
-rw-r--r--opcodes/ChangeLog26
-rw-r--r--opcodes/micromips-opc.c149
-rw-r--r--opcodes/mips-dis.c1401
-rw-r--r--opcodes/mips-formats.h113
-rw-r--r--opcodes/mips-opc.c117
9 files changed, 1134 insertions, 1434 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog
index dc609b9ba5..0162174715 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,5 +1,14 @@
2013-07-14 Richard Sandiford <rdsandiford@googlemail.com>
+ * config/tc-mips.c (validate_mips_insn): Move further up file.
+ Add insn_bits and decode_operand arguments. Use the mips_operand
+ fields to work out which bits an operand occupies. Detect double
+ definitions.
+ (validate_micromips_insn): Move further up file. Call into
+ validate_mips_insn.
+
+2013-07-14 Richard Sandiford <rdsandiford@googlemail.com>
+
* config/tc-mips.c (mips16_macro_build): Remove 'Y' case.
2013-07-14 Richard Sandiford <rdsandiford@googlemail.com>
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index 4e878c89d3..6bb44c571b 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -1326,8 +1326,6 @@ static void s_mips_file (int);
static void s_mips_loc (int);
static bfd_boolean pic_need_relax (symbolS *, asection *);
static int relaxed_branch_length (fragS *, asection *, int);
-static int validate_mips_insn (const struct mips_opcode *);
-static int validate_micromips_insn (const struct mips_opcode *);
static int relaxed_micromips_16bit_branch_length (fragS *, asection *, int);
static int relaxed_micromips_32bit_branch_length (fragS *, asection *, int);
@@ -2707,6 +2705,111 @@ is_delay_slot_valid (const struct mips_opcode *mo)
return TRUE;
}
+/* For consistency checking, verify that all bits of OPCODE are
+ specified either by the match/mask part of the instruction
+ definition, or by the operand list. INSN_BITS says which
+ bits of the instruction are significant and DECODE_OPERAND
+ provides the mips_operand description of each operand. */
+
+static int
+validate_mips_insn (const struct mips_opcode *opcode,
+ unsigned long insn_bits,
+ const struct mips_operand *(*decode_operand) (const char *))
+{
+ const char *s;
+ unsigned long used_bits, doubled, undefined;
+ const struct mips_operand *operand;
+
+ if ((opcode->mask & opcode->match) != opcode->match)
+ {
+ as_bad (_("internal: bad mips opcode (mask error): %s %s"),
+ opcode->name, opcode->args);
+ return 0;
+ }
+ used_bits = 0;
+ for (s = opcode->args; *s; ++s)
+ switch (*s)
+ {
+ case ',':
+ case '(':
+ case ')':
+ break;
+
+ default:
+ operand = decode_operand (s);
+ if (!operand)
+ {
+ as_bad (_("internal: unknown operand type: %s %s"),
+ opcode->name, opcode->args);
+ return 0;
+ }
+ used_bits |= ((1 << operand->size) - 1) << operand->lsb;
+ if (operand->type == OP_MDMX_IMM_REG)
+ /* Bit 5 is the format selector (OB vs QH). The opcode table
+ has separate entries for each format. */
+ used_bits &= ~(1 << (operand->lsb + 5));
+ /* Skip prefix characters. */
+ if (*s == '+' || *s == 'm')
+ ++s;
+ break;
+ }
+ doubled = used_bits & opcode->mask & insn_bits;
+ if (doubled)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%08lx doubly defined):"
+ " %s %s"), doubled, opcode->name, opcode->args);
+ return 0;
+ }
+ used_bits |= opcode->mask;
+ undefined = ~used_bits & insn_bits;
+ if (undefined)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%08lx undefined): %s %s"),
+ undefined, opcode->name, opcode->args);
+ return 0;
+ }
+ used_bits &= ~insn_bits;
+ if (used_bits)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%08lx defined): %s %s"),
+ used_bits, opcode->name, opcode->args);
+ return 0;
+ }
+ return 1;
+}
+
+/* The microMIPS version of validate_mips_insn. */
+
+static int
+validate_micromips_insn (const struct mips_opcode *opc)
+{
+ unsigned long insn_bits;
+ unsigned long major;
+ unsigned int length;
+
+ length = micromips_insn_length (opc);
+ if (length != 2 && length != 4)
+ {
+ as_bad (_("Internal error: bad microMIPS opcode (incorrect length: %u): "
+ "%s %s"), length, opc->name, opc->args);
+ return 0;
+ }
+ major = opc->match >> (10 + 8 * (length - 2));
+ if ((length == 2 && (major & 7) != 1 && (major & 6) != 2)
+ || (length == 4 && (major & 7) != 0 && (major & 4) != 4))
+ {
+ as_bad (_("Internal error: bad microMIPS opcode "
+ "(opcode/length mismatch): %s %s"), opc->name, opc->args);
+ return 0;
+ }
+
+ /* Shift piecewise to avoid an overflow where unsigned long is 32-bit. */
+ insn_bits = 1 << 4 * length;
+ insn_bits <<= 4 * length;
+ insn_bits -= 1;
+ return validate_mips_insn (opc, insn_bits, decode_micromips_operand);
+}
+
/* This function is called once, at assembler startup time. It should set up
all the tables, etc. that the MD part of the assembler will need. */
@@ -2745,7 +2848,8 @@ md_begin (void)
{
if (mips_opcodes[i].pinfo != INSN_MACRO)
{
- if (!validate_mips_insn (&mips_opcodes[i]))
+ if (!validate_mips_insn (&mips_opcodes[i], 0xffffffff,
+ decode_mips_operand))
broken = 1;
if (nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
{
@@ -10748,351 +10852,6 @@ mips16_macro (struct mips_cl_insn *ip)
}
}
-/* For consistency checking, verify that all bits are specified either
- by the match/mask part of the instruction definition, or by the
- operand list. */
-static int
-validate_mips_insn (const struct mips_opcode *opc)
-{
- const char *p = opc->args;
- char c;
- unsigned long used_bits = opc->mask;
-
- if ((used_bits & opc->match) != opc->match)
- {
- as_bad (_("internal: bad mips opcode (mask error): %s %s"),
- opc->name, opc->args);
- return 0;
- }
-#define USE_BITS(mask,shift) (used_bits |= ((mask) << (shift)))
- while (*p)
- switch (c = *p++)
- {
- case ',': break;
- case '(': break;
- case ')': break;
- case '+':
- switch (c = *p++)
- {
- case '1': USE_BITS (OP_MASK_UDI1, OP_SH_UDI1); break;
- case '2': USE_BITS (OP_MASK_UDI2, OP_SH_UDI2); break;
- case '3': USE_BITS (OP_MASK_UDI3, OP_SH_UDI3); break;
- case '4': USE_BITS (OP_MASK_UDI4, OP_SH_UDI4); break;
- case 'A': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case 'B': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break;
- case 'C': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
- case 'E': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case 'F': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break;
- case 'G': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
- case 'H': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
- case 'I': break;
- case 'J': USE_BITS (OP_MASK_CODE10, OP_SH_CODE10); break;
- case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'x': USE_BITS (OP_MASK_BBITIND, OP_SH_BBITIND); break;
- case 'X': USE_BITS (OP_MASK_BBITIND, OP_SH_BBITIND); break;
- case 'p': USE_BITS (OP_MASK_CINSPOS, OP_SH_CINSPOS); break;
- case 'P': USE_BITS (OP_MASK_CINSPOS, OP_SH_CINSPOS); break;
- case 'Q': USE_BITS (OP_MASK_SEQI, OP_SH_SEQI); break;
- case 's': USE_BITS (OP_MASK_CINSLM1, OP_SH_CINSLM1); break;
- case 'S': USE_BITS (OP_MASK_CINSLM1, OP_SH_CINSLM1); break;
- case 'z': USE_BITS (OP_MASK_RZ, OP_SH_RZ); break;
- case 'Z': USE_BITS (OP_MASK_FZ, OP_SH_FZ); break;
- case 'a': USE_BITS (OP_MASK_OFFSET_A, OP_SH_OFFSET_A); break;
- case 'b': USE_BITS (OP_MASK_OFFSET_B, OP_SH_OFFSET_B); break;
- case 'c': USE_BITS (OP_MASK_OFFSET_C, OP_SH_OFFSET_C); break;
- case 'i': USE_BITS (OP_MASK_TARGET, OP_SH_TARGET); break;
- case 'j': USE_BITS (OP_MASK_EVAOFFSET, OP_SH_EVAOFFSET); break;
-
- default:
- as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
- c, opc->name, opc->args);
- return 0;
- }
- break;
- case '<': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case 'A': break;
- case 'B': USE_BITS (OP_MASK_CODE20, OP_SH_CODE20); break;
- case 'C': USE_BITS (OP_MASK_COPZ, OP_SH_COPZ); break;
- case 'D': USE_BITS (OP_MASK_FD, OP_SH_FD); break;
- case 'E': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'F': break;
- case 'G': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- case 'H': USE_BITS (OP_MASK_SEL, OP_SH_SEL); break;
- case 'I': break;
- case 'J': USE_BITS (OP_MASK_CODE19, OP_SH_CODE19); break;
- case 'K': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- case 'L': break;
- case 'M': USE_BITS (OP_MASK_CCC, OP_SH_CCC); break;
- case 'N': USE_BITS (OP_MASK_BCC, OP_SH_BCC); break;
- case 'O': USE_BITS (OP_MASK_ALN, OP_SH_ALN); break;
- case 'Q': USE_BITS (OP_MASK_VSEL, OP_SH_VSEL);
- USE_BITS (OP_MASK_FT, OP_SH_FT); break;
- case 'R': USE_BITS (OP_MASK_FR, OP_SH_FR); break;
- case 'S': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
- case 'T': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
- case 'V': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
- case 'W': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
- case 'X': USE_BITS (OP_MASK_FD, OP_SH_FD); break;
- case 'Y': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
- case 'Z': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
- case 'a': USE_BITS (OP_MASK_TARGET, OP_SH_TARGET); break;
- case 'b': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case 'c': USE_BITS (OP_MASK_CODE, OP_SH_CODE); break;
- case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- case 'f': break;
- case 'h': USE_BITS (OP_MASK_PREFX, OP_SH_PREFX); break;
- case 'i': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break;
- case 'j': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
- case 'k': USE_BITS (OP_MASK_CACHE, OP_SH_CACHE); break;
- case 'l': break;
- case 'o': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
- case 'p': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
- case 'q': USE_BITS (OP_MASK_CODE2, OP_SH_CODE2); break;
- case 'r': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case 's': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'u': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break;
- case 'v': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case 'w': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'x': break;
- case 'z': break;
- case 'P': USE_BITS (OP_MASK_PERFREG, OP_SH_PERFREG); break;
- case 'U': USE_BITS (OP_MASK_RD, OP_SH_RD);
- USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'e': USE_BITS (OP_MASK_VECBYTE, OP_SH_VECBYTE); break;
- case '%': USE_BITS (OP_MASK_VECALIGN, OP_SH_VECALIGN); break;
- case '1': USE_BITS (OP_MASK_STYPE, OP_SH_STYPE); break;
- case '2': USE_BITS (OP_MASK_BP, OP_SH_BP); break;
- case '3': USE_BITS (OP_MASK_SA3, OP_SH_SA3); break;
- case '4': USE_BITS (OP_MASK_SA4, OP_SH_SA4); break;
- case '5': USE_BITS (OP_MASK_IMM8, OP_SH_IMM8); break;
- case '6': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case '7': USE_BITS (OP_MASK_DSPACC, OP_SH_DSPACC); break;
- case '8': USE_BITS (OP_MASK_WRDSP, OP_SH_WRDSP); break;
- case '9': USE_BITS (OP_MASK_DSPACC_S, OP_SH_DSPACC_S);break;
- case '0': USE_BITS (OP_MASK_DSPSFT, OP_SH_DSPSFT); break;
- case '\'': USE_BITS (OP_MASK_RDDSP, OP_SH_RDDSP); break;
- case ':': USE_BITS (OP_MASK_DSPSFT_7, OP_SH_DSPSFT_7);break;
- case '@': USE_BITS (OP_MASK_IMM10, OP_SH_IMM10); break;
- case '!': USE_BITS (OP_MASK_MT_U, OP_SH_MT_U); break;
- case '$': USE_BITS (OP_MASK_MT_H, OP_SH_MT_H); break;
- case '*': USE_BITS (OP_MASK_MTACC_T, OP_SH_MTACC_T); break;
- case '&': USE_BITS (OP_MASK_MTACC_D, OP_SH_MTACC_D); break;
- case '\\': USE_BITS (OP_MASK_3BITPOS, OP_SH_3BITPOS); break;
- case '~': USE_BITS (OP_MASK_OFFSET12, OP_SH_OFFSET12); break;
- case 'g': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- default:
- as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"),
- c, opc->name, opc->args);
- return 0;
- }
-#undef USE_BITS
- if (used_bits != 0xffffffff)
- {
- as_bad (_("internal: bad mips opcode (bits 0x%lx undefined): %s %s"),
- ~used_bits & 0xffffffff, opc->name, opc->args);
- return 0;
- }
- return 1;
-}
-
-/* For consistency checking, verify that the length implied matches the
- major opcode and that all bits are specified either by the match/mask
- part of the instruction definition, or by the operand list. */
-
-static int
-validate_micromips_insn (const struct mips_opcode *opc)
-{
- unsigned long match = opc->match;
- unsigned long mask = opc->mask;
- const char *p = opc->args;
- unsigned long insn_bits;
- unsigned long used_bits;
- unsigned long major;
- unsigned int length;
- char e;
- char c;
-
- if ((mask & match) != match)
- {
- as_bad (_("Internal error: bad microMIPS opcode (mask error): %s %s"),
- opc->name, opc->args);
- return 0;
- }
- length = micromips_insn_length (opc);
- if (length != 2 && length != 4)
- {
- as_bad (_("Internal error: bad microMIPS opcode (incorrect length: %u): "
- "%s %s"), length, opc->name, opc->args);
- return 0;
- }
- major = match >> (10 + 8 * (length - 2));
- if ((length == 2 && (major & 7) != 1 && (major & 6) != 2)
- || (length == 4 && (major & 7) != 0 && (major & 4) != 4))
- {
- as_bad (_("Internal error: bad microMIPS opcode "
- "(opcode/length mismatch): %s %s"), opc->name, opc->args);
- return 0;
- }
-
- /* Shift piecewise to avoid an overflow where unsigned long is 32-bit. */
- insn_bits = 1 << 4 * length;
- insn_bits <<= 4 * length;
- insn_bits -= 1;
- used_bits = mask;
-#define USE_BITS(field) \
- (used_bits |= MICROMIPSOP_MASK_##field << MICROMIPSOP_SH_##field)
- while (*p)
- switch (c = *p++)
- {
- case ',': break;
- case '(': break;
- case ')': break;
- case '+':
- e = c;
- switch (c = *p++)
- {
- case 'A': USE_BITS (EXTLSB); break;
- case 'B': USE_BITS (INSMSB); break;
- case 'C': USE_BITS (EXTMSBD); break;
- case 'E': USE_BITS (EXTLSB); break;
- case 'F': USE_BITS (INSMSB); break;
- case 'G': USE_BITS (EXTMSBD); break;
- case 'H': USE_BITS (EXTMSBD); break;
- case 'i': USE_BITS (TARGET); break;
- case 'j': USE_BITS (EVAOFFSET); break;
- default:
- as_bad (_("Internal error: bad mips opcode "
- "(unknown extension operand type `%c%c'): %s %s"),
- e, c, opc->name, opc->args);
- return 0;
- }
- break;
- case 'm':
- e = c;
- switch (c = *p++)
- {
- case 'A': USE_BITS (IMMA); break;
- case 'B': USE_BITS (IMMB); break;
- case 'C': USE_BITS (IMMC); break;
- case 'D': USE_BITS (IMMD); break;
- case 'E': USE_BITS (IMME); break;
- case 'F': USE_BITS (IMMF); break;
- case 'G': USE_BITS (IMMG); break;
- case 'H': USE_BITS (IMMH); break;
- case 'I': USE_BITS (IMMI); break;
- case 'J': USE_BITS (IMMJ); break;
- case 'L': USE_BITS (IMML); break;
- case 'M': USE_BITS (IMMM); break;
- case 'N': USE_BITS (IMMN); break;
- case 'O': USE_BITS (IMMO); break;
- case 'P': USE_BITS (IMMP); break;
- case 'Q': USE_BITS (IMMQ); break;
- case 'U': USE_BITS (IMMU); break;
- case 'W': USE_BITS (IMMW); break;
- case 'X': USE_BITS (IMMX); break;
- case 'Y': USE_BITS (IMMY); break;
- case 'Z': break;
- case 'a': break;
- case 'b': USE_BITS (MB); break;
- case 'c': USE_BITS (MC); break;
- case 'd': USE_BITS (MD); break;
- case 'e': USE_BITS (ME); break;
- case 'f': USE_BITS (MF); break;
- case 'g': USE_BITS (MG); break;
- case 'h': USE_BITS (MH); break;
- case 'j': USE_BITS (MJ); break;
- case 'l': USE_BITS (ML); break;
- case 'm': USE_BITS (MM); break;
- case 'n': USE_BITS (MN); break;
- case 'p': USE_BITS (MP); break;
- case 'q': USE_BITS (MQ); break;
- case 'r': break;
- case 's': break;
- case 't': break;
- case 'x': break;
- case 'y': break;
- case 'z': break;
- default:
- as_bad (_("Internal error: bad mips opcode "
- "(unknown extension operand type `%c%c'): %s %s"),
- e, c, opc->name, opc->args);
- return 0;
- }
- break;
- case '.': USE_BITS (OFFSET10); break;
- case '1': USE_BITS (STYPE); break;
- case '2': USE_BITS (BP); break;
- case '3': USE_BITS (SA3); break;
- case '4': USE_BITS (SA4); break;
- case '5': USE_BITS (IMM8); break;
- case '6': USE_BITS (RS); break;
- case '7': USE_BITS (DSPACC); break;
- case '8': USE_BITS (WRDSP); break;
- case '0': USE_BITS (DSPSFT); break;
- case '<': USE_BITS (SHAMT); break;
- case '>': USE_BITS (SHAMT); break;
- case '@': USE_BITS (IMM10); break;
- case 'B': USE_BITS (CODE10); break;
- case 'C': USE_BITS (COPZ); break;
- case 'D': USE_BITS (FD); break;
- case 'E': USE_BITS (RT); break;
- case 'G': USE_BITS (RS); break;
- case 'H': USE_BITS (SEL); break;
- case 'K': USE_BITS (RS); break;
- case 'M': USE_BITS (CCC); break;
- case 'N': USE_BITS (BCC); break;
- case 'R': USE_BITS (FR); break;
- case 'S': USE_BITS (FS); break;
- case 'T': USE_BITS (FT); break;
- case 'V': USE_BITS (FS); break;
- case '\\': USE_BITS (3BITPOS); break;
- case '^': USE_BITS (RD); break;
- case 'a': USE_BITS (TARGET); break;
- case 'b': USE_BITS (RS); break;
- case 'c': USE_BITS (CODE); break;
- case 'd': USE_BITS (RD); break;
- case 'h': USE_BITS (PREFX); break;
- case 'i': USE_BITS (IMMEDIATE); break;
- case 'j': USE_BITS (DELTA); break;
- case 'k': USE_BITS (CACHE); break;
- case 'n': USE_BITS (RT); break;
- case 'o': USE_BITS (DELTA); break;
- case 'p': USE_BITS (DELTA); break;
- case 'q': USE_BITS (CODE2); break;
- case 'r': USE_BITS (RS); break;
- case 's': USE_BITS (RS); break;
- case 't': USE_BITS (RT); break;
- case 'u': USE_BITS (IMMEDIATE); break;
- case 'v': USE_BITS (RS); break;
- case 'w': USE_BITS (RT); break;
- case 'y': USE_BITS (RS3); break;
- case 'z': break;
- case '|': USE_BITS (TRAP); break;
- case '~': USE_BITS (OFFSET12); break;
- default:
- as_bad (_("Internal error: bad microMIPS opcode "
- "(unknown operand type `%c'): %s %s"),
- c, opc->name, opc->args);
- return 0;
- }
-#undef USE_BITS
- if (used_bits != insn_bits)
- {
- if (~used_bits & insn_bits)
- as_bad (_("Internal error: bad microMIPS opcode "
- "(bits 0x%lx undefined): %s %s"),
- ~used_bits & insn_bits, opc->name, opc->args);
- if (used_bits & ~insn_bits)
- as_bad (_("Internal error: bad microMIPS opcode "
- "(bits 0x%lx defined): %s %s"),
- used_bits & ~insn_bits, opc->name, opc->args);
- return 0;
- }
- return 1;
-}
-
/* UDI immediates. */
struct mips_immed {
char type;
diff --git a/include/opcode/ChangeLog b/include/opcode/ChangeLog
index a96a4ab1ba..fc991a01e4 100644
--- a/include/opcode/ChangeLog
+++ b/include/opcode/ChangeLog
@@ -1,5 +1,15 @@
2013-07-14 Richard Sandiford <rdsandiford@googlemail.com>
+ * mips.h (mips_operand_type, mips_reg_operand_type): New enums.
+ (mips_operand, mips_int_operand, mips_mapped_int_operand)
+ (mips_msb_operand, mips_reg_operand, mips_reg_pair_operand)
+ (mips_pcrel_operand): New structures.
+ (mips_insert_operand, mips_extract_operand, mips_signed_operand)
+ (mips_decode_int_operand, mips_decode_pcrel_operand): New functions.
+ (decode_mips_operand, decode_micromips_operand): Declare.
+
+2013-07-14 Richard Sandiford <rdsandiford@googlemail.com>
+
* mips.h: Document MIPS16 "I" opcode.
2013-07-07 Richard Sandiford <rdsandiford@googlemail.com>
diff --git a/include/opcode/mips.h b/include/opcode/mips.h
index 21128892a9..2d77b95940 100644
--- a/include/opcode/mips.h
+++ b/include/opcode/mips.h
@@ -332,6 +332,292 @@
#define OP_SH_EVAOFFSET 7
#define OP_MASK_EVAOFFSET 0x1ff
+/* Enumerates the various types of MIPS operand. */
+enum mips_operand_type {
+ /* Described by mips_int_operand. */
+ OP_INT,
+
+ /* Described by mips_mapped_int_operand. */
+ OP_MAPPED_INT,
+
+ /* Described by mips_msb_operand. */
+ OP_MSB,
+
+ /* Described by mips_reg_operand. */
+ OP_REG,
+
+ /* Described by mips_reg_pair_operand. */
+ OP_REG_PAIR,
+
+ /* Described by mips_pcrel_operand. */
+ OP_PCREL,
+
+ /* A performance register. The field is 5 bits in size, but the supported
+ values are much more restricted. */
+ OP_PERF_REG,
+
+ /* The final operand in a microMIPS ADDIUSP instruction. It mostly acts
+ as a normal 9-bit signed offset that is multiplied by four, but there
+ are four special cases:
+
+ -2 * 4 => -258 * 4
+ -1 * 4 => -257 * 4
+ 0 * 4 => 256 * 4
+ 1 * 4 => 257 * 4. */
+ OP_ADDIUSP_INT,
+
+ /* The target of a (D)CLO or (D)CLZ instruction. The operand spans two
+ 5-bit register fields, both of which must be set to the destination
+ register. */
+ OP_CLO_CLZ_DEST,
+
+ /* A register list for a microMIPS LWM or SWM instruction. The operand
+ size determines whether the 16-bit or 32-bit encoding is required. */
+ OP_LWM_SWM_LIST,
+
+ /* A 10-bit field VVVVVNNNNN used for octobyte and quadhalf instructions:
+
+ V Meaning
+ ----- -------
+ 0EEE0 8 copies of $vN[E], OB format
+ 0EE01 4 copies of $vN[E], QH format
+ 10110 all 8 elements of $vN, OB format
+ 10101 all 4 elements of $vN, QH format
+ 11110 8 copies of immediate N, OB format
+ 11101 4 copies of immediate N, QH format. */
+ OP_MDMX_IMM_REG,
+
+ /* A register operand that must match the destination register. */
+ OP_REPEAT_DEST_REG,
+
+ /* A register operand that must match the previous register. */
+ OP_REPEAT_PREV_REG,
+
+ /* $pc, which has no encoding in the architectural instruction. */
+ OP_PC
+};
+
+/* Enumerates the types of MIPS register. */
+enum mips_reg_operand_type {
+ /* General registers $0-$31. Software names like $at can also be used. */
+ OP_REG_GP,
+
+ /* Floating-point registers $f0-$f31. */
+ OP_REG_FP,
+
+ /* Coprocessor condition code registers $cc0-$cc7. FPU condition codes
+ can also be written $fcc0-$fcc7. */
+ OP_REG_CCC,
+
+ /* FPRs used in a vector capacity. They can be written $f0-$f31
+ or $v0-$v31, although the latter form is not used for the VR5400
+ vector instructions. */
+ OP_REG_VEC,
+
+ /* DSP accumulator registers $ac0-$ac3. */
+ OP_REG_ACC,
+
+ /* Coprocessor registers $0-$31. Mnemonic names like c0_cause can
+ also be used in some contexts. */
+ OP_REG_COPRO,
+
+ /* Hardware registers $0-$31. Mnemonic names like hwr_cpunum can
+ also be used in some contexts. */
+ OP_REG_HW
+};
+
+/* Base class for all operands. */
+struct mips_operand
+{
+ /* The type of the operand. */
+ enum mips_operand_type type;
+
+ /* The operand occupies SIZE bits of the instruction, starting at LSB. */
+ unsigned short size;
+ unsigned short lsb;
+};
+
+/* Describes an integer operand with a regular encoding pattern. */
+struct mips_int_operand
+{
+ struct mips_operand root;
+
+ /* The low ROOT.SIZE bits of MAX_VAL encodes (MAX_VAL + BIAS) << SHIFT.
+ The cyclically previous field value encodes 1 << SHIFT less than that,
+ and so on. E.g.
+
+ - for { { T, 4, L }, 14, 0, 0 }, field values 0...14 encode themselves,
+ but 15 encodes -1.
+
+ - { { T, 8, L }, 127, 0, 2 } is a normal signed 8-bit operand that is
+ shifted left two places.
+
+ - { { T, 3, L }, 8, 0, 0 } is a normal unsigned 3-bit operand except
+ that 0 encodes 8.
+
+ - { { ... }, 0, 1, 3 } means that N encodes (N + 1) << 3. */
+ unsigned int max_val;
+ int bias;
+ unsigned int shift;
+
+ /* True if the operand should be printed as hex rather than decimal. */
+ bfd_boolean print_hex;
+};
+
+/* Uses a lookup table to describe a small integer operand. */
+struct mips_mapped_int_operand
+{
+ struct mips_operand root;
+
+ /* Maps each encoding value to the integer that it represents. */
+ const int *int_map;
+
+ /* True if the operand should be printed as hex rather than decimal. */
+ bfd_boolean print_hex;
+};
+
+/* An operand that encodes the most significant bit position of a bitfield.
+ Given a bitfield that spans bits [MSB, LSB], some operands of this type
+ encode MSB directly while others encode MSB - LSB. Each operand of this
+ type is preceded by an integer operand that specifies LSB.
+
+ The assembly form varies between instructions. For some instructions,
+ such as EXT, the operand is written as the bitfield size. For others,
+ such as EXTS, it is written in raw MSB - LSB form. */
+struct mips_msb_operand
+{
+ struct mips_operand root;
+
+ /* The assembly-level operand encoded by a field value of 0. */
+ int bias;
+
+ /* True if the operand encodes MSB directly, false if it encodes
+ MSB - LSB. */
+ bfd_boolean add_lsb;
+
+ /* The maximum value of MSB + 1. */
+ unsigned int opsize;
+};
+
+/* Describes a single register operand. */
+struct mips_reg_operand
+{
+ struct mips_operand root;
+
+ /* The type of register. */
+ enum mips_reg_operand_type reg_type;
+
+ /* If nonnull, REG_MAP[N] gives the register associated with encoding N,
+ otherwise the encoding is the same as the register number. */
+ const unsigned char *reg_map;
+};
+
+/* Describes an operand that encodes a pair of registers. */
+struct mips_reg_pair_operand
+{
+ struct mips_operand root;
+
+ /* The type of register. */
+ enum mips_reg_operand_type reg_type;
+
+ /* Encoding N represents REG1_MAP[N], REG2_MAP[N]. */
+ unsigned char *reg1_map;
+ unsigned char *reg2_map;
+};
+
+/* Describes an operand that is calculated relative to a base PC.
+ The base PC is usually the address of the following instruction,
+ but the rules for MIPS16 instructions like ADDIUPC are more complicated. */
+struct mips_pcrel_operand
+{
+ struct mips_operand root;
+
+ /* The low ALIGN_LOG2 bits of the base PC are cleared to give PC'. */
+ unsigned int align_log2 : 8;
+
+ /* The operand is shifted left SHIFT places and added to PC'.
+ The operand is signed if IS_SIGNED. */
+ unsigned int shift : 8;
+ unsigned int is_signed : 1;
+
+ /* If INCLUDE_ISA_BIT, the ISA bit of the original base PC is then
+ reinstated. This is true for jumps and branches and false for
+ PC-relative data instructions. */
+ unsigned int include_isa_bit : 1;
+
+ /* If FLIP_ISA_BIT, the ISA bit of the result is inverted.
+ This is true for JALX and false otherwise. */
+ unsigned int flip_isa_bit : 1;
+};
+
+/* Return a version of INSN in which the field specified by OPERAND
+ has value UVAL. */
+
+static inline unsigned int
+mips_insert_operand (const struct mips_operand *operand, unsigned int insn,
+ unsigned int uval)
+{
+ unsigned int mask;
+
+ mask = (1 << operand->size) - 1;
+ insn &= ~(mask << operand->lsb);
+ insn |= (uval & mask) << operand->lsb;
+ return insn;
+}
+
+/* Extract OPERAND from instruction INSN. */
+
+static inline unsigned int
+mips_extract_operand (const struct mips_operand *operand, unsigned int insn)
+{
+ return (insn >> operand->lsb) & ((1 << operand->size) - 1);
+}
+
+/* UVAL is the value encoded by OPERAND. Return it in signed form. */
+
+static inline int
+mips_signed_operand (const struct mips_operand *operand, unsigned int uval)
+{
+ unsigned int sign_bit, mask;
+
+ mask = (1 << operand->size) - 1;
+ sign_bit = 1 << (operand->size - 1);
+ return ((uval + sign_bit) & mask) - sign_bit;
+}
+
+/* Return the integer that OPERAND encodes as UVAL. */
+
+static inline int
+mips_decode_int_operand (const struct mips_int_operand *operand,
+ unsigned int uval)
+{
+ uval |= (operand->max_val - uval) & -(1 << operand->root.size);
+ uval += operand->bias;
+ uval <<= operand->shift;
+ return uval;
+}
+
+/* PC-relative operand OPERAND has value UVAL and is relative to BASE_PC.
+ Return the address that it encodes. */
+
+static inline bfd_vma
+mips_decode_pcrel_operand (const struct mips_pcrel_operand *operand,
+ bfd_vma base_pc, unsigned int uval)
+{
+ bfd_vma addr;
+
+ addr = base_pc & -(1 << operand->align_log2);
+ if (operand->is_signed)
+ addr += mips_signed_operand (&operand->root, uval) * (1 << operand->shift);
+ else
+ addr += uval << operand->shift;
+ if (operand->include_isa_bit)
+ addr |= base_pc & 1;
+ if (operand->flip_isa_bit)
+ addr ^= 1;
+ return addr;
+}
+
/* This structure holds information for a particular instruction. */
struct mips_opcode
@@ -1215,6 +1501,7 @@ enum
Many instructions are short hand for other instructions (i.e., The
jal <register> instruction is short for jalr <register>). */
+extern const struct mips_operand *decode_mips_operand (const char *);
extern const struct mips_opcode mips_builtin_opcodes[];
extern const int bfd_mips_num_builtin_opcodes;
extern struct mips_opcode *mips_opcodes;
@@ -1780,6 +2067,7 @@ extern const int bfd_mips16_num_opcodes;
" bcdefghij lmn pq st xyz"
*/
+extern const struct mips_operand *decode_micromips_operand (const char *);
extern const struct mips_opcode micromips_opcodes[];
extern const int bfd_micromips_num_opcodes;
diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog
index aa4d0f4606..17cc1c6d49 100644
--- a/opcodes/ChangeLog
+++ b/opcodes/ChangeLog
@@ -1,5 +1,31 @@
2013-07-14 Richard Sandiford <rdsandiford@googlemail.com>
+ * mips-formats.h: New file.
+ * mips-opc.c: Include mips-formats.h.
+ (reg_0_map): New static array.
+ (decode_mips_operand): New function.
+ * micromips-opc.c: Remove <stdio.h> include. Include mips-formats.h.
+ (reg_0_map, reg_28_map, reg_29_map, reg_31_map, reg_m16_map)
+ (reg_mn_map, reg_q_map, reg_h_map1, reg_h_map2, int_b_map)
+ (int_c_map): New static arrays.
+ (decode_micromips_operand): New function.
+ * mips-dis.c (micromips_to_32_reg_b_map, micromips_to_32_reg_c_map)
+ (micromips_to_32_reg_d_map, micromips_to_32_reg_e_map)
+ (micromips_to_32_reg_f_map, micromips_to_32_reg_g_map)
+ (micromips_to_32_reg_h_map1, micromips_to_32_reg_h_map2)
+ (micromips_to_32_reg_l_map, micromips_to_32_reg_m_map)
+ (micromips_to_32_reg_n_map, micromips_to_32_reg_q_map)
+ (micromips_imm_b_map, micromips_imm_c_map): Delete.
+ (print_reg): New function.
+ (mips_print_arg_state): New structure.
+ (init_print_arg_state, print_insn_arg): New functions.
+ (print_insn_args): Change interface and use mips_operand structures.
+ Delete GET_OP_S. Move GET_OP definition to...
+ (print_insn_mips): ...here. Update the call to print_insn_args.
+ (print_insn_micromips): Use print_insn_args.
+
+2013-07-14 Richard Sandiford <rdsandiford@googlemail.com>
+
* mips16-opc.c (mips16_opcodes): Use "I" for immediate operands
in macros.
diff --git a/opcodes/micromips-opc.c b/opcodes/micromips-opc.c
index de8053c5ed..8630769d3e 100644
--- a/opcodes/micromips-opc.c
+++ b/opcodes/micromips-opc.c
@@ -20,8 +20,155 @@
MA 02110-1301, USA. */
#include "sysdep.h"
-#include <stdio.h>
#include "opcode/mips.h"
+#include "mips-formats.h"
+
+static unsigned char reg_0_map[] = { 0 };
+static unsigned char reg_28_map[] = { 28 };
+static unsigned char reg_29_map[] = { 29 };
+static unsigned char reg_31_map[] = { 31 };
+static unsigned char reg_m16_map[] = { 16, 17, 2, 3, 4, 5, 6, 7 };
+static unsigned char reg_mn_map[] = { 0, 17, 2, 3, 16, 18, 19, 20 };
+static unsigned char reg_q_map[] = { 0, 17, 2, 3, 4, 5, 6, 7 };
+
+static unsigned char reg_h_map1[] = { 5, 5, 6, 4, 4, 4, 4, 4 };
+static unsigned char reg_h_map2[] = { 6, 7, 7, 21, 22, 5, 6, 7 };
+
+static int int_b_map[] = {
+ 1, 4, 8, 12, 16, 20, 24, -1
+};
+static int int_c_map[] = {
+ 128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535
+};
+
+/* Return the mips_operand structure for the operand at the beginning of P. */
+
+const struct mips_operand *
+decode_micromips_operand (const char *p)
+{
+ switch (p[0])
+ {
+ case 'm':
+ switch (p[1])
+ {
+ case 'a': MAPPED_REG (0, 0, GP, reg_28_map);
+ case 'b': MAPPED_REG (3, 23, GP, reg_m16_map);
+ case 'c': MAPPED_REG (3, 4, GP, reg_m16_map);
+ case 'd': MAPPED_REG (3, 7, GP, reg_m16_map);
+ case 'e': MAPPED_REG (3, 1, GP, reg_m16_map);
+ case 'f': MAPPED_REG (3, 3, GP, reg_m16_map);
+ case 'g': MAPPED_REG (3, 0, GP, reg_m16_map);
+ case 'h': REG_PAIR (3, 7, GP, reg_h_map);
+ case 'j': REG (5, 0, GP);
+ case 'l': MAPPED_REG (3, 4, GP, reg_m16_map);
+ case 'm': MAPPED_REG (3, 1, GP, reg_mn_map);
+ case 'n': MAPPED_REG (3, 4, GP, reg_mn_map);
+ case 'p': REG (5, 5, GP);
+ case 'q': MAPPED_REG (3, 7, GP, reg_q_map);
+ case 'r': SPECIAL (0, 0, PC);
+ case 's': MAPPED_REG (0, 0, GP, reg_29_map);
+ case 't': SPECIAL (0, 0, REPEAT_PREV_REG);
+ case 'x': SPECIAL (0, 0, REPEAT_DEST_REG);
+ case 'y': MAPPED_REG (0, 0, GP, reg_31_map);
+ case 'z': MAPPED_REG (0, 0, GP, reg_0_map);
+
+ case 'A': INT_ADJ (7, 0, 63, 2, FALSE); /* (-64 .. 63) << 2 */
+ case 'B': MAPPED_INT (3, 1, int_b_map, FALSE);
+ case 'C': MAPPED_INT (4, 0, int_c_map, TRUE);
+ case 'D': BRANCH (10, 0, 1);
+ case 'E': BRANCH (7, 0, 1);
+ case 'F': HINT (4, 0);
+ case 'G': INT_ADJ (4, 0, 14, 0, FALSE); /* (-1 .. 14) */
+ case 'H': INT_ADJ (4, 0, 15, 1, FALSE); /* (0 .. 15) << 1 */
+ case 'I': INT_ADJ (7, 0, 126, 0, FALSE); /* (-1 .. 126) */
+ case 'J': INT_ADJ (4, 0, 15, 2, FALSE); /* (0 .. 15) << 2 */
+ case 'L': INT_ADJ (4, 0, 15, 0, FALSE); /* (0 .. 15) */
+ case 'M': INT_ADJ (3, 1, 8, 0, FALSE); /* (1 .. 8) */
+ case 'N': SPECIAL (2, 4, LWM_SWM_LIST);
+ case 'O': HINT (4, 0);
+ case 'P': INT_ADJ (5, 0, 31, 2, FALSE); /* (0 .. 31) << 2 */
+ case 'Q': INT_ADJ (23, 0, 4194303, 2, FALSE);
+ /* (-4194304 .. 4194303) */
+ case 'U': INT_ADJ (5, 0, 31, 2, FALSE); /* (0 .. 31) << 2 */
+ case 'W': INT_ADJ (6, 1, 63, 2, FALSE); /* (0 .. 63) << 2 */
+ case 'X': SINT (4, 1);
+ case 'Y': SPECIAL (9, 1, ADDIUSP_INT);
+ case 'Z': UINT (0, 0); /* 0 only */
+ }
+ break;
+
+ case '+':
+ switch (p[1])
+ {
+ case 'A': BIT (5, 6, 0); /* (0 .. 31) */
+ case 'B': MSB (5, 11, 1, TRUE, 32); /* (1 .. 32), 32-bit op */
+ case 'C': MSB (5, 11, 1, FALSE, 32); /* (1 .. 32), 32-bit op */
+ case 'E': BIT (5, 6, 32); /* (32 .. 63) */
+ case 'F': MSB (5, 11, 33, TRUE, 64); /* (33 .. 64), 64-bit op */
+ case 'G': MSB (5, 11, 33, FALSE, 64); /* (33 .. 64), 64-bit op */
+ case 'H': MSB (5, 11, 1, FALSE, 64); /* (1 .. 32), 64-bit op */
+
+ case 'i': JALX (26, 0, 2);
+ case 'j': SINT (9, 0);
+ }
+ break;
+
+ case '.': SINT (10, 6);
+ case '<': BIT (5, 11, 0); /* (0 .. 31) */
+ case '>': BIT (5, 11, 32); /* (32 .. 63) */
+ case '\\': BIT (3, 21, 0); /* (0 .. 7) */
+ case '|': HINT (4, 12);
+ case '~': SINT (12, 0);
+ case '@': SINT (10, 16);
+ case '^': HINT (5, 11);
+
+ case '0': SINT (6, 16);
+ case '1': HINT (5, 16);
+ case '2': HINT (2, 14);
+ case '3': HINT (3, 13);
+ case '4': HINT (4, 12);
+ case '5': HINT (8, 13);
+ case '6': HINT (5, 16);
+ case '7': REG (2, 14, ACC);
+ case '8': HINT (6, 14);
+
+ case 'B': HINT (10, 16);
+ case 'C': HINT (23, 3);
+ case 'D': REG (5, 11, FP);
+ case 'E': REG (5, 21, COPRO);
+ case 'G': REG (5, 16, COPRO);
+ case 'K': REG (5, 16, HW);
+ case 'H': UINT (3, 11);
+ case 'M': REG (3, 13, CCC);
+ case 'N': REG (3, 18, CCC);
+ case 'R': REG (5, 6, FP);
+ case 'S': REG (5, 16, FP);
+ case 'T': REG (5, 21, FP);
+ case 'V': REG (5, 16, FP);
+
+ case 'a': JUMP (26, 0, 1);
+ case 'b': REG (5, 16, GP);
+ case 'c': HINT (10, 16);
+ case 'd': REG (5, 11, GP);
+ case 'h': HINT (5, 11);
+ case 'i': HINT (16, 0);
+ case 'j': SINT (16, 0);
+ case 'k': HINT (5, 21);
+ case 'n': SPECIAL (5, 21, LWM_SWM_LIST);
+ case 'o': SINT (16, 0);
+ case 'p': BRANCH (16, 0, 1);
+ case 'q': HINT (10, 6);
+ case 'r': REG (5, 16, GP);
+ case 's': REG (5, 16, GP);
+ case 't': REG (5, 21, GP);
+ case 'u': HINT (16, 0);
+ case 'v': REG (5, 16, GP);
+ case 'w': REG (5, 21, GP);
+ case 'y': REG (5, 6, GP);
+ case 'z': MAPPED_REG (0, 0, GP, reg_0_map);
+ }
+ return 0;
+}
#define UBD INSN_UNCOND_BRANCH_DELAY
#define CBD INSN_COND_BRANCH_DELAY
diff --git a/opcodes/mips-dis.c b/opcodes/mips-dis.c
index 7e3d123232..cd7c63387d 100644
--- a/opcodes/mips-dis.c
+++ b/opcodes/mips-dis.c
@@ -57,89 +57,6 @@ static const unsigned int mips16_to_32_reg_map[] =
16, 17, 2, 3, 4, 5, 6, 7
};
-/* The microMIPS registers with type b. */
-#define micromips_to_32_reg_b_map mips16_to_32_reg_map
-
-/* The microMIPS registers with type c. */
-#define micromips_to_32_reg_c_map mips16_to_32_reg_map
-
-/* The microMIPS registers with type d. */
-#define micromips_to_32_reg_d_map mips16_to_32_reg_map
-
-/* The microMIPS registers with type e. */
-#define micromips_to_32_reg_e_map mips16_to_32_reg_map
-
-/* The microMIPS registers with type f. */
-#define micromips_to_32_reg_f_map mips16_to_32_reg_map
-
-/* The microMIPS registers with type g. */
-#define micromips_to_32_reg_g_map mips16_to_32_reg_map
-
-/* The microMIPS registers with type h. */
-static const unsigned int micromips_to_32_reg_h_map1[] =
-{
- 5, 5, 6, 4, 4, 4, 4, 4
-};
-static const unsigned int micromips_to_32_reg_h_map2[] =
-{
- 6, 7, 7, 21, 22, 5, 6, 7
-};
-
-/* The microMIPS registers with type j: 32 registers. */
-
-/* The microMIPS registers with type l. */
-#define micromips_to_32_reg_l_map mips16_to_32_reg_map
-
-/* The microMIPS registers with type m. */
-static const unsigned int micromips_to_32_reg_m_map[] =
-{
- 0, 17, 2, 3, 16, 18, 19, 20
-};
-
-/* The microMIPS registers with type n. */
-#define micromips_to_32_reg_n_map micromips_to_32_reg_m_map
-
-/* The microMIPS registers with type p: 32 registers. */
-
-/* The microMIPS registers with type q. */
-static const unsigned int micromips_to_32_reg_q_map[] =
-{
- 0, 17, 2, 3, 4, 5, 6, 7
-};
-
-/* reg type s is $29. */
-
-/* reg type t is the same as the last register. */
-
-/* reg type y is $31. */
-
-/* reg type z is $0. */
-
-/* micromips imm B type. */
-static const int micromips_imm_b_map[8] =
-{
- 1, 4, 8, 12, 16, 20, 24, -1
-};
-
-/* micromips imm C type. */
-static const int micromips_imm_c_map[16] =
-{
- 128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535
-};
-
-/* micromips imm D type: (-512..511)<<1. */
-/* micromips imm E type: (-64..63)<<1. */
-/* micromips imm F type: (0..63). */
-/* micromips imm G type: (-1..14). */
-/* micromips imm H type: (0..15)<<1. */
-/* micromips imm I type: (-1..126). */
-/* micromips imm J type: (0..15)<<2. */
-/* micromips imm L type: (0..15). */
-/* micromips imm M type: (1..8). */
-/* micromips imm W type: (0..63)<<2. */
-/* micromips imm X type: (-8..7). */
-/* micromips imm Y type: (-258..-3, 2..257)<<2. */
-
#define mips16_reg_names(rn) mips_gpr_names[mips16_to_32_reg_map[rn]]
@@ -964,476 +881,361 @@ lookup_mips_cp0sel_name (const struct mips_cp0sel_name *names,
return &names[i];
return NULL;
}
-
-/* Print insn arguments for 32/64-bit code. */
+
+/* Print register REGNO, of type TYPE, for instruction OPCODE. */
static void
-print_insn_args (const char *d,
- int l,
- bfd_vma pc,
- struct disassemble_info *info,
- const struct mips_opcode *opp)
+print_reg (struct disassemble_info *info, const struct mips_opcode *opcode,
+ enum mips_reg_operand_type type, int regno)
{
- const fprintf_ftype infprintf = info->fprintf_func;
- unsigned int lsb, msb, msbd, cpreg;
- void *is = info->stream;
+ switch (type)
+ {
+ case OP_REG_GP:
+ info->fprintf_func (info->stream, "%s", mips_gpr_names[regno]);
+ break;
- lsb = 0;
+ case OP_REG_FP:
+ info->fprintf_func (info->stream, "%s", mips_fpr_names[regno]);
+ break;
-#define GET_OP(insn, field) \
- (((insn) >> OP_SH_##field) & OP_MASK_##field)
-#define GET_OP_S(insn, field) \
- ((GET_OP (insn, field) ^ ((OP_MASK_##field >> 1) + 1)) \
- - ((OP_MASK_##field >> 1) + 1))
- for (; *d != '\0'; d++)
- {
- switch (*d)
- {
- case ',':
- case '(':
- case ')':
- infprintf (is, "%c", *d);
- break;
+ case OP_REG_CCC:
+ if (opcode->pinfo & (FP_D | FP_S))
+ info->fprintf_func (info->stream, "$fcc%d", regno);
+ else
+ info->fprintf_func (info->stream, "$cc%d", regno);
+ break;
- case '+':
- /* Extension character; switch for second char. */
- d++;
- switch (*d)
- {
- case '\0':
- /* xgettext:c-format */
- infprintf (is,
- _("# internal error, "
- "incomplete extension sequence (+)"));
- return;
+ case OP_REG_VEC:
+ if (opcode->membership & INSN_5400)
+ info->fprintf_func (info->stream, "$f%d", regno);
+ else
+ info->fprintf_func (info->stream, "$v%d", regno);
+ break;
- case 'A':
- lsb = GET_OP (l, SHAMT);
- infprintf (is, "0x%x", lsb);
- break;
-
- case 'B':
- msb = GET_OP (l, INSMSB);
- infprintf (is, "0x%x", msb - lsb + 1);
- break;
-
- case '1':
- infprintf (is, "0x%x", GET_OP (l, UDI1));
- break;
-
- case '2':
- infprintf (is, "0x%x", GET_OP (l, UDI2));
- break;
-
- case '3':
- infprintf (is, "0x%x", GET_OP (l, UDI3));
- break;
-
- case '4':
- infprintf (is, "0x%x", GET_OP (l, UDI4));
- break;
-
- case 'C':
- case 'H':
- msbd = GET_OP (l, EXTMSBD);
- infprintf (is, "0x%x", msbd + 1);
- break;
-
- case 'E':
- lsb = GET_OP (l, SHAMT) + 32;
- infprintf (is, "0x%x", lsb);
- break;
-
- case 'F':
- msb = GET_OP (l, INSMSB) + 32;
- infprintf (is, "0x%x", msb - lsb + 1);
- break;
-
- case 'G':
- msbd = GET_OP (l, EXTMSBD) + 32;
- infprintf (is, "0x%x", msbd + 1);
- break;
-
- case 'J': /* hypcall operand */
- infprintf (is, "0x%x", GET_OP (l, CODE10));
- break;
-
- case 't': /* Coprocessor 0 reg name */
- infprintf (is, "%s", mips_cp0_names[GET_OP (l, RT)]);
- break;
-
- case 'x': /* bbit bit index */
- infprintf (is, "0x%x", GET_OP (l, BBITIND));
- break;
-
- case 'p': /* cins, cins32, exts and exts32 position */
- infprintf (is, "0x%x", GET_OP (l, CINSPOS));
- break;
-
- case 's': /* cins32 and exts32 length-minus-one */
- case 'S': /* cins and exts length-minus-one field */
- infprintf (is, "0x%x", GET_OP (l, CINSLM1));
- break;
-
- case 'Q': /* seqi/snei immediate field */
- infprintf (is, "%d", GET_OP_S (l, SEQI));
- break;
-
- case 'a': /* 8-bit signed offset in bit 6 */
- infprintf (is, "%d", GET_OP_S (l, OFFSET_A));
- break;
-
- case 'b': /* 8-bit signed offset in bit 3 */
- infprintf (is, "%d", GET_OP_S (l, OFFSET_B));
- break;
-
- case 'c': /* 9-bit signed offset in bit 6 */
- /* Left shift 4 bits to print the real offset. */
- infprintf (is, "%d", GET_OP_S (l, OFFSET_C) << 4);
- break;
-
- case 'z':
- infprintf (is, "%s", mips_gpr_names[GET_OP (l, RZ)]);
- break;
-
- case 'Z':
- infprintf (is, "%s", mips_fpr_names[GET_OP (l, FZ)]);
- break;
-
- case 'i': /* JALX destination */
- info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff)
- | (GET_OP (l, TARGET) << 2));
- /* For gdb disassembler, force odd address on jalx. */
- if (info->flavour == bfd_target_unknown_flavour)
- info->target |= 1;
- (*info->print_address_func) (info->target, info);
- break;
-
- case 'j': /* 9-bit signed offset in bit 7. */
- infprintf (is, "%d", GET_OP_S (l, EVAOFFSET));
- break;
-
- default:
- /* xgettext:c-format */
- infprintf (is,
- _("# internal error, "
- "undefined extension sequence (+%c)"),
- *d);
- return;
- }
- break;
+ case OP_REG_ACC:
+ info->fprintf_func (info->stream, "$ac%d", regno);
+ break;
- case '2':
- infprintf (is, "0x%x", GET_OP (l, BP));
- break;
+ case OP_REG_COPRO:
+ if (opcode->name[strlen (opcode->name) - 1] == '0')
+ info->fprintf_func (info->stream, "%s", mips_cp0_names[regno]);
+ else
+ info->fprintf_func (info->stream, "$%d", regno);
+ break;
- case '3':
- infprintf (is, "0x%x", GET_OP (l, SA3));
- break;
+ case OP_REG_HW:
+ info->fprintf_func (info->stream, "%s", mips_hwr_names[regno]);
+ break;
+ }
+}
+
+/* Used to track the state carried over from previous operands in
+ an instruction. */
+struct mips_print_arg_state {
+ /* The value of the last OP_INT seen. We only use this for OP_MSB,
+ where the value is known to be unsigned and small. */
+ unsigned int last_int;
+
+ /* The type and number of the last OP_REG seen. We only use this for
+ OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG. */
+ enum mips_reg_operand_type last_reg_type;
+ unsigned int last_regno;
+};
- case '4':
- infprintf (is, "0x%x", GET_OP (l, SA4));
- break;
+/* Initialize STATE for the start of an instruction. */
- case '5':
- infprintf (is, "0x%x", GET_OP (l, IMM8));
- break;
+static inline void
+init_print_arg_state (struct mips_print_arg_state *state)
+{
+ memset (state, 0, sizeof (*state));
+}
- case '6':
- infprintf (is, "0x%x", GET_OP (l, RS));
- break;
+/* Print operand OPERAND of OPCODE, using STATE to track inter-operand state.
+ UVAL is the encoding of the operand (shifted into bit 0) and BASE_PC is
+ the base address for OP_PCREL operands. */
- case '7':
- infprintf (is, "$ac%d", GET_OP (l, DSPACC));
- break;
+static void
+print_insn_arg (struct disassemble_info *info,
+ struct mips_print_arg_state *state,
+ const struct mips_opcode *opcode,
+ const struct mips_operand *operand,
+ bfd_vma base_pc,
+ unsigned int uval)
+{
+ const fprintf_ftype infprintf = info->fprintf_func;
+ void *is = info->stream;
- case '8':
- infprintf (is, "0x%x", GET_OP (l, WRDSP));
- break;
+ switch (operand->type)
+ {
+ case OP_INT:
+ {
+ const struct mips_int_operand *int_op;
- case '9':
- infprintf (is, "$ac%d", GET_OP (l, DSPACC_S));
- break;
+ int_op = (const struct mips_int_operand *) operand;
+ uval = mips_decode_int_operand (int_op, uval);
+ state->last_int = uval;
+ if (int_op->print_hex)
+ infprintf (is, "0x%x", uval);
+ else
+ infprintf (is, "%d", uval);
+ }
+ break;
- case '0': /* dsp 6-bit signed immediate in bit 20 */
- infprintf (is, "%d", GET_OP_S (l, DSPSFT));
- break;
+ case OP_MAPPED_INT:
+ {
+ const struct mips_mapped_int_operand *mint_op;
- case ':': /* dsp 7-bit signed immediate in bit 19 */
- infprintf (is, "%d", GET_OP_S (l, DSPSFT_7));
- break;
+ mint_op = (const struct mips_mapped_int_operand *) operand;
+ uval = mint_op->int_map[uval];
+ state->last_int = uval;
+ if (mint_op->print_hex)
+ infprintf (is, "0x%x", uval);
+ else
+ infprintf (is, "%d", uval);
+ }
+ break;
- case '~':
- infprintf (is, "%d", GET_OP_S (l, OFFSET12));
- break;
+ case OP_MSB:
+ {
+ const struct mips_msb_operand *msb_op;
- case '\\':
- infprintf (is, "0x%x", GET_OP (l, 3BITPOS));
- break;
+ msb_op = (const struct mips_msb_operand *) operand;
+ uval += msb_op->bias;
+ if (msb_op->add_lsb)
+ uval -= state->last_int;
+ infprintf (is, "0x%x", uval);
+ }
+ break;
- case '\'':
- infprintf (is, "0x%x", GET_OP (l, RDDSP));
- break;
+ case OP_REG:
+ {
+ const struct mips_reg_operand *reg_op;
- case '@': /* dsp 10-bit signed immediate in bit 16 */
- infprintf (is, "%d", GET_OP_S (l, IMM10));
- break;
+ reg_op = (const struct mips_reg_operand *) operand;
+ if (reg_op->reg_map)
+ uval = reg_op->reg_map[uval];
+ print_reg (info, opcode, reg_op->reg_type, uval);
- case '!':
- infprintf (is, "%d", GET_OP (l, MT_U));
- break;
+ state->last_reg_type = reg_op->reg_type;
+ state->last_regno = uval;
+ }
+ break;
- case '$':
- infprintf (is, "%d", GET_OP (l, MT_H));
- break;
+ case OP_REG_PAIR:
+ {
+ const struct mips_reg_pair_operand *pair_op;
+
+ pair_op = (const struct mips_reg_pair_operand *) operand;
+ print_reg (info, opcode, pair_op->reg_type,
+ pair_op->reg1_map[uval]);
+ infprintf (is, ",");
+ print_reg (info, opcode, pair_op->reg_type,
+ pair_op->reg2_map[uval]);
+ }
+ break;
- case '*':
- infprintf (is, "$ac%d", GET_OP (l, MTACC_T));
- break;
+ case OP_PCREL:
+ {
+ const struct mips_pcrel_operand *pcrel_op;
- case '&':
- infprintf (is, "$ac%d", GET_OP (l, MTACC_D));
- break;
+ pcrel_op = (const struct mips_pcrel_operand *) operand;
+ info->target = mips_decode_pcrel_operand (pcrel_op, base_pc, uval);
- case 'g':
- /* Coprocessor register for CTTC1, MTTC2, MTHC2, CTTC2. */
- infprintf (is, "$%d", GET_OP (l, RD));
- break;
+ /* Preserve the ISA bit for the GDB disassembler,
+ otherwise clear it. */
+ if (info->flavour != bfd_target_unknown_flavour)
+ info->target &= -2;
- case 's':
- case 'b':
- case 'r':
- case 'v':
- infprintf (is, "%s", mips_gpr_names[GET_OP (l, RS)]);
- break;
+ (*info->print_address_func) (info->target, info);
+ }
+ break;
- case 't':
- case 'w':
- infprintf (is, "%s", mips_gpr_names[GET_OP (l, RT)]);
- break;
+ case OP_PERF_REG:
+ infprintf (is, "%d", uval);
+ break;
- case 'i':
- case 'u':
- infprintf (is, "0x%x", GET_OP (l, IMMEDIATE));
- break;
+ case OP_ADDIUSP_INT:
+ {
+ int sval;
- case 'j': /* Same as i, but sign-extended. */
- case 'o':
- infprintf (is, "%d", GET_OP_S (l, DELTA));
- break;
+ sval = mips_signed_operand (operand, uval) * 4;
+ if (sval >= -8 && sval < 8)
+ sval ^= 0x400;
+ infprintf (is, "%d", sval);
+ break;
+ }
- case 'h':
- infprintf (is, "0x%x", GET_OP (l, PREFX));
- break;
+ case OP_CLO_CLZ_DEST:
+ {
+ unsigned int reg1, reg2;
+
+ reg1 = uval & 31;
+ reg2 = uval >> 5;
+ /* If one is zero use the other. */
+ if (reg1 == reg2 || reg2 == 0)
+ infprintf (is, "%s", mips_gpr_names[reg1]);
+ else if (reg1 == 0)
+ infprintf (is, "%s", mips_gpr_names[reg2]);
+ else
+ /* Bogus, result depends on processor. */
+ infprintf (is, "%s or %s", mips_gpr_names[reg1],
+ mips_gpr_names[reg2]);
+ }
+ break;
- case 'k':
- infprintf (is, "0x%x", GET_OP (l, CACHE));
- break;
+ case OP_LWM_SWM_LIST:
+ if (operand->size == 2)
+ {
+ if (uval == 0)
+ infprintf (is, "%s,%s",
+ mips_gpr_names[16],
+ mips_gpr_names[31]);
+ else
+ infprintf (is, "%s-%s,%s",
+ mips_gpr_names[16],
+ mips_gpr_names[16 + uval],
+ mips_gpr_names[31]);
+ }
+ else
+ {
+ int s_reg_encode;
- case 'a':
- info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff)
- | (GET_OP (l, TARGET) << 2));
- (*info->print_address_func) (info->target, info);
- break;
+ s_reg_encode = uval & 0xf;
+ if (s_reg_encode != 0)
+ {
+ if (s_reg_encode == 1)
+ infprintf (is, "%s", mips_gpr_names[16]);
+ else if (s_reg_encode < 9)
+ infprintf (is, "%s-%s",
+ mips_gpr_names[16],
+ mips_gpr_names[15 + s_reg_encode]);
+ else if (s_reg_encode == 9)
+ infprintf (is, "%s-%s,%s",
+ mips_gpr_names[16],
+ mips_gpr_names[23],
+ mips_gpr_names[30]);
+ else
+ infprintf (is, "UNKNOWN");
+ }
- case 'p':
- /* Sign extend the displacement. */
- info->target = (GET_OP_S (l, DELTA) << 2) + pc + INSNLEN;
- (*info->print_address_func) (info->target, info);
- break;
+ if (uval & 0x10) /* For ra. */
+ {
+ if (s_reg_encode == 0)
+ infprintf (is, "%s", mips_gpr_names[31]);
+ else
+ infprintf (is, ",%s", mips_gpr_names[31]);
+ }
+ }
+ break;
- case 'd':
- infprintf (is, "%s", mips_gpr_names[GET_OP (l, RD)]);
- break;
+ case OP_MDMX_IMM_REG:
+ {
+ unsigned int vsel;
- case 'U':
+ vsel = uval >> 5;
+ uval &= 31;
+ if ((vsel & 0x10) == 0)
{
- /* First check for both rd and rt being equal. */
- unsigned int reg;
-
- reg = GET_OP (l, RD);
- if (reg == GET_OP (l, RT))
- infprintf (is, "%s", mips_gpr_names[reg]);
- else
- {
- /* If one is zero use the other. */
- if (reg == 0)
- infprintf (is, "%s", mips_gpr_names[GET_OP (l, RT)]);
- else if (GET_OP (l, RT) == 0)
- infprintf (is, "%s", mips_gpr_names[reg]);
- else /* Bogus, result depends on processor. */
- infprintf (is, "%s or %s",
- mips_gpr_names[reg],
- mips_gpr_names[GET_OP (l, RT)]);
- }
+ int fmt;
+
+ vsel &= 0x0f;
+ for (fmt = 0; fmt < 3; fmt++, vsel >>= 1)
+ if ((vsel & 1) == 0)
+ break;
+ print_reg (info, opcode, OP_REG_VEC, uval);
+ infprintf (is, "[%d]", vsel >> 1);
}
- break;
-
- case 'z':
- infprintf (is, "%s", mips_gpr_names[0]);
- break;
-
- case '<':
- case '1':
- infprintf (is, "0x%x", GET_OP (l, SHAMT));
- break;
-
- case 'c':
- infprintf (is, "0x%x", GET_OP (l, CODE));
- break;
-
- case 'q':
- infprintf (is, "0x%x", GET_OP (l, CODE2));
- break;
-
- case 'C':
- infprintf (is, "0x%x", GET_OP (l, COPZ));
- break;
+ else if ((vsel & 0x08) == 0)
+ print_reg (info, opcode, OP_REG_VEC, uval);
+ else
+ infprintf (is, "0x%x", uval);
+ }
+ break;
- case 'B':
- infprintf (is, "0x%x", GET_OP (l, CODE20));
- break;
+ case OP_REPEAT_PREV_REG:
+ print_reg (info, opcode, state->last_reg_type, state->last_regno);
+ break;
- case 'J':
- infprintf (is, "0x%x", GET_OP (l, CODE19));
- break;
+ case OP_REPEAT_DEST_REG:
+ /* Should always match OP_REPEAT_PREV_REG first. */
+ abort ();
- case 'S':
- case 'V':
- infprintf (is, "%s", mips_fpr_names[GET_OP (l, FS)]);
- break;
+ case OP_PC:
+ infprintf (is, "$pc");
+ break;
+ }
+}
- case 'T':
- case 'W':
- infprintf (is, "%s", mips_fpr_names[GET_OP (l, FT)]);
- break;
+/* Print the arguments for INSN, which is described by OPCODE.
+ Use DECODE_OPERAND to get the encoding of each operand. Use BASE_PC
+ as the base of OP_PCREL operands. */
- case 'D':
- infprintf (is, "%s", mips_fpr_names[GET_OP (l, FD)]);
- break;
+static void
+print_insn_args (struct disassemble_info *info,
+ const struct mips_opcode *opcode,
+ const struct mips_operand *(*decode_operand) (const char *),
+ unsigned int insn, bfd_vma base_pc)
+{
+ const fprintf_ftype infprintf = info->fprintf_func;
+ void *is = info->stream;
+ struct mips_print_arg_state state;
+ const struct mips_operand *operand;
+ const char *s;
- case 'R':
- infprintf (is, "%s", mips_fpr_names[GET_OP (l, FR)]);
+ init_print_arg_state (&state);
+ for (s = opcode->args; *s; ++s)
+ {
+ switch (*s)
+ {
+ case ',':
+ case '(':
+ case ')':
+ infprintf (is, "%c", *s);
break;
- case 'E':
- cpreg = GET_OP (l, RT);
- goto copro;
-
- case 'G':
- cpreg = GET_OP (l, RD);
- copro:
- /* Coprocessor register for mtcN instructions, et al. Note
- that FPU (cp1) instructions disassemble this field using
- 'S' format. Therefore, we only need to worry about cp0,
- cp2, and cp3. */
- if (opp->name[strlen (opp->name) - 1] == '0')
+ default:
+ operand = decode_operand (s);
+ if (!operand)
{
- if (d[1] == ',' && d[2] == 'H')
- {
- const struct mips_cp0sel_name *n;
- unsigned int sel;
-
- sel = GET_OP (l, SEL);
-
- /* CP0 register including 'sel' code for mtcN (et al.), to be
- printed textually if known. If not known, print both
- CP0 register name and sel numerically since CP0 register
- with sel 0 may have a name unrelated to register being
- printed. */
- n = lookup_mips_cp0sel_name (mips_cp0sel_names,
- mips_cp0sel_names_len,
- cpreg, sel);
- if (n != NULL)
- infprintf (is, "%s", n->name);
- else
- infprintf (is, "$%d,%d", cpreg, sel);
- d += 2;
- }
+ /* xgettext:c-format */
+ infprintf (is,
+ _("# internal error, undefined operand in `%s %s'"),
+ opcode->name, opcode->args);
+ return;
+ }
+ if (operand->type == OP_REG
+ && s[1] == ','
+ && s[2] == 'H'
+ && opcode->name[strlen (opcode->name) - 1] == '0')
+ {
+ /* Coprocessor register 0 with sel field (MT ASE). */
+ const struct mips_cp0sel_name *n;
+ unsigned int reg, sel;
+
+ reg = mips_extract_operand (operand, insn);
+ s += 2;
+ operand = decode_operand (s);
+ sel = mips_extract_operand (operand, insn);
+
+ /* CP0 register including 'sel' code for mftc0, to be
+ printed textually if known. If not known, print both
+ CP0 register name and sel numerically since CP0 register
+ with sel 0 may have a name unrelated to register being
+ printed. */
+ n = lookup_mips_cp0sel_name (mips_cp0sel_names,
+ mips_cp0sel_names_len,
+ reg, sel);
+ if (n != NULL)
+ infprintf (is, "%s", n->name);
else
- infprintf (is, "%s", mips_cp0_names[cpreg]);
+ infprintf (is, "$%d,%d", reg, sel);
}
else
- infprintf (is, "$%d", cpreg);
- break;
-
- case 'K':
- infprintf (is, "%s", mips_hwr_names[GET_OP (l, RD)]);
- break;
-
- case 'N':
- infprintf (is,
- (opp->pinfo & (FP_D | FP_S)) != 0 ? "$fcc%d" : "$cc%d",
- GET_OP (l, BCC));
- break;
-
- case 'M':
- infprintf (is, "$fcc%d", GET_OP (l, CCC));
- break;
-
- case 'P':
- infprintf (is, "%d", GET_OP (l, PERFREG));
- break;
-
- case 'e':
- infprintf (is, "%d", GET_OP (l, VECBYTE));
+ print_insn_arg (info, &state, opcode, operand, base_pc,
+ mips_extract_operand (operand, insn));
+ if (*s == 'm' || *s == '+')
+ ++s;
break;
-
- case '%':
- infprintf (is, "%d", GET_OP (l, VECALIGN));
- break;
-
- case 'H':
- infprintf (is, "%d", GET_OP (l, SEL));
- break;
-
- case 'O':
- infprintf (is, "%d", GET_OP (l, ALN));
- break;
-
- case 'Q':
- {
- unsigned int vsel = GET_OP (l, VSEL);
- char prefix;
-
- prefix = opp->membership & INSN_5400 ? 'f' : 'v';
- if ((vsel & 0x10) == 0)
- {
- int fmt;
-
- vsel &= 0x0f;
- for (fmt = 0; fmt < 3; fmt++, vsel >>= 1)
- if ((vsel & 1) == 0)
- break;
- infprintf (is, "$%c%d[%d]", prefix, GET_OP (l, FT), vsel >> 1);
- }
- else if ((vsel & 0x08) == 0)
- {
- infprintf (is, "$%c%d", prefix, GET_OP (l, FT));
- }
- else
- {
- infprintf (is, "0x%x", GET_OP (l, FT));
- }
- }
- break;
-
- case 'X':
- infprintf (is, "$v%d", GET_OP (l, FD));
- break;
-
- case 'Y':
- infprintf (is, "$v%d", GET_OP (l, FS));
- break;
-
- case 'Z':
- infprintf (is, "$v%d", GET_OP (l, FT));
- break;
-
- default:
- /* xgettext:c-format */
- infprintf (is, _("# internal error, undefined modifier (%c)"), *d);
- return;
}
}
}
@@ -1448,6 +1250,8 @@ print_insn_mips (bfd_vma memaddr,
int word,
struct disassemble_info *info)
{
+#define GET_OP(insn, field) \
+ (((insn) >> OP_SH_##field) & OP_MASK_##field)
static const struct mips_opcode *mips_hash[OP_MASK_OP + 1];
const fprintf_ftype infprintf = info->fprintf_func;
const struct mips_opcode *op;
@@ -1495,8 +1299,6 @@ print_insn_mips (bfd_vma memaddr,
&& !(no_aliases && (op->pinfo2 & INSN2_ALIAS))
&& (word & op->mask) == op->match)
{
- const char *d;
-
/* We always allow to disassemble the jalx instruction. */
if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor)
&& strcmp (op->name, "jalx"))
@@ -1527,18 +1329,17 @@ print_insn_mips (bfd_vma memaddr,
infprintf (is, "%s", op->name);
- d = op->args;
- if (d != NULL && *d != '\0')
+ if (op->args[0])
{
infprintf (is, "\t");
- print_insn_args (d, word, memaddr, info, op);
+ print_insn_args (info, op, decode_mips_operand, word,
+ memaddr + 4);
}
return INSNLEN;
}
}
}
-#undef GET_OP_S
#undef GET_OP
/* Handle undefined instructions. */
@@ -2235,19 +2036,12 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
{
const fprintf_ftype infprintf = info->fprintf_func;
const struct mips_opcode *op, *opend;
- unsigned int lsb, msbd, msb;
void *is = info->stream;
- unsigned int regno;
bfd_byte buffer[2];
- int lastregno = 0;
- int higher;
- int length;
+ unsigned int higher;
+ unsigned int length;
int status;
- int delta;
- int immed;
- int insn;
-
- lsb = 0;
+ unsigned int insn;
info->bytes_per_chunk = 2;
info->display_endian = info->endian;
@@ -2331,11 +2125,6 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
/* FIXME: Should probably use a hash table on the major opcode here. */
-#define GET_OP(insn, field) \
- (((insn) >> MICROMIPSOP_SH_##field) & MICROMIPSOP_MASK_##field)
-#define GET_OP_S(insn, field) \
- ((GET_OP (insn, field) ^ ((MICROMIPSOP_MASK_##field >> 1) + 1)) \
- - ((MICROMIPSOP_MASK_##field >> 1) + 1))
opend = micromips_opcodes + bfd_micromips_num_opcodes;
for (op = micromips_opcodes; op < opend; op++)
{
@@ -2345,569 +2134,13 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
&& ((length == 2 && (op->mask & 0xffff0000) == 0)
|| (length == 4 && (op->mask & 0xffff0000) != 0)))
{
- const char *s;
-
infprintf (is, "%s", op->name);
- if (op->args[0] != '\0')
- infprintf (is, "\t");
- for (s = op->args; *s != '\0'; s++)
+ if (op->args[0])
{
- switch (*s)
- {
- case ',':
- case '(':
- case ')':
- infprintf (is, "%c", *s);
- break;
-
- case '.':
- infprintf (is, "%d", GET_OP_S (insn, OFFSET10));
- break;
-
- case '1':
- infprintf (is, "0x%x", GET_OP (insn, STYPE));
- break;
-
- case '2':
- infprintf (is, "0x%x", GET_OP (insn, BP));
- break;
-
- case '3':
- infprintf (is, "0x%x", GET_OP (insn, SA3));
- break;
-
- case '4':
- infprintf (is, "0x%x", GET_OP (insn, SA4));
- break;
-
- case '5':
- infprintf (is, "0x%x", GET_OP (insn, IMM8));
- break;
-
- case '6':
- infprintf (is, "0x%x", GET_OP (insn, RS));
- break;
-
- case '7':
- infprintf (is, "$ac%d", GET_OP (insn, DSPACC));
- break;
-
- case '8':
- infprintf (is, "0x%x", GET_OP (insn, WRDSP));
- break;
-
- case '0': /* DSP 6-bit signed immediate in bit 16. */
- delta = (GET_OP (insn, DSPSFT) ^ 0x20) - 0x20;
- infprintf (is, "%d", delta);
- break;
-
- case '<':
- infprintf (is, "0x%x", GET_OP (insn, SHAMT));
- break;
-
- case '\\':
- infprintf (is, "0x%x", GET_OP (insn, 3BITPOS));
- break;
-
- case '^':
- infprintf (is, "0x%x", GET_OP (insn, RD));
- break;
-
- case '|':
- infprintf (is, "0x%x", GET_OP (insn, TRAP));
- break;
-
- case '~':
- infprintf (is, "%d", GET_OP_S (insn, OFFSET12));
- break;
-
- case 'a':
- info->target = (((memaddr + 4) & ~(bfd_vma) 0x07ffffff)
- | (GET_OP (insn, TARGET) << 1));
- /* For gdb disassembler, maintain odd address. */
- if (info->flavour == bfd_target_unknown_flavour)
- info->target |= 1;
- (*info->print_address_func) (info->target, info);
- break;
-
- case 'b':
- case 'r':
- case 's':
- case 'v':
- infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RS)]);
- break;
-
- case 'c':
- infprintf (is, "0x%x", GET_OP (insn, CODE));
- break;
-
- case 'd':
- infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RD)]);
- break;
-
- case 'h':
- infprintf (is, "0x%x", GET_OP (insn, PREFX));
- break;
-
- case 'i':
- case 'u':
- infprintf (is, "0x%x", GET_OP (insn, IMMEDIATE));
- break;
-
- case 'j': /* Same as i, but sign-extended. */
- case 'o':
- infprintf (is, "%d", GET_OP_S (insn, DELTA));
- break;
-
- case 'k':
- infprintf (is, "0x%x", GET_OP (insn, CACHE));
- break;
-
- case 'n':
- {
- int s_reg_encode;
-
- immed = GET_OP (insn, RT);
- s_reg_encode = immed & 0xf;
- if (s_reg_encode != 0)
- {
- if (s_reg_encode == 1)
- infprintf (is, "%s", mips_gpr_names[16]);
- else if (s_reg_encode < 9)
- infprintf (is, "%s-%s",
- mips_gpr_names[16],
- mips_gpr_names[15 + s_reg_encode]);
- else if (s_reg_encode == 9)
- infprintf (is, "%s-%s,%s",
- mips_gpr_names[16],
- mips_gpr_names[23],
- mips_gpr_names[30]);
- else
- infprintf (is, "UNKNOWN");
- }
-
- if (immed & 0x10) /* For ra. */
- {
- if (s_reg_encode == 0)
- infprintf (is, "%s", mips_gpr_names[31]);
- else
- infprintf (is, ",%s", mips_gpr_names[31]);
- }
- break;
- }
-
- case 'p':
- /* Sign-extend the displacement. */
- delta = GET_OP_S (insn, DELTA);
- info->target = (delta << 1) + memaddr + length;
- (*info->print_address_func) (info->target, info);
- break;
-
- case 'q':
- infprintf (is, "0x%x", GET_OP (insn, CODE2));
- break;
-
- case 't':
- case 'w':
- infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RT)]);
- break;
-
- case 'y':
- infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RS3)]);
- break;
-
- case 'z':
- infprintf (is, "%s", mips_gpr_names[0]);
- break;
-
- case '@': /* DSP 10-bit signed immediate in bit 16. */
- delta = (GET_OP (insn, IMM10) ^ 0x200) - 0x200;
- infprintf (is, "%d", delta);
- break;
-
- case 'B':
- infprintf (is, "0x%x", GET_OP (insn, CODE10));
- break;
-
- case 'C':
- infprintf (is, "0x%x", GET_OP (insn, COPZ));
- break;
-
- case 'D':
- infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FD)]);
- break;
-
- case 'E':
- /* Coprocessor register for lwcN instructions, et al.
-
- Note that there is no load/store cp0 instructions, and
- that FPU (cp1) instructions disassemble this field using
- 'T' format. Therefore, until we gain understanding of
- cp2 register names, we can simply print the register
- numbers. */
- infprintf (is, "$%d", GET_OP (insn, RT));
- break;
-
- case 'G':
- /* Coprocessor register for mtcN instructions, et al. Note
- that FPU (cp1) instructions disassemble this field using
- 'S' format. Therefore, we only need to worry about cp0,
- cp2, and cp3. */
- if (op->name[strlen (op->name) - 1] == '0')
- {
- if (s[1] == ',' && s[2] == 'H')
- {
- const struct mips_cp0sel_name *n;
- unsigned int cp0reg, sel;
-
- cp0reg = GET_OP (insn, RS);
- sel = GET_OP (insn, SEL);
-
- /* CP0 register including 'sel' code for mtcN
- (et al.), to be printed textually if known.
- If not known, print both CP0 register name and
- sel numerically since CP0 register with sel 0 may
- have a name unrelated to register being
- printed. */
- n = lookup_mips_cp0sel_name (mips_cp0sel_names,
- mips_cp0sel_names_len,
- cp0reg, sel);
- if (n != NULL)
- infprintf (is, "%s", n->name);
- else
- infprintf (is, "$%d,%d", cp0reg, sel);
- s += 2;
- }
- else
- infprintf (is, "%s", mips_cp0_names[GET_OP (insn, RS)]);
- }
- else
- infprintf (is, "$%d", GET_OP (insn, RS));
- break;
-
- case 'H':
- infprintf (is, "%d", GET_OP (insn, SEL));
- break;
-
- case 'K':
- infprintf (is, "%s", mips_hwr_names[GET_OP (insn, RS)]);
- break;
-
- case 'M':
- infprintf (is, "$fcc%d", GET_OP (insn, CCC));
- break;
-
- case 'N':
- infprintf (is,
- (op->pinfo & (FP_D | FP_S)) != 0
- ? "$fcc%d" : "$cc%d",
- GET_OP (insn, BCC));
- break;
-
- case 'R':
- infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FR)]);
- break;
-
- case 'S':
- case 'V':
- infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FS)]);
- break;
-
- case 'T':
- infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FT)]);
- break;
-
- case '+':
- /* Extension character; switch for second char. */
- s++;
- switch (*s)
- {
- case 'A':
- lsb = GET_OP (insn, EXTLSB);
- infprintf (is, "0x%x", lsb);
- break;
-
- case 'B':
- msb = GET_OP (insn, INSMSB);
- infprintf (is, "0x%x", msb - lsb + 1);
- break;
-
- case 'C':
- case 'H':
- msbd = GET_OP (insn, EXTMSBD);
- infprintf (is, "0x%x", msbd + 1);
- break;
-
- case 'E':
- lsb = GET_OP (insn, EXTLSB) + 32;
- infprintf (is, "0x%x", lsb);
- break;
-
- case 'F':
- msb = GET_OP (insn, INSMSB) + 32;
- infprintf (is, "0x%x", msb - lsb + 1);
- break;
-
- case 'G':
- msbd = GET_OP (insn, EXTMSBD) + 32;
- infprintf (is, "0x%x", msbd + 1);
- break;
-
- case 'i':
- info->target = (((memaddr + 4) & ~(bfd_vma) 0x0fffffff)
- | (GET_OP (insn, TARGET) << 2));
- (*info->print_address_func) (info->target, info);
- break;
-
- case 'j': /* 9-bit signed offset in bit 0. */
- delta = GET_OP_S (insn, EVAOFFSET);
- infprintf (is, "%d", delta);
- break;
-
- default:
- /* xgettext:c-format */
- infprintf (is,
- _("# internal disassembler error, "
- "unrecognized modifier (+%c)"),
- *s);
- abort ();
- }
- break;
-
- case 'm':
- /* Extension character; switch for second char. */
- s++;
- switch (*s)
- {
- case 'a': /* global pointer. */
- infprintf (is, "%s", mips_gpr_names[28]);
- break;
-
- case 'b':
- regno = micromips_to_32_reg_b_map[GET_OP (insn, MB)];
- infprintf (is, "%s", mips_gpr_names[regno]);
- break;
-
- case 'c':
- regno = micromips_to_32_reg_c_map[GET_OP (insn, MC)];
- infprintf (is, "%s", mips_gpr_names[regno]);
- break;
-
- case 'd':
- regno = micromips_to_32_reg_d_map[GET_OP (insn, MD)];
- infprintf (is, "%s", mips_gpr_names[regno]);
- break;
-
- case 'e':
- regno = micromips_to_32_reg_e_map[GET_OP (insn, ME)];
- infprintf (is, "%s", mips_gpr_names[regno]);
- break;
-
- case 'f':
- /* Save lastregno for "mt" to print out later. */
- lastregno = micromips_to_32_reg_f_map[GET_OP (insn, MF)];
- infprintf (is, "%s", mips_gpr_names[lastregno]);
- break;
-
- case 'g':
- regno = micromips_to_32_reg_g_map[GET_OP (insn, MG)];
- infprintf (is, "%s", mips_gpr_names[regno]);
- break;
-
- case 'h':
- regno = micromips_to_32_reg_h_map1[GET_OP (insn, MH)];
- infprintf (is, "%s", mips_gpr_names[regno]);
- regno = micromips_to_32_reg_h_map2[GET_OP (insn, MH)];
- infprintf (is, ",%s", mips_gpr_names[regno]);
- break;
-
- case 'j':
- infprintf (is, "%s", mips_gpr_names[GET_OP (insn, MJ)]);
- break;
-
- case 'l':
- regno = micromips_to_32_reg_l_map[GET_OP (insn, ML)];
- infprintf (is, "%s", mips_gpr_names[regno]);
- break;
-
- case 'm':
- regno = micromips_to_32_reg_m_map[GET_OP (insn, MM)];
- infprintf (is, "%s", mips_gpr_names[regno]);
- break;
-
- case 'n':
- regno = micromips_to_32_reg_n_map[GET_OP (insn, MN)];
- infprintf (is, "%s", mips_gpr_names[regno]);
- break;
-
- case 'p':
- /* Save lastregno for "mt" to print out later. */
- lastregno = GET_OP (insn, MP);
- infprintf (is, "%s", mips_gpr_names[lastregno]);
- break;
-
- case 'q':
- regno = micromips_to_32_reg_q_map[GET_OP (insn, MQ)];
- infprintf (is, "%s", mips_gpr_names[regno]);
- break;
-
- case 'r': /* program counter. */
- infprintf (is, "$pc");
- break;
-
- case 's': /* stack pointer. */
- lastregno = 29;
- infprintf (is, "%s", mips_gpr_names[29]);
- break;
-
- case 't':
- infprintf (is, "%s", mips_gpr_names[lastregno]);
- break;
-
- case 'z': /* $0. */
- infprintf (is, "%s", mips_gpr_names[0]);
- break;
-
- case 'A':
- /* Sign-extend the immediate. */
- immed = GET_OP_S (insn, IMMA) << 2;
- infprintf (is, "%d", immed);
- break;
-
- case 'B':
- immed = micromips_imm_b_map[GET_OP (insn, IMMB)];
- infprintf (is, "%d", immed);
- break;
-
- case 'C':
- immed = micromips_imm_c_map[GET_OP (insn, IMMC)];
- infprintf (is, "0x%x", immed);
- break;
-
- case 'D':
- /* Sign-extend the displacement. */
- delta = GET_OP_S (insn, IMMD);
- info->target = (delta << 1) + memaddr + length;
- (*info->print_address_func) (info->target, info);
- break;
-
- case 'E':
- /* Sign-extend the displacement. */
- delta = GET_OP_S (insn, IMME);
- info->target = (delta << 1) + memaddr + length;
- (*info->print_address_func) (info->target, info);
- break;
-
- case 'F':
- immed = GET_OP (insn, IMMF);
- infprintf (is, "0x%x", immed);
- break;
-
- case 'G':
- immed = (insn >> MICROMIPSOP_SH_IMMG) + 1;
- immed = (immed & MICROMIPSOP_MASK_IMMG) - 1;
- infprintf (is, "%d", immed);
- break;
-
- case 'H':
- immed = GET_OP (insn, IMMH) << 1;
- infprintf (is, "%d", immed);
- break;
-
- case 'I':
- immed = (insn >> MICROMIPSOP_SH_IMMI) + 1;
- immed = (immed & MICROMIPSOP_MASK_IMMI) - 1;
- infprintf (is, "%d", immed);
- break;
-
- case 'J':
- immed = GET_OP (insn, IMMJ) << 2;
- infprintf (is, "%d", immed);
- break;
-
- case 'L':
- immed = GET_OP (insn, IMML);
- infprintf (is, "%d", immed);
- break;
-
- case 'M':
- immed = (insn >> MICROMIPSOP_SH_IMMM) - 1;
- immed = (immed & MICROMIPSOP_MASK_IMMM) + 1;
- infprintf (is, "%d", immed);
- break;
-
- case 'N':
- immed = GET_OP (insn, IMMN);
- if (immed == 0)
- infprintf (is, "%s,%s",
- mips_gpr_names[16],
- mips_gpr_names[31]);
- else
- infprintf (is, "%s-%s,%s",
- mips_gpr_names[16],
- mips_gpr_names[16 + immed],
- mips_gpr_names[31]);
- break;
-
- case 'O':
- immed = GET_OP (insn, IMMO);
- infprintf (is, "0x%x", immed);
- break;
-
- case 'P':
- immed = GET_OP (insn, IMMP) << 2;
- infprintf (is, "%d", immed);
- break;
-
- case 'Q':
- /* Sign-extend the immediate. */
- immed = GET_OP_S (insn, IMMQ) << 2;
- infprintf (is, "%d", immed);
- break;
-
- case 'U':
- immed = GET_OP (insn, IMMU) << 2;
- infprintf (is, "%d", immed);
- break;
-
- case 'W':
- immed = GET_OP (insn, IMMW) << 2;
- infprintf (is, "%d", immed);
- break;
-
- case 'X':
- /* Sign-extend the immediate. */
- immed = GET_OP_S (insn, IMMX);
- infprintf (is, "%d", immed);
- break;
-
- case 'Y':
- /* Sign-extend the immediate. */
- immed = GET_OP_S (insn, IMMY) << 2;
- if ((unsigned int) (immed + 8) < 16)
- immed ^= 0x400;
- infprintf (is, "%d", immed);
- break;
-
- default:
- /* xgettext:c-format */
- infprintf (is,
- _("# internal disassembler error, "
- "unrecognized modifier (m%c)"),
- *s);
- abort ();
- }
- break;
-
- default:
- /* xgettext:c-format */
- infprintf (is,
- _("# internal disassembler error, "
- "unrecognized modifier (%c)"),
- *s);
- abort ();
- }
+ infprintf (is, "\t");
+ print_insn_args (info, op, decode_micromips_operand, insn,
+ memaddr + length + 1);
}
/* Figure out instruction type and branch delay information. */
@@ -2937,8 +2170,6 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
return length;
}
}
-#undef GET_OP_S
-#undef GET_OP
infprintf (is, "0x%x", insn);
info->insn_type = dis_noninsn;
diff --git a/opcodes/mips-formats.h b/opcodes/mips-formats.h
new file mode 100644
index 0000000000..7deba5e0eb
--- /dev/null
+++ b/opcodes/mips-formats.h
@@ -0,0 +1,113 @@
+/* mips-formats.h
+ Copyright 2013 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ It is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING3. If not,
+ see <http://www.gnu.org/licenses/>. */
+
+/* For ARRAY_SIZE. */
+#include "libiberty.h"
+
+#define INT_ADJ(SIZE, LSB, MAX_VAL, SHIFT, PRINT_HEX) \
+ { \
+ static const struct mips_int_operand op = { \
+ { OP_INT, SIZE, LSB }, MAX_VAL, 0, SHIFT, PRINT_HEX \
+ }; \
+ return &op.root; \
+ }
+
+#define UINT(SIZE, LSB) \
+ INT_ADJ(SIZE, LSB, (1 << (SIZE)) - 1, 0, FALSE)
+
+#define SINT(SIZE, LSB) \
+ INT_ADJ(SIZE, LSB, (1 << ((SIZE) - 1)) - 1, 0, FALSE)
+
+#define HINT(SIZE, LSB) \
+ INT_ADJ(SIZE, LSB, (1 << (SIZE)) - 1, 0, TRUE)
+
+#define BIT(SIZE, LSB, BIAS) \
+ { \
+ static const struct mips_int_operand op = { \
+ { OP_INT, SIZE, LSB }, (1 << (SIZE)) - 1, BIAS, 0, TRUE \
+ }; \
+ return &op.root; \
+ }
+
+#define MAPPED_INT(SIZE, LSB, MAP, PRINT_HEX) \
+ { \
+ typedef char static_assert[(1 << (SIZE)) == ARRAY_SIZE (MAP)]; \
+ static const struct mips_mapped_int_operand op = { \
+ { OP_MAPPED_INT, SIZE, LSB }, MAP, PRINT_HEX \
+ }; \
+ return &op.root; \
+ }
+
+#define MSB(SIZE, LSB, BIAS, ADD_LSB, OPSIZE) \
+ { \
+ static const struct mips_msb_operand op = { \
+ { OP_MSB, SIZE, LSB }, BIAS, ADD_LSB, OPSIZE \
+ }; \
+ return &op.root; \
+ }
+
+#define REG(SIZE, LSB, BANK) \
+ { \
+ static const struct mips_reg_operand op = { \
+ { OP_REG, SIZE, LSB }, OP_REG_##BANK, 0 \
+ }; \
+ return &op.root; \
+ }
+
+#define MAPPED_REG(SIZE, LSB, BANK, MAP) \
+ { \
+ typedef char static_assert[(1 << (SIZE)) == ARRAY_SIZE (MAP)]; \
+ static const struct mips_reg_operand op = { \
+ { OP_REG, SIZE, LSB }, OP_REG_##BANK, MAP \
+ }; \
+ return &op.root; \
+ }
+
+#define REG_PAIR(SIZE, LSB, BANK, MAP) \
+ { \
+ typedef char static_assert1[(1 << (SIZE)) == ARRAY_SIZE (MAP##1)]; \
+ typedef char static_assert2[(1 << (SIZE)) == ARRAY_SIZE (MAP##2)]; \
+ static const struct mips_reg_pair_operand op = { \
+ { OP_REG_PAIR, SIZE, LSB }, OP_REG_##BANK, MAP##1, MAP##2 \
+ }; \
+ return &op.root; \
+ }
+
+#define PCREL(SIZE, LSB, ALIGN_LOG2, SHIFT, IS_SIGNED, INCLUDE_ISA_BIT, \
+ FLIP_ISA_BIT) \
+ { \
+ static const struct mips_pcrel_operand op = { \
+ { OP_PCREL, SIZE, LSB }, ALIGN_LOG2, SHIFT, IS_SIGNED, \
+ INCLUDE_ISA_BIT, FLIP_ISA_BIT \
+ }; \
+ return &op.root; \
+ }
+
+#define JUMP(SIZE, LSB, SHIFT) \
+ PCREL (SIZE, LSB, SIZE + SHIFT, SHIFT, FALSE, TRUE, FALSE)
+
+#define JALX(SIZE, LSB, SHIFT) \
+ PCREL (SIZE, LSB, SIZE + SHIFT, SHIFT, FALSE, TRUE, TRUE)
+
+#define BRANCH(SIZE, LSB, SHIFT) \
+ PCREL (SIZE, LSB, 0, SHIFT, TRUE, TRUE, FALSE)
+
+#define SPECIAL(SIZE, LSB, TYPE) \
+ { \
+ static const struct mips_operand op = { OP_##TYPE, SIZE, LSB }; \
+ return &op; \
+ }
diff --git a/opcodes/mips-opc.c b/opcodes/mips-opc.c
index e6833f7438..702016b2c3 100644
--- a/opcodes/mips-opc.c
+++ b/opcodes/mips-opc.c
@@ -28,6 +28,123 @@
#include "sysdep.h"
#include <stdio.h>
#include "opcode/mips.h"
+#include "mips-formats.h"
+
+static unsigned char reg_0_map[] = { 0 };
+
+/* Return the mips_operand structure for the operand at the beginning of P. */
+
+const struct mips_operand *
+decode_mips_operand (const char *p)
+{
+ switch (p[0])
+ {
+ case '+':
+ switch (p[1])
+ {
+ case '1': HINT (5, 6);
+ case '2': HINT (10, 6);
+ case '3': HINT (15, 6);
+ case '4': HINT (20, 6);
+
+ case 'A': BIT (5, 6, 0); /* (0 .. 31) */
+ case 'B': MSB (5, 11, 1, TRUE, 32); /* (1 .. 32), 32-bit op */
+ case 'C': MSB (5, 11, 1, FALSE, 32); /* (1 .. 32), 32-bit op */
+ case 'E': BIT (5, 6, 32); /* (32 .. 63) */
+ case 'F': MSB (5, 11, 33, TRUE, 64); /* (33 .. 64), 64-bit op */
+ case 'G': MSB (5, 11, 33, FALSE, 64); /* (33 .. 64), 64-bit op */
+ case 'H': MSB (5, 11, 1, FALSE, 64); /* (1 .. 32), 64-bit op */
+ case 'J': HINT (10, 11);
+ case 'P': BIT (5, 6, 32); /* (32 .. 63) */
+ case 'Q': SINT (10, 6);
+ case 'S': MSB (5, 11, 0, FALSE, 63); /* (0 .. 31), 64-bit op */
+ case 'X': BIT (5, 16, 32); /* (32 .. 63) */
+ case 'Z': REG (5, 0, FP);
+
+ case 'a': SINT (8, 6);
+ case 'b': SINT (8, 3);
+ case 'c': INT_ADJ (9, 6, 255, 4, FALSE); /* (-256 .. 255) << 4 */
+ case 'i': JALX (26, 0, 2);
+ case 'j': SINT (9, 7);
+ case 'p': BIT (5, 6, 0); /* (0 .. 31), 32-bit op */
+ case 's': MSB (5, 11, 0, FALSE, 31); /* (0 .. 31) */
+ case 't': REG (5, 16, COPRO);
+ case 'x': BIT (5, 16, 0); /* (0 .. 31) */
+ case 'z': REG (5, 0, GP);
+ }
+ break;
+
+ case '<': BIT (5, 6, 0); /* (0 .. 31) */
+ case '>': BIT (5, 6, 32); /* (32 .. 63) */
+ case '%': UINT (3, 21);
+ case ':': SINT (7, 19);
+ case '\'': HINT (6, 16);
+ case '@': SINT (10, 16);
+ case '!': UINT (1, 5);
+ case '$': UINT (1, 4);
+ case '*': REG (2, 18, ACC);
+ case '&': REG (2, 13, ACC);
+ case '~': SINT (12, 0);
+ case '\\': BIT (3, 12, 0); /* (0 .. 7) */
+
+ case '0': SINT (6, 20);
+ case '1': HINT (5, 6);
+ case '2': HINT (2, 11);
+ case '3': HINT (3, 21);
+ case '4': HINT (4, 21);
+ case '5': HINT (8, 16);
+ case '6': HINT (5, 21);
+ case '7': REG (2, 11, ACC);
+ case '8': HINT (6, 11);
+ case '9': REG (2, 21, ACC);
+
+ case 'B': HINT (20, 6);
+ case 'C': HINT (25, 0);
+ case 'D': REG (5, 6, FP);
+ case 'E': REG (5, 16, COPRO);
+ case 'G': REG (5, 11, COPRO);
+ case 'H': UINT (3, 0);
+ case 'J': HINT (19, 6);
+ case 'K': REG (5, 11, HW);
+ case 'M': REG (3, 8, CCC);
+ case 'N': REG (3, 18, CCC);
+ case 'O': UINT (3, 21);
+ case 'P': SPECIAL (5, 1, PERF_REG);
+ case 'Q': SPECIAL (10, 16, MDMX_IMM_REG);
+ case 'R': REG (5, 21, FP);
+ case 'S': REG (5, 11, FP);
+ case 'T': REG (5, 16, FP);
+ case 'U': SPECIAL (10, 11, CLO_CLZ_DEST);
+ case 'V': REG (5, 11, FP);
+ case 'W': REG (5, 16, FP);
+ case 'X': REG (5, 6, VEC);
+ case 'Y': REG (5, 11, VEC);
+ case 'Z': REG (5, 16, VEC);
+
+ case 'a': JUMP (26, 0, 2);
+ case 'b': REG (5, 21, GP);
+ case 'c': HINT (10, 16);
+ case 'd': REG (5, 11, GP);
+ case 'e': UINT (3, 22)
+ case 'g': REG (5, 11, COPRO);
+ case 'h': HINT (5, 11);
+ case 'i': HINT (16, 0);
+ case 'j': SINT (16, 0);
+ case 'k': HINT (5, 16);
+ case 'o': SINT (16, 0);
+ case 'p': BRANCH (16, 0, 2);
+ case 'q': HINT (10, 6);
+ case 'r': REG (5, 21, GP);
+ case 's': REG (5, 21, GP);
+ case 't': REG (5, 16, GP);
+ case 'u': HINT (16, 0);
+ case 'v': REG (5, 21, GP);
+ case 'w': REG (5, 16, GP);
+ case 'x': REG (0, 0, GP);
+ case 'z': MAPPED_REG (0, 0, GP, reg_0_map);
+ }
+ return 0;
+}
/* Short hand so the lines aren't too long. */
OpenPOWER on IntegriCloud