Prefix instructions push a prefix operation onto the prefix coprocessor
registers. These registers are used when reading/writing registers at
a later instruction. For this reason, the assembler also supports a
shorter syntax where registers are decorated and the required prefix
instructions are emitted before the actual instruction.

Signed-off-by: David Guillen Fandos <[email protected]>
---
 gas/config/tc-mips.c                          | 249 +++++++++++++++++-
 gas/testsuite/gas/mips/allegrex-vfpu-errors.d |   3 +
 gas/testsuite/gas/mips/allegrex-vfpu-errors.l |  10 +
 gas/testsuite/gas/mips/allegrex-vfpu-errors.s |  16 ++
 gas/testsuite/gas/mips/allegrex-vfpu.d        |  16 ++
 gas/testsuite/gas/mips/allegrex-vfpu.s        |  11 +
 .../gas/mips/[email protected]        |   2 +-
 gas/testsuite/gas/mips/mips.exp               |   1 +
 include/opcode/mips.h                         |   5 +
 opcodes/mips-dis.c                            |  55 ++++
 opcodes/mips-formats.h                        |  16 +-
 opcodes/mips-opc.c                            |  13 +
 12 files changed, 390 insertions(+), 7 deletions(-)
 create mode 100644 gas/testsuite/gas/mips/allegrex-vfpu-errors.d
 create mode 100644 gas/testsuite/gas/mips/allegrex-vfpu-errors.l
 create mode 100644 gas/testsuite/gas/mips/allegrex-vfpu-errors.s

diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index f46d81ecfa2..eda2b641b80 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -675,6 +675,8 @@ static int g_switch_seen = 0;
 #define N_RMASK 0xc4
 #define N_VFP   0xd4
 
