Просмотр исходного кода

Add ARM32 support on the translator (#561)

* Remove ARM32 interpreter and add ARM32 support on the translator

* Nits.

* Rename Cond -> Condition

* Align code again

* Rename Data to Alu

* Enable ARM32 support and handle undefined instructions

* Use the IsThumb method to check if its a thumb opcode

* Remove another 32-bits check
gdkchan 7 лет назад
Родитель
Сommit
36b9ab0e48
57 измененных файлов с 1267 добавлено и 488 удалено
  1. 0 2
      ChocolArm64/CpuThread.cs
  2. 11 1
      ChocolArm64/Decoders/BitUtils.cs
  3. 1 1
      ChocolArm64/Decoders/Condition.cs
  4. 49 18
      ChocolArm64/Decoders/Decoder.cs
  5. 5 0
      ChocolArm64/Decoders/DecoderHelper.cs
  6. 9 0
      ChocolArm64/Decoders/IOpCode32.cs
  7. 10 0
      ChocolArm64/Decoders/IOpCodeAlu32.cs
  8. 7 0
      ChocolArm64/Decoders/IOpCodeBImm.cs
  9. 4 0
      ChocolArm64/Decoders/IOpCodeBImm32.cs
  10. 7 0
      ChocolArm64/Decoders/IOpCodeBReg32.cs
  11. 1 1
      ChocolArm64/Decoders/IOpCodeCond64.cs
  12. 24 0
      ChocolArm64/Decoders/OpCode32.cs
  13. 5 5
      ChocolArm64/Decoders/OpCode64.cs
  14. 20 0
      ChocolArm64/Decoders/OpCodeAlu32.cs
  15. 21 0
      ChocolArm64/Decoders/OpCodeAluImm32.cs
  16. 22 0
      ChocolArm64/Decoders/OpCodeAluImm8T16.cs
  17. 20 0
      ChocolArm64/Decoders/OpCodeAluRsImm32.cs
  18. 29 0
      ChocolArm64/Decoders/OpCodeBImm32.cs
  19. 1 1
      ChocolArm64/Decoders/OpCodeBImm64.cs
  20. 2 2
      ChocolArm64/Decoders/OpCodeBImmCond64.cs
  21. 14 0
      ChocolArm64/Decoders/OpCodeBReg32.cs
  22. 14 0
      ChocolArm64/Decoders/OpCodeBRegT16.cs
  23. 5 5
      ChocolArm64/Decoders/OpCodeCcmp64.cs
  24. 3 3
      ChocolArm64/Decoders/OpCodeCsel64.cs
  25. 3 3
      ChocolArm64/Decoders/OpCodeSimdFcond64.cs
  26. 14 0
      ChocolArm64/Decoders/OpCodeT16.cs
  27. 0 15
      ChocolArm64/Decoders32/A32OpCode.cs
  28. 0 16
      ChocolArm64/Decoders32/A32OpCodeBImmAl.cs
  29. 6 8
      ChocolArm64/Instructions/Inst.cs
  30. 99 0
      ChocolArm64/Instructions/InstEmit32Helper.cs
  31. 62 45
      ChocolArm64/Instructions/InstEmitAlu.cs
  32. 123 0
      ChocolArm64/Instructions/InstEmitAlu32.cs
  33. 279 38
      ChocolArm64/Instructions/InstEmitAluHelper.cs
  34. 1 1
      ChocolArm64/Instructions/InstEmitCcmp.cs
  35. 5 31
      ChocolArm64/Instructions/InstEmitFlow.cs
  36. 99 0
      ChocolArm64/Instructions/InstEmitFlow32.cs
  37. 39 0
      ChocolArm64/Instructions/InstEmitFlowHelper.cs
  38. 1 1
      ChocolArm64/Instructions/InstEmitSimdMemory.cs
  39. 0 8
      ChocolArm64/Instructions/InstInterpreter.cs
  40. 0 7
      ChocolArm64/Instructions32/A32InstInterpretAlu.cs
  41. 0 70
      ChocolArm64/Instructions32/A32InstInterpretFlow.cs
  42. 0 65
      ChocolArm64/Instructions32/A32InstInterpretHelper.cs
  43. 88 62
      ChocolArm64/OpCodeTable.cs
  44. 15 0
      ChocolArm64/State/Aarch32Mode.cs
  45. 16 12
      ChocolArm64/State/CpuThreadState.cs
  46. 3 2
      ChocolArm64/State/ExecutionMode.cs
  47. 4 0
      ChocolArm64/State/PState.cs
  48. 2 0
      ChocolArm64/State/Register.cs
  49. 41 0
      ChocolArm64/State/RegisterAlias.cs
  50. 58 15
      ChocolArm64/Translation/ILEmitterCtx.cs
  51. 8 33
      ChocolArm64/Translator.cs
  52. 0 1
      ChocolArm64/TranslatorCache.cs
  53. 0 15
      Ryujinx.HLE/HOS/Horizon.cs
  54. 2 0
      Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs
  55. 7 0
      Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
  56. 2 0
      Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs
  57. 6 1
      Ryujinx.HLE/HOS/ProgramLoader.cs

+ 0 - 2
ChocolArm64/CpuThread.cs

@@ -25,8 +25,6 @@ namespace ChocolArm64
 
             ThreadState = new CpuThreadState();
 
-            ThreadState.ExecutionMode = ExecutionMode.AArch64;
-
             ThreadState.Running = true;
 
             Work = new Thread(delegate()

+ 11 - 1
ChocolArm64/BitUtils.cs → ChocolArm64/Decoders/BitUtils.cs

@@ -1,4 +1,4 @@
-namespace ChocolArm64
+namespace ChocolArm64.Decoders
 {
     static class BitUtils
     {
@@ -36,6 +36,16 @@ namespace ChocolArm64
             return bits == 64 ? -1L : (1L << bits) - 1;
         }
 
+        public static int RotateRight(int bits, int shift, int size)
+        {
+            return (int)RotateRight((uint)bits, shift, size);
+        }
+
+        public static uint RotateRight(uint bits, int shift, int size)
+        {
+            return (bits >> shift) | (bits << (size - shift));
+        }
+
         public static long RotateRight(long bits, int shift, int size)
         {
             return (long)RotateRight((ulong)bits, shift, size);

+ 1 - 1
ChocolArm64/Decoders/Cond.cs → ChocolArm64/Decoders/Condition.cs

@@ -1,6 +1,6 @@
 namespace ChocolArm64.Decoders
 {
-    enum Cond
+    enum Condition
     {
         Eq   = 0,
         Ne   = 1,

+ 49 - 18
ChocolArm64/Decoders/Decoder.cs

@@ -19,20 +19,20 @@ namespace ChocolArm64.Decoders
             _opActivators = new ConcurrentDictionary<Type, OpActivator>();
         }
 
-        public static Block DecodeBasicBlock(CpuThreadState state, MemoryManager memory, long start)
+        public static Block DecodeBasicBlock(MemoryManager memory, long start, ExecutionMode mode)
         {
             Block block = new Block(start);
 
-            FillBlock(state, memory, block);
+            FillBlock(memory, mode, block);
 
             return block;
         }
 
         public static Block DecodeSubroutine(
             TranslatorCache cache,
-            CpuThreadState  state,
             MemoryManager   memory,
-            long            start)
+            long            start,
+            ExecutionMode   mode)
         {
             Dictionary<long, Block> visited    = new Dictionary<long, Block>();
             Dictionary<long, Block> visitedEnd = new Dictionary<long, Block>();
@@ -59,7 +59,7 @@ namespace ChocolArm64.Decoders
             {
                 Block current = blocks.Dequeue();
 
-                FillBlock(state, memory, current);
+                FillBlock(memory, mode, current);
 
                 //Set child blocks. "Branch" is the block the branch instruction
                 //points to (when taken), "Next" is the block at the next address,
@@ -71,7 +71,7 @@ namespace ChocolArm64.Decoders
 
                     OpCode64 lastOp = current.GetLastOp();
 
-                    if (lastOp is OpCodeBImm64 op)
+                    if (lastOp is IOpCodeBImm op)
                     {
                         if (op.Emitter == InstEmit.Bl)
                         {
@@ -83,8 +83,7 @@ namespace ChocolArm64.Decoders
                         }
                     }
 
-                    if (!((lastOp is OpCodeBImmAl64) ||
-                          (lastOp is OpCodeBReg64)) || hasCachedSub)
+                    if (!IsUnconditionalBranch(lastOp) || hasCachedSub)
                     {
                         current.Next = Enqueue(current.EndPosition);
                     }
@@ -121,7 +120,7 @@ namespace ChocolArm64.Decoders
             return entry;
         }
 
-        private static void FillBlock(CpuThreadState state, MemoryManager memory, Block block)
+        private static void FillBlock(MemoryManager memory, ExecutionMode mode, Block block)
         {
             long position = block.Position;
 
@@ -129,13 +128,11 @@ namespace ChocolArm64.Decoders
 
             do
             {
-                //TODO: This needs to be changed to support both AArch32 and AArch64,
-                //once JIT support is introduced on AArch32 aswell.
-                opCode = DecodeOpCode(state, memory, position);
+                opCode = DecodeOpCode(memory, position, mode);
 
                 block.OpCodes.Add(opCode);
 
-                position += 4;
+                position += opCode.OpCodeSizeInBytes;
             }
             while (!(IsBranch(opCode) || IsException(opCode)));
 
@@ -145,7 +142,35 @@ namespace ChocolArm64.Decoders
         private static bool IsBranch(OpCode64 opCode)
         {
             return opCode is OpCodeBImm64 ||
-                   opCode is OpCodeBReg64;
+                   opCode is OpCodeBReg64 || IsAarch32Branch(opCode);
+        }
+
+        private static bool IsUnconditionalBranch(OpCode64 opCode)
+        {
+            return opCode is OpCodeBImmAl64 ||
+                   opCode is OpCodeBReg64   || IsAarch32UnconditionalBranch(opCode);
+        }
+
+        private static bool IsAarch32UnconditionalBranch(OpCode64 opCode)
+        {
+            if (!(opCode is OpCode32 op))
+            {
+                return false;
+            }
+
+            //Note: On ARM32, most instructions have conditional execution,
+            //so there's no "Always" (unconditional) branch like on ARM64.
+            //We need to check if the condition is "Always" instead.
+            return IsAarch32Branch(op) && op.Cond >= Condition.Al;
+        }
+
+        private static bool IsAarch32Branch(OpCode64 opCode)
+        {
+            //Note: On ARM32, most ALU operations can write to R15 (PC),
+            //so we must consider such operations as a branch in potential aswell.
+            return  opCode is IOpCodeBImm32 ||
+                    opCode is IOpCodeBReg32 ||
+                   (opCode is IOpCodeAlu32 op && op.Rd == RegisterAlias.Aarch32Pc);
         }
 
         private static bool IsException(OpCode64 opCode)
@@ -155,20 +180,26 @@ namespace ChocolArm64.Decoders
                    opCode.Emitter == InstEmit.Und;
         }
 
-        public static OpCode64 DecodeOpCode(CpuThreadState state, MemoryManager memory, long position)
+        public static OpCode64 DecodeOpCode(MemoryManager memory, long position, ExecutionMode mode)
         {
             int opCode = memory.ReadInt32(position);
 
             Inst inst;
 
-            if (state.ExecutionMode == ExecutionMode.AArch64)
+            if (mode == ExecutionMode.Aarch64)
             {
                 inst = OpCodeTable.GetInstA64(opCode);
             }
             else
             {
-                //TODO: Thumb support.
-                inst = OpCodeTable.GetInstA32(opCode);
+                if (mode == ExecutionMode.Aarch32Arm)
+                {
+                    inst = OpCodeTable.GetInstA32(opCode);
+                }
+                else /* if (mode == ExecutionMode.Aarch32Thumb) */
+                {
+                    inst = OpCodeTable.GetInstT32(opCode);
+                }
             }
 
             OpCode64 decodedOpCode = new OpCode64(Inst.Undefined, position, opCode);

+ 5 - 0
ChocolArm64/Decoders/DecoderHelper.cs

@@ -89,6 +89,11 @@ namespace ChocolArm64.Decoders
             return value;
         }
 
+        public static long DecodeImm24_2(int opCode)
+        {
+            return ((long)opCode << 40) >> 38;
+        }
+
         public static long DecodeImm26_2(int opCode)
         {
             return ((long)opCode << 38) >> 36;

+ 9 - 0
ChocolArm64/Decoders/IOpCode32.cs

@@ -0,0 +1,9 @@
+namespace ChocolArm64.Decoders
+{
+    interface IOpCode32 : IOpCode64
+    {
+        Condition Cond { get; }
+
+        uint GetPc();
+    }
+}

+ 10 - 0
ChocolArm64/Decoders/IOpCodeAlu32.cs

@@ -0,0 +1,10 @@
+namespace ChocolArm64.Decoders
+{
+    interface IOpCodeAlu32 : IOpCode32
+    {
+        int Rd { get; }
+        int Rn { get; }
+
+        bool SetFlags { get; }
+    }
+}

+ 7 - 0
ChocolArm64/Decoders/IOpCodeBImm.cs

@@ -0,0 +1,7 @@
+namespace ChocolArm64.Decoders
+{
+    interface IOpCodeBImm : IOpCode64
+    {
+        long Imm { get; }
+    }
+}

+ 4 - 0
ChocolArm64/Decoders/IOpCodeBImm32.cs

@@ -0,0 +1,4 @@
+namespace ChocolArm64.Decoders
+{
+    interface IOpCodeBImm32 : IOpCode32, IOpCodeBImm { }
+}

+ 7 - 0
ChocolArm64/Decoders/IOpCodeBReg32.cs

@@ -0,0 +1,7 @@
+namespace ChocolArm64.Decoders
+{
+    interface IOpCodeBReg32 : IOpCode32
+    {
+        int Rm { get; }
+    }
+}

+ 1 - 1
ChocolArm64/Decoders/IOpCodeCond64.cs

@@ -2,6 +2,6 @@ namespace ChocolArm64.Decoders
 {
     interface IOpCodeCond64 : IOpCode64
     {
-        Cond Cond { get; }
+        Condition Cond { get; }
     }
 }

+ 24 - 0
ChocolArm64/Decoders/OpCode32.cs

@@ -0,0 +1,24 @@
+using ChocolArm64.Instructions;
+using ChocolArm64.State;
+
+namespace ChocolArm64.Decoders
+{
+    class OpCode32 : OpCode64
+    {
+        public Condition Cond { get; protected set; }
+
+        public OpCode32(Inst inst, long position, int opCode) : base(inst, position, opCode)
+        {
+            RegisterSize = RegisterSize.Int32;
+
+            Cond = (Condition)((uint)opCode >> 28);
+        }
+
+        public uint GetPc()
+        {
+            //Due to backwards compatibility and legacy behavior of ARMv4 CPUs pipeline,
+            //the PC actually points 2 instructions ahead.
+            return (uint)Position + (uint)OpCodeSizeInBytes * 2;
+        }
+    }
+}

+ 5 - 5
ChocolArm64/Decoders/OpCode64.cs

@@ -9,9 +9,10 @@ namespace ChocolArm64.Decoders
         public long Position  { get; private set; }
         public int  RawOpCode { get; private set; }
 
-        public InstEmitter     Emitter      { get; protected set; }
-        public InstInterpreter Interpreter  { get; protected set; }
-        public RegisterSize    RegisterSize { get; protected set; }
+        public int OpCodeSizeInBytes { get; protected set; } = 4;
+
+        public InstEmitter  Emitter      { get; protected set; }
+        public RegisterSize RegisterSize { get; protected set; }
 
         public OpCode64(Inst inst, long position, int opCode)
         {
@@ -20,8 +21,7 @@ namespace ChocolArm64.Decoders
 
             RegisterSize = RegisterSize.Int64;
 
-            Emitter     = inst.Emitter;
-            Interpreter = inst.Interpreter;
+            Emitter = inst.Emitter;
         }
 
         public int GetBitsCount()

+ 20 - 0
ChocolArm64/Decoders/OpCodeAlu32.cs

@@ -0,0 +1,20 @@
+using ChocolArm64.Instructions;
+
+namespace ChocolArm64.Decoders
+{
+    class OpCodeAlu32 : OpCode32, IOpCodeAlu32
+    {
+        public int Rd { get; private set; }
+        public int Rn { get; private set; }
+
+        public bool SetFlags { get; private set; }
+
+        public OpCodeAlu32(Inst inst, long position, int opCode) : base(inst, position, opCode)
+        {
+            Rd = (opCode >> 12) & 0xf;
+            Rn = (opCode >> 16) & 0xf;
+
+            SetFlags = ((opCode >> 20) & 1) != 0;
+        }
+    }
+}

+ 21 - 0
ChocolArm64/Decoders/OpCodeAluImm32.cs

@@ -0,0 +1,21 @@
+using ChocolArm64.Instructions;
+
+namespace ChocolArm64.Decoders
+{
+    class OpCodeAluImm32 : OpCodeAlu32
+    {
+        public int Imm { get; private set; }
+
+        public bool IsRotated { get; private set; }
+
+        public OpCodeAluImm32(Inst inst, long position, int opCode) : base(inst, position, opCode)
+        {
+            int value = (opCode >> 0) & 0xff;
+            int shift = (opCode >> 8) & 0xf;
+
+            Imm = BitUtils.RotateRight(value, shift * 2, 32);
+
+            IsRotated = shift != 0;
+        }
+    }
+}

+ 22 - 0
ChocolArm64/Decoders/OpCodeAluImm8T16.cs

@@ -0,0 +1,22 @@
+using ChocolArm64.Instructions;
+
+namespace ChocolArm64.Decoders
+{
+    class OpCodeAluImm8T16 : OpCodeT16, IOpCodeAlu32
+    {
+        private int _rdn;
+
+        public int Rd => _rdn;
+        public int Rn => _rdn;
+
+        public bool SetFlags => false;
+
+        public int Imm { get; private set; }
+
+        public OpCodeAluImm8T16(Inst inst, long position, int opCode) : base(inst, position, opCode)
+        {
+            Imm  = (opCode >> 0) & 0xff;
+            _rdn = (opCode >> 8) & 0x7;
+        }
+    }
+}

+ 20 - 0
ChocolArm64/Decoders/OpCodeAluRsImm32.cs

@@ -0,0 +1,20 @@
+using ChocolArm64.Instructions;
+
+namespace ChocolArm64.Decoders
+{
+    class OpCodeAluRsImm32 : OpCodeAlu32
+    {
+        public int Rm  { get; private set; }
+        public int Imm { get; private set; }
+
+        public ShiftType ShiftType { get; private set; }
+
+        public OpCodeAluRsImm32(Inst inst, long position, int opCode) : base(inst, position, opCode)
+        {
+            Rm  = (opCode >> 0) & 0xf;
+            Imm = (opCode >> 7) & 0x1f;
+
+            ShiftType = (ShiftType)((opCode >> 5) & 3);
+        }
+    }
+}

+ 29 - 0
ChocolArm64/Decoders/OpCodeBImm32.cs

@@ -0,0 +1,29 @@
+using ChocolArm64.Instructions;
+
+namespace ChocolArm64.Decoders
+{
+    class OpCodeBImm32 : OpCode32, IOpCodeBImm32
+    {
+        public long Imm { get; private set; }
+
+        public OpCodeBImm32(Inst inst, long position, int opCode) : base(inst, position, opCode)
+        {
+            uint pc = GetPc();
+
+            //When the codition is never, the instruction is BLX to Thumb mode.
+            if (Cond != Condition.Nv)
+            {
+                pc &= ~3u;
+            }
+
+            Imm = pc + DecoderHelper.DecodeImm24_2(opCode);
+
+            if (Cond == Condition.Nv)
+            {
+                long H = (opCode >> 23) & 2;
+
+                Imm |= H;
+            }
+        }
+    }
+}

+ 1 - 1
ChocolArm64/Decoders/OpCodeBImm64.cs

@@ -2,7 +2,7 @@ using ChocolArm64.Instructions;
 
 namespace ChocolArm64.Decoders
 {
-    class OpCodeBImm64 : OpCode64
+    class OpCodeBImm64 : OpCode64, IOpCodeBImm
     {
         public long Imm { get; protected set; }
 

+ 2 - 2
ChocolArm64/Decoders/OpCodeBImmCond64.cs

@@ -4,7 +4,7 @@ namespace ChocolArm64.Decoders
 {
     class OpCodeBImmCond64 : OpCodeBImm64, IOpCodeCond64
     {
-        public Cond Cond { get; private set; }
+        public Condition Cond { get; private set; }
 
         public OpCodeBImmCond64(Inst inst, long position, int opCode) : base(inst, position, opCode)
         {
@@ -17,7 +17,7 @@ namespace ChocolArm64.Decoders
                 return;
             }
 
-            Cond = (Cond)(opCode & 0xf);
+            Cond = (Condition)(opCode & 0xf);
 
             Imm = position + DecoderHelper.DecodeImmS19_2(opCode);
         }

+ 14 - 0
ChocolArm64/Decoders/OpCodeBReg32.cs

@@ -0,0 +1,14 @@
+using ChocolArm64.Instructions;
+
+namespace ChocolArm64.Decoders
+{
+    class OpCodeBReg32 : OpCode32, IOpCodeBReg32
+    {
+        public int Rm { get; private set; }
+
+        public OpCodeBReg32(Inst inst, long position, int opCode) : base(inst, position, opCode)
+        {
+            Rm = opCode & 0xf;
+        }
+    }
+}

+ 14 - 0
ChocolArm64/Decoders/OpCodeBRegT16.cs

@@ -0,0 +1,14 @@
+using ChocolArm64.Instructions;
+
+namespace ChocolArm64.Decoders
+{
+    class OpCodeBRegT16 : OpCodeT16, IOpCodeBReg32
+    {
+        public int Rm { get; private set; }
+
+        public OpCodeBRegT16(Inst inst, long position, int opCode) : base(inst, position, opCode)
+        {
+            Rm = (opCode >> 3) & 0xf;
+        }
+    }
+}

+ 5 - 5
ChocolArm64/Decoders/OpCodeCcmp64.cs

@@ -8,7 +8,7 @@ namespace ChocolArm64.Decoders
         public    int Nzcv { get; private set; }
         protected int RmImm;
 
-        public Cond Cond { get; private set; }
+        public Condition Cond { get; private set; }
 
         public OpCodeCcmp64(Inst inst, long position, int opCode) : base(inst, position, opCode)
         {
@@ -21,11 +21,11 @@ namespace ChocolArm64.Decoders
                 return;
             }
 
-            Nzcv  =        (opCode >>  0) & 0xf;
-            Cond  = (Cond)((opCode >> 12) & 0xf);
-            RmImm =        (opCode >> 16) & 0x1f;
+            Nzcv  =             (opCode >>  0) & 0xf;
+            Cond  = (Condition)((opCode >> 12) & 0xf);
+            RmImm =             (opCode >> 16) & 0x1f;
 
-            Rd = CpuThreadState.ZrIndex;
+            Rd = RegisterAlias.Zr;
         }
     }
 }

+ 3 - 3
ChocolArm64/Decoders/OpCodeCsel64.cs

@@ -6,12 +6,12 @@ namespace ChocolArm64.Decoders
     {
         public int Rm { get; private set; }
 
-        public Cond Cond { get; private set; }
+        public Condition Cond { get; private set; }
 
         public OpCodeCsel64(Inst inst, long position, int opCode) : base(inst, position, opCode)
         {
-            Rm   =        (opCode >> 16) & 0x1f;
-            Cond = (Cond)((opCode >> 12) & 0xf);
+            Rm   =             (opCode >> 16) & 0x1f;
+            Cond = (Condition)((opCode >> 12) & 0xf);
         }
     }
 }

+ 3 - 3
ChocolArm64/Decoders/OpCodeSimdFcond64.cs

@@ -6,12 +6,12 @@ namespace ChocolArm64.Decoders
     {
         public int Nzcv { get; private set; }
 
-        public Cond Cond { get; private set; }
+        public Condition Cond { get; private set; }
 
         public OpCodeSimdFcond64(Inst inst, long position, int opCode) : base(inst, position, opCode)
         {
-            Nzcv =        (opCode >>  0) & 0xf;
-            Cond = (Cond)((opCode >> 12) & 0xf);
+            Nzcv =             (opCode >>  0) & 0xf;
+            Cond = (Condition)((opCode >> 12) & 0xf);
         }
     }
 }

+ 14 - 0
ChocolArm64/Decoders/OpCodeT16.cs

@@ -0,0 +1,14 @@
+using ChocolArm64.Instructions;
+
+namespace ChocolArm64.Decoders
+{
+    class OpCodeT16 : OpCode32
+    {
+        public OpCodeT16(Inst inst, long position, int opCode) : base(inst, position, opCode)
+        {
+            Cond = Condition.Al;
+
+            OpCodeSizeInBytes = 2;
+        }
+    }
+}

+ 0 - 15
ChocolArm64/Decoders32/A32OpCode.cs

@@ -1,15 +0,0 @@
-using ChocolArm64.Decoders;
-using ChocolArm64.Instructions;
-
-namespace ChocolArm64.Decoders32
-{
-    class A32OpCode : OpCode64
-    {
-        public Cond Cond { get; private set; }
-
-        public A32OpCode(Inst inst, long position, int opCode) : base(inst, position, opCode)
-        {
-            Cond = (Cond)((uint)opCode >> 28);
-        }
-    }
-}

+ 0 - 16
ChocolArm64/Decoders32/A32OpCodeBImmAl.cs

@@ -1,16 +0,0 @@
-using ChocolArm64.Instructions;
-
-namespace ChocolArm64.Decoders32
-{
-    class A32OpCodeBImmAl : A32OpCode
-    {
-        public int Imm;
-        public int H;
-
-        public A32OpCodeBImmAl(Inst inst, long position, int opCode) : base(inst, position, opCode)
-        {
-            Imm = (opCode <<  8) >> 6;
-            H   = (opCode >> 23) &  2;
-        }
-    }
-}

+ 6 - 8
ChocolArm64/Instructions/Inst.cs

@@ -4,17 +4,15 @@ namespace ChocolArm64.Instructions
 {
     struct Inst
     {
-        public InstInterpreter Interpreter { get; private set; }
-        public InstEmitter     Emitter     { get; private set; }
-        public Type             Type        { get; private set; }
+        public InstEmitter Emitter { get; }
+        public Type        Type    { get; }
 
-        public static Inst Undefined => new Inst(null, InstEmit.Und, null);
+        public static Inst Undefined => new Inst(InstEmit.Und, null);
 
-        public Inst(InstInterpreter interpreter, InstEmitter emitter, Type type)
+        public Inst(InstEmitter emitter, Type type)
         {
-            Interpreter = interpreter;
-            Emitter     = emitter;
-            Type        = type;
+            Emitter = emitter;
+            Type    = type;
         }
     }
 }

+ 99 - 0
ChocolArm64/Instructions/InstEmit32Helper.cs

@@ -0,0 +1,99 @@
+using ChocolArm64.Decoders;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System;
+
+namespace ChocolArm64.Instructions
+{
+    static class InstEmit32Helper
+    {
+        public static bool IsThumb(OpCode64 op)
+        {
+            return op is OpCodeT16;
+        }
+
+        public static void EmitLoadFromRegister(ILEmitterCtx context, int register)
+        {
+            if (register == RegisterAlias.Aarch32Pc)
+            {
+                OpCode32 op = (OpCode32)context.CurrOp;
+
+                context.EmitLdc_I4((int)op.GetPc());
+            }
+            else
+            {
+                context.EmitLdint(InstEmit32Helper.GetRegisterAlias(context.Mode, register));
+            }
+        }
+
+        public static int GetRegisterAlias(Aarch32Mode mode, int register)
+        {
+            //Only registers >= 8 are banked, with registers in the range [8, 12] being
+            //banked for the FIQ mode, and registers 13 and 14 being banked for all modes.
+            if ((uint)register < 8)
+            {
+                return register;
+            }
+
+            return GetBankedRegisterAlias(mode, register);
+        }
+
+        public static int GetBankedRegisterAlias(Aarch32Mode mode, int register)
+        {
+            switch (register)
+            {
+                case 8: return mode == Aarch32Mode.Fiq
+                    ? RegisterAlias.R8Fiq
+                    : RegisterAlias.R8Usr;
+
+                case 9: return mode == Aarch32Mode.Fiq
+                    ? RegisterAlias.R9Fiq
+                    : RegisterAlias.R9Usr;
+
+                case 10: return mode == Aarch32Mode.Fiq
+                    ? RegisterAlias.R10Fiq
+                    : RegisterAlias.R10Usr;
+
+                case 11: return mode == Aarch32Mode.Fiq
+                    ? RegisterAlias.R11Fiq
+                    : RegisterAlias.R11Usr;
+
+                case 12: return mode == Aarch32Mode.Fiq
+                    ? RegisterAlias.R12Fiq
+                    : RegisterAlias.R12Usr;
+
+                case 13:
+                    switch (mode)
+                    {
+                        case Aarch32Mode.User:
+                        case Aarch32Mode.System:      return RegisterAlias.SpUsr;
+                        case Aarch32Mode.Fiq:         return RegisterAlias.SpFiq;
+                        case Aarch32Mode.Irq:         return RegisterAlias.SpIrq;
+                        case Aarch32Mode.Supervisor:  return RegisterAlias.SpSvc;
+                        case Aarch32Mode.Abort:       return RegisterAlias.SpAbt;
+                        case Aarch32Mode.Hypervisor:  return RegisterAlias.SpHyp;
+                        case Aarch32Mode.Undefined:   return RegisterAlias.SpUnd;
+
+                        default: throw new ArgumentException(nameof(mode));
+                    }
+
+                case 14:
+                    switch (mode)
+                    {
+                        case Aarch32Mode.User:
+                        case Aarch32Mode.Hypervisor:
+                        case Aarch32Mode.System:      return RegisterAlias.LrUsr;
+                        case Aarch32Mode.Fiq:         return RegisterAlias.LrFiq;
+                        case Aarch32Mode.Irq:         return RegisterAlias.LrIrq;
+                        case Aarch32Mode.Supervisor:  return RegisterAlias.LrSvc;
+                        case Aarch32Mode.Abort:       return RegisterAlias.LrAbt;
+                        case Aarch32Mode.Undefined:   return RegisterAlias.LrUnd;
+
+                        default: throw new ArgumentException(nameof(mode));
+                    }
+
+                default: throw new ArgumentOutOfRangeException(nameof(register));
+            }
+        }
+    }
+}

+ 62 - 45
ChocolArm64/Instructions/InstEmitAlu.cs

@@ -17,7 +17,7 @@ namespace ChocolArm64.Instructions
 
         private static void EmitAdc(ILEmitterCtx context, bool setFlags)
         {
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.Add);
 
@@ -44,14 +44,14 @@ namespace ChocolArm64.Instructions
                 EmitAddsVCheck(context);
             }
 
-            EmitDataStore(context);
+            EmitAluStore(context);
         }
 
-        public static void Add(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Add);
+        public static void Add(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Add);
 
         public static void Adds(ILEmitterCtx context)
         {
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.Add);
 
@@ -59,14 +59,14 @@ namespace ChocolArm64.Instructions
 
             EmitAddsCCheck(context);
             EmitAddsVCheck(context);
-            EmitDataStoreS(context);
+            EmitAluStoreS(context);
         }
 
-        public static void And(ILEmitterCtx context) => EmitDataOp(context, OpCodes.And);
+        public static void And(ILEmitterCtx context) => EmitAluOp(context, OpCodes.And);
 
         public static void Ands(ILEmitterCtx context)
         {
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.And);
 
@@ -74,17 +74,17 @@ namespace ChocolArm64.Instructions
 
             context.EmitZnFlagCheck();
 
-            EmitDataStoreS(context);
+            EmitAluStoreS(context);
         }
 
