gas: pru: Add TSEN and MVI instructions

Message ID 20260222192311.638480-1-dimitar@dinux.eu
State New
Headers
Series gas: pru: Add TSEN and MVI instructions |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Dimitar Dimitrov Feb. 22, 2026, 7:23 p.m. UTC
  Add support for TSEN and MVI instructions to GAS and libopcodes.
TSEN is available in newer PRU core revisions, and can be used to
implement multitasking.  MVI allows indirectly addressing CPU registers
using a pointer in R1 register.

Ok for trunk?

References:
 - https://www.ti.com/lit/ug/spruij2/spruij2.pdf
   Chapter "Move Register File Indirect (MVIx)"
 - https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1426480/am2431-pru-assembly-instruction-user-guide

Signed-off-by: Dimitar Dimitrov <dimitar@dinux.eu>
---
 gas/config/tc-pru.c          | 109 +++++++++++++++++++++++++++++++++++
 gas/testsuite/gas/pru/mvi.d  |  17 ++++++
 gas/testsuite/gas/pru/mvi.s  |  12 ++++
 gas/testsuite/gas/pru/tsen.d |  10 ++++
 gas/testsuite/gas/pru/tsen.s |   5 ++
 include/opcode/pru.h         |  39 ++++++++++++-
 opcodes/pru-dis.c            |  41 ++++++++++++-
 opcodes/pru-opc.c            |   9 +++
 8 files changed, 237 insertions(+), 5 deletions(-)
 create mode 100644 gas/testsuite/gas/pru/mvi.d
 create mode 100644 gas/testsuite/gas/pru/mvi.s
 create mode 100644 gas/testsuite/gas/pru/tsen.d
 create mode 100644 gas/testsuite/gas/pru/tsen.s
  

Comments

Jan Beulich March 6, 2026, 1:20 p.m. UTC | #1
On 22.02.2026 20:23, Dimitar Dimitrov wrote:
> Add support for TSEN and MVI instructions to GAS and libopcodes.
> TSEN is available in newer PRU core revisions, and can be used to
> implement multitasking.  MVI allows indirectly addressing CPU registers
> using a pointer in R1 register.

There don't look to be any provisions to limit insn availability based on
a targeted core revision. This isn't quite nice, but probably shouldn't
get in the way of getting this in. Seeing there is no maintainer for this
target - okay, with one nit addressed:

