/ src / ARMeilleure / Instructions / InstEmitSystem32.cs
InstEmitSystem32.cs
  1  using ARMeilleure.Decoders;
  2  using ARMeilleure.IntermediateRepresentation;
  3  using ARMeilleure.State;
  4  using ARMeilleure.Translation;
  5  using System;
  6  using System.Reflection;
  7  using static ARMeilleure.Instructions.InstEmitHelper;
  8  using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  9  
 10  namespace ARMeilleure.Instructions
 11  {
 12      static partial class InstEmit32
 13      {
 14          public static void Mcr(ArmEmitterContext context)
 15          {
 16              OpCode32System op = (OpCode32System)context.CurrOp;
 17  
 18              if (op.Coproc != 15 || op.Opc1 != 0)
 19              {
 20                  InstEmit.Und(context);
 21  
 22                  return;
 23              }
 24  
 25              switch (op.CRn)
 26              {
 27                  case 13: // Process and Thread Info.
 28                      if (op.CRm != 0)
 29                      {
 30                          throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
 31                      }
 32  
 33                      switch (op.Opc2)
 34                      {
 35                          case 2:
 36                              EmitSetTpidrEl0(context);
 37                              return;
 38  
 39                          default:
 40                              throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
 41                      }
 42  
 43                  case 7:
 44                      switch (op.CRm) // Cache and Memory barrier.
 45                      {
 46                          case 10:
 47                              switch (op.Opc2)
 48                              {
 49                                  case 5: // Data Memory Barrier Register.
 50                                      return; // No-op.
 51  
 52                                  default:
 53                                      throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X16} at 0x{op.Address:X16} (0x{op.RawOpCode:X}).");
 54                              }
 55  
 56                          default:
 57                              throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X16} at 0x{op.Address:X16} (0x{op.RawOpCode:X}).");
 58                      }
 59  
 60                  default:
 61                      throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
 62              }
 63          }
 64  
 65          public static void Mrc(ArmEmitterContext context)
 66          {
 67              OpCode32System op = (OpCode32System)context.CurrOp;
 68  
 69              if (op.Coproc != 15 || op.Opc1 != 0)
 70              {
 71                  InstEmit.Und(context);
 72  
 73                  return;
 74              }
 75  
 76              Operand result;
 77  
 78              switch (op.CRn)
 79              {
 80                  case 13: // Process and Thread Info.
 81                      if (op.CRm != 0)
 82                      {
 83                          throw new NotImplementedException($"Unknown MRC CRm 0x{op.CRm:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
 84                      }
 85  
 86                      result = op.Opc2 switch
 87                      {
 88                          2 => EmitGetTpidrEl0(context),
 89                          3 => EmitGetTpidrroEl0(context),
 90                          _ => throw new NotImplementedException(
 91                              $"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."),
 92                      };
 93  
 94                      break;
 95  
 96                  default:
 97                      throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
 98              }
 99  
100              if (op.Rt == RegisterAlias.Aarch32Pc)
101              {
102                  // Special behavior: copy NZCV flags into APSR.
103                  EmitSetNzcv(context, result);
104  
105                  return;
106              }
107              else
108              {
109                  SetIntA32(context, op.Rt, result);
110              }
111          }
112  
113          public static void Mrrc(ArmEmitterContext context)
114          {
115              OpCode32System op = (OpCode32System)context.CurrOp;
116  
117              if (op.Coproc != 15)
118              {
119                  InstEmit.Und(context);
120  
121                  return;
122              }
123  
124              int opc = op.MrrcOp;
125              MethodInfo info = op.CRm switch
126              {
127                  // Timer.
128                  14 => opc switch
129                  {
130                      0 => typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)),
131                      _ => throw new NotImplementedException($"Unknown MRRC Opc1 0x{opc:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."),
132                  },
133                  _ => throw new NotImplementedException($"Unknown MRRC 0x{op.RawOpCode:X} at 0x{op.Address:X}."),
134              };
135              Operand result = context.Call(info);
136  
137              SetIntA32(context, op.Rt, context.ConvertI64ToI32(result));
138              SetIntA32(context, op.CRn, context.ConvertI64ToI32(context.ShiftRightUI(result, Const(32))));
139          }
140  
141          public static void Mrs(ArmEmitterContext context)
142          {
143              OpCode32Mrs op = (OpCode32Mrs)context.CurrOp;
144  
145              if (op.R)
146              {
147                  throw new NotImplementedException("SPSR");
148              }
149              else
150              {
151                  Operand spsr = context.ShiftLeft(GetFlag(PState.VFlag), Const((int)PState.VFlag));
152                  spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.CFlag), Const((int)PState.CFlag)));
153                  spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.ZFlag), Const((int)PState.ZFlag)));
154                  spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.NFlag), Const((int)PState.NFlag)));
155                  spsr = context.BitwiseOr(spsr, context.ShiftLeft(GetFlag(PState.QFlag), Const((int)PState.QFlag)));
156  
157                  // TODO: Remaining flags.
158  
159                  SetIntA32(context, op.Rd, spsr);
160              }
161          }
162  
163          public static void Msr(ArmEmitterContext context)
164          {
165              OpCode32MsrReg op = (OpCode32MsrReg)context.CurrOp;
166  
167              if (op.R)
168              {
169                  throw new NotImplementedException("SPSR");
170              }
171              else
172              {
173                  if ((op.Mask & 8) != 0)
174                  {
175                      Operand value = GetIntA32(context, op.Rn);
176  
177                      EmitSetNzcv(context, value);
178  
179                      Operand q = context.BitwiseAnd(context.ShiftRightUI(value, Const((int)PState.QFlag)), Const(1));
180  
181                      SetFlag(context, PState.QFlag, q);
182                  }
183  
184                  if ((op.Mask & 4) != 0)
185                  {
186                      throw new NotImplementedException("APSR_g");
187                  }
188  
189                  if ((op.Mask & 2) != 0)
190                  {
191                      throw new NotImplementedException("CPSR_x");
192                  }
193  
194                  if ((op.Mask & 1) != 0)
195                  {
196                      throw new NotImplementedException("CPSR_c");
197                  }
198              }
199          }
200  
201          public static void Nop(ArmEmitterContext context) { }
202  
203          public static void Vmrs(ArmEmitterContext context)
204          {
205              OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
206  
207              if (op.Rt == RegisterAlias.Aarch32Pc && op.Sreg == 0b0001)
208              {
209                  // Special behavior: copy NZCV flags into APSR.
210                  SetFlag(context, PState.VFlag, GetFpFlag(FPState.VFlag));
211                  SetFlag(context, PState.CFlag, GetFpFlag(FPState.CFlag));
212                  SetFlag(context, PState.ZFlag, GetFpFlag(FPState.ZFlag));
213                  SetFlag(context, PState.NFlag, GetFpFlag(FPState.NFlag));
214  
215                  return;
216              }
217  
218              switch (op.Sreg)
219              {
220                  case 0b0000: // FPSID
221                      throw new NotImplementedException("Supervisor Only");
222                  case 0b0001: // FPSCR
223                      EmitGetFpscr(context);
224                      return;
225                  case 0b0101: // MVFR2
226                      throw new NotImplementedException("MVFR2");
227                  case 0b0110: // MVFR1
228                      throw new NotImplementedException("MVFR1");
229                  case 0b0111: // MVFR0
230                      throw new NotImplementedException("MVFR0");
231                  case 0b1000: // FPEXC
232                      throw new NotImplementedException("Supervisor Only");
233                  default:
234                      throw new NotImplementedException($"Unknown VMRS 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
235              }
236          }
237  
238          public static void Vmsr(ArmEmitterContext context)
239          {
240              OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
241  
242              switch (op.Sreg)
243              {
244                  case 0b0000: // FPSID
245                      throw new NotImplementedException("Supervisor Only");
246                  case 0b0001: // FPSCR
247                      EmitSetFpscr(context);
248                      return;
249                  case 0b0101: // MVFR2
250                      throw new NotImplementedException("MVFR2");
251                  case 0b0110: // MVFR1
252                      throw new NotImplementedException("MVFR1");
253                  case 0b0111: // MVFR0
254                      throw new NotImplementedException("MVFR0");
255                  case 0b1000: // FPEXC
256                      throw new NotImplementedException("Supervisor Only");
257                  default:
258                      throw new NotImplementedException($"Unknown VMSR 0x{op.RawOpCode:X} at 0x{op.Address:X}.");
259              }
260          }
261  
262          private static void EmitSetNzcv(ArmEmitterContext context, Operand t)
263          {
264              Operand v = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.VFlag)), Const(1));
265              Operand c = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.CFlag)), Const(1));
266              Operand z = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.ZFlag)), Const(1));
267              Operand n = context.BitwiseAnd(context.ShiftRightUI(t, Const((int)PState.NFlag)), Const(1));
268  
269              SetFlag(context, PState.VFlag, v);
270              SetFlag(context, PState.CFlag, c);
271              SetFlag(context, PState.ZFlag, z);
272              SetFlag(context, PState.NFlag, n);
273          }
274  
275          private static void EmitGetFpscr(ArmEmitterContext context)
276          {
277              OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
278  
279              Operand fpscr = Const(0);
280  
281              for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
282              {
283                  if (FPSCR.Mask.HasFlag((FPSCR)(1u << flag)))
284                  {
285                      fpscr = context.BitwiseOr(fpscr, context.ShiftLeft(GetFpFlag((FPState)flag), Const(flag)));
286                  }
287              }
288  
289              SetIntA32(context, op.Rt, fpscr);
290          }
291  
292          private static void EmitSetFpscr(ArmEmitterContext context)
293          {
294              OpCode32SimdSpecial op = (OpCode32SimdSpecial)context.CurrOp;
295  
296              Operand fpscr = GetIntA32(context, op.Rt);
297  
298              for (int flag = 0; flag < RegisterConsts.FpFlagsCount; flag++)
299              {
300                  if (FPSCR.Mask.HasFlag((FPSCR)(1u << flag)))
301                  {
302                      SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpscr, Const(flag)), Const(1)));
303                  }
304              }
305  
306              context.UpdateArmFpMode();
307          }
308  
309          private static Operand EmitGetTpidrEl0(ArmEmitterContext context)
310          {
311              OpCode32System op = (OpCode32System)context.CurrOp;
312  
313              Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
314  
315              return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())));
316          }
317  
318          private static Operand EmitGetTpidrroEl0(ArmEmitterContext context)
319          {
320              OpCode32System op = (OpCode32System)context.CurrOp;
321  
322              Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
323  
324              return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset())));
325          }
326  
327          private static void EmitSetTpidrEl0(ArmEmitterContext context)
328          {
329              OpCode32System op = (OpCode32System)context.CurrOp;
330  
331              Operand value = GetIntA32(context, op.Rt);
332  
333              Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
334  
335              context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), context.ZeroExtend32(OperandType.I64, value));
336          }
337      }
338  }