+#define PFX_DST 0x80
+
 /* If we can determine in advance that GP optimization won't be
    possible, we can skip the relaxation stuff that tries to produce
    GP-relative references.  This makes delay slot optimization work
@@ -3426,6 +3428,106 @@ mips_vfpu_parse_register (char **sptr, unsigned int 
*regval,
   return true;
 }
 
+/* Parses one VFPU prefix channel, which can be either source or destination.
+   Returns NULL if the parsed data cannot be a valid prefix. */
+static char *
+mips_vfpu_parse_prefix_channel (char *s, unsigned char *chan)
+{
+  static const struct
+  {
+    const char *token;
+    unsigned char value;
+  } fixed_tokens[] = {
+    { "m",    PFX_DST | 4 }, { "M",      PFX_DST | 4 },
+    { "0:1",  PFX_DST | 1 }, { "[0:1]",  PFX_DST | 1 },
+    { "-1:1", PFX_DST | 3 }, { "[-1:1]", PFX_DST | 3 },
+    { "1/6",  0x8 | 7 }, { "-1/6", 0x10 | 0x8 | 7 },
+    { "1/4",  0x8 | 6 }, { "-1/4", 0x10 | 0x8 | 6 },
+    { "1/3",  0x8 | 5 }, { "-1/3", 0x10 | 0x8 | 5 },
+    { "3",    0x8 | 4 }, { "-3",   0x10 | 0x8 | 4 },
+    { "1/2",  0x8 | 3 }, { "-1/2", 0x10 | 0x8 | 3 },
+    { "2",    0x8 | 2 }, { "-2",   0x10 | 0x8 | 2 },
+    { "1",    0x8 | 1 }, { "-1",   0x10 | 0x8 | 1 },
+    { "0",    0x8 | 0 }, { "-0",   0x10 | 0x8 | 0 },
+  };
+
+  SKIP_SPACE_TABS (s);
+
+  if (!(*s) || *s == ',' || *s == ']')
+    return s;
+
+  for (unsigned i = 0; i < ARRAY_SIZE(fixed_tokens); i++) {
+    size_t tokenl = strlen(fixed_tokens[i].token);
+    if (!strncmp(s, fixed_tokens[i].token, tokenl)) {
+      *chan = fixed_tokens[i].value;
+      return &s[tokenl];
+    }
+  }
+
+  *chan = 0;
+  if (*s == '-')
+  {
+    *chan |= 0x10;
+    s++;
+    SKIP_SPACE_TABS (s);
+  }
+
+  if (*s == '|')
+  {
+    *chan |= 0x4;
+    s++;
+    SKIP_SPACE_TABS (s);
+  }
+
+  switch (*s++) {
+    case 'x': case 'X': *chan |= 0; break;
+    case 'y': case 'Y': *chan |= 1; break;
+    case 'z': case 'Z': *chan |= 2; break;
+    case 'w': case 'W': *chan |= 3; break;
+    default:
+      set_insn_error (0, _("invalid VFPU prefix"));
+      return NULL;
+  };
+  SKIP_SPACE_TABS (s);
+
+  if (*chan & 0x4 && *s++ != '|')
+  {
+    set_insn_error (0, _("unmatched '|' in VFPU prefix"));
+    return NULL;
+  }
+  return s;
+}
+
+/* Try to parse a VFPU prefix expression only validating its syntax.  */
+static char *
+mips_vfpu_parse_prefix (char *s, unsigned char *channels,
+                       unsigned int *numchs)
+{
+  int i;
+  for (i = 0; i < 4; i++)
+    channels[i] = 0xff;  /* Unespecified channel expression.  */
+
+  for (i = 0; i < 4; i++)
+  {
+    s = mips_vfpu_parse_prefix_channel (s, &channels[i]);
+    if (!s)
+      break;
+
+    SKIP_SPACE_TABS (s);
+    *numchs = i + 1;
+
+    if (!(*s) || *s == ']')
+      break;
+
+    if (*s++ != ',')
+    {
+      set_insn_error (0, _("invalid VFPU prefix, expecting ',' or ']'"));
+      return NULL;
+    }
+  }
+  return s;
+}
+
 /* Token types for parsed operand lists.  */
 enum mips_operand_token_type {
   /* A plain register, e.g. $f2.  */
@@ -3446,6 +3548,9 @@ enum mips_operand_token_type {
   /* A VFPU register, eg. S000.s.  */
   OT_REG_VFPU,
 
+  /* A VFPU prefix expression for a register or a prefix instruction.  */
+  OT_PFX_VFPU,
+
   /* A (possibly relocated) expression.  */
   OT_INTEGER,
 
@@ -3491,6 +3596,12 @@ struct mips_operand_token
       enum mips_vfpu_reg_shape shape;
     } reg_vfpu;
 
+    /* The prefix value (from 1 to 4 channels) to apply to a register.  */
+    struct {
+      unsigned char prefixes[4];
+      unsigned int num_chs;
+    } pfx_vfpu;
+
     /* The value of an OT_INTEGER.  The value is represented as an
        expression and the relocation operators that were applied to
        that expression.  The reloc entries are BFD_RELOC_UNUSED if no
@@ -3606,6 +3717,17 @@ mips_parse_argument_token (char *s, char float_format,
   if (end)
     return end;
 
+  /* Handle VFPU specially encoded immediates.  */
+  if (mo->pinfo2 & INSN2_ALLEGREX_PREFIX)
+  {
+    s = mips_vfpu_parse_prefix(s, &token.u.pfx_vfpu.prefixes[0],
+                              &token.u.pfx_vfpu.num_chs);
+    if (s)
+      mips_add_token (&token, OT_PFX_VFPU);
+
+    return s;
+  }
+
   /* Handle other characters that end up as OT_CHARs.  */
   if (*s == ')' || *s == ',')
     {
@@ -3621,6 +3743,19 @@ mips_parse_argument_token (char *s, char float_format,
     {
       token.u.reg_vfpu.regno = regno1;
       mips_add_token (&token, OT_REG_VFPU);
+
+      SKIP_SPACE_TABS (s);
+      if (*s == '[')
+      {
+       /* The register can feature a prefix, expressed as a suffix.  */
+       s = mips_vfpu_parse_prefix (&s[1], &token.u.pfx_vfpu.prefixes[0],
+                                   &token.u.pfx_vfpu.num_chs);
+       if (s && *s == ']')
+       {
+         mips_add_token (&token, OT_PFX_VFPU);
+         return ++s;
+       }
+      }
       return s;
     }
 
@@ -5099,6 +5234,7 @@ operand_reg_mask (const struct mips_cl_insn *insn,
     case OP_VU0_SUFFIX:
     case OP_VU0_MATCH_SUFFIX:
     case OP_VFPU_REG:
+    case OP_VFPU_PFX:
     case OP_IMM_INDEX:
       abort ();
 
@@ -6683,6 +6819,83 @@ match_vu0_suffix_operand (struct mips_arg_info *arg,
   return true;
 }
 
+/* Generates a VFPU prefix immediate for a given reg type and size.  */
+static bfd_boolean
+calculate_vfpu_prefix_imm (struct mips_arg_info *arg,
+                          enum mips_vfpu_reg_type regtype, unsigned int rsize,
+                          unsigned int *immediate)
+{
+  unsigned int i;
+  unsigned imm24 = 0;
+  unsigned char *pfxchan = &arg->token->u.pfx_vfpu.prefixes[0];
+
+  for (i = 0; i <= rsize; i++)
+  {
+    if (pfxchan[i] == 0xff)
+    {
+      if (regtype == OP_VFPU_REG_D)
+       pfxchan[i] = PFX_DST;
+      else
+       pfxchan[i] = i;  /* Natural swizzle when not specified.  */
+    }
+
+    if (regtype == OP_VFPU_REG_D)
+    {
+      if (!(pfxchan[i] & PFX_DST))
+      {
+       set_insn_error (arg->argnum,
+                       _("invalid destination prefix (can only contain 
masking/saturation)"));
+       return false;
+      }
+    }
+    else
+    {
+      if (pfxchan[i] & PFX_DST)
+      {
+       set_insn_error (arg->argnum,
+                       _("invalid source prefix (can only contain 
constant/swizzle/neg/abs)"));
+       return false;
+      }
+      if (!(pfxchan[i] & 0x8) && (pfxchan[i] & 3) > rsize)
+      {
+       set_insn_error (arg->argnum,
+                       _("invalid swizzle operation (out of range)"));
+       return false;
+      }
+    }
+
+    imm24 |= ((pfxchan[i] & 3) << (i * 2))
+             | ((pfxchan[i] & 0x4) << (6 + i))
+             | ((pfxchan[i] & 0x8) << (9 + i))
+             | ((pfxchan[i] & 0x10) << (12 + i));
+  }
+
+  *immediate = imm24;
+  return true;
+}
+
+/* Matches VFPU prefix operands.  */
+static bfd_boolean
+match_vfpu_prefix_operand (struct mips_arg_info *arg,
+                          const struct mips_operand *operand)
+{
+  unsigned imm24;
+  const struct mips_vfpu_reg_operand *regvfpuop;
+  if (arg->token->type != OT_PFX_VFPU)
+    return false;
+
+  regvfpuop = (struct mips_vfpu_reg_operand*)operand;
+
+  if (!calculate_vfpu_prefix_imm (arg, regvfpuop->regtype, regvfpuop->rsize,
+                                 &imm24))
+    return false;
+
+  insn_insert_operand (arg->insn, operand, imm24);
+
+  ++arg->token;
+  return true;
+}
+
 /* Matches VFPU register operands.  */
 static bfd_boolean
 match_vfpu_reg_operand (struct mips_arg_info *arg,
@@ -6719,8 +6932,39 @@ match_vfpu_reg_operand (struct mips_arg_info *arg,
   }
 
   insn_insert_operand (arg->insn, operand, uval);
-
   ++arg->token;
+
+  /* Check if this register has an associated prefix.  */
+  if (arg->token->type == OT_PFX_VFPU)
+  {
+    unsigned int imm24;
+    struct mips_cl_insn pfxinstr;
+    struct mips_opcode *pop;
+    bfd_reloc_code_real_type unused_reloc[3]
+      = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+
+    const char *pfxmemo = regvfpuop->regtype == OP_VFPU_REG_S ? "vpfxs" :
+                         regvfpuop->regtype == OP_VFPU_REG_T ? "vpfxt" : 
"vpfxd";
+
+    if (regvfpuop->rsize + 1 != arg->token->u.pfx_vfpu.num_chs)
+    {
+      set_insn_error (arg->argnum, _("invalid number of prefix elements"));
+      return false;
+    }
+
+    /* Create a prefix instruction that precedes this one.  */
+    pop = (struct mips_opcode *) str_hash_find (op_hash, pfxmemo);
+    create_insn (&pfxinstr, pop);
+
+    if (!calculate_vfpu_prefix_imm (arg, regvfpuop->regtype, regvfpuop->rsize,
+                                   &imm24))
+      return false;
+
+    insn_insert_operand (&pfxinstr, decode_mips_operand (pop->args), imm24);
+    append_insn (&pfxinstr, NULL, unused_reloc, false);
+    ++arg->token;
+  }
+
   return true;
 }
 
@@ -6794,6 +7038,9 @@ match_operand (struct mips_arg_info *arg,
     case OP_VFPU_REG:
       return match_vfpu_reg_operand (arg, operand);
 
+    case OP_VFPU_PFX:
+      return match_vfpu_prefix_operand (arg, operand);
+
     case OP_IMM_INDEX:
       return match_imm_index_operand (arg, operand);
 
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu-errors.d 
b/gas/testsuite/gas/mips/allegrex-vfpu-errors.d
new file mode 100644
index 00000000000..de8edaafdf7
--- /dev/null
+++ b/gas/testsuite/gas/mips/allegrex-vfpu-errors.d
@@ -0,0 +1,3 @@
+#name: Allegrex VFPU assembly errors
+#as: -march=allegrex -mabi=32
+#error_output: allegrex-vfpu-errors.l
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu-errors.l 
b/gas/testsuite/gas/mips/allegrex-vfpu-errors.l
new file mode 100644
index 00000000000..aa10cf49e75
--- /dev/null
+++ b/gas/testsuite/gas/mips/allegrex-vfpu-errors.l
@@ -0,0 +1,10 @@
+.*: Assembler messages:
+.*:4: Error: invalid number of prefix elements `vadd.q R000\[,m,\],C000,R002'
+.*:5: Error: invalid swizzle operation \(out of range\) `vadd.t 
R000,C000,R002\[w,x,y\]'
+.*:6: Error: invalid number of prefix elements `vmul.q R000,C000,R002\[,y,z\]'
+.*:7: Error: invalid VFPU prefix `vsub.s S000,S000,S000\[p\]'
+.*:8: Error: invalid VFPU prefix, expecting ',' or '\]' `vsub.s 
S000,S000,S000\[1/7\]'
+.*:9: Error: unmatched '|' in VFPU prefix `vsub.s S000,S000,S000\[|x\]'
+.*:10: Error: invalid VFPU prefix, expecting ',' or '\]' `vsub.s 
S000,S000,S000\[xx\]'
+.*:11: Error: invalid source prefix \(can only contain 
constant/swizzle/neg/abs\) `vsub.t R000,R000,R000\[m,m,\]'
+.*:12: Error: invalid destination prefix \(can only contain 
masking/saturation\) `vsub.t R000\[x,y,z\],R000,R000'
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu-errors.s 
b/gas/testsuite/gas/mips/allegrex-vfpu-errors.s
new file mode 100644
index 00000000000..c2fa6dda856
--- /dev/null
+++ b/gas/testsuite/gas/mips/allegrex-vfpu-errors.s
@@ -0,0 +1,16 @@
+       .text
+       .set noreorder
+
+       vadd.q R000[,m,], C000, R002
+       vadd.t R000, C000, R002[w,x,y]
+       vmul.q R000, C000, R002[,y,z]
+       vsub.s S000, S000, S000[p]
+       vsub.s S000, S000, S000[1/7]
+       vsub.s S000, S000, S000[|x]
+       vsub.s S000, S000, S000[xx]
+       vsub.t R000, R000, R000[m,m,]
+       vsub.t R000[x,y,z], R000, R000
+
+# Force some (non-delay-slot) zero bytes, to make 'objdump' print ...
+       .align  4, 0
+       .space  16
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu.d 
b/gas/testsuite/gas/mips/allegrex-vfpu.d
index e5fb8e13595..c39541364d5 100644
--- a/gas/testsuite/gas/mips/allegrex-vfpu.d
+++ b/gas/testsuite/gas/mips/allegrex-vfpu.d
@@ -63,4 +63,20 @@ Disassembly of section .text:
 0x000000d8 d00600e4    vzero.p R120.p
 0x000000dc d0068028    vzero.t R200.t
 0x000000e0 d00680a8    vzero.q R200.q
+0x000000e4 dc0000e4    vpfxs   x,y,z,w
+0x000000e8 dd0000e4    vpfxt   x,y,z,w
+0x000000ec dc000000    vpfxs   x,x,x,x
+0x000000f0 dc08dc30    vpfxs   0,x,1/6,-3
+0x000000f4 dc0305b3    vpfxs   -|w|,-x,|w|,z
+0x000000f8 de000000    vpfxd   ,,,
+0x000000fc de000174    vpfxd   m,\[0:1\],\[-1:1\],\[0:1\]
+0x00000100 de000a00    vpfxd   ,m,,m
+0x00000104 602280a0    vadd.q  R000.q,C000.q,R002.q
+0x00000108 dc000050    vpfxs   x,x,y,y
+0x0000010c dd0000fa    vpfxt   z,z,w,w
+0x00000110 602280a0    vadd.q  R000.q,C000.q,R002.q
+0x00000114 de000200    vpfxd   ,m,,
+0x00000118 dc00000a    vpfxs   z,z,x,x
+0x0000011c dd000018    vpfxt   x,z,y,x
+0x00000120 64228020    vmul.t  R000.t,C000.t,R002.t
        \.\.\.
diff --git a/gas/testsuite/gas/mips/allegrex-vfpu.s 
b/gas/testsuite/gas/mips/allegrex-vfpu.s
index 7bbe5db61b9..228c17229f8 100644
--- a/gas/testsuite/gas/mips/allegrex-vfpu.s
+++ b/gas/testsuite/gas/mips/allegrex-vfpu.s
@@ -60,6 +60,17 @@
        vzero.t R200
        vzero.q R200
 
+       vpfxs ,,,
+       vpfxt ,,,
+       vpfxs x,x,x,x
+       vpfxs 0,x,1/6,-3
+       vpfxs -|w|, -x,| w |, z
+       vpfxd ,,,
+       vpfxd m,0:1,[-1:1],[0:1]
+       vadd.q R000[,m,,m], C000, R002
+       vadd.q R000, C000[x,x,y,y], R002[z,z,w,w]
+       vmul.t R000[,m,], C000[z,z,x], R002[x,z,y]
+
 # Force some (non-delay-slot) zero bytes, to make 'objdump' print ...
        .align  4, 0
        .space  16
diff --git a/gas/testsuite/gas/mips/[email protected] 
b/gas/testsuite/gas/mips/[email protected]
index 99c9c129159..57e0123ee05 100644
--- a/gas/testsuite/gas/mips/[email protected]
+++ b/gas/testsuite/gas/mips/[email protected]
@@ -10,7 +10,7 @@ Disassembly of section \.text:
 [0-9a-f]+ <[^>]*> 8c830004     lw      v1,4\(a0\)
 [0-9a-f]+ <[^>]*> 3c0189ab     lui     at,0x89ab
 [0-9a-f]+ <[^>]*> 00411025     or      v0,v0,at
-[0-9a-f]+ <[^>]*> dc820000     .word   0xdc820000
+[0-9a-f]+ <[^>]*> dc820000     vpfxs   x,-x,x,x
 [0-9a-f]+ <[^>]*> 340189ab     li      at,0x89ab
 [0-9a-f]+ <[^>]*> 00010c38     .word   0x10c38
 [0-9a-f]+ <[^>]*> 00411025     or      v0,v0,at
diff --git a/gas/testsuite/gas/mips/mips.exp b/gas/testsuite/gas/mips/mips.exp
index 15aabc44e2a..f6f3a8cf0a1 100644
--- a/gas/testsuite/gas/mips/mips.exp
+++ b/gas/testsuite/gas/mips/mips.exp
@@ -1714,6 +1714,7 @@ if { [istarget mips*-*-vxworks*] } {
     run_dump_test "allegrex"
     run_dump_test "allegrex-removed"
     run_dump_test "allegrex-vfpu"
+    run_dump_test "allegrex-vfpu-errors"
 
     run_list_test_arches "ext-ill"     [mips_arch_list_matching mips64r2]
 
diff --git a/include/opcode/mips.h b/include/opcode/mips.h
index 7c177f6a283..f535591e54d 100644
--- a/include/opcode/mips.h
+++ b/include/opcode/mips.h
@@ -171,6 +171,9 @@ enum mips_operand_type {
   /* A VFPU register.  */
   OP_VFPU_REG,
 
+  /* A VFPU register prefix.  */
+  OP_VFPU_PFX,
+
   /* An index selected by an integer, e.g. [1].  */
   OP_IMM_INDEX,
 
@@ -915,6 +918,8 @@ mips_opcode_32bit_p (const struct mips_opcode *mo)
    encoding is needed or otherwise the final EXTEND entry will apply,
    for the disassembly of the prefix only.  */
 #define INSN2_SHORT_ONLY           0x00010000
+/* Has special encoded prefix expressions that require parsing.  */
+#define INSN2_ALLEGREX_PREFIX      0x00020000
 
 /* Masks used to mark instructions to indicate which MIPS ISA level
    they were introduced in.  INSN_ISA_MASK masks an enumeration that
diff --git a/opcodes/mips-dis.c b/opcodes/mips-dis.c
index 7ed84a952a4..ed421e2aa71 100644
--- a/opcodes/mips-dis.c
+++ b/opcodes/mips-dis.c
@@ -1381,6 +1381,56 @@ print_vfpu_reg (struct disassemble_info *info,
   }
 }
 
+/* Print OP_VFPU_PFX operand OPERAND, whose value is given by UVAL.  */
+static void
+print_vfpu_pfx (struct disassemble_info *info,
+               const struct mips_operand *operand, unsigned int uval)
+{
+  unsigned int i;
+  const fprintf_styled_ftype infprintf = info->fprintf_styled_func;
+  void *is = info->stream;
+  const struct mips_vfpu_reg_operand *vfpureg_op;
+
+  vfpureg_op = (const struct mips_vfpu_reg_operand *) operand;
+
+  for (i = 0; i <= vfpureg_op->rsize; i++)
+  {
+    if (i)
+      infprintf (is, dis_style_mnemonic, ",");
+
+    if (vfpureg_op->regtype == OP_VFPU_REG_D)
+    {
+      if (uval & (1 << (8 + i)))
+       infprintf (is, dis_style_mnemonic, "m");
+      else
+      {
+       static const char * const satimms[] = { "", "[0:1]", "", "[-1:1]" };
+       unsigned int sat = (uval >> (i * 2)) & 3;
+       infprintf (is, dis_style_mnemonic, "%s", satimms[sat]);
+      }
+    }
+    else
+    {
+      static const char * const pfxswz_str = "xyzw";
+      static const char * const pfxcst_str[2][4] = {
+       { "0",  "1",  "2",  "1/2" }, { "3",  "1/3",  "1/4",  "1/6" }
+      };
+      unsigned int hib = (uval >> (8 + i)) & 1;
+      unsigned int lob = (uval >> (i * 2)) & 3;
+
+      if (uval & (1 << (16 + i)))
+       infprintf (is, dis_style_mnemonic, "-");
+
+      if (uval & (1 << (12 + i)))
+       infprintf (is, dis_style_mnemonic, "%s", pfxcst_str[hib][lob]);
+      else if (hib)
+       infprintf (is, dis_style_mnemonic, "|%c|", pfxswz_str[lob]);
+      else
+       infprintf (is, dis_style_mnemonic, "%c", pfxswz_str[lob]);
+    }
+  }
+}
+
 
 /* Record information about a register operand.  */
 
@@ -1595,6 +1645,10 @@ print_insn_arg (struct disassemble_info *info,
       print_vfpu_reg (info, operand, uval);
       break;
 
+    case OP_VFPU_PFX:
+      print_vfpu_pfx (info, operand, uval);
+      break;
+
     case OP_PERF_REG:
       infprintf (is, dis_style_register, "%d", uval);
       break;
@@ -1935,6 +1989,7 @@ validate_insn_args (const struct mips_opcode *opcode,
                case OP_REG_INDEX:
                case OP_SAVE_RESTORE_LIST:
                case OP_VFPU_REG:
+               case OP_VFPU_PFX:
                  break;
                }
            }
diff --git a/opcodes/mips-formats.h b/opcodes/mips-formats.h
index 87d85e06dae..d31a514d51e 100644
--- a/opcodes/mips-formats.h
+++ b/opcodes/mips-formats.h
@@ -112,17 +112,23 @@
     return &op.root; \
   }
 
-#define VFPU_REG(SIZE, LSB, REGTYPE, REGSIZE) \
+#define VFPU_REGEXP(OP_TYPE, SIZE, LSB, REGTYPE, REGSIZE) \
   { \
     static const struct mips_vfpu_reg_operand op[] = { \
-      { { OP_VFPU_REG, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 0 }, \
-      { { OP_VFPU_REG, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 1 }, \
-      { { OP_VFPU_REG, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 2 }, \
-      { { OP_VFPU_REG, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 3 }, \
+      { { OP_TYPE, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 0 }, \
+      { { OP_TYPE, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 1 }, \
+      { { OP_TYPE, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 2 }, \
+      { { OP_TYPE, SIZE, LSB }, OP_VFPU_REG_##REGTYPE, 3 }, \
     }; \
     return &op[REGSIZE].root; \
   }
 
+#define VFPU_REG(SIZE, LSB, REGTYPE, REGSIZE) \
+  VFPU_REGEXP(OP_VFPU_REG, SIZE, LSB, REGTYPE, REGSIZE)
+
+#define VFPU_PFX(SIZE, LSB, REGTYPE, REGSIZE) \
+  VFPU_REGEXP(OP_VFPU_PFX, SIZE, LSB, REGTYPE, REGSIZE)
+
 #define PCREL(SIZE, LSB, IS_SIGNED, SHIFT, ALIGN_LOG2, INCLUDE_ISA_BIT, \
               FLIP_ISA_BIT) \
   { \
diff --git a/opcodes/mips-opc.c b/opcodes/mips-opc.c
index f9341c94af9..e8c0418f1ca 100644
--- a/opcodes/mips-opc.c
+++ b/opcodes/mips-opc.c
@@ -148,6 +148,13 @@ decode_mips_operand (const char *p)
        case 'd': VFPU_REG(7,  0, D, p[2] - '0');
        case 's': VFPU_REG(7,  8, S, p[2] - '0');
        case 't': VFPU_REG(7, 16, T, p[2] - '0');
+       case 'p':
+         switch (p[2])
+         {
+           case 'd': VFPU_PFX(24, 0, D, 3);
+           case 's': VFPU_PFX(24, 0, S, 3);
+           case 't': VFPU_PFX(24, 0, T, 3);
+         }
        }
       break;
 
@@ -367,6 +374,9 @@ decode_mips_operand (const char *p)
 #define VU0    EE
 #define VU0CH  INSN2_VU0_CHANNEL_SUFFIX
 
+/* Support for Allegrex VFPU Coprocessor instructions */
+#define ALXPFX INSN2_ALLEGREX_PREFIX
+
 /* MIPS DSP ASE support.
    NOTE:
    1. MIPS DSP ASE includes 4 accumulators ($ac0 - $ac3).  $ac0 is the pair
@@ -709,6 +719,9 @@ const struct mips_opcode mips_builtin_opcodes[] =
 {"vmul.q",             "?d3,?s3,?t3",    0x64008080, 0xff808080,       
RD_C2|WR_C2,    0,              ALX,            0,      0 },
 {"vmul.s",             "?d0,?s0,?t0",    0x64000000, 0xff808080,       
RD_C2|WR_C2,    0,              ALX,            0,      0 },
 {"vmul.t",             "?d2,?s2,?t2",    0x64008000, 0xff808080,       
RD_C2|WR_C2,    0,              ALX,            0,      0 },
+{"vpfxd",              "?pd",            0xde000000, 0xff000000,       CP,     
        ALXPFX,         ALX,            0,      0 },
+{"vpfxs",              "?ps",            0xdc000000, 0xff000000,       CP,     
        ALXPFX,         ALX,            0,      0 },
+{"vpfxt",              "?pt",            0xdd000000, 0xff000000,       CP,     
        ALXPFX,         ALX,            0,      0 },
 {"vsge.p",             "?d1,?s1,?t1",    0x6f000080, 0xff808080,       
RD_C2|WR_C2,    0,              ALX,            0,      0 },
 {"vsge.q",             "?d3,?s3,?t3",    0x6f008080, 0xff808080,       
RD_C2|WR_C2,    0,              ALX,            0,      0 },
 {"vsge.s",             "?d0,?s0,?t0",    0x6f000000, 0xff808080,       
RD_C2|WR_C2,    0,              ALX,            0,      0 },
-- 
2.51.1

Reply via email to