> --- a/opcodes/pru-dis.c
> +++ b/opcodes/pru-dis.c
> @@ -66,15 +66,34 @@ pru_find_opcode (unsigned long opcode)
>  #define NUMREGNAMES (32 * 8)
>  
>  static void
> -pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
> -			disassemble_info *info)
> +pru_print_insn_arg_indreg (unsigned int r, unsigned int sel,
> +			   unsigned int mode,
> +			   disassemble_info *info)
>  {
> +  const char *fmtstr;
>    unsigned int i = r * RSEL_NUM_ITEMS + sel;
>    assert (i < (unsigned int)pru_num_regs);
>    assert (i < NUMREGNAMES);
> -  (*info->fprintf_func) (info->stream, "%s", pru_regs[i].name);
> +
> +  switch (mode)
> +    {
> +    case MVI_OP_MODE_DIRECT:		fmtstr = "%s"; break;
> +    case MVI_OP_MODE_INDIRECT:		fmtstr = "*%s"; break;
> +    case MVI_OP_MODE_INDIRECT_POSTINC:	fmtstr = "*%s++"; break;
> +    case MVI_OP_MODE_INDIRECT_PREDEC:	fmtstr = "*--%s"; break;
> +    default:				fmtstr = "<invalid>%s"; break;
> +    }
> +  (*info->fprintf_func) (info->stream, fmtstr, pru_regs[i].name);
> +}
> +
> +static void
> +pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
> +			disassemble_info *info)
> +{
> +  pru_print_insn_arg_indreg (r, sel, MVI_OP_MODE_DIRECT, info);
>  }
>  
> +
>  /* The function pru_print_insn_arg uses the character pointed

Please don't introduce double blank lines.

Seeing that your original contribution of the port was committed by Alan,
I assume this change will again want committing on your behalf?

Jan
  
Dimitar Dimitrov March 6, 2026, 6:09 p.m. UTC | #2
On Fri, Mar 06, 2026 at 02:20:41PM +0100, Jan Beulich wrote:
> On 22.02.2026 20:23, Dimitar Dimitrov wrote:
> > Add support for TSEN and MVI instructions to GAS and libopcodes.
> > TSEN is available in newer PRU core revisions, and can be used to
> > implement multitasking.  MVI allows indirectly addressing CPU registers
> > using a pointer in R1 register.
> 
> There don't look to be any provisions to limit insn availability based on
> a targeted core revision. This isn't quite nice, but probably shouldn't
> get in the way of getting this in. Seeing there is no maintainer for this
> target - okay, with one nit addressed:

I agree.  I'll try to follow with a change to introduce a core revision
flag.

I volunteer to maintain the pru port, if that helps.

> 
> > --- a/opcodes/pru-dis.c
> > +++ b/opcodes/pru-dis.c
> > @@ -66,15 +66,34 @@ pru_find_opcode (unsigned long opcode)
> >  #define NUMREGNAMES (32 * 8)
> >  
> >  static void
> > -pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
> > -			disassemble_info *info)
> > +pru_print_insn_arg_indreg (unsigned int r, unsigned int sel,
> > +			   unsigned int mode,
> > +			   disassemble_info *info)
> >  {
> > +  const char *fmtstr;
> >    unsigned int i = r * RSEL_NUM_ITEMS + sel;
> >    assert (i < (unsigned int)pru_num_regs);
> >    assert (i < NUMREGNAMES);
> > -  (*info->fprintf_func) (info->stream, "%s", pru_regs[i].name);
> > +
> > +  switch (mode)
> > +    {
> > +    case MVI_OP_MODE_DIRECT:		fmtstr = "%s"; break;
> > +    case MVI_OP_MODE_INDIRECT:		fmtstr = "*%s"; break;
> > +    case MVI_OP_MODE_INDIRECT_POSTINC:	fmtstr = "*%s++"; break;
> > +    case MVI_OP_MODE_INDIRECT_PREDEC:	fmtstr = "*--%s"; break;
> > +    default:				fmtstr = "<invalid>%s"; break;
> > +    }
> > +  (*info->fprintf_func) (info->stream, fmtstr, pru_regs[i].name);
> > +}
> > +
> > +static void
> > +pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
> > +			disassemble_info *info)
> > +{
> > +  pru_print_insn_arg_indreg (r, sel, MVI_OP_MODE_DIRECT, info);
> >  }
> >  
> > +
> >  /* The function pru_print_insn_arg uses the character pointed
> 
> Please don't introduce double blank lines.

Sorry. I fixed it.
> 
> Seeing that your original contribution of the port was committed by Alan,
> I assume this change will again want committing on your behalf?

I have write access, so with your approval I pushed this as
bd2f485e24365d6a5717efeeb3c6d4edb5231796.

Thank you,
Dimitar
> 
> Jan
  
Jan Beulich March 9, 2026, 7:11 a.m. UTC | #3
Nick,

On 06.03.2026 19:09, Dimitar Dimitrov wrote:
> On Fri, Mar 06, 2026 at 02:20:41PM +0100, Jan Beulich wrote:
>> On 22.02.2026 20:23, Dimitar Dimitrov wrote:
>>> Add support for TSEN and MVI instructions to GAS and libopcodes.
>>> TSEN is available in newer PRU core revisions, and can be used to
>>> implement multitasking.  MVI allows indirectly addressing CPU registers
>>> using a pointer in R1 register.
>>
>> There don't look to be any provisions to limit insn availability based on
>> a targeted core revision. This isn't quite nice, but probably shouldn't
>> get in the way of getting this in. Seeing there is no maintainer for this
>> target - okay, with one nit addressed:
> 
> I agree.  I'll try to follow with a change to introduce a core revision
> flag.
> 
> I volunteer to maintain the pru port, if that helps.

I think we would be quite happy to have a maintainer for that port, wouldn't
we?

Jan
  
Nick Clifton March 11, 2026, 1:47 p.m. UTC | #4
Hi Jan, Hi Dimitar

>> I volunteer to maintain the pru port, if that helps.
> 
> I think we would be quite happy to have a maintainer for that port, wouldn't
> we?

Yes indeed.

Dimitar - assuming that you are still willing, please could you add yourself
to the list of port maintainers in the binutils/MAINTAINERS file.  (Patch
pre-approved).

Cheers
   Nick
  
Dimitar Dimitrov March 11, 2026, 7:03 p.m. UTC | #5
On Wed, Mar 11, 2026 at 01:47:35PM +0000, Nick Clifton wrote:
> Hi Jan, Hi Dimitar
> 
> > > I volunteer to maintain the pru port, if that helps.
> > 
> > I think we would be quite happy to have a maintainer for that port, wouldn't
> > we?
> 
> Yes indeed.
> 
> Dimitar - assuming that you are still willing, please could you add yourself
> to the list of port maintainers in the binutils/MAINTAINERS file.  (Patch
> pre-approved).
> 
> Cheers
>   Nick

Thank you, it would be an honor for me.   I pushed the MAINTAINERS update as
cd764745c794944601d7aa1b836597475f5f0f33.

I consider that I can now self-approve this patch for PRU:
https://sourceware.org/pipermail/binutils/2026-March/148457.html
If there are no objections, I'll commit it in three days.

Regards,
Dimitar
> 
>
  

Patch

diff --git a/gas/config/tc-pru.c b/gas/config/tc-pru.c
index e55ce4790ba..86ce28f6fb5 100644
--- a/gas/config/tc-pru.c
+++ b/gas/config/tc-pru.c
@@ -1253,6 +1253,92 @@  pru_assemble_arg_n (pru_insn_infoS *insn_info, const char *argstr)
     SET_INSN_FIELD (XFR_LENGTH, insn_info->insn_code, burstlen);
 }
 
+static void
+pru_parse_mvi_operand (const char *argstr,
+		       struct pru_reg **blreg,
+		       unsigned int *mode)
+{
+  char *regstr;
+
+  *mode = MVI_OP_MODE_DIRECT;
+
+  if (*argstr == '*')
+    {
+      *mode = MVI_OP_MODE_INDIRECT;
+      argstr++;
+
+      if (argstr[0] == '-' && argstr[1] == '-')
+	{
+	  argstr += 2;
+	  *mode = MVI_OP_MODE_INDIRECT_PREDEC;
+	}
+    }
+  /* Decouple register string from the post increment operator.  */
+  regstr = strdup (argstr);
+  *strchrnul (regstr, '+') = '\0';
+  *blreg = pru_reg_lookup (regstr);
+  if (*blreg == NULL)
+    as_bad (_("unknown register %s"), regstr);
+  free (regstr);
+  regstr = NULL;
+  argstr += strlen ((*blreg)->name);
+
+  if (argstr[0] == '+' && argstr[1] == '+')
+    {
+      argstr += 2;
+      if (*mode == MVI_OP_MODE_DIRECT)
+	as_bad (_("missing indirect operator for post-increment operand"));
+      if (*mode == MVI_OP_MODE_INDIRECT_PREDEC)
+	as_bad (_("cannot both pre-decrement and post-increment an operand"));
+      *mode = MVI_OP_MODE_INDIRECT_POSTINC;
+    }
+  if (argstr[0] != '\0')
+      as_bad (_("unexpected statements at and of instruction"));
+
+  if (*mode != MVI_OP_MODE_DIRECT)
+    {
+      if ((*blreg)->index != 1)
+	as_bad (_("only R1 can be used for indirect addressing"));
+
+      if ((*blreg)->regsel != RSEL_7_0
+	  && (*blreg)->regsel != RSEL_15_8
+	  && (*blreg)->regsel != RSEL_23_16
+	  && (*blreg)->regsel != RSEL_31_24)
+	as_bad (_("only byte mode can be used for R1"));
+    }
+}
+
+static void
+pru_assemble_arg_M (pru_insn_infoS *insn_info, const char *argstr)
+{
+  unsigned int mode, rdmode;
+  struct pru_reg *blreg;
+
+  pru_parse_mvi_operand (argstr, &blreg, &mode);
+
+  SET_INSN_FIELD (MVI_RS1_MODE, insn_info->insn_code, mode);
+  SET_INSN_FIELD (RS1, insn_info->insn_code, blreg->index);
+  SET_INSN_FIELD (RS1SEL, insn_info->insn_code, blreg->regsel);
+
+  /* Assume source operand would be parsed after destination one.  */
+  rdmode = GET_INSN_FIELD (MVI_RD_MODE, insn_info->insn_code);
+  if (rdmode == MVI_OP_MODE_DIRECT && mode == MVI_OP_MODE_DIRECT)
+    as_bad (_("at least one MVI operand must be indirect"));
+}
+
+static void
+pru_assemble_arg_m (pru_insn_infoS *insn_info, const char *argstr)
+{
+  unsigned int mode;
+  struct pru_reg *blreg;
+
+  pru_parse_mvi_operand (argstr, &blreg, &mode);
+
+  SET_INSN_FIELD (MVI_RD_MODE, insn_info->insn_code, mode);
+  SET_INSN_FIELD (RD, insn_info->insn_code, blreg->index);
+  SET_INSN_FIELD (RDSEL, insn_info->insn_code, blreg->regsel);
+}
+
 static void
 pru_assemble_arg_c (pru_insn_infoS *insn_info, const char *argstr)
 {
@@ -1264,6 +1350,17 @@  pru_assemble_arg_c (pru_insn_infoS *insn_info, const char *argstr)
     SET_INSN_FIELD (CB, insn_info->insn_code, cb);
 }
 