-        public static void Asrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr);
+        public static void Asrv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shr);
 
         public static void Bic(ILEmitterCtx context)  => EmitBic(context, false);
         public static void Bics(ILEmitterCtx context) => EmitBic(context, true);
 
         private static void EmitBic(ILEmitterCtx context, bool setFlags)
         {
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.Not);
             context.Emit(OpCodes.And);
@@ -96,7 +96,7 @@ namespace ChocolArm64.Instructions
                 context.EmitZnFlagCheck();
             }
 
-            EmitDataStore(context, setFlags);
+            EmitAluStore(context, setFlags);
         }
 
         public static void Cls(ILEmitterCtx context)
@@ -136,15 +136,15 @@ namespace ChocolArm64.Instructions
 
         public static void Eon(ILEmitterCtx context)
         {
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.Not);
             context.Emit(OpCodes.Xor);
 
-            EmitDataStore(context);
+            EmitAluStore(context);
         }
 
-        public static void Eor(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Xor);
+        public static void Eor(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Xor);
 
         public static void Extr(ILEmitterCtx context)
         {
@@ -166,18 +166,18 @@ namespace ChocolArm64.Instructions
                 context.Emit(OpCodes.Or);
             }
 
-            EmitDataStore(context);
+            EmitAluStore(context);
         }
 
