From 501c3d5cea6b96f991453cc6f8d395d358d0d4c3 Mon Sep 17 00:00:00 2001
From: Mary <me@thog.eu>
Date: Fri, 27 Aug 2021 00:07:44 +0200
Subject: [PATCH] Implement MSR instruction for A32 (#2585)

* Implement MSR instruction

Fix #1342.

Now Pocket Rumble is playable.

* Address gdkchan's comments

* Address gdkchan's comments

* Address gdkchan's comment
---
 ARMeilleure/Decoders/OpCode32MsrReg.cs       | 29 +++++++++++++++
 ARMeilleure/Decoders/OpCodeTable.cs          |  1 +
 ARMeilleure/Instructions/InstEmitSystem32.cs | 39 ++++++++++++++++++++
 3 files changed, 69 insertions(+)
 create mode 100644 ARMeilleure/Decoders/OpCode32MsrReg.cs

diff --git a/ARMeilleure/Decoders/OpCode32MsrReg.cs b/ARMeilleure/Decoders/OpCode32MsrReg.cs
new file mode 100644
index 00000000..d897ffd8
--- /dev/null
+++ b/ARMeilleure/Decoders/OpCode32MsrReg.cs
@@ -0,0 +1,29 @@
+using ARMeilleure.State;
+
+namespace ARMeilleure.Decoders
+{
+    class OpCode32MsrReg : OpCode32
+    {
+        public bool R      { get; }
+        public int  Mask   { get; }
+        public int  Rd     { get; }
+        public bool Banked { get; }
+        public int  Rn     { get; }
+
+        public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32MsrReg(inst, address, opCode);
+
+        public OpCode32MsrReg(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
+        {
+            R = ((opCode >> 22) & 1) != 0;
+            Mask = (opCode >> 16) & 0xf;
+            Rd = (opCode >> 12) & 0xf;
+            Banked = ((opCode >> 9) & 1) != 0;
+            Rn = (opCode >> 0) & 0xf;
+
+            if (Rn == RegisterAlias.Aarch32Pc || Mask == 0)
+            {
+                Instruction = InstDescriptor.Undefined;
+            }
+        }
+    }
+}
diff --git a/ARMeilleure/Decoders/OpCodeTable.cs b/ARMeilleure/Decoders/OpCodeTable.cs
index c1ac8363..12ff051c 100644
--- a/ARMeilleure/Decoders/OpCodeTable.cs
+++ b/ARMeilleure/Decoders/OpCodeTable.cs
@@ -702,6 +702,7 @@ namespace ARMeilleure.Decoders
             SetA32("<<<<00110100xxxxxxxxxxxxxxxxxxxx", InstName.Movt,    InstEmit32.Movt,    OpCode32AluImm16.Create);
             SetA32("<<<<1110xxx1xxxxxxxx111xxxx1xxxx", InstName.Mrc,     InstEmit32.Mrc,     OpCode32System.Create);
             SetA32("<<<<11000101xxxxxxxx111xxxxxxxxx", InstName.Mrrc,    InstEmit32.Mrrc,    OpCode32System.Create);
+            SetA32("<<<<00010x10xxxx111100000000xxxx", InstName.Msr,     InstEmit32.Msr,     OpCode32MsrReg.Create);
             SetA32("<<<<0000000xxxxx0000xxxx1001xxxx", InstName.Mul,     InstEmit32.Mul,     OpCode32AluMla.Create);
             SetA32("<<<<0011111x0000xxxxxxxxxxxxxxxx", InstName.Mvn,     InstEmit32.Mvn,     OpCode32AluImm.Create);
             SetA32("<<<<0001111x0000xxxxxxxxxxx0xxxx", InstName.Mvn,     InstEmit32.Mvn,     OpCode32AluRsImm.Create);
diff --git a/ARMeilleure/Instructions/InstEmitSystem32.cs b/ARMeilleure/Instructions/InstEmitSystem32.cs
index 9e28a1a1..3e752659 100644
--- a/ARMeilleure/Instructions/InstEmitSystem32.cs
+++ b/ARMeilleure/Instructions/InstEmitSystem32.cs
@@ -169,6 +169,45 @@ namespace ARMeilleure.Instructions
             SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32))));
         }
 
+        public static void Msr(ArmEmitterContext context)
+        {
+            OpCode32MsrReg op = (OpCode32MsrReg)context.CurrOp;
+
+            if (op.R)
+            {
+                throw new NotImplementedException("SPSR");
+            }
+            else
+            {
+                if ((op.Mask & 8) != 0)
+                {
+                    Operand value = GetIntA32(context, op.Rn);
+
+                    EmitSetNzcv(context, value);
+
+                    Operand q = context.ShiftRightUI(value, Const((int)PState.QFlag));
+                    q = context.BitwiseAnd(q, Const(1));
+
+                    SetFlag(context, PState.QFlag, q);
+                }
+
+                if ((op.Mask & 4) != 0)
+                {
+                    throw new NotImplementedException("APSR_g");
+                }
+
+                if ((op.Mask & 2) != 0)
+                {
+                    throw new NotImplementedException("CPSR_x");
+                }
+
+                if ((op.Mask & 1) != 0)
+                {
+                    throw new NotImplementedException("CPSR_c");
+                }
+            }
+        }
+
         public static void Nop(ArmEmitterContext context) { }
 
         public static void Vmrs(ArmEmitterContext context)