+static void
+pru_assemble_arg_t (pru_insn_infoS *insn_info, const char *argstr)
+{
+  unsigned long val = pru_assemble_noreloc_expression (argstr);
+
+  if (val != 0 && val != 1)
+    as_bad (_("invalid task manager mode %ld"), val);
+  else
+    SET_INSN_FIELD (TSKMGR_MODE, insn_info->insn_code, val);
+}
+
 static void
 pru_assemble_arg_w (pru_insn_infoS *insn_info, const char *argstr)
 {
@@ -1354,7 +1451,10 @@  pru_consume_arg (char *argstr, const char *parsestr)
     case 'S':
     case 'l':
     case 'n':
+    case 'm':
+    case 'M':
     case 'R':
+    case 't':
     case 'w':
     case 'x':
       /* We can't have %pmem here.  */
@@ -1680,9 +1780,18 @@  md_assemble (char *op_str)
 	    case 'n':
 	      pru_assemble_arg_n (insn, *argtk++);
 	      continue;
+	    case 'm':
+	      pru_assemble_arg_m (insn, *argtk++);
+	      continue;
+	    case 'M':
+	      pru_assemble_arg_M (insn, *argtk++);
+	      continue;
 	    case 'c':
 	      pru_assemble_arg_c (insn, *argtk++);
 	      continue;
+	    case 't':
+	      pru_assemble_arg_t (insn, *argtk++);
+	      continue;
 	    case 'w':
 	      pru_assemble_arg_w (insn, *argtk++);
 	      continue;
diff --git a/gas/testsuite/gas/pru/mvi.d b/gas/testsuite/gas/pru/mvi.d
new file mode 100644
index 00000000000..626fc0cf0c4
--- /dev/null
+++ b/gas/testsuite/gas/pru/mvi.d
@@ -0,0 +1,17 @@ 
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: PRU mvi
+
+# Test the mvi instruction
+
+.*: +file format elf32-pru
+
+Disassembly of section .text:
+0+0000 <[^>]*> 2c204198 	mvib	r24.w0, \*r1.b2
+0+0004 <[^>]*> 2ca20101 	mvid	\*r1.b0, \*r1.b0
+0+0008 <[^>]*> 2ce20101 	mvid	\*r1.b0, \*--r1.b0
+0+000c <[^>]*> 2dc20101 	mvid	\*--r1.b0, \*r1.b0\+\+
+0+0010 <[^>]*> 2cc14101 	mviw	\*r1.b0, \*r1.b2\+\+
+0+0014 <[^>]*> 2cc14121 	mviw	\*r1.b1, \*r1.b2\+\+
+0+0018 <[^>]*> 2c4261f4 	mvid	r20, \*r1.b3\+\+
+0+001c <[^>]*> 2c6241f4 	mvid	r20, \*--r1.b2
+0+0020 <[^>]*> 2d82ff41 	mvid	\*--r1.b2, r31
diff --git a/gas/testsuite/gas/pru/mvi.s b/gas/testsuite/gas/pru/mvi.s
new file mode 100644
index 00000000000..cf23515668f
--- /dev/null
+++ b/gas/testsuite/gas/pru/mvi.s
@@ -0,0 +1,12 @@ 
+# Source file used to test the MVI instruction.
+
+foo:
+	mvib	r24.w0, *r1.b2
+	mvid	*r1.b0, *r1.b0
+	mvid	*r1.b0, *--r1.b0
+	mvid	*--r1.b0, *r1.b0++
+	mviw	*r1.b0, *r1.b2++
+	mviw	*r1.b1, *r1.b2++
+	mvid	r20, *r1.b3++
+	mvid	r20, *--r1.b2
+	mvid	*--r1.b2, r31
diff --git a/gas/testsuite/gas/pru/tsen.d b/gas/testsuite/gas/pru/tsen.d
new file mode 100644
index 00000000000..5c4779bb015
--- /dev/null
+++ b/gas/testsuite/gas/pru/tsen.d
@@ -0,0 +1,10 @@ 
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: PRU tsen
+
+# Test the TSEN instruction
+
+.*: +file format elf32-pru
+
+Disassembly of section .text:
+0+0000 <[^>]*> 32000000 	tsen	0
+0+0004 <[^>]*> 32800000 	tsen	1
diff --git a/gas/testsuite/gas/pru/tsen.s b/gas/testsuite/gas/pru/tsen.s
new file mode 100644
index 00000000000..716d7f476c5
--- /dev/null
+++ b/gas/testsuite/gas/pru/tsen.s
@@ -0,0 +1,5 @@ 
+# Source file used to test the TSEN instruction.
+
+foo:
+	tsen	0
+	tsen	1
diff --git a/include/opcode/pru.h b/include/opcode/pru.h
index f47860a681c..379b11e56a7 100644
--- a/include/opcode/pru.h
+++ b/include/opcode/pru.h
@@ -64,7 +64,8 @@  enum pru_instr_type
   prui_halt, prui_slp, prui_xin, prui_xout, prui_xchg, prui_sxin,
   prui_sxout, prui_sxchg, prui_loop, prui_iloop, prui_qbgt, prui_qbge,
   prui_qblt, prui_qble, prui_qbeq, prui_qbne, prui_qba, prui_qbbs,
-  prui_qbbc, prui_lbbo, prui_sbbo, prui_lbco, prui_sbco
+  prui_qbbc, prui_lbbo, prui_sbbo, prui_lbco, prui_sbco, prui_tsen,
+  prui_mvib, prui_mviw, prui_mvid
 };
 
 /* This structure holds information for a particular instruction.
@@ -81,11 +82,14 @@  enum pru_instr_type
      j - a 5.3-bit right source register index OR 18-bit PC address
      l - burst length (unsigned 7-bit immediate or r0.b[0-3]) for xLBCO
      n - burst length (unsigned 7-bit immediate or r0.b[0-3]) for XFR
+     m - MVI destination operand
+     M - MVI source operand
      o - a 10-bit signed PC-relative offset
      O - an 8-bit unsigned PC-relative offset for LOOP termination point
      R - a 5-bit destination register index
      s - a 5.3-bit left source register index
      S - a 5-bit left source register index
+     t - task manager mode (0: disabled, 1: enabled)
      w - a single bit for "WakeOnStatus"
      W - a 16-bit unsigned immediate with IO=0 field (LDI)
      x - an 8-bit XFR wide-bus address immediate
@@ -236,15 +240,19 @@  struct pru_reg
 #define SUBOP_LMBD			3u
 #define SUBOP_SCAN			4u
 #define SUBOP_HALT			5u
-#define SUBOP_RSVD_FOR_MVIx		6u
+#define SUBOP_MVIX			6u
 #define SUBOP_XFR			7u
 #define SUBOP_LOOP			8u
+#define SUBOP_TSEN			9u
 #define SUBOP_RSVD_FOR_RFI		14u
 #define SUBOP_SLP			15u
 
 #define OP_SH_WAKEONSTATUS		23
 #define OP_MASK_WAKEONSTATUS		(0x1u << 23)
 
+#define OP_SH_TSKMGR_MODE		23
+#define OP_MASK_TSKMGR_MODE		(0x1u << 23)
+
 /* Format 2 XFR specific fields.  */
 #define OP_SH_SUBOP_XFR			23
 #define OP_MASK_SUBOP_XFR		(3u << 23)