-        public static void Lslv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shl);
-        public static void Lsrv(ILEmitterCtx context) => EmitDataOpShift(context, OpCodes.Shr_Un);
+        public static void Lslv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shl);
+        public static void Lsrv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shr_Un);
 
         public static void Sbc(ILEmitterCtx context)  => EmitSbc(context, false);
         public static void Sbcs(ILEmitterCtx context) => EmitSbc(context, true);
 
         private static void EmitSbc(ILEmitterCtx context, bool setFlags)
         {
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.Sub);
 
@@ -208,16 +208,16 @@ namespace ChocolArm64.Instructions
                 EmitSubsVCheck(context);
             }
 
-            EmitDataStore(context);
+            EmitAluStore(context);
         }
 
-        public static void Sub(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Sub);
+        public static void Sub(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Sub);
 
         public static void Subs(ILEmitterCtx context)
         {
             context.TryOptMarkCondWithoutCmp();
 
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.Sub);
 
@@ -225,20 +225,20 @@ namespace ChocolArm64.Instructions
 
             EmitSubsCCheck(context);
             EmitSubsVCheck(context);
-            EmitDataStoreS(context);
+            EmitAluStoreS(context);
         }
 
         public static void Orn(ILEmitterCtx context)
         {
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.Not);
             context.Emit(OpCodes.Or);
 
