/ src / ARMeilleure / Instructions / InstEmitMemory32.cs
InstEmitMemory32.cs
  1  using ARMeilleure.Decoders;
  2  using ARMeilleure.IntermediateRepresentation;
  3  using ARMeilleure.State;
  4  using ARMeilleure.Translation;
  5  using System;
  6  using static ARMeilleure.Instructions.InstEmitHelper;
  7  using static ARMeilleure.Instructions.InstEmitMemoryHelper;
  8  using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  9  
 10  namespace ARMeilleure.Instructions
 11  {
 12      static partial class InstEmit32
 13      {
 14          private const int ByteSizeLog2 = 0;
 15          private const int HWordSizeLog2 = 1;
 16          private const int WordSizeLog2 = 2;
 17          private const int DWordSizeLog2 = 3;
 18  
 19          [Flags]
 20          enum AccessType
 21          {
 22              Store = 0,
 23              Signed = 1,
 24              Load = 2,
 25              Ordered = 4,
 26              Exclusive = 8,
 27  
 28              LoadZx = Load,
 29              LoadSx = Load | Signed,
 30          }
 31  
 32          public static void Ldm(ArmEmitterContext context)
 33          {
 34              IOpCode32MemMult op = (IOpCode32MemMult)context.CurrOp;
 35  
 36              Operand n = GetIntA32(context, op.Rn);
 37  
 38              Operand baseAddress = context.Add(n, Const(op.Offset));
 39  
 40              bool writesToPc = (op.RegisterMask & (1 << RegisterAlias.Aarch32Pc)) != 0;
 41  
 42              bool writeBack = op.PostOffset != 0 && (op.Rn != RegisterAlias.Aarch32Pc || !writesToPc);
 43  
 44              if (writeBack)
 45              {
 46                  SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
 47              }
 48  
 49              int mask = op.RegisterMask;
 50              int offset = 0;
 51  
 52              for (int register = 0; mask != 0; mask >>= 1, register++)
 53              {
 54                  if ((mask & 1) != 0)
 55                  {
 56                      Operand address = context.Add(baseAddress, Const(offset));
 57  
 58                      EmitLoadZx(context, address, register, WordSizeLog2);
 59  
 60                      offset += 4;
 61                  }
 62              }
 63          }
 64  
 65          public static void Ldr(ArmEmitterContext context)
 66          {
 67              EmitLoadOrStore(context, WordSizeLog2, AccessType.LoadZx);
 68          }
 69  
 70          public static void Ldrb(ArmEmitterContext context)
 71          {
 72              EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadZx);
 73          }
 74  
 75          public static void Ldrd(ArmEmitterContext context)
 76          {
 77              EmitLoadOrStore(context, DWordSizeLog2, AccessType.LoadZx);
 78          }
 79  
 80          public static void Ldrh(ArmEmitterContext context)
 81          {
 82              EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadZx);
 83          }
 84  
 85          public static void Ldrsb(ArmEmitterContext context)
 86          {
 87              EmitLoadOrStore(context, ByteSizeLog2, AccessType.LoadSx);
 88          }
 89  
 90          public static void Ldrsh(ArmEmitterContext context)
 91          {
 92              EmitLoadOrStore(context, HWordSizeLog2, AccessType.LoadSx);
 93          }
 94  
 95          public static void Stm(ArmEmitterContext context)
 96          {
 97              IOpCode32MemMult op = (IOpCode32MemMult)context.CurrOp;
 98  
 99              Operand n = context.Copy(GetIntA32(context, op.Rn));
100  
101              Operand baseAddress = context.Add(n, Const(op.Offset));
102  
103              int mask = op.RegisterMask;
104              int offset = 0;
105  
106              for (int register = 0; mask != 0; mask >>= 1, register++)
107              {
108                  if ((mask & 1) != 0)
109                  {
110                      Operand address = context.Add(baseAddress, Const(offset));
111  
112                      EmitStore(context, address, register, WordSizeLog2);
113  
114                      // Note: If Rn is also specified on the register list,
115                      // and Rn is the first register on this list, then the
116                      // value that is written to memory is the unmodified value,
117                      // before the write back. If it is on the list, but it's
118                      // not the first one, then the value written to memory
119                      // varies between CPUs.
120                      if (offset == 0 && op.PostOffset != 0)
121                      {
122                          // Emit write back after the first write.
123                          SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
124                      }
125  
126                      offset += 4;
127                  }
128              }
129          }
130  
131          public static void Str(ArmEmitterContext context)
132          {
133              EmitLoadOrStore(context, WordSizeLog2, AccessType.Store);
134          }
135  
136          public static void Strb(ArmEmitterContext context)
137          {
138              EmitLoadOrStore(context, ByteSizeLog2, AccessType.Store);
139          }
140  
141          public static void Strd(ArmEmitterContext context)
142          {
143              EmitLoadOrStore(context, DWordSizeLog2, AccessType.Store);
144          }
145  
146          public static void Strh(ArmEmitterContext context)
147          {
148              EmitLoadOrStore(context, HWordSizeLog2, AccessType.Store);
149          }
150  
151          private static void EmitLoadOrStore(ArmEmitterContext context, int size, AccessType accType)
152          {
153              IOpCode32Mem op = (IOpCode32Mem)context.CurrOp;
154  
155              Operand n = context.Copy(GetIntA32AlignedPC(context, op.Rn));
156              Operand m = GetMemM(context, setCarry: false);
157  
158              Operand temp = default;
159  
160              if (op.Index || op.WBack)
161              {
162                  temp = op.Add
163                      ? context.Add(n, m)
164                      : context.Subtract(n, m);
165              }
166  
167              if (op.WBack)
168              {
169                  SetIntA32(context, op.Rn, temp);
170              }
171  
172              Operand address;
173  
174              if (op.Index)
175              {
176                  address = temp;
177              }
178              else
179              {
180                  address = n;
181              }
182  
183              if ((accType & AccessType.Load) != 0)
184              {
185                  void Load(int rt, int offs, int loadSize)
186                  {
187                      Operand addr = context.Add(address, Const(offs));
188  
189                      if ((accType & AccessType.Signed) != 0)
190                      {
191                          EmitLoadSx32(context, addr, rt, loadSize);
192                      }
193                      else
194                      {
195                          EmitLoadZx(context, addr, rt, loadSize);
196                      }
197                  }
198  
199                  if (size == DWordSizeLog2)
200                  {
201                      Operand lblBigEndian = Label();
202                      Operand lblEnd = Label();
203  
204                      context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
205  
206                      Load(op.Rt, 0, WordSizeLog2);
207                      Load(op.Rt2, 4, WordSizeLog2);
208  
209                      context.Branch(lblEnd);
210  
211                      context.MarkLabel(lblBigEndian);
212  
213                      Load(op.Rt2, 0, WordSizeLog2);
214                      Load(op.Rt, 4, WordSizeLog2);
215  
216                      context.MarkLabel(lblEnd);
217                  }
218                  else
219                  {
220                      Load(op.Rt, 0, size);
221                  }
222              }
223              else
224              {
225                  void Store(int rt, int offs, int storeSize)
226                  {
227                      Operand addr = context.Add(address, Const(offs));
228  
229                      EmitStore(context, addr, rt, storeSize);
230                  }
231  
232                  if (size == DWordSizeLog2)
233                  {
234                      Operand lblBigEndian = Label();
235                      Operand lblEnd = Label();
236  
237                      context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
238  
239                      Store(op.Rt, 0, WordSizeLog2);
240                      Store(op.Rt2, 4, WordSizeLog2);
241  
242                      context.Branch(lblEnd);
243  
244                      context.MarkLabel(lblBigEndian);
245  
246                      Store(op.Rt2, 0, WordSizeLog2);
247                      Store(op.Rt, 4, WordSizeLog2);
248  
249                      context.MarkLabel(lblEnd);
250                  }
251                  else
252                  {
253                      Store(op.Rt, 0, size);
254                  }
255              }
256          }
257  
258          public static void Adr(ArmEmitterContext context)
259          {
260              IOpCode32Adr op = (IOpCode32Adr)context.CurrOp;
261              SetIntA32(context, op.Rd, Const(op.Immediate));
262          }
263      }
264  }