@@ -265,6 +273,23 @@  struct pru_reg
 #define OP_SH_LOOP_JMPOFFS		0
 #define OP_MASK_LOOP_JMPOFFS		(0xffu << 0)
 
+/* Format 2 MVI specific fields.  */
+#define OP_SH_MVI_RS1_MODE		21
+#define OP_MASK_MVI_RS1_MODE		(3u << 21)
+#define OP_SH_MVI_RD_MODE		23
+#define OP_MASK_MVI_RD_MODE		(3u << 23)
+#define OP_SH_MVI_LEN			16
+#define OP_MASK_MVI_LEN			(3u << 16)
+
+#define MVI_OP_MODE_DIRECT		0u
+#define MVI_OP_MODE_INDIRECT		1u
+#define MVI_OP_MODE_INDIRECT_POSTINC	2u
+#define MVI_OP_MODE_INDIRECT_PREDEC	3u
+
+#define MVI_LEN_BYTE			0u
+#define MVI_LEN_WORD			1u
+#define MVI_LEN_DWORD			2u
+
 /* Format 4 specific fields.  */
 #define OP_SH_BROFF98			25
 #define OP_MASK_BROFF98			(0x3u << 25)
@@ -331,6 +356,12 @@  struct pru_reg
 #define OP_MATCH_SCAN	(OP_MATCH_FMT2_OP | (SUBOP_SCAN << OP_SH_SUBOP))
 #define OP_MATCH_HALT	(OP_MATCH_FMT2_OP | (SUBOP_HALT << OP_SH_SUBOP))
 #define OP_MATCH_SLP	(OP_MATCH_FMT2_OP | (SUBOP_SLP << OP_SH_SUBOP))