-            EmitDataStore(context);
+            EmitAluStore(context);
         }
 
-        public static void Orr(ILEmitterCtx context) => EmitDataOp(context, OpCodes.Or);
+        public static void Orr(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Or);
 
         public static void Rbit(ILEmitterCtx context) => EmitFallback32_64(context,
             nameof(SoftFallback.ReverseBits32),
@@ -283,22 +283,22 @@ namespace ChocolArm64.Instructions
 
         public static void Rorv(ILEmitterCtx context)
         {
-            EmitDataLoadRn(context);
-            EmitDataLoadShift(context);
+            EmitAluLoadRn(context);
+            EmitAluLoadShift(context);
 
             context.Emit(OpCodes.Shr_Un);
 
-            EmitDataLoadRn(context);
+            EmitAluLoadRn(context);
 
             context.EmitLdc_I4(context.CurrOp.GetBitsCount());
 
-            EmitDataLoadShift(context);
+            EmitAluLoadShift(context);
 
             context.Emit(OpCodes.Sub);
             context.Emit(OpCodes.Shl);
             context.Emit(OpCodes.Or);
 
-            EmitDataStore(context);
+            EmitAluStore(context);
         }
 
         public static void Sdiv(ILEmitterCtx context) => EmitDiv(context, OpCodes.Div);
@@ -309,7 +309,7 @@ namespace ChocolArm64.Instructions
             //If Rm == 0, Rd = 0 (division by zero).
             context.EmitLdc_I(0);
 
-            EmitDataLoadRm(context);
+            EmitAluLoadRm(context);
 
             context.EmitLdc_I(0);
 
@@ -325,13 +325,13 @@ namespace ChocolArm64.Instructions
 
                 context.EmitLdc_I(intMin);
 
-                EmitDataLoadRn(context);
+                EmitAluLoadRn(context);
 
                 context.EmitLdc_I(intMin);
 
                 context.Emit(OpCodes.Ceq);
 
-                EmitDataLoadRm(context);
+                EmitAluLoadRm(context);
 
                 context.EmitLdc_I(-1);
 
@@ -341,38 +341,38 @@ namespace ChocolArm64.Instructions
                 context.Emit(OpCodes.Pop);
             }
 
-            EmitDataLoadRn(context);
-            EmitDataLoadRm(context);
+            EmitAluLoadRn(context);
+            EmitAluLoadRm(context);
 
             context.Emit(ilOp);
 
             context.MarkLabel(badDiv);
 
-            EmitDataStore(context);
+            EmitAluStore(context);
         }
 
-        private static void EmitDataOp(ILEmitterCtx context, OpCode ilOp)
+        private static void EmitAluOp(ILEmitterCtx context, OpCode ilOp)
         {
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(ilOp);
 
-            EmitDataStore(context);
+            EmitAluStore(context);
         }
 
-        private static void EmitDataOpShift(ILEmitterCtx context, OpCode ilOp)
+        private static void EmitAluOpShift(ILEmitterCtx context, OpCode ilOp)
         {
-            EmitDataLoadRn(context);
-            EmitDataLoadShift(context);
+            EmitAluLoadRn(context);
+            EmitAluLoadShift(context);
 
             context.Emit(ilOp);
 
-            EmitDataStore(context);
+            EmitAluStore(context);
         }
 
-        private static void EmitDataLoadShift(ILEmitterCtx context)
+        private static void EmitAluLoadShift(ILEmitterCtx context)
         {
-            EmitDataLoadRm(context);
+            EmitAluLoadRm(context);
 
             context.EmitLdc_I(context.CurrOp.GetBitsCount() - 1);
 
@@ -398,5 +398,22 @@ namespace ChocolArm64.Instructions
 
             context.EmitStflg((int)PState.CBit);
         }
+
+        public static void EmitAluStore(ILEmitterCtx context)  => EmitAluStore(context, false);
+        public static void EmitAluStoreS(ILEmitterCtx context) => EmitAluStore(context, true);
+
+        public static void EmitAluStore(ILEmitterCtx context, bool setFlags)
+        {
+            IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp;
+
+            if (setFlags || op is IOpCodeAluRs64)
+            {
+                context.EmitStintzr(op.Rd);
+            }
+            else
+            {
+                context.EmitStint(op.Rd);
+            }
+        }
     }
 }

+ 123 - 0
ChocolArm64/Instructions/InstEmitAlu32.cs

@@ -0,0 +1,123 @@
+using ChocolArm64.Decoders;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System.Reflection.Emit;
+
+using static ChocolArm64.Instructions.InstEmit32Helper;
+using static ChocolArm64.Instructions.InstEmitAluHelper;
+
+namespace ChocolArm64.Instructions
+{
+    static partial class InstEmit32
+    {
+        public static void Add(ILEmitterCtx context)
+        {
+            IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp;
+
+            EmitAluLoadOpers(context, setCarry: false);
+
+            context.Emit(OpCodes.Add);
+
+            if (op.SetFlags)
+            {
+                context.EmitZnFlagCheck();
+
+                EmitAddsCCheck(context);
+                EmitAddsVCheck(context);
+            }
+
+            EmitAluStore(context);
+        }
+
+        public static void Mov(ILEmitterCtx context)
+        {
+            IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp;
+
+            EmitAluLoadOper2(context);
+
+            if (op.SetFlags)
+            {
+                context.EmitZnFlagCheck();
+            }
+
+            EmitAluStore(context);
+        }
+
+        public static void Sub(ILEmitterCtx context)
+        {
+            IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp;
+
+            EmitAluLoadOpers(context, setCarry: false);
+
+            context.Emit(OpCodes.Sub);
+
+            if (op.SetFlags)
+            {
+                context.EmitZnFlagCheck();
+
+                EmitSubsCCheck(context);
+                EmitSubsVCheck(context);
+            }
+
+            EmitAluStore(context);
+        }
+
+        private static void EmitAluStore(ILEmitterCtx context)
+        {
+            IOpCodeAlu32 op = (IOpCodeAlu32)context.CurrOp;
+
+            if (op.Rd == RegisterAlias.Aarch32Pc)
+            {
+                if (op.SetFlags)
+                {
+                    //TODO: Load SPSR etc.
+
+                    context.EmitLdflg((int)PState.TBit);
+
+                    ILLabel lblThumb = new ILLabel();
+                    ILLabel lblEnd   = new ILLabel();
+
+                    context.Emit(OpCodes.Brtrue_S, lblThumb);
+
+                    context.EmitLdc_I4(~3);
+
+                    context.Emit(OpCodes.Br_S, lblEnd);
+
+                    context.MarkLabel(lblThumb);
+
+                    context.EmitLdc_I4(~1);
+
+                    context.MarkLabel(lblEnd);
+
+                    context.Emit(OpCodes.And);
+                    context.Emit(OpCodes.Conv_U8);
+                    context.Emit(OpCodes.Ret);
+                }
+                else
+                {
+                    EmitAluWritePc(context);
+                }
+            }
+            else
+            {
+                context.EmitStint(GetRegisterAlias(context.Mode, op.Rd));
+            }
+        }
+
+        private static void EmitAluWritePc(ILEmitterCtx context)
+        {
+            if (IsThumb(context.CurrOp))
+            {
+                context.EmitLdc_I4(~1);
+
+                context.Emit(OpCodes.And);
+                context.Emit(OpCodes.Conv_U8);
+                context.Emit(OpCodes.Ret);
+            }
+            else
+            {
+                EmitBxWritePc(context);
+            }
+        }
+    }
+}

+ 279 - 38
ChocolArm64/Instructions/InstEmitAluHelper.cs

@@ -1,6 +1,7 @@
 using ChocolArm64.Decoders;
 using ChocolArm64.State;
 using ChocolArm64.Translation;
+using System;
 using System.Reflection.Emit;
 
 namespace ChocolArm64.Instructions
@@ -14,7 +15,7 @@ namespace ChocolArm64.Instructions
             context.EmitLdtmp();
             context.EmitLdtmp();
 
-            EmitDataLoadRn(context);
+            EmitAluLoadRn(context);
 
             context.Emit(OpCodes.Ceq);
 
@@ -24,7 +25,7 @@ namespace ChocolArm64.Instructions
 
             context.EmitLdtmp();
 
-            EmitDataLoadRn(context);
+            EmitAluLoadRn(context);
 
             context.Emit(OpCodes.Clt_Un);
             context.Emit(OpCodes.Or);
@@ -37,7 +38,7 @@ namespace ChocolArm64.Instructions
             //C = Rd < Rn
             context.Emit(OpCodes.Dup);
 
-            EmitDataLoadRn(context);
+            EmitAluLoadRn(context);
 
             context.Emit(OpCodes.Clt_Un);
 
@@ -49,11 +50,11 @@ namespace ChocolArm64.Instructions
             //V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0
             context.Emit(OpCodes.Dup);
 
-            EmitDataLoadRn(context);
+            EmitAluLoadRn(context);
 
             context.Emit(OpCodes.Xor);
 
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.Xor);
             context.Emit(OpCodes.Not);
@@ -69,7 +70,7 @@ namespace ChocolArm64.Instructions
         public static void EmitSbcsCCheck(ILEmitterCtx context)
         {
             //C = (Rn == Rm && CIn) || Rn > Rm
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.Ceq);
 
@@ -77,7 +78,7 @@ namespace ChocolArm64.Instructions
 
             context.Emit(OpCodes.And);
 
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.Cgt_Un);
             context.Emit(OpCodes.Or);
@@ -88,7 +89,7 @@ namespace ChocolArm64.Instructions
         public static void EmitSubsCCheck(ILEmitterCtx context)
         {
             //C = Rn == Rm || Rn > Rm = !(Rn < Rm)
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.Clt_Un);
 
@@ -104,11 +105,11 @@ namespace ChocolArm64.Instructions
             //V = (Rd ^ Rn) & (Rn ^ Rm) < 0
             context.Emit(OpCodes.Dup);
 
-            EmitDataLoadRn(context);
+            EmitAluLoadRn(context);
 
             context.Emit(OpCodes.Xor);
 
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             context.Emit(OpCodes.Xor);
             context.Emit(OpCodes.And);
@@ -120,35 +121,76 @@ namespace ChocolArm64.Instructions
             context.EmitStflg((int)PState.VBit);
         }
 
-        public static void EmitDataLoadRm(ILEmitterCtx context)
+        public static void EmitAluLoadRm(ILEmitterCtx context)
         {
-            context.EmitLdintzr(((IOpCodeAluRs64)context.CurrOp).Rm);
+            if (context.CurrOp is IOpCodeAluRs64 op)
+            {
+                context.EmitLdintzr(op.Rm);
+            }
+            else if (context.CurrOp is OpCodeAluRsImm32 op32)
+            {
+                InstEmit32Helper.EmitLoadFromRegister(context, op32.Rm);
+            }
+            else
+            {
+                throw new InvalidOperationException();
+            }
         }
 
-        public static void EmitDataLoadOpers(ILEmitterCtx context)
+        public static void EmitAluLoadOpers(ILEmitterCtx context, bool setCarry = true)
         {
-            EmitDataLoadRn(context);
-            EmitDataLoadOper2(context);
+            EmitAluLoadRn(context);
+            EmitAluLoadOper2(context, setCarry);
         }
 