+#define OP_MATCH_MVIB	(OP_MATCH_FMT2_OP | (SUBOP_MVIX << OP_SH_SUBOP) \
+			 | (MVI_LEN_BYTE << OP_SH_MVI_LEN))
+#define OP_MATCH_MVIW	(OP_MATCH_FMT2_OP | (SUBOP_MVIX << OP_SH_SUBOP) \
+			 | (MVI_LEN_WORD << OP_SH_MVI_LEN))
+#define OP_MATCH_MVID	(OP_MATCH_FMT2_OP | (SUBOP_MVIX << OP_SH_SUBOP) \
+			 | (MVI_LEN_DWORD << OP_SH_MVI_LEN))
 #define OP_MATCH_XFR	(OP_MATCH_FMT2_OP | (SUBOP_XFR << OP_SH_SUBOP))
 #define OP_MATCH_SXFR	(OP_MATCH_XFR | OP_MASK_XFR_S)
 #define OP_MATCH_XIN	(OP_MATCH_XFR | (SUBOP_XFR_XIN << OP_SH_SUBOP_XFR))
@@ -342,6 +373,7 @@  struct pru_reg
 #define OP_MATCH_LOOP	(OP_MATCH_FMT2_OP | (SUBOP_LOOP << OP_SH_SUBOP))
 #define OP_MATCH_ILOOP	(OP_MATCH_FMT2_OP | (SUBOP_LOOP << OP_SH_SUBOP) \
 			 | OP_MASK_LOOP_INTERRUPTIBLE)