-        public static void EmitDataLoadRn(ILEmitterCtx context)
+        public static void EmitAluLoadRn(ILEmitterCtx context)
         {
-            IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp;
-
-            if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64)
+            if (context.CurrOp is IOpCodeAlu64 op)
             {
-                context.EmitLdintzr(op.Rn);
+                if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs64)
+                {
+                    context.EmitLdintzr(op.Rn);
+                }
+                else
+                {
+                    context.EmitLdint(op.Rn);
+                }
+            }
+            else if (context.CurrOp is IOpCodeAlu32 op32)
+            {
+                InstEmit32Helper.EmitLoadFromRegister(context, op32.Rn);
             }
             else
             {
-                context.EmitLdint(op.Rn);
+                throw new InvalidOperationException();
             }
         }
 
-        public static void EmitDataLoadOper2(ILEmitterCtx context)
+        public static void EmitAluLoadOper2(ILEmitterCtx context, bool setCarry = true)
         {
             switch (context.CurrOp)
             {
+                //ARM32.
+                case OpCodeAluImm32 op:
+                    context.EmitLdc_I4(op.Imm);
+
+                    if (op.SetFlags && op.IsRotated)
+                    {
+                        context.EmitLdc_I4((int)((uint)op.Imm >> 31));
+
+                        context.EmitStflg((int)PState.CBit);
+                    }
+                    break;
+
+                case OpCodeAluRsImm32 op:
+                    EmitLoadRmShiftedByImmediate(context, op, setCarry);
+                    break;
+
+                case OpCodeAluImm8T16 op:
+                    context.EmitLdc_I4(op.Imm);
+                    break;
+
+                //ARM64.
                 case IOpCodeAluImm64 op:
                     context.EmitLdc_I(op.Imm);
                     break;
@@ -170,23 +212,8 @@ namespace ChocolArm64.Instructions
                     context.EmitCast(op.IntType);
                     context.EmitLsl(op.Shift);
                     break;
-            }
-        }
 
-        public static void EmitDataStore(ILEmitterCtx context)  => EmitDataStore(context, false);
-        public static void EmitDataStoreS(ILEmitterCtx context) => EmitDataStore(context, true);
-
-        public static void EmitDataStore(ILEmitterCtx context, bool setFlags)
-        {
-            IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp;
-
-            if (setFlags || op is IOpCodeAluRs64)
-            {
-                context.EmitStintzr(op.Rd);
-            }
-            else
-            {
-                context.EmitStint(op.Rd);
+                default: throw new InvalidOperationException();
             }
         }
 
@@ -217,5 +244,219 @@ namespace ChocolArm64.Instructions
             context.Emit(OpCodes.And);
             context.EmitStflg((int)PState.NBit);
         }
+
+        //ARM32 helpers.
+        private static void EmitLoadRmShiftedByImmediate(ILEmitterCtx context, OpCodeAluRsImm32 op, bool setCarry)
+        {
+            int shift = op.Imm;
+
+            if (shift == 0)
+            {
+                switch (op.ShiftType)
+                {
+                    case ShiftType.Lsr: shift = 32; break;
+                    case ShiftType.Asr: shift = 32; break;
+                    case ShiftType.Ror: shift = 1;  break;
+                }
+            }
+
+            context.EmitLdint(op.Rm);
+
+            if (shift != 0)
+            {
+                setCarry &= op.SetFlags;
+
+                switch (op.ShiftType)
+                {
+                    case ShiftType.Lsl: EmitLslC(context, setCarry, shift); break;
+                    case ShiftType.Lsr: EmitLsrC(context, setCarry, shift); break;
+                    case ShiftType.Asr: EmitAsrC(context, setCarry, shift); break;
+                    case ShiftType.Ror:
+                        if (op.Imm != 0)
+                        {
+                            EmitRorC(context, setCarry, shift);
+                        }
+                        else
+                        {
+                            EmitRrxC(context, setCarry);
+                        }
+                        break;
+                }
+            }
+        }
+
+        private static void EmitLslC(ILEmitterCtx context, bool setCarry, int shift)
+        {
+            if ((uint)shift > 32)
+            {
+                EmitShiftByMoreThan32(context, setCarry);
+            }
+            else if (shift == 32)
+            {
+                if (setCarry)
+                {
+                    context.EmitLdc_I4(1);
+
+                    context.Emit(OpCodes.And);
+
+                    context.EmitStflg((int)PState.CBit);
+                }
+                else
+                {
+                    context.Emit(OpCodes.Pop);
+                }
+
+                context.EmitLdc_I4(0);
+            }
+            else
+            {
+                if (setCarry)
+                {
+                    context.Emit(OpCodes.Dup);
+
+                    context.EmitLsr(32 - shift);
+
+                    context.EmitLdc_I4(1);
+
+                    context.Emit(OpCodes.And);
+
+                    context.EmitStflg((int)PState.CBit);
+                }
+
+                context.EmitLsl(shift);
+            }
+        }
+
+        private static void EmitLsrC(ILEmitterCtx context, bool setCarry, int shift)
+        {
+            if ((uint)shift > 32)
+            {
+                EmitShiftByMoreThan32(context, setCarry);
+            }
+            else if (shift == 32)
+            {
+                if (setCarry)
+                {
+                    context.EmitLsr(31);
+
+                    context.EmitStflg((int)PState.CBit);
+                }
+                else
+                {
+                    context.Emit(OpCodes.Pop);
+                }
+
+                context.EmitLdc_I4(0);
+            }
+            else
+            {
+                context.Emit(OpCodes.Dup);
+
+                context.EmitLsr(shift - 1);
+
+                context.EmitLdc_I4(1);
+
+                context.Emit(OpCodes.And);
+
+                context.EmitStflg((int)PState.CBit);
+
+                context.EmitLsr(shift);
+            }
+        }
+
+        private static void EmitShiftByMoreThan32(ILEmitterCtx context, bool setCarry)
+        {
+            context.Emit(OpCodes.Pop);
+
+            context.EmitLdc_I4(0);
+
+            if (setCarry)
+            {
+                context.Emit(OpCodes.Dup);
+
+                context.EmitStflg((int)PState.CBit);
+            }
+        }
+
+        private static void EmitAsrC(ILEmitterCtx context, bool setCarry, int shift)
+        {
+            if ((uint)shift >= 32)
+            {
+                context.EmitAsr(31);
+
+                if (setCarry)
+                {
+                    context.Emit(OpCodes.Dup);
+
+                    context.EmitLdc_I4(1);
+
+                    context.Emit(OpCodes.And);
+
+                    context.EmitStflg((int)PState.CBit);
+                }
+            }
+            else
+            {
+                if (setCarry)
+                {
+                    context.Emit(OpCodes.Dup);
+
+                    context.EmitLsr(shift - 1);
+
+                    context.EmitLdc_I4(1);
+
+                    context.Emit(OpCodes.And);
+
+                    context.EmitStflg((int)PState.CBit);
+                }
+
+                context.EmitAsr(shift);
+            }
+        }
+
+        private static void EmitRorC(ILEmitterCtx context, bool setCarry, int shift)
+        {
+            shift &= 0x1f;
+
+            context.EmitRor(shift);
+
+            if (setCarry)
+            {
+                context.Emit(OpCodes.Dup);
+
+                context.EmitLsr(31);
+
+                context.EmitStflg((int)PState.CBit);
+            }
+        }
+
+        private static void EmitRrxC(ILEmitterCtx context, bool setCarry)
+        {
+            //Rotate right by 1 with carry.
+            if (setCarry)
+            {
+                context.Emit(OpCodes.Dup);
+
+                context.EmitLdc_I4(1);
+
+                context.Emit(OpCodes.And);
+
+                context.EmitSttmp();
+            }
+
+            context.EmitLsr(1);
+
+            context.EmitLdflg((int)PState.CBit);
+
+            context.EmitLsl(31);
+
+            context.Emit(OpCodes.Or);
+
+            if (setCarry)
+            {
+                context.EmitLdtmp();
+                context.EmitStflg((int)PState.CBit);
+            }
+        }
     }
 }

+ 1 - 1
ChocolArm64/Instructions/InstEmitCcmp.cs

@@ -48,7 +48,7 @@ namespace ChocolArm64.Instructions
 
             context.MarkLabel(lblTrue);
 
-            EmitDataLoadOpers(context);
+            EmitAluLoadOpers(context);
 
             if (cmpOp == CcmpOp.Cmp)
             {

+ 5 - 31
ChocolArm64/Instructions/InstEmitFlow.cs

@@ -36,36 +36,10 @@ namespace ChocolArm64.Instructions
             OpCodeBImmAl64 op = (OpCodeBImmAl64)context.CurrOp;
 
             context.EmitLdc_I(op.Position + 4);
-            context.EmitStint(CpuThreadState.LrIndex);
+            context.EmitStint(RegisterAlias.Lr);
             context.EmitStoreState();
 
-            if (context.TryOptEmitSubroutineCall())
-            {
-                //Note: the return value of the called method will be placed
-                //at the Stack, the return value is always a Int64 with the
-                //return address of the function. We check if the address is
-                //correct, if it isn't we keep returning until we reach the dispatcher.
-                context.Emit(OpCodes.Dup);
-
-                context.EmitLdc_I8(op.Position + 4);
-
-                ILLabel lblContinue = new ILLabel();
-
-                context.Emit(OpCodes.Beq_S, lblContinue);
-                context.Emit(OpCodes.Ret);
-
-                context.MarkLabel(lblContinue);
-
-                context.Emit(OpCodes.Pop);
-
-                context.EmitLoadState();
-            }
-            else
-            {
-                context.EmitLdc_I8(op.Imm);
-
-                context.Emit(OpCodes.Ret);
-            }
+            InstEmitFlowHelper.EmitCall(context, op.Imm);
         }
 
         public static void Blr(ILEmitterCtx context)
@@ -74,7 +48,7 @@ namespace ChocolArm64.Instructions
 
             context.EmitLdintzr(op.Rn);
             context.EmitLdc_I(op.Position + 4);
-            context.EmitStint(CpuThreadState.LrIndex);
+            context.EmitStint(RegisterAlias.Lr);
             context.EmitStoreState();
 
             context.Emit(OpCodes.Ret);
@@ -106,7 +80,7 @@ namespace ChocolArm64.Instructions
         public static void Ret(ILEmitterCtx context)
         {
             context.EmitStoreState();
-            context.EmitLdint(CpuThreadState.LrIndex);
+            context.EmitLdint(RegisterAlias.Lr);
 
             context.Emit(OpCodes.Ret);
         }
@@ -128,7 +102,7 @@ namespace ChocolArm64.Instructions
             EmitBranch(context, ilOp);
         }
 