+#define OP_MATCH_TSEN	(OP_MATCH_FMT2_OP | (SUBOP_TSEN << OP_SH_SUBOP))
 
 #define OP_MATCH_QBGT	(OP_MATCH_FMT4_OP | OP_MASK_GT)
 #define OP_MATCH_QBGE	(OP_MATCH_FMT4_OP | OP_MASK_GT | OP_MASK_EQ)
@@ -394,6 +426,9 @@  struct pru_reg
 #define OP_MASK_LOOP_OP		(OP_MASK_FMT2_OP | OP_MASK_SUBOP \
 				 | OP_MASK_LOOP_INTERRUPTIBLE)
 
+#define OP_MASK_MVIX_OP		(OP_MASK_FMT2_OP | OP_MASK_SUBOP \
+				 | OP_MASK_MVI_LEN)
+
 /* These are the data structures we use to hold the instruction information.  */
 extern const struct pru_opcode pru_opcodes[];
 extern const int bfd_pru_num_opcodes;
diff --git a/opcodes/pru-dis.c b/opcodes/pru-dis.c
index 5465c26a138..547e3f3a2f8 100644
--- a/opcodes/pru-dis.c
+++ b/opcodes/pru-dis.c
@@ -66,15 +66,34 @@  pru_find_opcode (unsigned long opcode)
 #define NUMREGNAMES (32 * 8)
 
 static void
-pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
-			disassemble_info *info)
+pru_print_insn_arg_indreg (unsigned int r, unsigned int sel,
+			   unsigned int mode,
+			   disassemble_info *info)
 {
+  const char *fmtstr;
   unsigned int i = r * RSEL_NUM_ITEMS + sel;
   assert (i < (unsigned int)pru_num_regs);
   assert (i < NUMREGNAMES);
-  (*info->fprintf_func) (info->stream, "%s", pru_regs[i].name);
+
+  switch (mode)
+    {
+    case MVI_OP_MODE_DIRECT:		fmtstr = "%s"; break;
+    case MVI_OP_MODE_INDIRECT:		fmtstr = "*%s"; break;
+    case MVI_OP_MODE_INDIRECT_POSTINC:	fmtstr = "*%s++"; break;
+    case MVI_OP_MODE_INDIRECT_PREDEC:	fmtstr = "*--%s"; break;
+    default:				fmtstr = "<invalid>%s"; break;
+    }
+  (*info->fprintf_func) (info->stream, fmtstr, pru_regs[i].name);
+}
+
+static void
+pru_print_insn_arg_reg (unsigned int r, unsigned int sel,
+			disassemble_info *info)
+{
+  pru_print_insn_arg_indreg (r, sel, MVI_OP_MODE_DIRECT, info);
 }
 
+
 /* The function pru_print_insn_arg uses the character pointed
    to by ARGPTR to determine how it print the next token or separator
    character in the arguments to an instruction.  */
@@ -186,6 +205,18 @@  pru_print_insn_arg (const char *argptr,
 	  (*info->fprintf_func) (info->stream, "r0.b%ld", i);
 	}
       break;
+    case 'm':
+      pru_print_insn_arg_indreg (GET_INSN_FIELD (RD, opcode),
+				 GET_INSN_FIELD (RDSEL, opcode),
+				 GET_INSN_FIELD (MVI_RD_MODE, opcode),
+				 info);
+      break;
+    case 'M':
+      pru_print_insn_arg_indreg (GET_INSN_FIELD (RS1, opcode),
+				 GET_INSN_FIELD (RS1SEL, opcode),
+				 GET_INSN_FIELD (MVI_RS1_MODE, opcode),
+				 info);
+      break;
     case 'n':
       i = GET_INSN_FIELD (XFR_LENGTH, opcode);
       if (i < LSSBBO_BYTECOUNT_R0_BITS7_0)
@@ -200,6 +231,10 @@  pru_print_insn_arg (const char *argptr,
       i = GET_INSN_FIELD (CB, opcode);
       (*info->fprintf_func) (info->stream, "%ld", i);
       break;
+    case 't':
+      i = GET_INSN_FIELD (TSKMGR_MODE, opcode);
+      (*info->fprintf_func) (info->stream, "%ld", i);
+      break;
     case 'w':
       i = GET_INSN_FIELD (WAKEONSTATUS, opcode);
       (*info->fprintf_func) (info->stream, "%ld", i);
diff --git a/opcodes/pru-opc.c b/opcodes/pru-opc.c
index ee7fb16d8d4..aeef5224b29 100644
--- a/opcodes/pru-opc.c
+++ b/opcodes/pru-opc.c
@@ -125,9 +125,18 @@  const struct pru_opcode pru_opcodes[] =
    OP_MATCH_LMBD, OP_MASK_FMT2_OP | OP_MASK_SUBOP, 0, unsigned_immed8_overflow},
   { "halt", prui_halt, "",
    OP_MATCH_HALT, OP_MASK_FMT2_OP | OP_MASK_SUBOP, 0, no_overflow},
+  { "tsen", prui_tsen, "t",
+   OP_MATCH_TSEN, OP_MASK_FMT2_OP | OP_MASK_SUBOP, 0, no_overflow},
   { "slp", prui_slp, "w",
    OP_MATCH_SLP, OP_MASK_FMT2_OP | OP_MASK_SUBOP, 0, no_overflow},
 
+  { "mvib", prui_mvib, "m,M",
+   OP_MATCH_MVIB, OP_MASK_MVIX_OP, 0, no_overflow},
+  { "mviw", prui_mviw, "m,M",
+   OP_MATCH_MVIW, OP_MASK_MVIX_OP, 0, no_overflow},
+  { "mvid", prui_mvid, "m,M",
+   OP_MATCH_MVID, OP_MASK_MVIX_OP, 0, no_overflow},
+
   { "xin", prui_xin, "x,D,n",
    OP_MATCH_XIN, OP_MASK_XFR_OP, 0, unsigned_immed8_overflow},
   { "xout", prui_xout, "x,D,n",