-        private static void EmitBranch(ILEmitterCtx context, Cond cond)
+        private static void EmitBranch(ILEmitterCtx context, Condition cond)
         {
             OpCodeBImm64 op = (OpCodeBImm64)context.CurrOp;
 

+ 99 - 0
ChocolArm64/Instructions/InstEmitFlow32.cs

@@ -0,0 +1,99 @@
+using ChocolArm64.Decoders;
+using ChocolArm64.State;
+using ChocolArm64.Translation;
+using System.Reflection.Emit;
+
+using static ChocolArm64.Instructions.InstEmit32Helper;
+
+namespace ChocolArm64.Instructions
+{
+    static partial class InstEmit32
+    {
+        public static void B(ILEmitterCtx context)
+        {
+            IOpCodeBImm32 op = (IOpCodeBImm32)context.CurrOp;
+
+            if (context.CurrBlock.Branch != null)
+            {
+                context.Emit(OpCodes.Br, context.GetLabel(op.Imm));
+            }
+            else
+            {
+                context.EmitStoreState();
+                context.EmitLdc_I8(op.Imm);
+
+                context.Emit(OpCodes.Ret);
+            }
+        }
+
+        public static void Bl(ILEmitterCtx context)
+        {
+            Blx(context, x: false);
+        }
+
+        public static void Blx(ILEmitterCtx context)
+        {
+            Blx(context, x: true);
+        }
+
+        public static void Bx(ILEmitterCtx context)
+        {
+            IOpCodeBReg32 op = (IOpCodeBReg32)context.CurrOp;
+
+            context.EmitStoreState();
+
+            EmitLoadFromRegister(context, op.Rm);
+
+            EmitBxWritePc(context);
+        }
+
+        private static void Blx(ILEmitterCtx context, bool x)
+        {
+            IOpCodeBImm32 op = (IOpCodeBImm32)context.CurrOp;
+
+            uint pc = op.GetPc();
+
+            bool isThumb = IsThumb(context.CurrOp);
+
+            if (!isThumb)
+            {
+                context.EmitLdc_I(op.GetPc() - 4);
+            }
+            else
+            {
+                context.EmitLdc_I(op.GetPc() | 1);
+            }
+
+            context.EmitStint(GetBankedRegisterAlias(context.Mode, RegisterAlias.Aarch32Lr));
+            context.EmitStoreState();
+
+            //If x is true, then this is a branch with link and exchange.
+            //In this case we need to swap the mode between Arm <-> Thumb.
+            if (x)
+            {
+                context.EmitLdc_I4(isThumb ? 0 : 1);
+
+                context.EmitStflg((int)PState.TBit);
+            }
+
+            InstEmitFlowHelper.EmitCall(context, op.Imm);
+        }
+
+        private static void EmitBxWritePc(ILEmitterCtx context)
+        {
+            context.Emit(OpCodes.Dup);
+
+            context.EmitLdc_I4(1);
+
+            context.Emit(OpCodes.And);
+
+            context.EmitStflg((int)PState.TBit);
+
+            context.EmitLdc_I4(~1);
+
+            context.Emit(OpCodes.And);
+            context.Emit(OpCodes.Conv_U8);
+            context.Emit(OpCodes.Ret);
+        }
+    }
+}

+ 39 - 0
ChocolArm64/Instructions/InstEmitFlowHelper.cs

@@ -0,0 +1,39 @@
+using ChocolArm64.Translation;
+using System.Reflection.Emit;
+
+namespace ChocolArm64.Instructions
+{
+    static class InstEmitFlowHelper
+    {
+        public static void EmitCall(ILEmitterCtx context, long imm)
+        {
+            if (context.TryOptEmitSubroutineCall())
+            {
+                //Note: the return value of the called method will be placed
+                //at the Stack, the return value is always a Int64 with the
+                //return address of the function. We check if the address is
+                //correct, if it isn't we keep returning until we reach the dispatcher.
+                context.Emit(OpCodes.Dup);
+
+                context.EmitLdc_I8(context.CurrOp.Position + 4);
+
+                ILLabel lblContinue = new ILLabel();
+
+                context.Emit(OpCodes.Beq_S, lblContinue);
+                context.Emit(OpCodes.Ret);
+
+                context.MarkLabel(lblContinue);
+
+                context.Emit(OpCodes.Pop);
+
+                context.EmitLoadState();
+            }
+            else
+            {
+                context.EmitLdc_I8(imm);
+
+                context.Emit(OpCodes.Ret);
+            }
+        }
+    }
+}

+ 1 - 1
ChocolArm64/Instructions/InstEmitSimdMemory.cs

@@ -168,7 +168,7 @@ namespace ChocolArm64.Instructions
 
             context.EmitLdint(op.Rn);
 
-            if (op.Rm != CpuThreadState.ZrIndex)
+            if (op.Rm != RegisterAlias.Zr)
             {
                 context.EmitLdint(op.Rm);
             }

+ 0 - 8
ChocolArm64/Instructions/InstInterpreter.cs

@@ -1,8 +0,0 @@
-using ChocolArm64.Decoders;
-using ChocolArm64.Memory;
-using ChocolArm64.State;
-
-namespace ChocolArm64.Instructions
-{
-    delegate void InstInterpreter(CpuThreadState state, MemoryManager memory, OpCode64 opCode);
-}

+ 0 - 7
ChocolArm64/Instructions32/A32InstInterpretAlu.cs

@@ -1,7 +0,0 @@
-namespace ChocolArm64.Instructions32
-{
-    static partial class A32InstInterpret
-    {
-
-    }
-}

+ 0 - 70
ChocolArm64/Instructions32/A32InstInterpretFlow.cs

@@ -1,70 +0,0 @@
-using ChocolArm64.Decoders;
-using ChocolArm64.Decoders32;
-using ChocolArm64.Memory;
-using ChocolArm64.State;
-
-using static ChocolArm64.Instructions32.A32InstInterpretHelper;
-
-namespace ChocolArm64.Instructions32
-{
-    static partial class A32InstInterpret
-    {
-        public static void B(CpuThreadState state, MemoryManager memory, OpCode64 opCode)
-        {
-            A32OpCodeBImmAl op = (A32OpCodeBImmAl)opCode;
-
-            if (IsConditionTrue(state, op.Cond))
-            {
-                BranchWritePc(state, GetPc(state) + (uint)op.Imm);
-            }
-        }
-
-        public static void Bl(CpuThreadState state, MemoryManager memory, OpCode64 opCode)
-        {
-            Blx(state, memory, opCode, false);
-        }
-
-        public static void Blx(CpuThreadState state, MemoryManager memory, OpCode64 opCode)
-        {
-            Blx(state, memory, opCode, true);
-        }
-
-        public static void Blx(CpuThreadState state, MemoryManager memory, OpCode64 opCode, bool x)
-        {
-            A32OpCodeBImmAl op = (A32OpCodeBImmAl)opCode;
-
-            if (IsConditionTrue(state, op.Cond))
-            {
-                uint pc = GetPc(state);
-
-                if (state.Thumb)
-                {
-                    state.R14 = pc | 1;
-                }
-                else
-                {
-                    state.R14 = pc - 4U;
-                }
-
-                if (x)
-                {
-                    state.Thumb = !state.Thumb;
-                }
-
-                if (!state.Thumb)
-                {
-                    pc &= ~3U;
-                }
-
-                BranchWritePc(state, pc + (uint)op.Imm);
-            }
-        }
-
-        private static void BranchWritePc(CpuThreadState state, uint pc)
-        {
-            state.R15 = state.Thumb
-                ? pc & ~1U
-                : pc & ~3U;
-        }
-    }
-}

+ 0 - 65
ChocolArm64/Instructions32/A32InstInterpretHelper.cs

@@ -1,65 +0,0 @@
-using ChocolArm64.Decoders;
-using ChocolArm64.State;
-using System;
-
-namespace ChocolArm64.Instructions32
-{
-    static class A32InstInterpretHelper
-    {
-        public static bool IsConditionTrue(CpuThreadState state, Cond cond)
-        {
-            switch (cond)
-            {
-                case Cond.Eq:   return  state.Zero;
-                case Cond.Ne:   return !state.Zero;
-                case Cond.GeUn: return  state.Carry;
-                case Cond.LtUn: return !state.Carry;
-                case Cond.Mi:   return  state.Negative;
-                case Cond.Pl:   return !state.Negative;
-                case Cond.Vs:   return  state.Overflow;
-                case Cond.Vc:   return !state.Overflow;
-                case Cond.GtUn: return  state.Carry    && !state.Zero;
-                case Cond.LeUn: return !state.Carry    &&  state.Zero;
-                case Cond.Ge:   return  state.Negative ==  state.Overflow;
-                case Cond.Lt:   return  state.Negative !=  state.Overflow;
-                case Cond.Gt:   return  state.Negative ==  state.Overflow && !state.Zero;
-                case Cond.Le:   return  state.Negative !=  state.Overflow &&  state.Zero;
-            }
-
-            return true;
-        }
-
-        public unsafe static uint GetReg(CpuThreadState state, int reg)
-        {
-            if ((uint)reg > 15)
-            {
-                throw new ArgumentOutOfRangeException(nameof(reg));
-            }
-
-            fixed (uint* ptr = &state.R0)
-            {
-                return *(ptr + reg);
-            }
-        }
-
-        public unsafe static void SetReg(CpuThreadState state, int reg, uint value)
-        {
-            if ((uint)reg > 15)
-            {
-                throw new ArgumentOutOfRangeException(nameof(reg));
-            }
-
-            fixed (uint* ptr = &state.R0)
-            {
-                *(ptr + reg) = value;
-            }
-        }
-
-        public static uint GetPc(CpuThreadState state)
-        {
-            //Due to the old fetch-decode-execute pipeline of old ARM CPUs,
-            //the PC is 4 or 8 bytes (2 instructions) ahead of the current instruction.
-            return state.R15 + (state.Thumb ? 2U : 4U);
-        }
-    }
-}

+ 88 - 62
ChocolArm64/OpCodeTable.cs

@@ -1,7 +1,5 @@
 using ChocolArm64.Decoders;
-using ChocolArm64.Decoders32;
 using ChocolArm64.Instructions;
-using ChocolArm64.Instructions32;
 using ChocolArm64.State;
 using System;
 using System.Collections.Generic;
@@ -10,13 +8,47 @@ namespace ChocolArm64
 {
     static class OpCodeTable
     {
+        private const int FastLookupSize = 0x1000;
+
+        private class InstInfo
+        {
+            public int Mask;
+            public int Value;
+
+            public Inst Inst;
+
+            public InstInfo(int mask, int value, Inst inst)
+            {
+                Mask  = mask;
+                Value = value;
+                Inst  = inst;
+            }
+        }
+
+        private static List<InstInfo> _allInstA32 = new List<InstInfo>();
+        private static List<InstInfo> _allInstT32 = new List<InstInfo>();
+        private static List<InstInfo> _allInstA64 = new List<InstInfo>();
+
+        private static InstInfo[][] _instA32FastLookup = new InstInfo[FastLookupSize][];
+        private static InstInfo[][] _instT32FastLookup = new InstInfo[FastLookupSize][];
+        private static InstInfo[][] _instA64FastLookup = new InstInfo[FastLookupSize][];
+
         static OpCodeTable()
         {
 #region "OpCode Table (AArch32)"
             //Integer
-            SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.B,      typeof(A32OpCodeBImmAl));
-            SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Bl,     typeof(A32OpCodeBImmAl));
-            SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", A32InstInterpret.Blx,    typeof(A32OpCodeBImmAl));
+            SetA32("<<<<0010100xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Add, typeof(OpCodeAluImm32));
+            SetA32("<<<<0000100xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Add, typeof(OpCodeAluRsImm32));
+            SetA32("<<<<1010xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.B,   typeof(OpCodeBImm32));
+            SetA32("<<<<1011xxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Bl,  typeof(OpCodeBImm32));
+            SetA32("1111101xxxxxxxxxxxxxxxxxxxxxxxxx", InstEmit32.Blx, typeof(OpCodeBImm32));
+            SetA32("<<<<000100101111111111110001xxxx", InstEmit32.Bx,  typeof(OpCodeBReg32));
+            SetT32(                "010001110xxxx000", InstEmit32.Bx,  typeof(OpCodeBRegT16));
+            SetA32("<<<<0011101x0000xxxxxxxxxxxxxxxx", InstEmit32.Mov, typeof(OpCodeAluImm32));
+            SetA32("<<<<0001101x0000xxxxxxxxxxx0xxxx", InstEmit32.Mov, typeof(OpCodeAluRsImm32));
+            SetT32(                "00100xxxxxxxxxxx", InstEmit32.Mov, typeof(OpCodeAluImm8T16));
+            SetA32("<<<<0010010xxxxxxxxxxxxxxxxxxxxx", InstEmit32.Sub, typeof(OpCodeAluImm32));
+            SetA32("<<<<0000010xxxxxxxxxxxxxxxx0xxxx", InstEmit32.Sub, typeof(OpCodeAluRsImm32));
 #endregion
 
 #region "OpCode Table (AArch64)"
@@ -544,63 +576,29 @@ namespace ChocolArm64
             SetA64("0>001110<<0xxxxx011110xxxxxxxxxx", InstEmit.Zip2_V,          typeof(OpCodeSimdReg64));
 #endregion
 
-#region "Generate InstA64FastLookup Table (AArch64)"
-            var tmp = new List<InstInfo>[_fastLookupSize];
-            for (int i = 0; i < _fastLookupSize; i++)
-            {
-                tmp[i] = new List<InstInfo>();
-            }
-
-            foreach (var inst in _allInstA64)
-            {
-                int mask  = ToFastLookupIndex(inst.Mask);
-                int value = ToFastLookupIndex(inst.Value);
-
-                for (int i = 0; i < _fastLookupSize; i++)
-                {
-                    if ((i & mask) == value)
-                    {
-                        tmp[i].Add(inst);
-                    }
-                }
-            }
-
-            for (int i = 0; i < _fastLookupSize; i++)
-            {
-                _instA64FastLookup[i] = tmp[i].ToArray();
-            }
-#endregion
+            FillFastLookupTable(_instA32FastLookup, _allInstA32);
+            FillFastLookupTable(_instT32FastLookup, _allInstT32);
+            FillFastLookupTable(_instA64FastLookup, _allInstA64);
         }
 
-        private class InstInfo
+        private static void SetA32(string encoding, InstEmitter emitter, Type type)
         {
-            public int Mask;
-            public int Value;
-
-            public Inst Inst;
+            Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch32Arm);
+        }
 
-            public InstInfo(int mask, int value, Inst inst)
+        private static void SetT32(string encoding, InstEmitter emitter, Type type)
+        {
+            if (encoding.Length == 16)
             {
-                Mask  = mask;
-                Value = value;
-                Inst  = inst;
+                encoding = "xxxxxxxxxxxxxxxx" + encoding;
             }
-        }
-
-        private static List<InstInfo> _allInstA32 = new List<InstInfo>();
-        private static List<InstInfo> _allInstA64 = new List<InstInfo>();
-
-        private static int _fastLookupSize = 0x1000;
-        private static InstInfo[][] _instA64FastLookup = new InstInfo[_fastLookupSize][];
 
-        private static void SetA32(string encoding, InstInterpreter interpreter, Type type)
-        {
-            Set(encoding, new Inst(interpreter, null, type), ExecutionMode.AArch32);
+            Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch32Thumb);
         }
 
         private static void SetA64(string encoding, InstEmitter emitter, Type type)
         {
-            Set(encoding, new Inst(null, emitter, type), ExecutionMode.AArch64);
+            Set(encoding, new Inst(emitter, type), ExecutionMode.Aarch64);
         }
 
         private static void Set(string encoding, Inst inst, ExecutionMode mode)
@@ -673,27 +671,55 @@ namespace ChocolArm64
             }
         }
 
-        private static void InsertInst(
-            int           xMask,
-            int           value,
-            Inst          inst,
-            ExecutionMode mode)
+        private static void InsertInst(int xMask, int value, Inst inst, ExecutionMode mode)
         {
             InstInfo info = new InstInfo(xMask, value, inst);
 
-            if (mode == ExecutionMode.AArch64)
+            switch (mode)
             {
-                _allInstA64.Add(info);
+                case ExecutionMode.Aarch32Arm:   _allInstA32.Add(info); break;
+                case ExecutionMode.Aarch32Thumb: _allInstT32.Add(info); break;
+                case ExecutionMode.Aarch64:      _allInstA64.Add(info); break;
             }
-            else
+        }
+
+        private static void FillFastLookupTable(InstInfo[][] table, List<InstInfo> allInsts)
+        {
+            List<InstInfo>[] tmp = new List<InstInfo>[FastLookupSize];
+
+            for (int i = 0; i < FastLookupSize; i++)
             {
-                _allInstA32.Add(info);
+                tmp[i] = new List<InstInfo>();
+            }
+
+            foreach (InstInfo inst in allInsts)
+            {
+                int mask  = ToFastLookupIndex(inst.Mask);
+                int value = ToFastLookupIndex(inst.Value);
+
+                for (int i = 0; i < FastLookupSize; i++)
+                {
+                    if ((i & mask) == value)
+                    {
+                        tmp[i].Add(inst);
+                    }
+                }
+            }
+
+            for (int i = 0; i < FastLookupSize; i++)
+            {
+                table[i] = tmp[i].ToArray();
             }
         }
 
         public static Inst GetInstA32(int opCode)
         {
-            return GetInstFromList(_allInstA32, opCode);
+            return GetInstFromList(_instA32FastLookup[ToFastLookupIndex(opCode)], opCode);
+        }
+
+        public static Inst GetInstT32(int opCode)
+        {
+            return GetInstFromList(_instT32FastLookup[ToFastLookupIndex(opCode)], opCode);
         }
 
         public static Inst GetInstA64(int opCode)
@@ -708,7 +734,7 @@ namespace ChocolArm64
 
         private static Inst GetInstFromList(IEnumerable<InstInfo> instList, int opCode)
         {
-            foreach (var node in instList)
+            foreach (InstInfo node in instList)
             {
                 if ((opCode & node.Mask) == node.Value)
                 {

+ 15 - 0
ChocolArm64/State/Aarch32Mode.cs

@@ -0,0 +1,15 @@
+namespace ChocolArm64.State
+{
+    enum Aarch32Mode
+    {
+        User       = 0b10000,
+        Fiq        = 0b10001,
+        Irq        = 0b10010,
+        Supervisor = 0b10011,
+        Monitor    = 0b10110,
+        Abort      = 0b10111,
+        Hypervisor = 0b11010,
+        Undefined  = 0b11011,
+        System     = 0b11111
+    }
+}

+ 16 - 12
ChocolArm64/State/CpuThreadState.cs

@@ -8,25 +8,13 @@ namespace ChocolArm64.State
 {
     public class CpuThreadState
     {
-        internal const int LrIndex = 30;
-        internal const int ZrIndex = 31;
-
         internal const int ErgSizeLog2 = 4;
         internal const int DczSizeLog2 = 4;
 
         private const int MinInstForCheck = 4000000;
 
-        internal ExecutionMode ExecutionMode;
-
-        //AArch32 state.
-        public uint R0,  R1,  R2,  R3,
-                    R4,  R5,  R6,  R7,
-                    R8,  R9,  R10, R11,
-                    R12, R13, R14, R15;
-
         public bool Thumb;
 
-        //AArch64 state.
         public ulong X0,  X1,  X2,  X3,  X4,  X5,  X6,  X7,
                      X8,  X9,  X10, X11, X12, X13, X14, X15,
                      X16, X17, X18, X19, X20, X21, X22, X23,
@@ -42,6 +30,10 @@ namespace ChocolArm64.State
         public bool Zero;
         public bool Negative;
 
+        public bool IsAarch32;
+
+        public int ElrHyp;
+
         public bool Running { get; set; }
         public int  Core    { get; set; }
 
@@ -146,6 +138,18 @@ namespace ChocolArm64.State
             Undefined?.Invoke(this, new InstUndefinedEventArgs(position, rawOpCode));
         }
 
+        internal ExecutionMode GetExecutionMode()
+        {
+            if (!IsAarch32)
+            {
+                return ExecutionMode.Aarch64;
+            }
+            else
+            {
+                return Thumb ? ExecutionMode.Aarch32Thumb : ExecutionMode.Aarch32Arm;
+            }
+        }
+
         internal bool GetFpcrFlag(Fpcr flag)
         {
             return (Fpcr & (1 << (int)flag)) != 0;

+ 3 - 2
ChocolArm64/State/ExecutionMode.cs

@@ -2,7 +2,8 @@ namespace ChocolArm64.State
 {
     enum ExecutionMode
     {
-        AArch32,
-        AArch64
+        Aarch64,
+        Aarch32Arm,
+        Aarch32Thumb
     }
 }

+ 4 - 0
ChocolArm64/State/PState.cs

@@ -5,11 +5,15 @@ namespace ChocolArm64.State
     [Flags]
     enum PState
     {
+        TBit = 5,
+
         VBit = 28,
         CBit = 29,
         ZBit = 30,
         NBit = 31,
 
+        T = 1 << TBit,
+
         V = 1 << VBit,
         C = 1 << CBit,
         Z = 1 << ZBit,

+ 2 - 0
ChocolArm64/State/Register.cs

@@ -43,6 +43,8 @@ namespace ChocolArm64.State
         {
             switch ((PState)Index)
             {
+                case PState.TBit: return GetField(nameof(CpuThreadState.Thumb));
+
                 case PState.VBit: return GetField(nameof(CpuThreadState.Overflow));
                 case PState.CBit: return GetField(nameof(CpuThreadState.Carry));
                 case PState.ZBit: return GetField(nameof(CpuThreadState.Zero));

+ 41 - 0
ChocolArm64/State/RegisterAlias.cs

@@ -0,0 +1,41 @@
+namespace ChocolArm64.State
+{
+    static class RegisterAlias
+    {
+        public const int R8Usr  = 8;
+        public const int R9Usr  = 9;
+        public const int R10Usr = 10;
+        public const int R11Usr = 11;
+        public const int R12Usr = 12;
+        public const int SpUsr  = 13;
+        public const int LrUsr  = 14;
+
+        public const int SpHyp = 15;
+
+        public const int LrIrq = 16;
+        public const int SpIrq = 17;
+
+        public const int LrSvc = 18;
+        public const int SpSvc = 19;
+
+        public const int LrAbt = 20;
+        public const int SpAbt = 21;
+
+        public const int LrUnd = 22;
+        public const int SpUnd = 23;
+
+        public const int R8Fiq  = 24;
+        public const int R9Fiq  = 25;
+        public const int R10Fiq = 26;
+        public const int R11Fiq = 27;
+        public const int R12Fiq = 28;
+        public const int SpFiq  = 29;
+        public const int LrFiq  = 30;
+
+        public const int Aarch32Lr = 14;
+        public const int Aarch32Pc = 15;
+
+        public const int Lr = 30;
+        public const int Zr = 31;
+    }
+}

+ 58 - 15
ChocolArm64/Translation/ILEmitterCtx.cs

@@ -23,6 +23,8 @@ namespace ChocolArm64.Translation
         public Block    CurrBlock => _currBlock;
         public OpCode64 CurrOp    => _currBlock?.OpCodes[_opcIndex];
 
+        public Aarch32Mode Mode { get; } = Aarch32Mode.User; //TODO
+
         private Dictionary<Block, ILBlock> _visitedBlocks;
 
         private Queue<Block> _branchTargets;
@@ -97,11 +99,52 @@ namespace ChocolArm64.Translation
                 EmitSynchronization();
             }
 
+            //On AARCH32 mode, (almost) all instruction can be conditionally
+            //executed, and the required condition is encoded on the opcode.
+            //We handle that here, skipping the instruction if the condition
+            //is not met. We can just ignore it when the condition is "Always",
+            //because in this case the instruction is always going to be executed.
+            //Condition "Never" is also ignored because this is a special encoding
+            //used by some unconditional instructions.
+            ILLabel lblSkip = null;
+
+            if (CurrOp is OpCode32 op && op.Cond < Condition.Al)
+            {
+                lblSkip = new ILLabel();
+
+                EmitCondBranch(lblSkip, GetInverseCond(op.Cond));
+            }
+
             CurrOp.Emitter(this);
 
+            if (lblSkip != null)
+            {
+                MarkLabel(lblSkip);
+
+                //If this is the last op on the block, and there's no "next" block
+                //after this one, then we have to return right now, with the address
+                //of the next instruction to be executed (in the case that the condition
+                //is false, and the branch was not taken, as all basic blocks should end with
+                //some kind of branch).
+                if (CurrOp == CurrBlock.GetLastOp() && CurrBlock.Next == null)
+                {
+                    EmitStoreState();
+                    EmitLdc_I8(CurrOp.Position + CurrOp.OpCodeSizeInBytes);
+
+                    Emit(OpCodes.Ret);
+                }
+            }
+
             _ilBlock.Add(new ILBarrier());
         }
 
+        private Condition GetInverseCond(Condition cond)
+        {
+            //Bit 0 of all conditions is basically a negation bit, so
+            //inverting this bit has the effect of inverting the condition.
+            return (Condition)((int)cond ^ 1);
+        }
+
         private void EmitSynchronization()
         {
             EmitLdarg(TranslatedSub.StateArgIdx);
@@ -243,27 +286,27 @@ namespace ChocolArm64.Translation
         {
             _optOpLastCompare = CurrOp;
 
-            InstEmitAluHelper.EmitDataLoadOpers(this);
+            InstEmitAluHelper.EmitAluLoadOpers(this);
 
             Stloc(CmpOptTmp2Index, IoType.Int);
             Stloc(CmpOptTmp1Index, IoType.Int);
         }
 
-        private Dictionary<Cond, OpCode> _branchOps = new Dictionary<Cond, OpCode>()
+        private Dictionary<Condition, OpCode> _branchOps = new Dictionary<Condition, OpCode>()
         {
-            { Cond.Eq,   OpCodes.Beq    },
-            { Cond.Ne,   OpCodes.Bne_Un },
-            { Cond.GeUn, OpCodes.Bge_Un },
-            { Cond.LtUn, OpCodes.Blt_Un },
-            { Cond.GtUn, OpCodes.Bgt_Un },
-            { Cond.LeUn, OpCodes.Ble_Un },
-            { Cond.Ge,   OpCodes.Bge    },
-            { Cond.Lt,   OpCodes.Blt    },
-            { Cond.Gt,   OpCodes.Bgt    },
-            { Cond.Le,   OpCodes.Ble    }
+            { Condition.Eq,   OpCodes.Beq    },
+            { Condition.Ne,   OpCodes.Bne_Un },
+            { Condition.GeUn, OpCodes.Bge_Un },
+            { Condition.LtUn, OpCodes.Blt_Un },
+            { Condition.GtUn, OpCodes.Bgt_Un },
+            { Condition.LeUn, OpCodes.Ble_Un },
+            { Condition.Ge,   OpCodes.Bge    },
+            { Condition.Lt,   OpCodes.Blt    },
+            { Condition.Gt,   OpCodes.Bgt    },
+            { Condition.Le,   OpCodes.Ble    }
         };
 
-        public void EmitCondBranch(ILLabel target, Cond cond)
+        public void EmitCondBranch(ILLabel target, Condition cond)
         {
             OpCode ilOp;
 
@@ -432,7 +475,7 @@ namespace ChocolArm64.Translation
 
         public void EmitLdintzr(int index)
         {
-            if (index != CpuThreadState.ZrIndex)
+            if (index != RegisterAlias.Zr)
             {
                 EmitLdint(index);
             }
@@ -444,7 +487,7 @@ namespace ChocolArm64.Translation
 
         public void EmitStintzr(int index)
         {
-            if (index != CpuThreadState.ZrIndex)
+            if (index != RegisterAlias.Zr)
             {
                 EmitStint(index);
             }

+ 8 - 33
ChocolArm64/Translator.cs

@@ -4,7 +4,6 @@ using ChocolArm64.Memory;
 using ChocolArm64.State;
 using ChocolArm64.Translation;
 using System;
-using System.Reflection.Emit;
 
 namespace ChocolArm64
 {
@@ -23,34 +22,10 @@ namespace ChocolArm64
 
         internal void ExecuteSubroutine(CpuThread thread, long position)
         {
-            //TODO: Both the execute A32/A64 methods should be merged on the future,
-            //when both ISAs are implemented with the interpreter and JIT.
-            //As of now, A32 only has a interpreter and A64 a JIT.
-            CpuThreadState state  = thread.ThreadState;
-            MemoryManager  memory = thread.Memory;
-
-            if (state.ExecutionMode == ExecutionMode.AArch32)
-            {
-                ExecuteSubroutineA32(state, memory);
-            }
-            else
-            {
-                ExecuteSubroutineA64(state, memory, position);
-            }
-        }
-
-        private void ExecuteSubroutineA32(CpuThreadState state, MemoryManager memory)
-        {
-            do
-            {
-                OpCode64 opCode = Decoder.DecodeOpCode(state, memory, state.R15);
-
-                opCode.Interpreter(state, memory, opCode);
-            }
-            while (state.R15 != 0 && state.Running);
+            ExecuteSubroutine(thread.ThreadState, thread.Memory, position);
         }
 
-        private void ExecuteSubroutineA64(CpuThreadState state, MemoryManager memory, long position)
+        private void ExecuteSubroutine(CpuThreadState state, MemoryManager memory, long position)
         {
             do
             {
@@ -61,12 +36,12 @@ namespace ChocolArm64
 
                 if (!_cache.TryGetSubroutine(position, out TranslatedSub sub))
                 {
-                    sub = TranslateTier0(state, memory, position);
+                    sub = TranslateTier0(memory, position, state.GetExecutionMode());
                 }
 
                 if (sub.ShouldReJit())
                 {
-                    TranslateTier1(state, memory, position);
+                    TranslateTier1(memory, position, state.GetExecutionMode());
                 }
 
                 position = sub.Execute(state, memory);
@@ -79,9 +54,9 @@ namespace ChocolArm64
             return _cache.HasSubroutine(position);
         }
 
-        private TranslatedSub TranslateTier0(CpuThreadState state, MemoryManager memory, long position)
+        private TranslatedSub TranslateTier0(MemoryManager memory, long position, ExecutionMode mode)
         {
-            Block block = Decoder.DecodeBasicBlock(state, memory, position);
+            Block block = Decoder.DecodeBasicBlock(memory, position, mode);
 
             ILEmitterCtx context = new ILEmitterCtx(_cache, block);
 
@@ -98,9 +73,9 @@ namespace ChocolArm64
             return subroutine;
         }
 
-        private void TranslateTier1(CpuThreadState state, MemoryManager memory, long position)
+        private void TranslateTier1(MemoryManager memory, long position, ExecutionMode mode)
         {
-            Block graph = Decoder.DecodeSubroutine(_cache, state, memory, position);
+            Block graph = Decoder.DecodeSubroutine(_cache, memory, position, mode);
 
             ILEmitterCtx context = new ILEmitterCtx(_cache, graph);
 

+ 0 - 1
ChocolArm64/TranslatorCache.cs

@@ -1,4 +1,3 @@
-using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Diagnostics;

+ 0 - 15
Ryujinx.HLE/HOS/Horizon.cs

@@ -225,11 +225,6 @@ namespace Ryujinx.HLE.HOS
                 }
             }
 
-            if (!metaData.Is64Bits)
-            {
-                throw new NotImplementedException("32-bit titles are unsupported!");
-            }
-
             CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
 
             LoadNso("rtld");
@@ -428,11 +423,6 @@ namespace Ryujinx.HLE.HOS
                 }
             }
 
-            if (!metaData.Is64Bits)
-            {
-                throw new NotImplementedException("32-bit titles are unsupported!");
-            }
-
             CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
 
             LoadNso("rtld");
@@ -543,11 +533,6 @@ namespace Ryujinx.HLE.HOS
                 CurrentTitle = metaData.Aci0.TitleId.ToString("x16");
             }
 
-            if (!metaData.Is64Bits)
-            {
-                throw new NotImplementedException("32-bit titles are not supported!");
-            }
-
             LoadNso("rtld");
             LoadNso("main");
             LoadNso("subsdk");

+ 2 - 0
Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs

@@ -78,6 +78,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
                 }
             }
 
+            //TODO: ARM32.
             long framePointer = (long)threadState.X29;
 
             while (framePointer != 0)
@@ -245,6 +246,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
             long ehHdrEndOffset   = memory.ReadInt32(mod0Offset + 0x14) + mod0Offset;
             long modObjOffset     = memory.ReadInt32(mod0Offset + 0x18) + mod0Offset;
 
+            //TODO: Elf32.
             while (true)
             {
                 long tagVal = memory.ReadInt64(dynamicOffset + 0);

+ 7 - 0
Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs

@@ -3,6 +3,7 @@ using ChocolArm64.Events;
 using ChocolArm64.Memory;
 using Ryujinx.Common;
 using Ryujinx.Common.Logging;
+using Ryujinx.HLE.Exceptions;
 using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Kernel.Memory;
 using Ryujinx.HLE.HOS.Kernel.SupervisorCall;
@@ -797,6 +798,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
         {
             context.ThreadState.Interrupt += InterruptHandler;
             context.ThreadState.SvcCall   += _svcHandler.SvcCall;
+            context.ThreadState.Undefined += UndefinedInstructionHandler;
         }
 
         private void InterruptHandler(object sender, EventArgs e)
@@ -1021,5 +1023,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
         {
             Logger.PrintInfo(LogClass.Cpu, $"Executing at 0x{e.Position:X16}.");
         }
+
+        private void UndefinedInstructionHandler(object sender, InstUndefinedEventArgs e)
+        {
+            throw new UndefinedInstructionException(e.Position, e.RawOpCode);
+        }
     }
 }

+ 2 - 0
Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs

@@ -152,6 +152,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
 
             Context = new CpuThread(owner.Translator, owner.CpuMemory, (long)entrypoint);
 
+            Context.ThreadState.IsAarch32 = (Owner.MmuFlags & 1) == 0;
+
             Context.ThreadState.X0  = argsPtr;
             Context.ThreadState.X31 = stackTop;
 

+ 6 - 1
Ryujinx.HLE/HOS/ProgramLoader.cs

@@ -125,9 +125,14 @@ namespace Ryujinx.HLE.HOS
             IExecutable[] staticObjects,
             byte[]        arguments = null)
         {
+            if (!metaData.Is64Bits)
+            {
+                Logger.PrintWarning(LogClass.Loader, "32-bits application detected!");
+            }
+
             ulong argsStart = 0;
             int   argsSize  = 0;
-            ulong codeStart = 0x8000000;
+            ulong codeStart = metaData.Is64Bits ? 0x8000000UL : 0x200000UL;
             int   codeSize  = 0;
 
             ulong[] nsoBase = new ulong[staticObjects.Length];