diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs
index d32bd9cd2..4dc157549 100644
--- a/ChocolArm64/AOpCodeTable.cs
+++ b/ChocolArm64/AOpCodeTable.cs
@@ -8,7 +8,7 @@ namespace ChocolArm64
     {
         static AOpCodeTable()
         {
- #region "OpCode Table"
+#region "OpCode Table"
             //Integer
             Set("x0011010000xxxxx000000xxxxxxxxxx", AInstEmit.Adc,           typeof(AOpCodeAluRs));
             Set("x0111010000xxxxx000000xxxxxxxxxx", AInstEmit.Adcs,          typeof(AOpCodeAluRs));
@@ -41,6 +41,7 @@ namespace ChocolArm64
             Set("x1111010010xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ccmp,          typeof(AOpCodeCcmpImm));
             Set("x1111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmp,          typeof(AOpCodeCcmpReg));
             Set("11010101000000110011xxxx01011111", AInstEmit.Clrex,         typeof(AOpCodeSystem));
+            Set("x101101011000000000101xxxxxxxxxx", AInstEmit.Cls,           typeof(AOpCodeAlu));
             Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz,           typeof(AOpCodeAlu));
             Set("x0011010110xxxxx010000xxxxxxxxxx", AInstEmit.Crc32b,        typeof(AOpCodeAluRs));
             Set("x0011010110xxxxx010001xxxxxxxxxx", AInstEmit.Crc32h,        typeof(AOpCodeAluRs));
@@ -68,7 +69,7 @@ namespace ChocolArm64
             Set("xx111000010xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr,           typeof(AOpCodeMemImm));
             Set("xx11100101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr,           typeof(AOpCodeMemImm));
             Set("xx111000011xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr,           typeof(AOpCodeMemReg));
-            Set("xx011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit,        typeof(AOpCodeMemLit));            
+            Set("xx011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit,        typeof(AOpCodeMemLit));
             Set("0x1110001x0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs,          typeof(AOpCodeMemImm));
             Set("0x1110011xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs,          typeof(AOpCodeMemImm));
             Set("10111000100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs,          typeof(AOpCodeMemImm));
@@ -91,6 +92,7 @@ namespace ChocolArm64
             Set("x01100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr,           typeof(AOpCodeAluImm));
             Set("x0101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr,           typeof(AOpCodeAluRs));
             Set("1111100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm,          typeof(AOpCodeMemImm));
+            Set("11111000100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Pfrm,          typeof(AOpCodeMemImm));
             Set("11011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm,          typeof(AOpCodeMemLit));
             Set("x101101011000000000000xxxxxxxxxx", AInstEmit.Rbit,          typeof(AOpCodeAlu));
             Set("11010110010xxxxx000000xxxxxxxxxx", AInstEmit.Ret,           typeof(AOpCodeBReg));
@@ -131,14 +133,20 @@ namespace ChocolArm64
             Set("10011011110xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Umulh,         typeof(AOpCodeMul));
 
             //Vector
+            Set("0101111011100000101110xxxxxxxxxx", AInstEmit.Abs_S,         typeof(AOpCodeSimd));
+            Set("0>001110<<100000101110xxxxxxxxxx", AInstEmit.Abs_V,         typeof(AOpCodeSimd));
+            Set("01011110111xxxxx100001xxxxxxxxxx", AInstEmit.Add_S,         typeof(AOpCodeSimdReg));
             Set("0>001110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Add_V,         typeof(AOpCodeSimdReg));
-            Set("01011110xx110001101110xxxxxxxxxx", AInstEmit.Addp_S,        typeof(AOpCodeSimd));
+            Set("0x001110<<1xxxxx010000xxxxxxxxxx", AInstEmit.Addhn_V,       typeof(AOpCodeSimdReg));
+            Set("0101111011110001101110xxxxxxxxxx", AInstEmit.Addp_S,        typeof(AOpCodeSimd));
             Set("0>001110<<1xxxxx101111xxxxxxxxxx", AInstEmit.Addp_V,        typeof(AOpCodeSimdReg));
             Set("000011100x110001101110xxxxxxxxxx", AInstEmit.Addv_V,        typeof(AOpCodeSimd));
             Set("01001110<<110001101110xxxxxxxxxx", AInstEmit.Addv_V,        typeof(AOpCodeSimd));
             Set("0x001110001xxxxx000111xxxxxxxxxx", AInstEmit.And_V,         typeof(AOpCodeSimdReg));
             Set("0x001110011xxxxx000111xxxxxxxxxx", AInstEmit.Bic_V,         typeof(AOpCodeSimdReg));
             Set("0x10111100000xxx<<x101xxxxxxxxxx", AInstEmit.Bic_Vi,        typeof(AOpCodeSimdImm));
+            Set("0x101110111xxxxx000111xxxxxxxxxx", AInstEmit.Bif_V,         typeof(AOpCodeSimdReg));
+            Set("0x101110101xxxxx000111xxxxxxxxxx", AInstEmit.Bit_V,         typeof(AOpCodeSimdReg));
             Set("0x101110011xxxxx000111xxxxxxxxxx", AInstEmit.Bsl_V,         typeof(AOpCodeSimdReg));
             Set("0>101110<<1xxxxx100011xxxxxxxxxx", AInstEmit.Cmeq_V,        typeof(AOpCodeSimdReg));
             Set("0>001110<<100000100110xxxxxxxxxx", AInstEmit.Cmeq_V,        typeof(AOpCodeSimd));
@@ -161,8 +169,25 @@ namespace ChocolArm64
             Set("000111100x100000110000xxxxxxxxxx", AInstEmit.Fabs_S,        typeof(AOpCodeSimd));
             Set("000111100x1xxxxx001010xxxxxxxxxx", AInstEmit.Fadd_S,        typeof(AOpCodeSimdReg));
             Set("0>0011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Fadd_V,        typeof(AOpCodeSimdReg));
+            Set("0>1011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Faddp_V,       typeof(AOpCodeSimdReg));
             Set("000111100x1xxxxxxxxx01xxxxx0xxxx", AInstEmit.Fccmp_S,       typeof(AOpCodeSimdFcond));
             Set("000111100x1xxxxxxxxx01xxxxx1xxxx", AInstEmit.Fccmpe_S,      typeof(AOpCodeSimdFcond));
+            Set("010111100x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmeq_S,       typeof(AOpCodeSimdReg));
+            Set("0>0011100<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmeq_V,       typeof(AOpCodeSimdReg));
+            Set("010111101x100000110110xxxxxxxxxx", AInstEmit.Fcmeq_S,       typeof(AOpCodeSimd));
+            Set("0>0011101<100000110110xxxxxxxxxx", AInstEmit.Fcmeq_V,       typeof(AOpCodeSimd));
+            Set("011111100x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmge_S,       typeof(AOpCodeSimdReg));
+            Set("0>1011100<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmge_V,       typeof(AOpCodeSimdReg));
+            Set("011111101x100000110010xxxxxxxxxx", AInstEmit.Fcmge_S,       typeof(AOpCodeSimd));
+            Set("0>1011101<100000110010xxxxxxxxxx", AInstEmit.Fcmge_V,       typeof(AOpCodeSimd));
+            Set("011111101x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmgt_S,       typeof(AOpCodeSimdReg));
+            Set("0>1011101<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmgt_V,       typeof(AOpCodeSimdReg));
+            Set("010111101x100000110010xxxxxxxxxx", AInstEmit.Fcmgt_S,       typeof(AOpCodeSimd));
+            Set("0>0011101<100000110010xxxxxxxxxx", AInstEmit.Fcmgt_V,       typeof(AOpCodeSimd));
+            Set("011111101x100000110110xxxxxxxxxx", AInstEmit.Fcmle_S,       typeof(AOpCodeSimd));
+            Set("0>1011101<100000110110xxxxxxxxxx", AInstEmit.Fcmle_V,       typeof(AOpCodeSimd));
+            Set("010111101x100000111010xxxxxxxxxx", AInstEmit.Fcmlt_S,       typeof(AOpCodeSimd));
+            Set("0>0011101<100000111010xxxxxxxxxx", AInstEmit.Fcmlt_V,       typeof(AOpCodeSimd));
             Set("000111100x1xxxxx001000xxxxx0x000", AInstEmit.Fcmp_S,        typeof(AOpCodeSimdReg));
             Set("000111100x1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S,       typeof(AOpCodeSimdReg));
             Set("000111100x1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S,       typeof(AOpCodeSimdFcond));
@@ -182,16 +207,20 @@ namespace ChocolArm64
             Set("x00111100x111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp,     typeof(AOpCodeSimdCvt));
             Set("x00111100x011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt));
             Set("0>1011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V,      typeof(AOpCodeSimd));
-            Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V,      typeof(AOpCodeSimdShImm));            
+            Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V,      typeof(AOpCodeSimdShImm));
             Set("000111100x1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S,        typeof(AOpCodeSimdReg));
             Set("0>1011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Fdiv_V,        typeof(AOpCodeSimdReg));
             Set("000111110x0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fmadd_S,       typeof(AOpCodeSimdReg));
             Set("000111100x1xxxxx010010xxxxxxxxxx", AInstEmit.Fmax_S,        typeof(AOpCodeSimdReg));
+            Set("0x0011100x1xxxxx111101xxxxxxxxxx", AInstEmit.Fmax_V,        typeof(AOpCodeSimdReg));
             Set("000111100x1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S,      typeof(AOpCodeSimdReg));
             Set("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S,        typeof(AOpCodeSimdReg));
+            Set("0x0011101x1xxxxx111101xxxxxxxxxx", AInstEmit.Fmin_V,        typeof(AOpCodeSimdReg));
             Set("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S,      typeof(AOpCodeSimdReg));
             Set("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V,        typeof(AOpCodeSimdReg));
             Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve,       typeof(AOpCodeSimdRegElemF));
+            Set("0>0011101<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmls_V,        typeof(AOpCodeSimdReg));
+            Set("0x0011111<<xxxxx0101x0xxxxxxxxxx", AInstEmit.Fmls_Ve,       typeof(AOpCodeSimdRegElemF));
             Set("000111100x100000010000xxxxxxxxxx", AInstEmit.Fmov_S,        typeof(AOpCodeSimd));
             Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si,       typeof(AOpCodeSimdFmov));
             Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V,        typeof(AOpCodeSimdImm));
@@ -201,16 +230,34 @@ namespace ChocolArm64
             Set("1001111010101111000000xxxxxxxxxx", AInstEmit.Fmov_Itof1,    typeof(AOpCodeSimdCvt));
             Set("000111110x0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S,       typeof(AOpCodeSimdReg));
             Set("000111100x1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S,        typeof(AOpCodeSimdReg));
+            Set("010111111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Se,       typeof(AOpCodeSimdRegElemF));
             Set("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V,        typeof(AOpCodeSimdReg));
             Set("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve,       typeof(AOpCodeSimdRegElemF));
-            Set("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S,        typeof(AOpCodeSimdReg));
+            Set("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S,        typeof(AOpCodeSimd));
+            Set("0>1011101<100000111110xxxxxxxxxx", AInstEmit.Fneg_V,        typeof(AOpCodeSimd));
+            Set("000111110x1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fnmadd_S,      typeof(AOpCodeSimdReg));
             Set("000111110x1xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fnmsub_S,      typeof(AOpCodeSimdReg));
             Set("000111100x1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S,       typeof(AOpCodeSimdReg));
+            Set("010111101x100001110110xxxxxxxxxx", AInstEmit.Frecpe_S,      typeof(AOpCodeSimd));
+            Set("0>0011101<100001110110xxxxxxxxxx", AInstEmit.Frecpe_V,      typeof(AOpCodeSimd));
+            Set("010111100x1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_S,      typeof(AOpCodeSimdReg));
+            Set("0>0011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_V,      typeof(AOpCodeSimdReg));
             Set("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S,      typeof(AOpCodeSimd));
+            Set("0>1011100<100001100010xxxxxxxxxx", AInstEmit.Frinta_V,      typeof(AOpCodeSimd));
+            Set("000111100x100111110000xxxxxxxxxx", AInstEmit.Frinti_S,      typeof(AOpCodeSimd));
+            Set("0>1011101<100001100110xxxxxxxxxx", AInstEmit.Frinti_V,      typeof(AOpCodeSimd));
             Set("000111100x100101010000xxxxxxxxxx", AInstEmit.Frintm_S,      typeof(AOpCodeSimd));
             Set("0>0011100<100001100110xxxxxxxxxx", AInstEmit.Frintm_V,      typeof(AOpCodeSimd));
+            Set("000111100x100100010000xxxxxxxxxx", AInstEmit.Frintn_S,      typeof(AOpCodeSimd));
+            Set("0>0011100<100001100010xxxxxxxxxx", AInstEmit.Frintn_V,      typeof(AOpCodeSimd));
             Set("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S,      typeof(AOpCodeSimd));
+            Set("0>0011101<100001100010xxxxxxxxxx", AInstEmit.Frintp_V,      typeof(AOpCodeSimd));
             Set("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S,      typeof(AOpCodeSimd));
+            Set("0>1011100<100001100110xxxxxxxxxx", AInstEmit.Frintx_V,      typeof(AOpCodeSimd));
+            Set("011111101x100001110110xxxxxxxxxx", AInstEmit.Frsqrte_S,     typeof(AOpCodeSimd));
+            Set("0>1011101<100001110110xxxxxxxxxx", AInstEmit.Frsqrte_V,     typeof(AOpCodeSimd));
+            Set("010111101x1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_S,     typeof(AOpCodeSimdReg));
+            Set("0>0011101<1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_V,     typeof(AOpCodeSimdReg));
             Set("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S,       typeof(AOpCodeSimd));
             Set("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S,        typeof(AOpCodeSimdReg));
             Set("0>0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V,        typeof(AOpCodeSimdReg));
@@ -218,8 +265,8 @@ namespace ChocolArm64
             Set("01101110000xxxxx0xxxx1xxxxxxxxxx", AInstEmit.Ins_V,         typeof(AOpCodeSimdIns));
             Set("0x00110001000000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms,       typeof(AOpCodeSimdMemMs));
             Set("0x001100110xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms,       typeof(AOpCodeSimdMemMs));
-            Set("0x00110101000000xx0xxxxxxxxxxxxx", AInstEmit.Ld__Vss,       typeof(AOpCodeSimdMemSs));
-            Set("0x001101110xxxxxxx0xxxxxxxxxxxxx", AInstEmit.Ld__Vss,       typeof(AOpCodeSimdMemSs));
+            Set("0x00110101x00000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vss,       typeof(AOpCodeSimdMemSs));
+            Set("0x00110111xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vss,       typeof(AOpCodeSimdMemSs));
             Set("xx10110xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp,           typeof(AOpCodeSimdMemPair));
             Set("xx111100x10xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ldr,           typeof(AOpCodeSimdMemImm));
             Set("xx111100x10xxxxxxxxx01xxxxxxxxxx", AInstEmit.Ldr,           typeof(AOpCodeSimdMemImm));
@@ -228,6 +275,7 @@ namespace ChocolArm64
             Set("xx111100x11xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr,           typeof(AOpCodeSimdMemReg));
             Set("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit,        typeof(AOpCodeSimdMemLit));
             Set("0x001110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mla_V,         typeof(AOpCodeSimdReg));
+            Set("0x101111xxxxxxxx0000x0xxxxxxxxxx", AInstEmit.Mla_Ve,        typeof(AOpCodeSimdRegElem));
             Set("0x101110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mls_V,         typeof(AOpCodeSimdReg));
             Set("0x00111100000xxx0xx001xxxxxxxxxx", AInstEmit.Movi_V,        typeof(AOpCodeSimdImm));
             Set("0x00111100000xxx10x001xxxxxxxxxx", AInstEmit.Movi_V,        typeof(AOpCodeSimdImm));
@@ -238,11 +286,14 @@ namespace ChocolArm64
             Set("0x10111100000xxx0xx001xxxxxxxxxx", AInstEmit.Mvni_V,        typeof(AOpCodeSimdImm));
             Set("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V,        typeof(AOpCodeSimdImm));
             Set("0x10111100000xxx110x01xxxxxxxxxx", AInstEmit.Mvni_V,        typeof(AOpCodeSimdImm));
-            Set("0>101110<<100000101110xxxxxxxxxx", AInstEmit.Neg_V,         typeof(AOpCodeSimdReg));
+            Set("0111111011100000101110xxxxxxxxxx", AInstEmit.Neg_S,         typeof(AOpCodeSimd));
+            Set("0>101110<<100000101110xxxxxxxxxx", AInstEmit.Neg_V,         typeof(AOpCodeSimd));
             Set("0x10111000100000010110xxxxxxxxxx", AInstEmit.Not_V,         typeof(AOpCodeSimd));
             Set("0x001110101xxxxx000111xxxxxxxxxx", AInstEmit.Orr_V,         typeof(AOpCodeSimdReg));
             Set("0x00111100000xxx<<x101xxxxxxxxxx", AInstEmit.Orr_Vi,        typeof(AOpCodeSimdImm));
+            Set("0x101110<<1xxxxx010000xxxxxxxxxx", AInstEmit.Raddhn_V,      typeof(AOpCodeSimdReg));
             Set("0x001110<<100000000010xxxxxxxxxx", AInstEmit.Rev64_V,       typeof(AOpCodeSimd));
+            Set("0x101110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Rsubhn_V,      typeof(AOpCodeSimdReg));
             Set("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V,       typeof(AOpCodeSimdReg));
             Set("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp,      typeof(AOpCodeSimdCvt));
             Set("010111100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_S,       typeof(AOpCodeSimd));
@@ -263,23 +314,30 @@ namespace ChocolArm64
             Set("0x0011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V,        typeof(AOpCodeSimdShImm));
             Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms,       typeof(AOpCodeSimdMemMs));
             Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms,       typeof(AOpCodeSimdMemMs));
-            Set("0x00110100000000xx0xxxxxxxxxxxxx", AInstEmit.St__Vss,       typeof(AOpCodeSimdMemSs));
-            Set("0x001101100xxxxxxx0xxxxxxxxxxxxx", AInstEmit.St__Vss,       typeof(AOpCodeSimdMemSs));
+            Set("0x00110100x00000xxxxxxxxxxxxxxxx", AInstEmit.St__Vss,       typeof(AOpCodeSimdMemSs));
+            Set("0x00110110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vss,       typeof(AOpCodeSimdMemSs));
             Set("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp,           typeof(AOpCodeSimdMemPair));
             Set("xx111100x00xxxxxxxxx00xxxxxxxxxx", AInstEmit.Str,           typeof(AOpCodeSimdMemImm));
             Set("xx111100x00xxxxxxxxx01xxxxxxxxxx", AInstEmit.Str,           typeof(AOpCodeSimdMemImm));
             Set("xx111100x00xxxxxxxxx11xxxxxxxxxx", AInstEmit.Str,           typeof(AOpCodeSimdMemImm));
             Set("xx111101x0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str,           typeof(AOpCodeSimdMemImm));
             Set("xx111100x01xxxxxxxxx10xxxxxxxxxx", AInstEmit.Str,           typeof(AOpCodeSimdMemReg));
-            Set("01111110xx1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_S,         typeof(AOpCodeSimdReg));
+            Set("01111110111xxxxx100001xxxxxxxxxx", AInstEmit.Sub_S,         typeof(AOpCodeSimdReg));
             Set("0>101110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_V,         typeof(AOpCodeSimdReg));
+            Set("0x001110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Subhn_V,       typeof(AOpCodeSimdReg));
             Set("0x001110000xxxxx0xx000xxxxxxxxxx", AInstEmit.Tbl_V,         typeof(AOpCodeSimdTbl));
+            Set("0>001110<<0xxxxx001010xxxxxxxxxx", AInstEmit.Trn1_V,        typeof(AOpCodeSimdReg));
+            Set("0>001110<<0xxxxx011010xxxxxxxxxx", AInstEmit.Trn2_V,        typeof(AOpCodeSimdReg));
+            Set("0x101110<<1xxxxx011101xxxxxxxxxx", AInstEmit.Uabd_V,        typeof(AOpCodeSimdReg));
+            Set("0x101110<<1xxxxx011100xxxxxxxxxx", AInstEmit.Uabdl_V,       typeof(AOpCodeSimdReg));
+            Set("0x101110<<1xxxxx000000xxxxxxxxxx", AInstEmit.Uaddl_V,       typeof(AOpCodeSimdReg));
             Set("001011100x110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V,      typeof(AOpCodeSimd));
             Set("01101110<<110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V,      typeof(AOpCodeSimd));
             Set("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V,       typeof(AOpCodeSimdReg));
             Set("x0011110xx100011000000xxxxxxxxxx", AInstEmit.Ucvtf_Gp,      typeof(AOpCodeSimdCvt));
             Set("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S,       typeof(AOpCodeSimd));
             Set("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V,       typeof(AOpCodeSimd));
+            Set("0x101110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Uhadd_V,       typeof(AOpCodeSimdReg));
             Set("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S,        typeof(AOpCodeSimdIns));
             Set("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V,       typeof(AOpCodeSimdReg));
             Set("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V,        typeof(AOpCodeSimdReg));
@@ -287,11 +345,11 @@ namespace ChocolArm64
             Set("011111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_S,        typeof(AOpCodeSimdShImm));
             Set("0x1011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V,        typeof(AOpCodeSimdShImm));
             Set("0x1011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V,        typeof(AOpCodeSimdShImm));
-            Set("0x001110xx0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V,        typeof(AOpCodeSimdReg));
-            Set("0x001110xx0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V,        typeof(AOpCodeSimdReg));
+            Set("0>001110<<0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V,        typeof(AOpCodeSimdReg));
+            Set("0>001110<<0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V,        typeof(AOpCodeSimdReg));
             Set("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V,         typeof(AOpCodeSimd));
-            Set("0x001110xx0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V,        typeof(AOpCodeSimdReg));
-            Set("0x001110xx0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V,        typeof(AOpCodeSimdReg));
+            Set("0>001110<<0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V,        typeof(AOpCodeSimdReg));
+            Set("0>001110<<0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V,        typeof(AOpCodeSimdReg));
 #endregion
         }
 
@@ -433,4 +491,4 @@ namespace ChocolArm64
             return AInst.Undefined;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/ChocolArm64/AOptimizations.cs b/ChocolArm64/AOptimizations.cs
index a3c82dccd..2627c2368 100644
--- a/ChocolArm64/AOptimizations.cs
+++ b/ChocolArm64/AOptimizations.cs
@@ -1,4 +1,6 @@
 public static class AOptimizations
 {
     public static bool DisableMemoryChecks = false;
+
+    public static bool GenerateCallStack = true;
 }
\ No newline at end of file
diff --git a/ChocolArm64/AThread.cs b/ChocolArm64/AThread.cs
index 62f9d2d39..16804a7c5 100644
--- a/ChocolArm64/AThread.cs
+++ b/ChocolArm64/AThread.cs
@@ -54,6 +54,14 @@ namespace ChocolArm64
             return true;
         }
 
-        public void StopExecution() => ThreadState.Running = false;
+        public void StopExecution()
+        {
+            ThreadState.Running = false;
+        }
+
+        public bool IsCurrentThread()
+        {
+            return Thread.CurrentThread == Work;
+        }
     }
 }
\ No newline at end of file
diff --git a/ChocolArm64/ATranslatedSub.cs b/ChocolArm64/ATranslatedSub.cs
index 414038ab6..9dbc378ec 100644
--- a/ChocolArm64/ATranslatedSub.cs
+++ b/ChocolArm64/ATranslatedSub.cs
@@ -3,6 +3,7 @@ using ChocolArm64.State;
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Linq;
 using System.Reflection;
 using System.Reflection.Emit;
 
@@ -23,7 +24,7 @@ namespace ChocolArm64
 
         public ReadOnlyCollection<ARegister> Params { get; private set; }
 
-        private HashSet<long> Callees;
+        private HashSet<long> Callers;
 
         private ATranslatedSubType Type;
 
@@ -33,7 +34,7 @@ namespace ChocolArm64
 
         private int MinCallCountForReJit = 250;
 
-        public ATranslatedSub(DynamicMethod Method, List<ARegister> Params, HashSet<long> Callees)
+        public ATranslatedSub(DynamicMethod Method, List<ARegister> Params)
         {
             if (Method == null)
             {
@@ -45,14 +46,10 @@ namespace ChocolArm64
                 throw new ArgumentNullException(nameof(Params));
             }
 
-            if (Callees == null)
-            {
-                throw new ArgumentNullException(nameof(Callees));
-            }
-
             this.Method  = Method;
             this.Params  = Params.AsReadOnly();
-            this.Callees = Callees;
+
+            Callers = new HashSet<long>();
 
             PrepareDelegate();
         }
@@ -107,17 +104,14 @@ namespace ChocolArm64
 
         public bool ShouldReJit()
         {
-            if (Type == ATranslatedSubType.SubTier0)
+            if (NeedsReJit && CallCount < MinCallCountForReJit)
             {
-                if (CallCount < MinCallCountForReJit)
-                {
-                    CallCount++;
-                }
+                CallCount++;
 
-                return CallCount == MinCallCountForReJit;
+                return false;
             }
 
-            return Type == ATranslatedSubType.SubTier1 && NeedsReJit;
+            return NeedsReJit;
         }
 
         public long Execute(AThreadState ThreadState, AMemory Memory)
@@ -125,10 +119,32 @@ namespace ChocolArm64
             return ExecDelegate(ThreadState, Memory);
         }
 
-        public void SetType(ATranslatedSubType Type) => this.Type = Type;
+        public void AddCaller(long Position)
+        {
+            lock (Callers)
+            {
+                Callers.Add(Position);
+            }
+        }
 
-        public bool HasCallee(long Position) => Callees.Contains(Position);
+        public long[] GetCallerPositions()
+        {
+            lock (Callers)
+            {
+                return Callers.ToArray();
+            }
+        }
 
-        public void MarkForReJit() => NeedsReJit = true;        
+        public void SetType(ATranslatedSubType Type)
+        {
+            this.Type = Type;
+
+            if (Type == ATranslatedSubType.SubTier0)
+            {
+                NeedsReJit = true;
+            }
+        }
+
+        public void MarkForReJit() => NeedsReJit = true;
     }
 }
\ No newline at end of file
diff --git a/ChocolArm64/ATranslator.cs b/ChocolArm64/ATranslator.cs
index 02c18efd2..e46750fce 100644
--- a/ChocolArm64/ATranslator.cs
+++ b/ChocolArm64/ATranslator.cs
@@ -107,25 +107,31 @@ namespace ChocolArm64
 
             ATranslatedSub Subroutine = Context.GetSubroutine();
 
-            if (SubBlocks.Contains(Position))
+            lock (SubBlocks)
             {
-                SubBlocks.Remove(Position);
+                if (SubBlocks.Contains(Position))
+                {
+                    SubBlocks.Remove(Position);
 
-                Subroutine.SetType(ATranslatedSubType.SubBlock);
-            }
-            else
-            {
-                Subroutine.SetType(ATranslatedSubType.SubTier0);
+                    Subroutine.SetType(ATranslatedSubType.SubBlock);
+                }
+                else
+                {
+                    Subroutine.SetType(ATranslatedSubType.SubTier0);
+                }
             }
 
             CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine);
 
             AOpCode LastOp = Block.GetLastOp();
 
-            if (LastOp.Emitter != AInstEmit.Ret &&
-                LastOp.Emitter != AInstEmit.Br)
+            lock (SubBlocks)
             {
-                SubBlocks.Add(LastOp.Position + 4);
+                if (LastOp.Emitter != AInstEmit.Ret &&
+                    LastOp.Emitter != AInstEmit.Br)
+                {
+                    SubBlocks.Add(LastOp.Position + 4);
+                }
             }
 
             return Subroutine;
@@ -154,11 +160,14 @@ namespace ChocolArm64
 
             //Mark all methods that calls this method for ReJiting,
             //since we can now call it directly which is faster.
-            foreach (ATranslatedSub TS in CachedSubs.Values)
+            if (CachedSubs.TryGetValue(Position, out ATranslatedSub OldSub))
             {
-                if (TS.HasCallee(Position))
+                foreach (long CallerPos in OldSub.GetCallerPositions())
                 {
-                    TS.MarkForReJit();
+                    if (CachedSubs.TryGetValue(Position, out ATranslatedSub CallerSub))
+                    {
+                        CallerSub.MarkForReJit();
+                    }
                 }
             }
 
diff --git a/ChocolArm64/Decoder/AOpCodeBImmCmp.cs b/ChocolArm64/Decoder/AOpCodeBImmCmp.cs
index 1b6185da6..0f16b73e0 100644
--- a/ChocolArm64/Decoder/AOpCodeBImmCmp.cs
+++ b/ChocolArm64/Decoder/AOpCodeBImmCmp.cs
@@ -1,4 +1,5 @@
 using ChocolArm64.Instruction;
+using ChocolArm64.State;
 
 namespace ChocolArm64.Decoder
 {
@@ -11,6 +12,10 @@ namespace ChocolArm64.Decoder
             Rt = OpCode & 0x1f;
 
             Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode);
+
+            RegisterSize = (OpCode >> 31) != 0
+                ? ARegisterSize.Int64
+                : ARegisterSize.Int32;
         }
     }
 }
\ No newline at end of file
diff --git a/ChocolArm64/Decoder/AOpCodeSimdMemMs.cs b/ChocolArm64/Decoder/AOpCodeSimdMemMs.cs
index 9ea979ba7..a54e2360c 100644
--- a/ChocolArm64/Decoder/AOpCodeSimdMemMs.cs
+++ b/ChocolArm64/Decoder/AOpCodeSimdMemMs.cs
@@ -25,8 +25,8 @@ namespace ChocolArm64.Decoder
                 default: Inst = AInst.Undefined; return;
             }
 
-            Size  =  (OpCode >> 10) & 0x3;
-            WBack = ((OpCode >> 23) & 0x1) != 0;
+            Size  =  (OpCode >> 10) & 3;
+            WBack = ((OpCode >> 23) & 1) != 0;
 
             bool Q = ((OpCode >> 30) & 1) != 0;
 
diff --git a/ChocolArm64/Decoder/AOpCodeSimdMemSs.cs b/ChocolArm64/Decoder/AOpCodeSimdMemSs.cs
index be4a8cd98..c8794ff5a 100644
--- a/ChocolArm64/Decoder/AOpCodeSimdMemSs.cs
+++ b/ChocolArm64/Decoder/AOpCodeSimdMemSs.cs
@@ -18,7 +18,7 @@ namespace ChocolArm64.Decoder
             int Scale  = (OpCode >> 14) & 3;
             int L      = (OpCode >> 22) & 1;
             int Q      = (OpCode >> 30) & 1;
-            
+
             SElems |= (OpCode >> 21) & 1;
 
             SElems++;
@@ -82,12 +82,13 @@ namespace ChocolArm64.Decoder
                 }
             }
 
+            this.Index  = Index;
             this.SElems = SElems;
             this.Size   = Scale;
 
             Extend64 = false;
 
-            WBack = ((OpCode >> 23) & 0x1) != 0;
+            WBack = ((OpCode >> 23) & 1) != 0;
 
             RegisterSize = Q != 0
                 ? ARegisterSize.SIMD128
diff --git a/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs b/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs
index 721da88fa..d6dc4bd23 100644
--- a/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs
+++ b/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs
@@ -11,9 +11,8 @@ namespace ChocolArm64.Decoder
             switch (Size)
             {
                 case 1:
-                    Index = (OpCode >> 21) & 1 |
-                            (OpCode >> 10) & 2 |
-                            (OpCode >> 18) & 4;
+                    Index = (OpCode >> 20) & 3 |
+                            (OpCode >>  9) & 4;
 
                     Rm &= 0xf;
 
diff --git a/ChocolArm64/Instruction/AInstEmitAlu.cs b/ChocolArm64/Instruction/AInstEmitAlu.cs
index 57020364b..bacbfc9e8 100644
--- a/ChocolArm64/Instruction/AInstEmitAlu.cs
+++ b/ChocolArm64/Instruction/AInstEmitAlu.cs
@@ -100,6 +100,24 @@ namespace ChocolArm64.Instruction
             EmitDataStore(Context, SetFlags);
         }
 
+        public static void Cls(AILEmitterCtx Context)
+        {
+            AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
+
+            Context.EmitLdintzr(Op.Rn);
+
+            if (Op.RegisterSize == ARegisterSize.Int32)
+            {
+                ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingSigns32));
+            }
+            else
+            {
+                ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingSigns64));
+            }
+
+            Context.EmitStintzr(Op.Rd);
+        }
+
         public static void Clz(AILEmitterCtx Context)
         {
             AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
@@ -383,4 +401,4 @@ namespace ChocolArm64.Instruction
             Context.EmitStflg((int)APState.CBit);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/ChocolArm64/Instruction/AInstEmitException.cs b/ChocolArm64/Instruction/AInstEmitException.cs
index 3964c9497..3e444c730 100644
--- a/ChocolArm64/Instruction/AInstEmitException.cs
+++ b/ChocolArm64/Instruction/AInstEmitException.cs
@@ -1,15 +1,12 @@
 using ChocolArm64.Decoder;
 using ChocolArm64.State;
 using ChocolArm64.Translation;
-using System.Reflection;
 using System.Reflection.Emit;
 
 namespace ChocolArm64.Instruction
 {
     static partial class AInstEmit
     {
-        private const BindingFlags Binding = BindingFlags.NonPublic | BindingFlags.Instance;
-
         public static void Brk(AILEmitterCtx Context)
         {
             EmitExceptionCall(Context, nameof(AThreadState.OnBreak));
@@ -30,9 +27,7 @@ namespace ChocolArm64.Instruction
 
             Context.EmitLdc_I4(Op.Id);
 
-            MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
-
-            Context.EmitCall(MthdInfo);
+            Context.EmitPrivateCall(typeof(AThreadState), MthdName);
 
             //Check if the thread should still be running, if it isn't then we return 0
             //to force a return to the dispatcher and then exit the thread.
@@ -73,11 +68,7 @@ namespace ChocolArm64.Instruction
             Context.EmitLdc_I8(Op.Position);
             Context.EmitLdc_I4(Op.RawOpCode);
 
-            string MthdName = nameof(AThreadState.OnUndefined);
-
-            MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
-
-            Context.EmitCall(MthdInfo);
+            Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.OnUndefined));
 
             if (Context.CurrBlock.Next != null)
             {
diff --git a/ChocolArm64/Instruction/AInstEmitFlow.cs b/ChocolArm64/Instruction/AInstEmitFlow.cs
index 91262834f..89979d050 100644
--- a/ChocolArm64/Instruction/AInstEmitFlow.cs
+++ b/ChocolArm64/Instruction/AInstEmitFlow.cs
@@ -35,6 +35,14 @@ namespace ChocolArm64.Instruction
         {
             AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
 
+            if (AOptimizations.GenerateCallStack)
+            {
+                Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+                Context.EmitLdc_I8(Op.Imm);
+
+                Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod));
+            }
+
             Context.EmitLdc_I(Op.Position + 4);
             Context.EmitStint(AThreadState.LRIndex);
             Context.EmitStoreState();
@@ -72,6 +80,14 @@ namespace ChocolArm64.Instruction
         {
             AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
 
+            if (AOptimizations.GenerateCallStack)
+            {
+                Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+                Context.EmitLdintzr(Op.Rn);
+
+                Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod));
+            }
+
             Context.EmitLdc_I(Op.Position + 4);
             Context.EmitStint(AThreadState.LRIndex);
             Context.EmitStoreState();
@@ -84,6 +100,14 @@ namespace ChocolArm64.Instruction
         {
             AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
 
+            if (AOptimizations.GenerateCallStack)
+            {
+                Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+                Context.EmitLdintzr(Op.Rn);
+
+                Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.JumpMethod));
+            }
+
             Context.EmitStoreState();
             Context.EmitLdintzr(Op.Rn);
 
@@ -105,6 +129,13 @@ namespace ChocolArm64.Instruction
 
         public static void Ret(AILEmitterCtx Context)
         {
+            if (AOptimizations.GenerateCallStack)
+            {
+                Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+
+                Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.ExitMethod));
+            }
+
             Context.EmitStoreState();
             Context.EmitLdint(AThreadState.LRIndex);
 
diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
index bf980a581..2dce74107 100644
--- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
+++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs
@@ -11,11 +11,44 @@ namespace ChocolArm64.Instruction
 {
     static partial class AInstEmit
     {
+        public static void Abs_S(AILEmitterCtx Context)
+        {
+            EmitScalarUnaryOpSx(Context, () => EmitAbs(Context));
+        }
+
+        public static void Abs_V(AILEmitterCtx Context)
+        {
+            EmitVectorUnaryOpSx(Context, () => EmitAbs(Context));
+        }
+
+        private static void EmitAbs(AILEmitterCtx Context)
+        {
+            AILLabel LblTrue = new AILLabel();
+
+            Context.Emit(OpCodes.Dup);
+            Context.Emit(OpCodes.Ldc_I4_0);
+            Context.Emit(OpCodes.Bge_S, LblTrue);
+
+            Context.Emit(OpCodes.Neg);
+
+            Context.MarkLabel(LblTrue);
+        }
+
+        public static void Add_S(AILEmitterCtx Context)
+        {
+            EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
+        }
+
         public static void Add_V(AILEmitterCtx Context)
         {
             EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
         }
 
+        public static void Addhn_V(AILEmitterCtx Context)
+        {
+            EmitHighNarrow(Context, () => Context.Emit(OpCodes.Add), Round: false);
+        }
+
         public static void Addp_S(AILEmitterCtx Context)
         {
             AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@@ -101,6 +134,40 @@ namespace ChocolArm64.Instruction
             }
         }
 
+        private static void EmitHighNarrow(AILEmitterCtx Context, Action Emit, bool Round)
+        {
+            AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+            int Elems = 8 >> Op.Size;
+            int ESize = 8 << Op.Size;
+
+            int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1);
+                EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size + 1);
+
+                Emit();
+
+                if (Round)
+                {
+                    Context.EmitLdc_I8(1L << (ESize - 1));
+
+                    Context.Emit(OpCodes.Add);
+                }
+
+                Context.EmitLsr(ESize);
+
+                EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size);
+            }
+
+            if (Part == 0)
+            {
+                EmitVectorZeroUpper(Context, Op.Rd);
+            }
+        }
+
         public static void Fabd_S(AILEmitterCtx Context)
         {
             EmitScalarBinaryOpF(Context, () =>
@@ -129,6 +196,38 @@ namespace ChocolArm64.Instruction
             EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Add));
         }
 
+        public static void Faddp_V(AILEmitterCtx Context)
+        {
+            AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+            int Elems = Bytes >> SizeF + 2;
+            int Half  = Elems >> 1;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                int Elem = (Index & (Half - 1)) << 1;
+
+                EmitVectorExtractF(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, SizeF);
+                EmitVectorExtractF(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, SizeF);
+
+                Context.Emit(OpCodes.Add);
+
+                EmitVectorInsertTmpF(Context, Index, SizeF);
+            }
+
+            Context.EmitLdvectmp();
+            Context.EmitStvec(Op.Rd);
+
+            if (Op.RegisterSize == ARegisterSize.SIMD64)
+            {
+                EmitVectorZeroUpper(Context, Op.Rd);
+            }
+        }
+
         public static void Fdiv_S(AILEmitterCtx Context)
         {
             EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div));
@@ -150,17 +249,87 @@ namespace ChocolArm64.Instruction
 
         public static void Fmax_S(AILEmitterCtx Context)
         {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
             EmitScalarBinaryOpF(Context, () =>
             {
-                EmitBinaryMathCall(Context, nameof(Math.Max));
+                if (Op.Size == 0)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.MaxF));
+                }
+                else if (Op.Size == 1)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Max));
+                }
+                else
+                {
+                    throw new InvalidOperationException();
+                }
+            });
+        }
+
+        public static void Fmax_V(AILEmitterCtx Context)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            EmitVectorBinaryOpF(Context, () =>
+            {
+                if (Op.Size == 0)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.MaxF));
+                }
+                else if (Op.Size == 1)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Max));
+                }
+                else
+                {
+                    throw new InvalidOperationException();
+                }
             });
         }
 
         public static void Fmin_S(AILEmitterCtx Context)
         {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
             EmitScalarBinaryOpF(Context, () =>
             {
-                EmitBinaryMathCall(Context, nameof(Math.Min));
+                if (Op.Size == 0)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.MinF));
+                }
+                else if (Op.Size == 1)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Min));
+                }
+                else
+                {
+                    throw new InvalidOperationException();
+                }
+            });
+        }
+
+        public static void Fmin_V(AILEmitterCtx Context)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            EmitVectorBinaryOpF(Context, () =>
+            {
+                if (SizeF == 0)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.MinF));
+                }
+                else if (SizeF == 1)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Min));
+                }
+                else
+                {
+                    throw new InvalidOperationException();
+                }
             });
         }
 
@@ -192,6 +361,24 @@ namespace ChocolArm64.Instruction
             });
         }
 
+        public static void Fmls_V(AILEmitterCtx Context)
+        {
+            EmitVectorTernaryOpF(Context, () =>
+            {
+                Context.Emit(OpCodes.Mul);
+                Context.Emit(OpCodes.Sub);
+            });
+        }
+
+        public static void Fmls_Ve(AILEmitterCtx Context)
+        {
+            EmitVectorTernaryOpByElemF(Context, () =>
+            {
+                Context.Emit(OpCodes.Mul);
+                Context.Emit(OpCodes.Sub);
+            });
+        }
+
         public static void Fmsub_S(AILEmitterCtx Context)
         {
             EmitScalarTernaryRaOpF(Context, () =>
@@ -206,6 +393,11 @@ namespace ChocolArm64.Instruction
             EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
         }
 
+        public static void Fmul_Se(AILEmitterCtx Context)
+        {
+            EmitScalarBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul));
+        }
+
         public static void Fmul_V(AILEmitterCtx Context)
         {
             EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
@@ -221,13 +413,30 @@ namespace ChocolArm64.Instruction
             EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg));
         }
 
-        public static void Fnmul_S(AILEmitterCtx Context)
+        public static void Fneg_V(AILEmitterCtx Context)
         {
-            EmitScalarBinaryOpF(Context, () =>
-            {
-                Context.Emit(OpCodes.Mul);
-                Context.Emit(OpCodes.Neg);
-            });
+            EmitVectorUnaryOpF(Context, () => Context.Emit(OpCodes.Neg));
+        }
+
+        public static void Fnmadd_S(AILEmitterCtx Context)
+        {
+            AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
+
+            Context.Emit(OpCodes.Neg);
+
+            EmitVectorExtractF(Context, Op.Rm, 0, SizeF);
+
+            Context.Emit(OpCodes.Mul);
+
+            EmitVectorExtractF(Context, Op.Ra, 0, SizeF);
+
+            Context.Emit(OpCodes.Sub);
+
+            EmitScalarSetF(Context, Op.Rd, SizeF);
         }
 
         public static void Fnmsub_S(AILEmitterCtx Context)
@@ -235,7 +444,7 @@ namespace ChocolArm64.Instruction
             AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
 
             int SizeF = Op.Size & 1;
-            
+
             EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
             EmitVectorExtractF(Context, Op.Rm, 0, SizeF);
 
@@ -248,6 +457,119 @@ namespace ChocolArm64.Instruction
             EmitScalarSetF(Context, Op.Rd, SizeF);
         }
 
+        public static void Fnmul_S(AILEmitterCtx Context)
+        {
+            EmitScalarBinaryOpF(Context, () =>
+            {
+                Context.Emit(OpCodes.Mul);
+                Context.Emit(OpCodes.Neg);
+            });
+        }
+
+        public static void Frecpe_S(AILEmitterCtx Context)
+        {
+            EmitFrecpe(Context, 0, Scalar: true);
+        }
+
+        public static void Frecpe_V(AILEmitterCtx Context)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+            for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
+            {
+                EmitFrecpe(Context, Index, Scalar: false);
+            }
+
+            if (Op.RegisterSize == ARegisterSize.SIMD64)
+            {
+                EmitVectorZeroUpper(Context, Op.Rd);
+            }
+        }
+
+        private static void EmitFrecpe(AILEmitterCtx Context, int Index, bool Scalar)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            if (SizeF == 0)
+            {
+                Context.EmitLdc_R4(1);
+            }
+            else /* if (SizeF == 1) */
+            {
+                Context.EmitLdc_R8(1);
+            }
+
+            EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
+
+            Context.Emit(OpCodes.Div);
+
+            if (Scalar)
+            {
+                EmitVectorZeroAll(Context, Op.Rd);
+            }
+
+            EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
+        }
+
+        public static void Frecps_S(AILEmitterCtx Context)
+        {
+            EmitFrecps(Context, 0, Scalar: true);
+        }
+
+        public static void Frecps_V(AILEmitterCtx Context)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+            for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
+            {
+                EmitFrecps(Context, Index, Scalar: false);
+            }
+
+            if (Op.RegisterSize == ARegisterSize.SIMD64)
+            {
+                EmitVectorZeroUpper(Context, Op.Rd);
+            }
+        }
+
+        private static void EmitFrecps(AILEmitterCtx Context, int Index, bool Scalar)
+        {
+            AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            if (SizeF == 0)
+            {
+                Context.EmitLdc_R4(2);
+            }
+            else /* if (SizeF == 1) */
+            {
+                Context.EmitLdc_R8(2);
+            }
+
+            EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
+            EmitVectorExtractF(Context, Op.Rm, Index, SizeF);
+
+            Context.Emit(OpCodes.Mul);
+            Context.Emit(OpCodes.Sub);
+
+            if (Scalar)
+            {
+                EmitVectorZeroAll(Context, Op.Rd);
+            }
+
+            EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
+        }
+
         public static void Frinta_S(AILEmitterCtx Context)
         {
             AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@@ -259,6 +581,66 @@ namespace ChocolArm64.Instruction
             EmitScalarSetF(Context, Op.Rd, Op.Size);
         }
 
+        public static void Frinta_V(AILEmitterCtx Context)
+        {
+            EmitVectorUnaryOpF(Context, () =>
+            {
+                EmitRoundMathCall(Context, MidpointRounding.AwayFromZero);
+            });
+        }
+
+        public static void Frinti_S(AILEmitterCtx Context)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            EmitScalarUnaryOpF(Context, () =>
+            {
+                Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+
+                Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr));
+
+                if (Op.Size == 0)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.RoundF));
+                }
+                else if (Op.Size == 1)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Round));
+                }
+                else
+                {
+                    throw new InvalidOperationException();
+                }
+            });
+        }
+
+        public static void Frinti_V(AILEmitterCtx Context)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            EmitVectorUnaryOpF(Context, () =>
+            {
+                Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+
+                Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr));
+
+                if (SizeF == 0)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.RoundF));
+                }
+                else if (SizeF == 1)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Round));
+                }
+                else
+                {
+                    throw new InvalidOperationException();
+                }
+            });
+        }
+
         public static void Frintm_S(AILEmitterCtx Context)
         {
             EmitScalarUnaryOpF(Context, () =>
@@ -275,6 +657,25 @@ namespace ChocolArm64.Instruction
             });
         }
 
+        public static void Frintn_S(AILEmitterCtx Context)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
+
+            EmitRoundMathCall(Context, MidpointRounding.ToEven);
+
+            EmitScalarSetF(Context, Op.Rd, Op.Size);
+        }
+
+        public static void Frintn_V(AILEmitterCtx Context)
+        {
+            EmitVectorUnaryOpF(Context, () =>
+            {
+                EmitRoundMathCall(Context, MidpointRounding.ToEven);
+            });
+        }
+
         public static void Frintp_S(AILEmitterCtx Context)
         {
             EmitScalarUnaryOpF(Context, () =>
@@ -283,6 +684,14 @@ namespace ChocolArm64.Instruction
             });
         }
 
+        public static void Frintp_V(AILEmitterCtx Context)
+        {
+            EmitVectorUnaryOpF(Context, () =>
+            {
+                EmitUnaryMathCall(Context, nameof(Math.Ceiling));
+            });
+        }
+
         public static void Frintx_S(AILEmitterCtx Context)
         {
             AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@@ -308,6 +717,111 @@ namespace ChocolArm64.Instruction
             });
         }
 
+        public static void Frintx_V(AILEmitterCtx Context)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            EmitVectorUnaryOpF(Context, () =>
+            {
+                Context.EmitLdarg(ATranslatedSub.StateArgIdx);
+
+                Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr));
+
+                if (Op.Size == 0)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.RoundF));
+                }
+                else if (Op.Size == 1)
+                {
+                    ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Round));
+                }
+                else
+                {
+                    throw new InvalidOperationException();
+                }
+            });
+        }
+
+        public static void Frsqrte_S(AILEmitterCtx Context)
+        {
+            EmitScalarUnaryOpF(Context, () =>
+            {
+                EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.InvSqrtEstimate));
+            });
+        }
+
+        public static void Frsqrte_V(AILEmitterCtx Context)
+        {
+            EmitVectorUnaryOpF(Context, () =>
+            {
+                EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.InvSqrtEstimate));
+            });
+        }
+
+        public static void Frsqrts_S(AILEmitterCtx Context)
+        {
+            EmitFrsqrts(Context, 0, Scalar: true);
+        }
+
+        public static void Frsqrts_V(AILEmitterCtx Context)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+            for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
+            {
+                EmitFrsqrts(Context, Index, Scalar: false);
+            }
+
+            if (Op.RegisterSize == ARegisterSize.SIMD64)
+            {
+                EmitVectorZeroUpper(Context, Op.Rd);
+            }
+        }
+
+        private static void EmitFrsqrts(AILEmitterCtx Context, int Index, bool Scalar)
+        {
+            AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            if (SizeF == 0)
+            {
+                Context.EmitLdc_R4(3);
+            }
+            else /* if (SizeF == 1) */
+            {
+                Context.EmitLdc_R8(3);
+            }
+
+            EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
+            EmitVectorExtractF(Context, Op.Rm, Index, SizeF);
+
+            Context.Emit(OpCodes.Mul);
+            Context.Emit(OpCodes.Sub);
+
+            if (SizeF == 0)
+            {
+                Context.EmitLdc_R4(0.5f);
+            }
+            else /* if (SizeF == 1) */
+            {
+                Context.EmitLdc_R8(0.5);
+            }
+
+            Context.Emit(OpCodes.Mul);
+
+            if (Scalar)
+            {
+                EmitVectorZeroAll(Context, Op.Rd);
+            }
+
+            EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
+        }
+
         public static void Fsqrt_S(AILEmitterCtx Context)
         {
             EmitScalarUnaryOpF(Context, () =>
@@ -335,6 +849,15 @@ namespace ChocolArm64.Instruction
             });
         }
 
+        public static void Mla_Ve(AILEmitterCtx Context)
+        {
+            EmitVectorTernaryOpByElemZx(Context, () =>
+            {
+                Context.Emit(OpCodes.Mul);
+                Context.Emit(OpCodes.Add);
+            });
+        }
+
         public static void Mls_V(AILEmitterCtx Context)
         {
             EmitVectorTernaryOpZx(Context, () =>
@@ -354,11 +877,26 @@ namespace ChocolArm64.Instruction
             EmitVectorBinaryOpByElemZx(Context, () => Context.Emit(OpCodes.Mul));
         }
 
+        public static void Neg_S(AILEmitterCtx Context)
+        {
+            EmitScalarUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
+        }
+
         public static void Neg_V(AILEmitterCtx Context)
         {
             EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
         }
 
+        public static void Raddhn_V(AILEmitterCtx Context)
+        {
+            EmitHighNarrow(Context, () => Context.Emit(OpCodes.Add), Round: true);
+        }
+
+        public static void Rsubhn_V(AILEmitterCtx Context)
+        {
+            EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: true);
+        }
+
         public static void Saddw_V(AILEmitterCtx Context)
         {
             EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add));
@@ -406,6 +944,35 @@ namespace ChocolArm64.Instruction
             EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
         }
 
+        public static void Subhn_V(AILEmitterCtx Context)
+        {
+            EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: false);
+        }
+
+        public static void Uabd_V(AILEmitterCtx Context)
+        {
+            EmitVectorBinaryOpZx(Context, () => EmitAbd(Context));
+        }
+
+        public static void Uabdl_V(AILEmitterCtx Context)
+        {
+            EmitVectorWidenRnRmBinaryOpZx(Context, () => EmitAbd(Context));
+        }
+
+        private static void EmitAbd(AILEmitterCtx Context)
+        {
+            Context.Emit(OpCodes.Sub);
+
+            Type[] Types = new Type[] { typeof(long) };
+
+            Context.EmitCall(typeof(Math).GetMethod(nameof(Math.Abs), Types));
+        }
+
+        public static void Uaddl_V(AILEmitterCtx Context)
+        {
+            EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
+        }
+
         public static void Uaddlv_V(AILEmitterCtx Context)
         {
             AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@@ -429,9 +996,21 @@ namespace ChocolArm64.Instruction
             EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
         }
 
+        public static void Uhadd_V(AILEmitterCtx Context)
+        {
+            EmitVectorBinaryOpZx(Context, () =>
+            {
+                Context.Emit(OpCodes.Add);
+
+                Context.EmitLdc_I4(1);
+
+                Context.Emit(OpCodes.Shr_Un);
+            });
+        }
+
         public static void Umull_V(AILEmitterCtx Context)
         {
             EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));
         }
     }
-}
\ No newline at end of file
+}
diff --git a/ChocolArm64/Instruction/AInstEmitSimdCmp.cs b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs
index 76861b73b..f155d7e86 100644
--- a/ChocolArm64/Instruction/AInstEmitSimdCmp.cs
+++ b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs
@@ -110,13 +110,64 @@ namespace ChocolArm64.Instruction
             Fccmp_S(Context);
         }
 
+        public static void Fcmeq_S(AILEmitterCtx Context)
+        {
+            EmitScalarFcmp(Context, OpCodes.Beq_S);
+        }
+
+        public static void Fcmeq_V(AILEmitterCtx Context)
+        {
+            EmitVectorFcmp(Context, OpCodes.Beq_S);
+        }
+
+        public static void Fcmge_S(AILEmitterCtx Context)
+        {
+            EmitScalarFcmp(Context, OpCodes.Bge_S);
+        }
+
+        public static void Fcmge_V(AILEmitterCtx Context)
+        {
+            EmitVectorFcmp(Context, OpCodes.Bge_S);
+        }
+
+        public static void Fcmgt_S(AILEmitterCtx Context)
+        {
+            EmitScalarFcmp(Context, OpCodes.Bgt_S);
+        }
+
+        public static void Fcmgt_V(AILEmitterCtx Context)
+        {
+            EmitVectorFcmp(Context, OpCodes.Bgt_S);
+        }
+
+        public static void Fcmle_S(AILEmitterCtx Context)
+        {
+            EmitScalarFcmp(Context, OpCodes.Ble_S);
+        }
+
+        public static void Fcmle_V(AILEmitterCtx Context)
+        {
+            EmitVectorFcmp(Context, OpCodes.Ble_S);
+        }
+
+        public static void Fcmlt_S(AILEmitterCtx Context)
+        {
+            EmitScalarFcmp(Context, OpCodes.Blt_S);
+        }
+
+        public static void Fcmlt_V(AILEmitterCtx Context)
+        {
+            EmitVectorFcmp(Context, OpCodes.Blt_S);
+        }
+
         public static void Fcmp_S(AILEmitterCtx Context)
         {
             AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
 
             bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false;
 
-            //Handle NaN case. If any number is NaN, then NZCV = 0011.
+            //Handle NaN case.
+            //If any number is NaN, then NZCV = 0011.
             if (CmpWithZero)
             {
                 EmitNaNCheck(Context, Op.Rn);
@@ -140,7 +191,14 @@ namespace ChocolArm64.Instruction
 
                 if (CmpWithZero)
                 {
-                    EmitLdcImmF(Context, 0, Op.Size);
+                    if (Op.Size == 0)
+                    {
+                        Context.EmitLdc_R4(0);
+                    }
+                    else /* if (SizeF == 1) */
+                    {
+                        Context.EmitLdc_R8(0);
+                    }
                 }
                 else
                 {
@@ -190,22 +248,6 @@ namespace ChocolArm64.Instruction
             Fcmp_S(Context);
         }
 
-        private static void EmitLdcImmF(AILEmitterCtx Context, double ImmF, int Size)
-        {
-            if (Size == 0)
-            {
-                Context.EmitLdc_R4((float)ImmF);
-            }
-            else if (Size == 1)
-            {
-                Context.EmitLdc_R8(ImmF);
-            }
-            else
-            {
-                throw new ArgumentOutOfRangeException(nameof(Size));
-            }
-        }
-
         private static void EmitNaNCheck(AILEmitterCtx Context, int Reg)
         {
             IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
@@ -268,5 +310,84 @@ namespace ChocolArm64.Instruction
                 EmitVectorZeroUpper(Context, Op.Rd);
             }
         }
+
+        private static void EmitScalarFcmp(AILEmitterCtx Context, OpCode ILOp)
+        {
+            EmitFcmp(Context, ILOp, 0, Scalar: true);
+        }
+
+        private static void EmitVectorFcmp(AILEmitterCtx Context, OpCode ILOp)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+            for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
+            {
+                EmitFcmp(Context, ILOp, Index, Scalar: false);
+            }
+
+            if (Op.RegisterSize == ARegisterSize.SIMD64)
+            {
+                EmitVectorZeroUpper(Context, Op.Rd);
+            }
+        }
+
+        private static void EmitFcmp(AILEmitterCtx Context, OpCode ILOp, int Index, bool Scalar)
+        {
+            AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            ulong SzMask = ulong.MaxValue >> (64 - (32 << SizeF));
+
+            EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
+
+            if (Op is AOpCodeSimdReg BinOp)
+            {
+                EmitVectorExtractF(Context, BinOp.Rm, Index, SizeF);
+            }
+            else if (SizeF == 0)
+            {
+                Context.EmitLdc_R4(0);
+            }
+            else /* if (SizeF == 1) */
+            {
+                Context.EmitLdc_R8(0);
+            }
+
+            AILLabel LblTrue = new AILLabel();
+            AILLabel LblEnd  = new AILLabel();
+
+            Context.Emit(ILOp, LblTrue);
+
+            if (Scalar)
+            {
+                EmitVectorZeroAll(Context, Op.Rd);
+            }
+            else
+            {
+                EmitVectorInsert(Context, Op.Rd, Index, SizeF + 2, 0);
+            }
+
+            Context.Emit(OpCodes.Br_S, LblEnd);
+
+            Context.MarkLabel(LblTrue);
+
+            if (Scalar)
+            {
+                EmitVectorInsert(Context, Op.Rd, Index, 3, (long)SzMask);
+
+                EmitVectorZeroUpper(Context, Op.Rd);
+            }
+            else
+            {
+                EmitVectorInsert(Context, Op.Rd, Index, SizeF + 2, (long)SzMask);
+            }
+
+            Context.MarkLabel(LblEnd);
+        }
     }
 }
\ No newline at end of file
diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs
index 4e45a11d6..9ef9d02f7 100644
--- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs
+++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs
@@ -100,6 +100,52 @@ namespace ChocolArm64.Instruction
             Context.EmitCall(MthdInfo);
         }
 
+        public static void EmitUnarySoftFloatCall(AILEmitterCtx Context, string Name)
+        {
+            IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            MethodInfo MthdInfo;
+
+            if (SizeF == 0)
+            {
+                MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(float) });
+            }
+            else /* if (SizeF == 1) */
+            {
+                MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(double) });
+            }
+
+            Context.EmitCall(MthdInfo);
+        }
+
+        public static void EmitScalarBinaryOpByElemF(AILEmitterCtx Context, Action Emit)
+        {
+            AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp;
+
+            EmitScalarOpByElemF(Context, Emit, Op.Index, Ternary: false);
+        }
+
+        public static void EmitScalarOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary)
+        {
+            AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+            int SizeF = Op.Size & 1;
+
+            if (Ternary)
+            {
+                EmitVectorExtractF(Context, Op.Rd, 0, SizeF);
+            }
+
+            EmitVectorExtractF(Context, Op.Rn, 0,    SizeF);
+            EmitVectorExtractF(Context, Op.Rm, Elem, SizeF);
+
+            Emit();
+
+            EmitScalarSetF(Context, Op.Rd, SizeF);
+        }
+
         public static void EmitScalarUnaryOpSx(AILEmitterCtx Context, Action Emit)
         {
             EmitScalarOp(Context, Emit, OperFlags.Rn, true);
@@ -381,13 +427,16 @@ namespace ChocolArm64.Instruction
                 }
 
                 EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
-                EmitVectorExtract(Context, Op.Rm, Index, Op.Size, Signed);
+                EmitVectorExtract(Context, Op.Rm, Elem,  Op.Size, Signed);
 
                 Emit();
 
-                EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
+                EmitVectorInsertTmp(Context, Index, Op.Size);
             }
 
+            Context.EmitLdvectmp();
+            Context.EmitStvec(Op.Rd);
+
             if (Op.RegisterSize == ARegisterSize.SIMD64)
             {
                 EmitVectorZeroUpper(Context, Op.Rd);
@@ -444,6 +493,9 @@ namespace ChocolArm64.Instruction
         {
             AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
 
+            Context.EmitLdvec(Op.Rd);
+            Context.EmitStvectmp();
+
             int Elems = 8 >> Op.Size;
 
             int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
@@ -486,6 +538,9 @@ namespace ChocolArm64.Instruction
         {
             AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
 
+            Context.EmitLdvec(Op.Rd);
+            Context.EmitStvectmp();
+
             int Elems = 8 >> Op.Size;
 
             int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
@@ -533,10 +588,7 @@ namespace ChocolArm64.Instruction
 
         public static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed)
         {
-            if (Size < 0 || Size > 3)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Size));
-            }
+            ThrowIfInvalid(Index, Size);
 
             IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
 
@@ -551,6 +603,8 @@ namespace ChocolArm64.Instruction
 
         public static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size)
         {
+            ThrowIfInvalidF(Index, Size);
+
             Context.EmitLdvec(Reg);
             Context.EmitLdc_I4(Index);
 
@@ -586,10 +640,7 @@ namespace ChocolArm64.Instruction
 
         public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size)
         {
-            if (Size < 0 || Size > 3)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Size));
-            }
+            ThrowIfInvalid(Index, Size);
 
             Context.EmitLdvec(Reg);
             Context.EmitLdc_I4(Index);
@@ -602,10 +653,7 @@ namespace ChocolArm64.Instruction
 
         public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size)
         {
-            if (Size < 0 || Size > 3)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Size));
-            }
+            ThrowIfInvalid(Index, Size);
 
             Context.EmitLdvectmp();
             Context.EmitLdc_I4(Index);
@@ -618,10 +666,7 @@ namespace ChocolArm64.Instruction
 
         public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value)
         {
-            if (Size < 0 || Size > 3)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Size));
-            }
+            ThrowIfInvalid(Index, Size);
 
             Context.EmitLdc_I8(Value);
             Context.EmitLdvec(Reg);
@@ -635,6 +680,8 @@ namespace ChocolArm64.Instruction
 
         public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size)
         {
+            ThrowIfInvalidF(Index, Size);
+
             Context.EmitLdvec(Reg);
             Context.EmitLdc_I4(Index);
 
@@ -656,6 +703,8 @@ namespace ChocolArm64.Instruction
 
         public static void EmitVectorInsertTmpF(AILEmitterCtx Context, int Index, int Size)
         {
+            ThrowIfInvalidF(Index, Size);
+
             Context.EmitLdvectmp();
             Context.EmitLdc_I4(Index);
 
@@ -674,5 +723,31 @@ namespace ChocolArm64.Instruction
 
             Context.EmitStvectmp();
         }
+
+        private static void ThrowIfInvalid(int Index, int Size)
+        {
+            if ((uint)Size > 3)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Size));
+            }
+
+            if ((uint)Index >= 16 >> Size)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Index));
+            }
+        }
+
+        private static void ThrowIfInvalidF(int Index, int Size)
+        {
+            if ((uint)Size > 1)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Size));
+            }
+
+            if ((uint)Index >= 4 >> Size)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Index));
+            }
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/ChocolArm64/Instruction/AInstEmitSimdLogical.cs b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs
index 5b71a0bbb..967c3d300 100644
--- a/ChocolArm64/Instruction/AInstEmitSimdLogical.cs
+++ b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs
@@ -32,6 +32,51 @@ namespace ChocolArm64.Instruction
             });
         }
 
+        public static void Bif_V(AILEmitterCtx Context)
+        {
+            EmitBitBif(Context, true);
+        }
+
+        public static void Bit_V(AILEmitterCtx Context)
+        {
+            EmitBitBif(Context, false);
+        }
+
+        public static void EmitBitBif(AILEmitterCtx Context, bool NotRm)
+        {
+            AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+            int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+            for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
+            {
+                EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size);
+                EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size);
+
+                Context.Emit(OpCodes.Xor);
+
+                EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size);
+
+                if (NotRm)
+                {
+                    Context.Emit(OpCodes.Not);
+                }
+
+                Context.Emit(OpCodes.And);
+
+                EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size);
+
+                Context.Emit(OpCodes.Xor);
+
+                EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
+            }
+
+            if (Op.RegisterSize == ARegisterSize.SIMD64)
+            {
+                EmitVectorZeroUpper(Context, Op.Rd);
+            }
+        }
+
         public static void Bsl_V(AILEmitterCtx Context)
         {
             EmitVectorTernaryOpZx(Context, () =>
diff --git a/ChocolArm64/Instruction/AInstEmitSimdMove.cs b/ChocolArm64/Instruction/AInstEmitSimdMove.cs
index 3f427ad8a..20268d583 100644
--- a/ChocolArm64/Instruction/AInstEmitSimdMove.cs
+++ b/ChocolArm64/Instruction/AInstEmitSimdMove.cs
@@ -61,6 +61,9 @@ namespace ChocolArm64.Instruction
         {
             AOpCodeSimdExt Op = (AOpCodeSimdExt)Context.CurrOp;
 
+            Context.EmitLdvec(Op.Rd);
+            Context.EmitStvectmp();
+
             int Bytes = Context.CurrOp.GetBitsCount() >> 3;
 
             int Position = Op.Imm4;
@@ -75,10 +78,12 @@ namespace ChocolArm64.Instruction
                 }
 
                 EmitVectorExtractZx(Context, Reg, Position++, 0);
-
-                EmitVectorInsert(Context, Op.Rd, Index, 0);
+                EmitVectorInsertTmp(Context, Index, 0);
             }
 
+            Context.EmitLdvectmp();
+            Context.EmitStvec(Op.Rd);
+
             if (Op.RegisterSize == ARegisterSize.SIMD64)
             {
                 EmitVectorZeroUpper(Context, Op.Rd);
@@ -113,7 +118,7 @@ namespace ChocolArm64.Instruction
 
             EmitVectorExtractZx(Context, Op.Rn, 0, 3);
 
-            EmitIntZeroHigherIfNeeded(Context);
+            EmitIntZeroUpperIfNeeded(Context);
 
             Context.EmitStintzr(Op.Rd);
         }
@@ -124,7 +129,7 @@ namespace ChocolArm64.Instruction
 
             EmitVectorExtractZx(Context, Op.Rn, 1, 3);
 
-            EmitIntZeroHigherIfNeeded(Context);
+            EmitIntZeroUpperIfNeeded(Context);
 
             Context.EmitStintzr(Op.Rd);
         }
@@ -135,7 +140,7 @@ namespace ChocolArm64.Instruction
 
             Context.EmitLdintzr(Op.Rn);
 
-            EmitIntZeroHigherIfNeeded(Context);
+            EmitIntZeroUpperIfNeeded(Context);
 
             EmitScalarSet(Context, Op.Rd, 3);
         }
@@ -146,7 +151,7 @@ namespace ChocolArm64.Instruction
 
             Context.EmitLdintzr(Op.Rn);
 
-            EmitIntZeroHigherIfNeeded(Context);
+            EmitIntZeroUpperIfNeeded(Context);
 
             EmitVectorInsert(Context, Op.Rd, 1, 3);
         }
@@ -251,6 +256,16 @@ namespace ChocolArm64.Instruction
             Context.EmitStvec(Op.Rd);
         }
 
+        public static void Trn1_V(AILEmitterCtx Context)
+        {
+            EmitVectorTranspose(Context, Part: 0);
+        }
+
+        public static void Trn2_V(AILEmitterCtx Context)
+        {
+            EmitVectorTranspose(Context, Part: 1);
+        }
+
         public static void Umov_S(AILEmitterCtx Context)
         {
             AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
@@ -301,7 +316,7 @@ namespace ChocolArm64.Instruction
             EmitVectorZip(Context, Part: 1);
         }
 
-        private static void EmitIntZeroHigherIfNeeded(AILEmitterCtx Context)
+        private static void EmitIntZeroUpperIfNeeded(AILEmitterCtx Context)
         {
             if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
             {
@@ -310,6 +325,29 @@ namespace ChocolArm64.Instruction
             }
         }
 
+        private static void EmitVectorTranspose(AILEmitterCtx Context, int Part)
+        {
+            AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
+
+            int Bytes = Context.CurrOp.GetBitsCount() >> 3;
+
+            int Elems = Bytes >> Op.Size;
+
+            for (int Index = 0; Index < Elems; Index++)
+            {
+                int Elem = (Index & ~1) + Part;
+
+                EmitVectorExtractZx(Context, (Index & 1) == 0 ? Op.Rn : Op.Rm, Elem, Op.Size);
+
+                EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
+            }
+
+            if (Op.RegisterSize == ARegisterSize.SIMD64)
+            {
+                EmitVectorZeroUpper(Context, Op.Rd);
+            }
+        }
+
         private static void EmitVectorUnzip(AILEmitterCtx Context, int Part)
         {
             AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
@@ -322,7 +360,7 @@ namespace ChocolArm64.Instruction
             for (int Index = 0; Index < Elems; Index++)
             {
                 int Elem = Part + ((Index & (Half - 1)) << 1);
-                
+
                 EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem, Op.Size);
 
                 EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
@@ -358,4 +396,4 @@ namespace ChocolArm64.Instruction
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs
index f79628ad0..c08f253e9 100644
--- a/ChocolArm64/Instruction/ASoftFallback.cs
+++ b/ChocolArm64/Instruction/ASoftFallback.cs
@@ -20,6 +20,14 @@ namespace ChocolArm64.Instruction
             Context.EmitCall(typeof(ASoftFallback), MthdName);
         }
 
+        public static uint  CountLeadingSigns32(uint Value)  => (uint)CountLeadingSigns(Value, 32);
+        public static ulong CountLeadingSigns64(ulong Value) => (ulong)CountLeadingSigns(Value, 64);
+
+        private static ulong CountLeadingSigns(ulong Value, int Size)
+        {
+            return CountLeadingZeros((Value >> 1) ^ Value, Size - 1);
+        }
+
         public static uint  CountLeadingZeros32(uint Value)  => (uint)CountLeadingZeros(Value, 32);
         public static ulong CountLeadingZeros64(ulong Value) => (ulong)CountLeadingZeros(Value, 64);
 
@@ -41,15 +49,15 @@ namespace ChocolArm64.Instruction
         private const uint Crc32RevPoly  = 0xedb88320;
         private const uint Crc32cRevPoly = 0x82f63b78;
 
-        public static uint Crc32b(uint Crc, byte Val) => Crc32 (Crc, Crc32RevPoly, Val);
-        public static uint Crc32h(uint Crc, byte Val) => Crc32h(Crc, Crc32RevPoly, Val);
-        public static uint Crc32w(uint Crc, byte Val) => Crc32w(Crc, Crc32RevPoly, Val);
-        public static uint Crc32x(uint Crc, byte Val) => Crc32x(Crc, Crc32RevPoly, Val);
+        public static uint Crc32b(uint Crc, byte   Val) => Crc32 (Crc, Crc32RevPoly, Val);
+        public static uint Crc32h(uint Crc, ushort Val) => Crc32h(Crc, Crc32RevPoly, Val);
+        public static uint Crc32w(uint Crc, uint   Val) => Crc32w(Crc, Crc32RevPoly, Val);
+        public static uint Crc32x(uint Crc, ulong  Val) => Crc32x(Crc, Crc32RevPoly, Val);
 
-        public static uint Crc32cb(uint Crc, byte Val) => Crc32 (Crc, Crc32cRevPoly, Val);
-        public static uint Crc32ch(uint Crc, byte Val) => Crc32h(Crc, Crc32cRevPoly, Val);
-        public static uint Crc32cw(uint Crc, byte Val) => Crc32w(Crc, Crc32cRevPoly, Val);
-        public static uint Crc32cx(uint Crc, byte Val) => Crc32x(Crc, Crc32cRevPoly, Val);
+        public static uint Crc32cb(uint Crc, byte   Val) => Crc32 (Crc, Crc32cRevPoly, Val);
+        public static uint Crc32ch(uint Crc, ushort Val) => Crc32h(Crc, Crc32cRevPoly, Val);
+        public static uint Crc32cw(uint Crc, uint   Val) => Crc32w(Crc, Crc32cRevPoly, Val);
+        public static uint Crc32cx(uint Crc, ulong  Val) => Crc32x(Crc, Crc32cRevPoly, Val);
 
         private static uint Crc32h(uint Crc, uint Poly, ushort Val)
         {
@@ -113,7 +121,7 @@ namespace ChocolArm64.Instruction
             Value = ((Value & 0xcccccccccccccccc) >>  2) | ((Value & 0x3333333333333333) <<  2);
             Value = ((Value & 0xf0f0f0f0f0f0f0f0) >>  4) | ((Value & 0x0f0f0f0f0f0f0f0f) <<  4);
             Value = ((Value & 0xff00ff00ff00ff00) >>  8) | ((Value & 0x00ff00ff00ff00ff) <<  8);
-            Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16);           
+            Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16);
 
             return (Value >> 32) | (Value << 32);
         }
@@ -242,10 +250,86 @@ namespace ChocolArm64.Instruction
 
         public static int CountSetBits8(byte Value)
         {
-            return (Value >> 0) & 1 + (Value >> 1) & 1 +
-                   (Value >> 2) & 1 + (Value >> 3) & 1 +
-                   (Value >> 4) & 1 + (Value >> 5) & 1 +
-                   (Value >> 6) & 1 + (Value >> 7);
+            return ((Value >> 0) & 1) + ((Value >> 1) & 1) +
+                   ((Value >> 2) & 1) + ((Value >> 3) & 1) +
+                   ((Value >> 4) & 1) + ((Value >> 5) & 1) +
+                   ((Value >> 6) & 1) +  (Value >> 7);
+        }
+
+        public static float MaxF(float val1, float val2)
+        {
+            if (val1 == 0.0 && val2 == 0.0)
+            {
+                if (BitConverter.SingleToInt32Bits(val1) < 0 && BitConverter.SingleToInt32Bits(val2) < 0)
+                    return -0.0f;
+                
+                return 0.0f;
+            }
+
+            if (val1 > val2) 
+                return val1; 
+     
+            if (float.IsNaN(val1)) 
+                return val1;
+     
+            return val2;
+        }
+
+        public static double Max(double val1, double val2)
+        {
+            if (val1 == 0.0 && val2 == 0.0)
+            {
+                if (BitConverter.DoubleToInt64Bits(val1) < 0 && BitConverter.DoubleToInt64Bits(val2) < 0)
+                    return -0.0;
+
+                return 0.0;
+            }
+
+            if (val1 > val2) 
+                return val1; 
+     
+            if (double.IsNaN(val1)) 
+                return val1;
+     
+            return val2;
+        }
+
+        public static float MinF(float val1, float val2)
+        {
+            if (val1 == 0.0 && val2 == 0.0)
+            {
+                if (BitConverter.SingleToInt32Bits(val1) < 0 || BitConverter.SingleToInt32Bits(val2) < 0)
+                    return -0.0f;
+                
+                return 0.0f;
+            }
+
+            if (val1 < val2) 
+                return val1; 
+     
+            if (float.IsNaN(val1)) 
+                return val1;
+     
+            return val2;
+        }
+
+        public static double Min(double val1, double val2)
+        {
+            if (val1 == 0.0 && val2 == 0.0)
+            {
+                if (BitConverter.DoubleToInt64Bits(val1) < 0 || BitConverter.DoubleToInt64Bits(val2) < 0)
+                    return -0.0;
+                
+                return 0.0;
+            }
+
+            if (val1 < val2) 
+                return val1; 
+     
+            if (double.IsNaN(val1)) 
+                return val1;
+     
+            return val2;
         }
 
         public static float RoundF(float Value, int Fpcr)
@@ -398,4 +482,4 @@ namespace ChocolArm64.Instruction
             throw new ArgumentOutOfRangeException(nameof(Size));
         }
     }
-}
\ No newline at end of file
+}
diff --git a/ChocolArm64/Instruction/ASoftFloat.cs b/ChocolArm64/Instruction/ASoftFloat.cs
new file mode 100644
index 000000000..7bee69bae
--- /dev/null
+++ b/ChocolArm64/Instruction/ASoftFloat.cs
@@ -0,0 +1,103 @@
+using System;
+
+namespace ChocolArm64.Instruction
+{
+    static class ASoftFloat
+    {
+        static ASoftFloat()
+        {
+            InvSqrtEstimateTable = BuildInvSqrtEstimateTable();
+        }
+
+        private static readonly byte[] InvSqrtEstimateTable;
+
+        private static byte[] BuildInvSqrtEstimateTable()
+        {
+            byte[] Table = new byte[512];
+            for (ulong index = 128; index < 512; index++)
+            {
+                ulong a = index;
+                if (a < 256)
+                {
+                    a = (a << 1) + 1;
+                }
+                else
+                {
+                    a = (a | 1) << 1;
+                }
+
+                ulong b = 256;
+                while (a * (b + 1) * (b + 1) < (1ul << 28))
+                {
+                    b++;
+                }
+                b = (b + 1) >> 1;
+
+                Table[index] = (byte)(b & 0xFF);
+            }
+            return Table;
+        }
+
+        public static float InvSqrtEstimate(float x)
+        {
+            return (float)InvSqrtEstimate((double)x);
+        }
+
+        public static double InvSqrtEstimate(double x)
+        {
+            ulong x_bits = (ulong)BitConverter.DoubleToInt64Bits(x);
+            ulong x_sign = x_bits & 0x8000000000000000;
+            long x_exp = (long)((x_bits >> 52) & 0x7FF);
+            ulong scaled = x_bits & ((1ul << 52) - 1);
+
+            if (x_exp == 0x7ff)
+            {
+                if (scaled == 0)
+                {
+                    // Infinity -> Zero
+                    return BitConverter.Int64BitsToDouble((long)x_sign);
+                }
+
+                // NaN
+                return BitConverter.Int64BitsToDouble((long)(x_bits | 0x0008000000000000));
+            }
+
+            if (x_exp == 0)
+            {
+                if (scaled == 0)
+                {
+                    // Zero -> Infinity
+                    return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7ff0000000000000));
+                }
+
+                // Denormal
+                while ((scaled & (1 << 51)) == 0)
+                {
+                    scaled <<= 1;
+                    x_exp--;
+                }
+                scaled <<= 1;
+            }
+
+            if (((ulong)x_exp & 1) == 1)
+            {
+                scaled >>= 45;
+                scaled &= 0xFF;
+                scaled |= 0x80;
+            }
+            else
+            {
+                scaled >>= 44;
+                scaled &= 0xFF;
+                scaled |= 0x100;
+            }
+
+            ulong result_exp = ((ulong)(3068 - x_exp) / 2) & 0x7FF;
+            ulong estimate = (ulong)InvSqrtEstimateTable[scaled];
+            ulong fraction = estimate << 44;
+
+            ulong result = x_sign | (result_exp << 52) | fraction;
+            return BitConverter.Int64BitsToDouble((long)result);
+        }
+    }
+}
\ No newline at end of file
diff --git a/ChocolArm64/Memory/AMemoryHelper.cs b/ChocolArm64/Memory/AMemoryHelper.cs
index 219aeebf9..1e3462985 100644
--- a/ChocolArm64/Memory/AMemoryHelper.cs
+++ b/ChocolArm64/Memory/AMemoryHelper.cs
@@ -1,4 +1,6 @@
+using System;
 using System.IO;
+using System.Runtime.InteropServices;
 using System.Text;
 
 namespace ChocolArm64.Memory
@@ -20,11 +22,11 @@ namespace ChocolArm64.Memory
             }
         }
 
-        public static byte[] ReadBytes(AMemory Memory, long Position, int Size)
+        public static byte[] ReadBytes(AMemory Memory, long Position, long Size)
         {
             byte[] Data = new byte[Size];
 
-            for (int Offs = 0; Offs < Size; Offs++)
+            for (long Offs = 0; Offs < Size; Offs++)
             {
                 Data[Offs] = (byte)Memory.ReadByte(Position + Offs);
             }
@@ -40,11 +42,39 @@ namespace ChocolArm64.Memory
             }
         }
 
-        public static string ReadAsciiString(AMemory Memory, long Position, int MaxSize = -1)
+        public unsafe static T Read<T>(AMemory Memory, long Position) where T : struct
+        {
+            long Size = Marshal.SizeOf<T>();
+
+            if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Position));
+            }
+
+            IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
+
+            return Marshal.PtrToStructure<T>(Ptr);
+        }
+
+        public unsafe static void Write<T>(AMemory Memory, long Position, T Value) where T : struct
+        {
+            long Size = Marshal.SizeOf<T>();
+
+            if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Position));
+            }
+
+            IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
+
+            Marshal.StructureToPtr<T>(Value, Ptr, false);
+        }
+
+        public static string ReadAsciiString(AMemory Memory, long Position, long MaxSize = -1)
         {
             using (MemoryStream MS = new MemoryStream())
             {
-                for (int Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++)
+                for (long Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++)
                 {
                     byte Value = (byte)Memory.ReadByte(Position + Offs);
 
diff --git a/ChocolArm64/State/AThreadState.cs b/ChocolArm64/State/AThreadState.cs
index 6f3f62f69..ce1278866 100644
--- a/ChocolArm64/State/AThreadState.cs
+++ b/ChocolArm64/State/AThreadState.cs
@@ -1,5 +1,6 @@
 using ChocolArm64.Events;
 using System;
+using System.Collections.Generic;
 using System.Diagnostics;
 
 namespace ChocolArm64.State
@@ -56,10 +57,17 @@ namespace ChocolArm64.State
         public event EventHandler<AInstExceptionEventArgs> SvcCall;
         public event EventHandler<AInstUndefinedEventArgs> Undefined;
 
+        private Stack<long> CallStack;
+
         private static Stopwatch TickCounter;
 
         private static double HostTickFreq;
 
+        public AThreadState()
+        {
+            CallStack = new Stack<long>();
+        }
+
         static AThreadState()
         {
             HostTickFreq = 1.0 / Stopwatch.Frequency;
@@ -83,5 +91,27 @@ namespace ChocolArm64.State
         {
             Undefined?.Invoke(this, new AInstUndefinedEventArgs(Position, RawOpCode));
         }
+
+        internal void EnterMethod(long Position)
+        {
+            CallStack.Push(Position);
+        }
+
+        internal void ExitMethod()
+        {
+            CallStack.TryPop(out _);
+        }
+
+        internal void JumpMethod(long Position)
+        {
+            CallStack.TryPop(out _);
+
+            CallStack.Push(Position);
+        }
+
+        public long[] GetCallStack()
+        {
+            return CallStack.ToArray();
+        }
     }
 }
\ No newline at end of file
diff --git a/ChocolArm64/Translation/AILEmitter.cs b/ChocolArm64/Translation/AILEmitter.cs
index af37a6c75..55b1751f6 100644
--- a/ChocolArm64/Translation/AILEmitter.cs
+++ b/ChocolArm64/Translation/AILEmitter.cs
@@ -60,11 +60,11 @@ namespace ChocolArm64.Translation
 
         public AILBlock GetILBlock(int Index) => ILBlocks[Index];
 
-        public ATranslatedSub GetSubroutine(HashSet<long> Callees)
+        public ATranslatedSub GetSubroutine()
         {
             LocalAlloc = new ALocalAlloc(ILBlocks, Root);
 
-            InitSubroutine(Callees);
+            InitSubroutine();
             InitLocals();
 
             foreach (AILBlock ILBlock in ILBlocks)
@@ -75,7 +75,7 @@ namespace ChocolArm64.Translation
             return Subroutine;
         }
 
-        private void InitSubroutine(HashSet<long> Callees)
+        private void InitSubroutine()
         {
             List<ARegister> Params = new List<ARegister>();
 
@@ -99,7 +99,7 @@ namespace ChocolArm64.Translation
 
             Generator = Mthd.GetILGenerator();
 
-            Subroutine = new ATranslatedSub(Mthd, Params, Callees);
+            Subroutine = new ATranslatedSub(Mthd, Params);
         }
 
         private void InitLocals()
@@ -115,7 +115,7 @@ namespace ChocolArm64.Translation
                 Generator.EmitLdarg(Index + ParamsStart);
                 Generator.EmitStloc(GetLocalIndex(Reg));
             }
-        }        
+        }
 
         private Type[] GetParamTypes(IList<ARegister> Params)
         {
diff --git a/ChocolArm64/Translation/AILEmitterCtx.cs b/ChocolArm64/Translation/AILEmitterCtx.cs
index 466594694..a004a9665 100644
--- a/ChocolArm64/Translation/AILEmitterCtx.cs
+++ b/ChocolArm64/Translation/AILEmitterCtx.cs
@@ -12,8 +12,6 @@ namespace ChocolArm64.Translation
     {
         private ATranslator Translator;
 
-        private HashSet<long> Callees;
-
         private Dictionary<long, AILLabel> Labels;
 
         private int BlkIndex;
@@ -66,13 +64,11 @@ namespace ChocolArm64.Translation
             this.Graph      = Graph;
             this.Root       = Root;
 
-            Callees = new HashSet<long>();
-
             Labels = new Dictionary<long, AILLabel>();
 
             Emitter = new AILEmitter(Graph, Root, SubName);
 
-            ILBlock = Emitter.GetILBlock(0);          
+            ILBlock = Emitter.GetILBlock(0);
 
             OpcIndex = -1;
 
@@ -84,7 +80,7 @@ namespace ChocolArm64.Translation
 
         public ATranslatedSub GetSubroutine()
         {
-            return Emitter.GetSubroutine(Callees);
+            return Emitter.GetSubroutine();
         }
 
         public bool AdvanceOpCode()
@@ -123,8 +119,6 @@ namespace ChocolArm64.Translation
 
         public bool TryOptEmitSubroutineCall()
         {
-            Callees.Add(((AOpCodeBImm)CurrOp).Imm);
-
             if (CurrBlock.Next == null)
             {
                 return false;
@@ -152,6 +146,8 @@ namespace ChocolArm64.Translation
 
             EmitCall(Sub.Method);
 
+            Sub.AddCaller(Root.Position);
+
             return true;
         }
 
@@ -260,18 +256,24 @@ namespace ChocolArm64.Translation
                 case AIntType.Int64:  Emit(OpCodes.Conv_I8); break;
             }
 
-            if (IntType == AIntType.UInt64 ||
-                IntType == AIntType.Int64)
+            bool Sz64 = CurrOp.RegisterSize != ARegisterSize.Int32;
+
+            if (Sz64 == (IntType == AIntType.UInt64 ||
+                         IntType == AIntType.Int64))
             {
                 return;
             }
 
-            if (CurrOp.RegisterSize != ARegisterSize.Int32)
+            if (Sz64)
             {
                 Emit(IntType >= AIntType.Int8
                     ? OpCodes.Conv_I8
                     : OpCodes.Conv_U8);
             }
+            else
+            {
+                Emit(OpCodes.Conv_U4);
+            }
         }
 
         public void EmitLsl(int Amount) => EmitILShift(Amount, OpCodes.Shl);
@@ -298,7 +300,7 @@ namespace ChocolArm64.Translation
                 EmitLdc_I4(Amount);
 
                 Emit(OpCodes.Shr_Un);
-                
+
                 Ldloc(Tmp2Index, AIoType.Int);
 
                 EmitLdc_I4(CurrOp.GetBitsCount() - Amount);
@@ -459,6 +461,21 @@ namespace ChocolArm64.Translation
             EmitCall(ObjType.GetMethod(MthdName));
         }
 
+        public void EmitPrivateCall(Type ObjType, string MthdName)
+        {
+            if (ObjType == null)
+            {
+                throw new ArgumentNullException(nameof(ObjType));
+            }
+
+            if (MthdName == null)
+            {
+                throw new ArgumentNullException(nameof(MthdName));
+            }
+
+            EmitCall(ObjType.GetMethod(MthdName, BindingFlags.Instance | BindingFlags.NonPublic));
+        }
+
         public void EmitCall(MethodInfo MthdInfo)
         {
             if (MthdInfo == null)
diff --git a/ChocolArm64/Translation/ILGeneratorEx.cs b/ChocolArm64/Translation/ILGeneratorEx.cs
index abb35ec3f..612993088 100644
--- a/ChocolArm64/Translation/ILGeneratorEx.cs
+++ b/ChocolArm64/Translation/ILGeneratorEx.cs
@@ -3,7 +3,7 @@ using System;
 namespace ChocolArm64
 {
     using System.Reflection.Emit;
-    
+
     static class ILGeneratorEx
     {
         public static void EmitLdc_I4(this ILGenerator Generator,int Value)
diff --git a/README.md b/README.md
index 841a8258c..94bcb0e2a 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,9 @@ https://openal.org/downloads/OpenAL11CoreSDK.zip
 
  - Config File: `Ryujinx.conf` should be present in executable folder.
    For more informations [you can go here](CONFIG.md).
+ 
+ - If you are a Windows user, you can configure your keys, the logs, install OpenAL, etc... with Ryujinx-Setting.
+ [Download it, right here](https://github.com/AcK77/Ryujinx-Settings)
 
 **Help**
 
diff --git a/Ryujinx.Audio/AudioFormat.cs b/Ryujinx.Audio/AudioFormat.cs
new file mode 100644
index 000000000..8250d1368
--- /dev/null
+++ b/Ryujinx.Audio/AudioFormat.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Audio
+{
+    public enum AudioFormat
+    {
+        Invalid  = 0,
+        PcmInt8  = 1,
+        PcmInt16 = 2,
+        PcmImt24 = 3,
+        PcmImt32 = 4,
+        PcmFloat = 5,
+        Adpcm    = 6
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Audio/IAalOutput.cs b/Ryujinx.Audio/IAalOutput.cs
new file mode 100644
index 000000000..f9978ee4d
--- /dev/null
+++ b/Ryujinx.Audio/IAalOutput.cs
@@ -0,0 +1,24 @@
+namespace Ryujinx.Audio
+{
+    public interface IAalOutput
+    {
+        int OpenTrack(
+            int             SampleRate,
+            int             Channels,
+            ReleaseCallback Callback,
+            out AudioFormat Format);
+
+        void CloseTrack(int Track);
+
+        bool ContainsBuffer(int Track, long Tag);
+
+        long[] GetReleasedBuffers(int Track, int MaxCount);
+
+        void AppendBuffer(int Track, long Tag, byte[] Buffer);
+
+        void Start(int Track);
+        void Stop(int Track);
+
+        PlaybackState GetState(int Track);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs
new file mode 100644
index 000000000..f574b46f3
--- /dev/null
+++ b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs
@@ -0,0 +1,365 @@
+using OpenTK.Audio;
+using OpenTK.Audio.OpenAL;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Ryujinx.Audio.OpenAL
+{
+    public class OpenALAudioOut : IAalOutput
+    {
+        private const int MaxTracks = 256;
+
+        private const int MaxReleased = 32;
+
+        private AudioContext Context;
+
+        private class Track : IDisposable
+        {
+            public int SourceId { get; private set; }
+
+            public int SampleRate { get; private set; }
+            
+            public ALFormat Format { get; private set; }
+
+            private ReleaseCallback Callback;
+
+            public PlaybackState State { get; set; }
+
+            private bool ShouldCallReleaseCallback;
+
+            private ConcurrentDictionary<long, int> Buffers;
+
+            private Queue<long> QueuedTagsQueue;
+
+            private Queue<long> ReleasedTagsQueue;
+
+            private int LastReleasedCount;
+
+            private bool Disposed;
+
+            public Track(int SampleRate, ALFormat Format, ReleaseCallback Callback)
+            {
+                this.SampleRate = SampleRate;
+                this.Format     = Format;
+                this.Callback   = Callback;
+
+                State = PlaybackState.Stopped;
+
+                SourceId = AL.GenSource();
+
+                Buffers = new ConcurrentDictionary<long, int>();
+
+                QueuedTagsQueue = new Queue<long>();
+
+                ReleasedTagsQueue = new Queue<long>();
+            }
+
+            public bool ContainsBuffer(long Tag)
+            {
+                SyncQueuedTags();
+
+                foreach (long QueuedTag in QueuedTagsQueue)
+                {
+                    if (QueuedTag == Tag)
+                    {
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+
+            public long[] GetReleasedBuffers(int MaxCount)
+            {
+                ClearReleased();
+
+                List<long> Tags = new List<long>();
+
+                HashSet<long> Unique = new HashSet<long>();
+
+                while (MaxCount-- > 0 && ReleasedTagsQueue.TryDequeue(out long Tag))
+                {
+                    if (Unique.Add(Tag))
+                    {
+                        Tags.Add(Tag);
+                    }
+                }
+
+                return Tags.ToArray();
+            }
+
+            public int AppendBuffer(long Tag)
+            {
+                if (Disposed)
+                {
+                    throw new ObjectDisposedException(nameof(Track));
+                }
+
+                int Id = AL.GenBuffer();
+
+                Buffers.AddOrUpdate(Tag, Id, (Key, OldId) =>
+                {
+                    AL.DeleteBuffer(OldId);
+
+                    return Id;
+                });
+
+                QueuedTagsQueue.Enqueue(Tag);
+
+                return Id;
+            }
+
+            public void ClearReleased()
+            {
+                SyncQueuedTags();
+
+                AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int ReleasedCount);
+
+                CheckReleaseChanges(ReleasedCount);
+
+                if (ReleasedCount > 0)
+                {
+                    AL.SourceUnqueueBuffers(SourceId, ReleasedCount);
+                }
+            }
+
+            public void CallReleaseCallbackIfNeeded()
+            {
+                CheckReleaseChanges();
+
+                if (ShouldCallReleaseCallback)
+                {
+                    ShouldCallReleaseCallback = false;
+
+                    Callback();
+                }
+            }
+
+            private void CheckReleaseChanges()
+            {
+                AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int ReleasedCount);
+
+                CheckReleaseChanges(ReleasedCount);
+            }
+
+            private void CheckReleaseChanges(int NewReleasedCount)
+            {
+                if (LastReleasedCount != NewReleasedCount)
+                {
+                    LastReleasedCount = NewReleasedCount;
+
+                    ShouldCallReleaseCallback = true;
+                }
+            }
+            
+            private void SyncQueuedTags()
+            {
+                AL.GetSource(SourceId, ALGetSourcei.BuffersQueued,    out int QueuedCount);
+                AL.GetSource(SourceId, ALGetSourcei.BuffersProcessed, out int ReleasedCount);
+
+                QueuedCount -= ReleasedCount;
+
+                while (QueuedTagsQueue.Count > QueuedCount)
+                {
+                    ReleasedTagsQueue.Enqueue(QueuedTagsQueue.Dequeue());
+                }
+
+                while (ReleasedTagsQueue.Count > MaxReleased)
+                {
+                    ReleasedTagsQueue.Dequeue();
+                }
+            }
+
+            public void Dispose()
+            {
+                Dispose(true);
+            }
+
+            protected virtual void Dispose(bool Disposing)
+            {
+                if (Disposing && !Disposed)
+                {
+                    Disposed = true;
+
+                    AL.DeleteSource(SourceId);
+
+                    foreach (int Id in Buffers.Values)
+                    {
+                        AL.DeleteBuffer(Id);
+                    }
+                }
+            }
+        }
+
+        private ConcurrentDictionary<int, Track> Tracks;
+
+        private Thread AudioPollerThread;
+
+        private bool KeepPolling;
+
+        public OpenALAudioOut()
+        {
+            Context = new AudioContext();
+
+            Tracks = new ConcurrentDictionary<int, Track>();
+
+            KeepPolling = true;
+
+            AudioPollerThread = new Thread(AudioPollerWork);
+
+            AudioPollerThread.Start();
+        }
+
+        private void AudioPollerWork()
+        {
+            do
+            {
+                foreach (Track Td in Tracks.Values)
+                {
+                    Td.CallReleaseCallbackIfNeeded();
+                }
+
+                Thread.Yield();
+            }
+            while (KeepPolling);
+        }
+
+        public int OpenTrack(
+            int             SampleRate,
+            int             Channels,
+            ReleaseCallback Callback,
+            out AudioFormat Format)
+        {
+            Format = AudioFormat.PcmInt16;
+
+            Track Td = new Track(SampleRate, GetALFormat(Channels, Format), Callback);
+
+            for (int Id = 0; Id < MaxTracks; Id++)
+            {
+                if (Tracks.TryAdd(Id, Td))
+                {
+                    return Id;
+                }
+            }
+
+            return -1;
+        }
+
+        private ALFormat GetALFormat(int Channels, AudioFormat Format)
+        {
+            if (Channels < 1 || Channels > 2)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Channels));
+            }
+
+            if (Channels == 1)
+            {
+                switch (Format)
+                {
+                    case AudioFormat.PcmInt8:  return ALFormat.Mono8;
+                    case AudioFormat.PcmInt16: return ALFormat.Mono16;
+                }
+            }
+            else /* if (Channels == 2) */
+            {
+                switch (Format)
+                {
+                    case AudioFormat.PcmInt8:  return ALFormat.Stereo8;
+                    case AudioFormat.PcmInt16: return ALFormat.Stereo16;
+                }
+            }
+
+            throw new ArgumentException(nameof(Format));
+        }
+
+        public void CloseTrack(int Track)
+        {
+            if (Tracks.TryRemove(Track, out Track Td))
+            {
+                Td.Dispose();
+            }
+        }
+
+        public bool ContainsBuffer(int Track, long Tag)
+        {
+            if (Tracks.TryGetValue(Track, out Track Td))
+            {
+                return Td.ContainsBuffer(Tag);
+            }
+            
+            return false;
+        }
+
+        public long[] GetReleasedBuffers(int Track, int MaxCount)
+        {
+            if (Tracks.TryGetValue(Track, out Track Td))
+            {
+                return Td.GetReleasedBuffers(MaxCount);
+            }
+            
+            return null;
+        }
+
+        public void AppendBuffer(int Track, long Tag, byte[] Buffer)
+        {
+            if (Tracks.TryGetValue(Track, out Track Td))
+            {
+                int BufferId = Td.AppendBuffer(Tag);
+
+                AL.BufferData(BufferId, Td.Format, Buffer, Buffer.Length, Td.SampleRate);
+
+                AL.SourceQueueBuffer(Td.SourceId, BufferId);
+
+                StartPlaybackIfNeeded(Td);
+            }
+        }
+
+        public void Start(int Track)
+        {
+            if (Tracks.TryGetValue(Track, out Track Td))
+            {
+                Td.State = PlaybackState.Playing;
+
+                StartPlaybackIfNeeded(Td);
+            }
+        }
+
+        private void StartPlaybackIfNeeded(Track Td)
+        {
+            AL.GetSource(Td.SourceId, ALGetSourcei.SourceState, out int StateInt);
+
+            ALSourceState State = (ALSourceState)StateInt;
+
+            if (State != ALSourceState.Playing && Td.State == PlaybackState.Playing)
+            {
+                Td.ClearReleased();
+
+                AL.SourcePlay(Td.SourceId);
+            }
+        }
+
+        public void Stop(int Track)
+        {
+            if (Tracks.TryGetValue(Track, out Track Td))
+            {
+                Td.State = PlaybackState.Stopped;
+
+                AL.SourceStop(Td.SourceId);
+            }
+        }
+
+        public PlaybackState GetState(int Track)
+        {
+            if (Tracks.TryGetValue(Track, out Track Td))
+            {
+                return Td.State;
+            }
+
+            return PlaybackState.Stopped;
+        }
+
+
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Audio/PlaybackState.cs b/Ryujinx.Audio/PlaybackState.cs
new file mode 100644
index 000000000..8b53128aa
--- /dev/null
+++ b/Ryujinx.Audio/PlaybackState.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Audio
+{
+    public enum PlaybackState
+    {
+        Playing = 0,
+        Stopped = 1
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Audio/ReleaseCallback.cs b/Ryujinx.Audio/ReleaseCallback.cs
new file mode 100644
index 000000000..f534e02cd
--- /dev/null
+++ b/Ryujinx.Audio/ReleaseCallback.cs
@@ -0,0 +1,4 @@
+namespace Ryujinx.Audio
+{
+    public delegate void ReleaseCallback();
+}
\ No newline at end of file
diff --git a/Ryujinx.Audio/Ryujinx.Audio.csproj b/Ryujinx.Audio/Ryujinx.Audio.csproj
new file mode 100644
index 000000000..2e7c86fa0
--- /dev/null
+++ b/Ryujinx.Audio/Ryujinx.Audio.csproj
@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netcoreapp2.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="OpenTK.NETCore" Version="1.1.2749.6433" />
+  </ItemGroup>
+
+</Project>
diff --git a/Ryujinx.Core/Config.cs b/Ryujinx.Core/Config.cs
index c9a76de34..11eb1c1df 100644
--- a/Ryujinx.Core/Config.cs
+++ b/Ryujinx.Core/Config.cs
@@ -9,14 +9,18 @@ namespace Ryujinx.Core
 {
     public static class Config
     {
-        public static bool LoggingEnableInfo    { get; private set; }
-        public static bool LoggingEnableTrace   { get; private set; }
-        public static bool LoggingEnableDebug   { get; private set; }
-        public static bool LoggingEnableWarn    { get; private set; }
-        public static bool LoggingEnableError   { get; private set; }
-        public static bool LoggingEnableFatal   { get; private set; }
-        public static bool LoggingEnableIpc     { get; private set; }
-        public static bool LoggingEnableLogFile { get; private set; }
+        public static bool   EnableMemoryChecks     { get; private set; }
+        public static bool   LoggingEnableInfo      { get; private set; }
+        public static bool   LoggingEnableTrace     { get; private set; }
+        public static bool   LoggingEnableDebug     { get; private set; }
+        public static bool   LoggingEnableWarn      { get; private set; }
+        public static bool   LoggingEnableError     { get; private set; }
+        public static bool   LoggingEnableFatal     { get; private set; }
+        public static bool   LoggingEnableIpc       { get; private set; }
+        public static bool   LoggingEnableStub      { get; private set; }
+        public static bool   LoggingEnableLogFile   { get; private set; }
+        public static bool   LoggingEnableFilter    { get; private set; }
+        public static bool[] LoggingFilteredClasses { get; private set; }
 
         public static JoyCon FakeJoyCon { get; private set; }
 
@@ -26,14 +30,33 @@ namespace Ryujinx.Core
             var iniPath = Path.Combine(iniFolder, "Ryujinx.conf");
             IniParser Parser = new IniParser(iniPath);
 
-            LoggingEnableInfo    = Convert.ToBoolean(Parser.Value("Logging_Enable_Info"));
-            LoggingEnableTrace   = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace"));
-            LoggingEnableDebug   = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"));
-            LoggingEnableWarn    = Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"));
-            LoggingEnableError   = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
-            LoggingEnableFatal   = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
-            LoggingEnableIpc     = Convert.ToBoolean(Parser.Value("Logging_Enable_Ipc"));
-            LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
+            EnableMemoryChecks     = Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
+            LoggingEnableInfo      = Convert.ToBoolean(Parser.Value("Logging_Enable_Info"));
+            LoggingEnableTrace     = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace"));
+            LoggingEnableDebug     = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"));
+            LoggingEnableWarn      = Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"));
+            LoggingEnableError     = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
+            LoggingEnableFatal     = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
+            LoggingEnableIpc       = Convert.ToBoolean(Parser.Value("Logging_Enable_Ipc"));
+            LoggingEnableStub      = Convert.ToBoolean(Parser.Value("Logging_Enable_Stub"));
+            LoggingEnableLogFile   = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
+            LoggingEnableFilter    = Convert.ToBoolean(Parser.Value("Logging_Enable_Filter"));
+            LoggingFilteredClasses = new bool[(int)LogClass.Count];
+
+            string[] FilteredLogClasses = Parser.Value("Logging_Filtered_Classes", string.Empty).Split(',');
+            foreach (string LogClass in FilteredLogClasses)
+            {
+                if (!string.IsNullOrEmpty(LogClass.Trim()))
+                {
+                    foreach (LogClass EnumItemName in Enum.GetValues(typeof(LogClass)))
+                    {
+                        if (EnumItemName.ToString().ToLower().Contains(LogClass.Trim().ToLower()))
+                        {
+                            LoggingFilteredClasses[(int)EnumItemName] = true;
+                        }
+                    }
+                }
+            }
 
             FakeJoyCon = new JoyCon
             {
diff --git a/Ryujinx.Core/Hid/Hid.cs b/Ryujinx.Core/Hid/Hid.cs
index f25a94375..be122c609 100644
--- a/Ryujinx.Core/Hid/Hid.cs
+++ b/Ryujinx.Core/Hid/Hid.cs
@@ -82,7 +82,7 @@ namespace Ryujinx.Core.Input
 
                 (AMemory Memory, long Position) ShMem = ShMemPositions[ShMemPositions.Length - 1];
 
-                Logging.Info($"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!");
+                Logging.Info(LogClass.ServiceHid, $"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!");
 
                 Init(ShMem.Memory, ShMem.Position);
             }
@@ -219,7 +219,7 @@ namespace Ryujinx.Core.Input
                     Memory.WriteInt64Unchecked(TouchScreenOffset + 0x8,  HidEntryCount);
                     Memory.WriteInt64Unchecked(TouchScreenOffset + 0x10, CurrEntry);
                     Memory.WriteInt64Unchecked(TouchScreenOffset + 0x18, HidEntryCount - 1);
-                    Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp);            
+                    Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp);
 
                     long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize;
 
diff --git a/Ryujinx.Core/Loaders/Executable.cs b/Ryujinx.Core/Loaders/Executable.cs
index 943b8e510..39ee9618b 100644
--- a/Ryujinx.Core/Loaders/Executable.cs
+++ b/Ryujinx.Core/Loaders/Executable.cs
@@ -7,14 +7,16 @@ namespace Ryujinx.Core.Loaders
 {
     class Executable
     {
-        private AMemory Memory;
-
         private List<ElfDyn> Dynamic;
 
         private Dictionary<long, string> m_SymbolTable;
 
         public IReadOnlyDictionary<long, string> SymbolTable => m_SymbolTable;
 
+        public string Name { get; private set; }
+
+        private AMemory Memory;
+
         public long ImageBase { get; private set; }
         public long ImageEnd  { get; private set; }
 
@@ -24,6 +26,8 @@ namespace Ryujinx.Core.Loaders
 
             m_SymbolTable = new Dictionary<long, string>();
 
+            Name = Exe.Name;
+
             this.Memory    = Memory;
             this.ImageBase = ImageBase;
             this.ImageEnd  = ImageBase;
diff --git a/Ryujinx.Core/Loaders/Executables/IExecutable.cs b/Ryujinx.Core/Loaders/Executables/IExecutable.cs
index 09d0aab23..412058d88 100644
--- a/Ryujinx.Core/Loaders/Executables/IExecutable.cs
+++ b/Ryujinx.Core/Loaders/Executables/IExecutable.cs
@@ -2,6 +2,8 @@ namespace Ryujinx.Core.Loaders.Executables
 {
     public interface IExecutable
     {
+        string Name { get; }
+
         byte[] Text { get; }
         byte[] RO   { get; }
         byte[] Data { get; }
diff --git a/Ryujinx.Core/Loaders/Executables/Nro.cs b/Ryujinx.Core/Loaders/Executables/Nro.cs
index 9f4ef59f5..c3411d226 100644
--- a/Ryujinx.Core/Loaders/Executables/Nro.cs
+++ b/Ryujinx.Core/Loaders/Executables/Nro.cs
@@ -4,6 +4,8 @@ namespace Ryujinx.Core.Loaders.Executables
 {
     class Nro : IExecutable
     {
+        public string Name { get; private set; }
+
         public byte[] Text { get; private set; }
         public byte[] RO   { get; private set; }
         public byte[] Data { get; private set; }
@@ -14,8 +16,10 @@ namespace Ryujinx.Core.Loaders.Executables
         public int DataOffset { get; private set; }
         public int BssSize    { get; private set; }
 
-        public Nro(Stream Input)
+        public Nro(Stream Input, string Name)
         {
+            this.Name = Name;
+
             BinaryReader Reader = new BinaryReader(Input);
 
             Input.Seek(4, SeekOrigin.Begin);
diff --git a/Ryujinx.Core/Loaders/Executables/Nso.cs b/Ryujinx.Core/Loaders/Executables/Nso.cs
index 7341ba622..bfe159d7c 100644
--- a/Ryujinx.Core/Loaders/Executables/Nso.cs
+++ b/Ryujinx.Core/Loaders/Executables/Nso.cs
@@ -6,6 +6,8 @@ namespace Ryujinx.Core.Loaders.Executables
 {
     class Nso : IExecutable
     {
+        public string Name { get; private set; }
+
         public byte[] Text { get; private set; }
         public byte[] RO   { get; private set; }
         public byte[] Data { get; private set; }
@@ -27,8 +29,10 @@ namespace Ryujinx.Core.Loaders.Executables
             HasDataHash      = 1 << 5
         }
 
-        public Nso(Stream Input)
+        public Nso(Stream Input, string Name)
         {
+            this.Name = Name;
+
             BinaryReader Reader = new BinaryReader(Input);
 
             Input.Seek(0, SeekOrigin.Begin);
diff --git a/Ryujinx.Core/LogClass.cs b/Ryujinx.Core/LogClass.cs
new file mode 100644
index 000000000..014b57325
--- /dev/null
+++ b/Ryujinx.Core/LogClass.cs
@@ -0,0 +1,38 @@
+namespace Ryujinx.Core
+{
+    public enum LogClass
+    {
+        Audio,
+        CPU,
+        GPU,
+        Kernel,
+        KernelIpc,
+        KernelScheduler,
+        KernelSvc,
+        Loader,
+        Service,
+        ServiceAcc,
+        ServiceAm,
+        ServiceApm,
+        ServiceAudio,
+        ServiceBsd,
+        ServiceCaps,
+        ServiceFriend,
+        ServiceFs,
+        ServiceHid,
+        ServiceLm,
+        ServiceNifm,
+        ServiceNs,
+        ServiceNv,
+        ServicePctl,
+        ServicePl,
+        ServicePrepo,
+        ServiceSet,
+        ServiceSfdnsres,
+        ServiceSm,
+        ServiceSss,
+        ServiceTime,
+        ServiceVi,
+        Count,
+    }
+}
diff --git a/Ryujinx.Core/Logging.cs b/Ryujinx.Core/Logging.cs
index 89064ccbc..f6e2079a6 100644
--- a/Ryujinx.Core/Logging.cs
+++ b/Ryujinx.Core/Logging.cs
@@ -2,7 +2,9 @@
 using System;
 using System.Diagnostics;
 using System.IO;
+using System.Runtime.CompilerServices;
 using System.Text;
+using System.Threading;
 
 namespace Ryujinx.Core
 {
@@ -12,14 +14,28 @@ namespace Ryujinx.Core
 
         private const string LogFileName = "Ryujinx.log";
 
-        private static bool EnableInfo    = Config.LoggingEnableInfo;
-        private static bool EnableTrace   = Config.LoggingEnableTrace;
-        private static bool EnableDebug   = Config.LoggingEnableDebug;
-        private static bool EnableWarn    = Config.LoggingEnableWarn;
-        private static bool EnableError   = Config.LoggingEnableError;
-        private static bool EnableFatal   = Config.LoggingEnableFatal;
-        private static bool EnableIpc     = Config.LoggingEnableIpc;
-        private static bool EnableLogFile = Config.LoggingEnableLogFile;
+        private static bool   EnableInfo         = Config.LoggingEnableInfo;
+        private static bool   EnableTrace        = Config.LoggingEnableTrace;
+        private static bool   EnableDebug        = Config.LoggingEnableDebug;
+        private static bool   EnableWarn         = Config.LoggingEnableWarn;
+        private static bool   EnableError        = Config.LoggingEnableError;
+        private static bool   EnableFatal        = Config.LoggingEnableFatal;
+        private static bool   EnableStub         = Config.LoggingEnableStub;
+        private static bool   EnableIpc          = Config.LoggingEnableIpc;
+        private static bool   EnableFilter       = Config.LoggingEnableFilter;
+        private static bool   EnableLogFile      = Config.LoggingEnableLogFile;
+        private static bool[] FilteredLogClasses = Config.LoggingFilteredClasses;
+
+        private enum LogLevel
+        {
+            Debug,
+            Error,
+            Fatal,
+            Info,
+            Stub,
+            Trace,
+            Warn
+        }
 
         static Logging()
         {
@@ -30,14 +46,51 @@ namespace Ryujinx.Core
             ExecutionTime.Start();
         }
 
-        public static string GetExecutionTime()
-        {
-            return ExecutionTime.ElapsedMilliseconds.ToString().PadLeft(8, '0') + "ms";
-        }
+        public static string GetExecutionTime() => ExecutionTime.ElapsedMilliseconds.ToString().PadLeft(8, '0') + "ms";
 
-        private static string WhoCalledMe()
+        private static void LogMessage(LogEntry LogEntry)
         {
-            return new StackTrace().GetFrame(2).GetMethod().Name;
+            if (EnableFilter)
+                if (!FilteredLogClasses[(int)LogEntry.LogClass])
+                    return;
+
+            ConsoleColor consoleColor = ConsoleColor.White;
+
+            switch (LogEntry.LogLevel)
+            {
+                case LogLevel.Debug:
+                    consoleColor = ConsoleColor.Gray;
+                    break;
+                case LogLevel.Error:
+                    consoleColor = ConsoleColor.Red;
+                    break;
+                case LogLevel.Fatal:
+                    consoleColor = ConsoleColor.Magenta;
+                    break;
+                case LogLevel.Info:
+                    consoleColor = ConsoleColor.White;
+                    break;
+                case LogLevel.Stub:
+                    consoleColor = ConsoleColor.DarkYellow;
+                    break;
+                case LogLevel.Trace:
+                    consoleColor = ConsoleColor.DarkGray;
+                    break;
+                case LogLevel.Warn:
+                    consoleColor = ConsoleColor.Yellow;
+                    break;
+            }
+
+            LogEntry.ManagedThreadId = Thread.CurrentThread.ManagedThreadId;
+
+            string Text = $"{LogEntry.ExecutionTime} | {LogEntry.ManagedThreadId} > {LogEntry.LogClass} > " +
+                $"{LogEntry.LogLevel.ToString()}  > {LogEntry.CallingMember} > {LogEntry.Message}";
+
+            Console.ForegroundColor = consoleColor;
+            Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
+            Console.ResetColor();
+
+            LogFile(Text);
         }
 
         private static void LogFile(string Message)
@@ -51,87 +104,108 @@ namespace Ryujinx.Core
             }
         }
 
-        public static void Info(string Message)
+        public static void Info(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
         {
             if (EnableInfo)
             {
-                string Text = $"{GetExecutionTime()} | INFO  > {Message}";
-
-                Console.ForegroundColor = ConsoleColor.White;
-                Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
-                Console.ResetColor();
-
-                LogFile(Text);
+                LogMessage(new LogEntry
+                {
+                    CallingMember = CallingMember,
+                    LogLevel      = LogLevel.Info,
+                    LogClass      = LogClass,
+                    Message       = Message,
+                    ExecutionTime = GetExecutionTime()
+                });
             }
         }
 
-        public static void Trace(string Message)
+        public static void Trace(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
         {
             if (EnableTrace)
             {
-                string Text = $"{GetExecutionTime()} | TRACE > {WhoCalledMe()} - {Message}";
-
-                Console.ForegroundColor = ConsoleColor.DarkGray;
-                Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
-                Console.ResetColor();
-
-                LogFile(Text);
+                LogMessage(new LogEntry
+                {
+                    CallingMember = CallingMember,
+                    LogLevel      = LogLevel.Trace,
+                    LogClass      = LogClass,
+                    Message       = Message,
+                    ExecutionTime = GetExecutionTime()
+                });
             }
         }
 
-        public static void Debug(string Message)
+        public static void Stub(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
+        {
+            if (EnableStub)
+            {
+                LogMessage(new LogEntry
+                {
+                    CallingMember = CallingMember,
+                    LogLevel      = LogLevel.Stub,
+                    LogClass      = LogClass,
+                    Message       = Message,
+                    ExecutionTime = GetExecutionTime()
+                });
+            }
+        }
+
+        public static void Debug(LogClass LogClass,string Message, [CallerMemberName] string CallingMember = "")
         {
             if (EnableDebug)
             {
-                string Text = $"{GetExecutionTime()} | DEBUG > {WhoCalledMe()} - {Message}";
-
-                Console.ForegroundColor = ConsoleColor.Gray;
-                Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
-                Console.ResetColor();
-
-                LogFile(Text);
+                LogMessage(new LogEntry
+                {
+                    CallingMember = CallingMember,
+                    LogLevel      = LogLevel.Debug,
+                    LogClass      = LogClass,
+                    Message       = Message,
+                    ExecutionTime = GetExecutionTime()
+                });
             }
         }
 
-        public static void Warn(string Message)
+        public static void Warn(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
         {
             if (EnableWarn)
             {
-                string Text = $"{GetExecutionTime()} | WARN  > {WhoCalledMe()} - {Message}";
-
-                Console.ForegroundColor = ConsoleColor.Yellow;
-                Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
-                Console.ResetColor();
-
-                LogFile(Text);
+                LogMessage(new LogEntry
+                {
+                    CallingMember = CallingMember,
+                    LogLevel      = LogLevel.Warn,
+                    LogClass      = LogClass,
+                    Message       = Message,
+                    ExecutionTime = GetExecutionTime()
+                });
             }
         }
 
-        public static void Error(string Message)
+        public static void Error(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
         {
             if (EnableError)
             {
-                string Text = $"{GetExecutionTime()} | ERROR > {WhoCalledMe()} - {Message}";
-
-                Console.ForegroundColor = ConsoleColor.Red;
-                Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
-                Console.ResetColor();
-
-                LogFile(Text);
+                LogMessage(new LogEntry
+                {
+                    CallingMember = CallingMember,
+                    LogLevel      = LogLevel.Error,
+                    LogClass      = LogClass,
+                    Message       = Message,
+                    ExecutionTime = GetExecutionTime()
+                });
             }
         }
 
-        public static void Fatal(string Message)
+        public static void Fatal(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
         {
             if (EnableFatal)
             {
-                string Text = $"{GetExecutionTime()} | FATAL > {WhoCalledMe()} - {Message}";
-
-                Console.ForegroundColor = ConsoleColor.Magenta;
-                Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
-                Console.ResetColor();
-
-                LogFile(Text);
+                LogMessage(new LogEntry
+                {
+                    CallingMember = CallingMember,
+                    LogLevel      = LogLevel.Fatal,
+                    LogClass      = LogClass,
+                    Message       = Message,
+                    ExecutionTime = GetExecutionTime()
+                });
             }
         }
 
@@ -160,7 +234,7 @@ namespace Ryujinx.Core
             int firstCharColumn = firstHexColumn
                 + bytesPerLine * 3       // - 2 digit for the hexadecimal value and 1 space
                 + (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th
-                + 2;                  // 2 spaces 
+                + 2;                  // 2 spaces
 
             int lineLength = firstCharColumn
                 + bytesPerLine           // - characters to show the ascii value
@@ -208,5 +282,15 @@ namespace Ryujinx.Core
             }
             return result.ToString();
         }
+
+        private struct LogEntry
+        {
+            public string   CallingMember;
+            public string   ExecutionTime;
+            public string   Message;
+            public int      ManagedThreadId;
+            public LogClass LogClass;
+            public LogLevel LogLevel;
+        }
     }
 }
diff --git a/Ryujinx.Core/OsHle/AppletStateMgr.cs b/Ryujinx.Core/OsHle/AppletStateMgr.cs
new file mode 100644
index 000000000..2199f43ee
--- /dev/null
+++ b/Ryujinx.Core/OsHle/AppletStateMgr.cs
@@ -0,0 +1,62 @@
+using Ryujinx.Core.OsHle.Handles;
+using Ryujinx.Core.OsHle.Services.Am;
+using System;
+using System.Collections.Concurrent;
+
+namespace Ryujinx.Core.OsHle
+{
+    class AppletStateMgr : IDisposable
+    {
+        private ConcurrentQueue<MessageInfo> Messages;
+
+        public FocusState FocusState { get; private set; }
+
+        public KEvent MessageEvent { get; private set; }
+
+        public AppletStateMgr()
+        {
+            Messages = new ConcurrentQueue<MessageInfo>();
+
+            MessageEvent = new KEvent();
+        }
+
+        public void SetFocus(bool IsFocused)
+        {
+            FocusState = IsFocused
+                ? FocusState.InFocus
+                : FocusState.OutOfFocus;
+
+            EnqueueMessage(MessageInfo.FocusStateChanged);
+        }
+
+        public void EnqueueMessage(MessageInfo Message)
+        {
+            Messages.Enqueue(Message);
+
+            MessageEvent.WaitEvent.Set();
+        }
+
+        public bool TryDequeueMessage(out MessageInfo Message)
+        {
+            if (Messages.Count < 2)
+            {
+                MessageEvent.WaitEvent.Reset();
+            }
+
+            return Messages.TryDequeue(out Message);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        protected virtual void Dispose(bool Disposing)
+        {
+            if (Disposing)
+            {
+                MessageEvent.Dispose();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/CondVar.cs b/Ryujinx.Core/OsHle/CondVar.cs
deleted file mode 100644
index f5fe3d292..000000000
--- a/Ryujinx.Core/OsHle/CondVar.cs
+++ /dev/null
@@ -1,142 +0,0 @@
-using Ryujinx.Core.OsHle.Handles;
-using System.Collections.Generic;
-using System.Threading;
-
-namespace Ryujinx.Core.OsHle
-{
-    class CondVar
-    {
-        private Process Process;
-
-        private long CondVarAddress;
-        private long Timeout;
-
-        private bool OwnsCondVarValue;
-
-        private List<HThread> WaitingThreads;
-
-        public CondVar(Process Process, long CondVarAddress, long Timeout)
-        {
-            this.Process        = Process;
-            this.CondVarAddress = CondVarAddress;
-            this.Timeout        = Timeout;
-
-            WaitingThreads = new List<HThread>();
-        }
-
-        public bool WaitForSignal(HThread Thread)
-        {
-            int Count = Process.Memory.ReadInt32(CondVarAddress);
-
-            if (Count <= 0)
-            {
-                lock (WaitingThreads)
-                {
-                    WaitingThreads.Add(Thread);
-                }
-
-                if (Timeout == -1)
-                {
-                    Process.Scheduler.WaitForSignal(Thread);
-                }
-                else
-                {
-                    bool Result = Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000));
-
-                    lock (WaitingThreads)
-                    {
-                        WaitingThreads.Remove(Thread);
-                    }
-
-                    return Result;
-                }
-            }
-
-            AcquireCondVarValue();
-
-            Count = Process.Memory.ReadInt32(CondVarAddress);
-
-            if (Count > 0)
-            {
-                Process.Memory.WriteInt32(CondVarAddress, Count - 1);
-            }
-
-            ReleaseCondVarValue();
-
-            return true;
-        }
-
-        public void SetSignal(HThread Thread, int Count)
-        {
-            lock (WaitingThreads)
-            {
-                if (Count == -1)
-                {
-                    Process.Scheduler.Signal(WaitingThreads.ToArray());
-
-                    AcquireCondVarValue();
-
-                    Process.Memory.WriteInt32(CondVarAddress, WaitingThreads.Count);
-
-                    ReleaseCondVarValue();
-
-                    WaitingThreads.Clear();
-                }
-                else
-                {
-                    if (WaitingThreads.Count > 0)
-                    {
-                        int HighestPriority  = WaitingThreads[0].Priority;
-                        int HighestPrioIndex = 0;
-
-                        for (int Index = 1; Index < WaitingThreads.Count; Index++)
-                        {
-                            if (HighestPriority > WaitingThreads[Index].Priority)
-                            {
-                                HighestPriority = WaitingThreads[Index].Priority;
-
-                                HighestPrioIndex = Index;
-                            }
-                        }
-
-                        Process.Scheduler.Signal(WaitingThreads[HighestPrioIndex]);
-
-                        WaitingThreads.RemoveAt(HighestPrioIndex);
-                    }
-
-                    AcquireCondVarValue();
-
-                    Process.Memory.WriteInt32(CondVarAddress, Count);
-
-                    ReleaseCondVarValue();
-                }
-            }
-
-            Process.Scheduler.Suspend(Thread.ProcessorId);
-            Process.Scheduler.Resume(Thread);
-        }
-
-        private void AcquireCondVarValue()
-        {
-            if (!OwnsCondVarValue)
-            {
-                while (!Process.Memory.AcquireAddress(CondVarAddress))
-                {
-                    Thread.Yield();
-                }
-
-                OwnsCondVarValue = true;
-            }
-        }
-
-        private void ReleaseCondVarValue()
-        {
-            if (OwnsCondVarValue)
-            {
-                OwnsCondVarValue = false;
-
-                Process.Memory.ReleaseAddress(CondVarAddress);
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/FileDesc.cs b/Ryujinx.Core/OsHle/FileDesc.cs
deleted file mode 100644
index 4be83bb07..000000000
--- a/Ryujinx.Core/OsHle/FileDesc.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Ryujinx.Core.OsHle
-{
-    class FileDesc
-    {
-        public string Name { get; private set; }
-
-        public FileDesc(string Name)
-        {
-            this.Name = Name;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/GlobalStateTable.cs b/Ryujinx.Core/OsHle/GlobalStateTable.cs
new file mode 100644
index 000000000..2a5714ad0
--- /dev/null
+++ b/Ryujinx.Core/OsHle/GlobalStateTable.cs
@@ -0,0 +1,69 @@
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle
+{
+    class GlobalStateTable
+    {
+        private ConcurrentDictionary<Process, IdDictionary> DictByProcess;
+
+        public GlobalStateTable()
+        {
+            DictByProcess = new ConcurrentDictionary<Process, IdDictionary>();
+        }
+
+        public bool Add(Process Process, int Id, object Data)
+        {
+            IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
+
+            return Dict.Add(Id, Data);
+        }
+
+        public int Add(Process Process, object Data)
+        {
+            IdDictionary Dict = DictByProcess.GetOrAdd(Process, (Key) => new IdDictionary());
+
+            return Dict.Add(Data);
+        }
+
+        public object GetData(Process Process, int Id)
+        {
+            if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
+            {
+                return Dict.GetData(Id);
+            }
+
+            return null;
+        }
+
+        public T GetData<T>(Process Process, int Id)
+        {
+            if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
+            {
+                return Dict.GetData<T>(Id);
+            }
+
+            return default(T);
+        }
+
+        public object Delete(Process Process, int Id)
+        {
+            if (DictByProcess.TryGetValue(Process, out IdDictionary Dict))
+            {
+                return Dict.Delete(Id);
+            }
+
+            return null;
+        }
+
+        public ICollection<object> DeleteProcess(Process Process)
+        {
+            if (DictByProcess.TryRemove(Process, out IdDictionary Dict))
+            {
+                return Dict.Clear();
+            }
+
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Handles/HDomain.cs b/Ryujinx.Core/OsHle/Handles/HDomain.cs
deleted file mode 100644
index 26c604554..000000000
--- a/Ryujinx.Core/OsHle/Handles/HDomain.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-
-namespace Ryujinx.Core.OsHle.Handles
-{
-    class HDomain : HSession, IDisposable
-    {
-        private IdDictionary Objects;
-
-        public HDomain(HSession Session) : base(Session)
-        {
-            Objects = new IdDictionary();
-        }
-
-        public int Add(object Obj)
-        {
-            return Objects.Add(Obj);
-        }
-
-        public bool Delete(int Id)
-        {
-            return Objects.Delete(Id);
-        }
-
-        public object GetObject(int Id)
-        {
-            return Objects.GetData(Id);
-        }
-
-        public void Dispose()
-        {
-            Dispose(true);
-        }
-
-        protected virtual void Dispose(bool Disposing)
-        {
-            if (Disposing)
-            {
-                foreach (object Obj in Objects)
-                {
-                    if (Obj != this && Obj is IDisposable DisposableObj)
-                    {
-                        DisposableObj.Dispose();
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Handles/HEvent.cs b/Ryujinx.Core/OsHle/Handles/HEvent.cs
deleted file mode 100644
index 4e881ca24..000000000
--- a/Ryujinx.Core/OsHle/Handles/HEvent.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Ryujinx.Core.OsHle.Handles
-{
-    class HEvent
-    {
-
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Handles/HSession.cs b/Ryujinx.Core/OsHle/Handles/HSession.cs
deleted file mode 100644
index f30e91f98..000000000
--- a/Ryujinx.Core/OsHle/Handles/HSession.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Ryujinx.Core.OsHle.IpcServices;
-
-namespace Ryujinx.Core.OsHle.Handles
-{
-    class HSession
-    {
-        public IIpcService Service { get; private set; }
-
-        public bool IsInitialized { get; private set; }
-
-        public int State { get; set; }
-
-        public HSession(IIpcService Service)
-        {
-            this.Service = Service;
-        }
-
-        public HSession(HSession Session)
-        {
-            Service       = Session.Service;
-            IsInitialized = Session.IsInitialized;
-        }
-
-        public void Initialize()
-        {
-            IsInitialized = true;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Handles/HSessionObj.cs b/Ryujinx.Core/OsHle/Handles/HSessionObj.cs
deleted file mode 100644
index ed0530f74..000000000
--- a/Ryujinx.Core/OsHle/Handles/HSessionObj.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System;
-
-namespace Ryujinx.Core.OsHle.Handles
-{
-    class HSessionObj : HSession, IDisposable
-    {
-        public object Obj { get; private set; }
-
-        public HSessionObj(HSession Session, object Obj) : base(Session)
-        {
-            this.Obj = Obj;
-        }
-
-        public void Dispose()
-        {
-            Dispose(true);
-        }
-
-        protected virtual void Dispose(bool Disposing)
-        {
-            if (Disposing && Obj != null)
-            {
-                if (Obj is IDisposable DisposableObj)
-                {
-                    DisposableObj.Dispose();
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Handles/HThread.cs b/Ryujinx.Core/OsHle/Handles/HThread.cs
deleted file mode 100644
index c631cedc6..000000000
--- a/Ryujinx.Core/OsHle/Handles/HThread.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using ChocolArm64;
-
-namespace Ryujinx.Core.OsHle.Handles
-{
-    public class HThread
-    {
-        public AThread Thread { get; private set; }
-
-        public int ProcessorId { get; private set; }
-        public int Priority    { get; set; }
-
-        public int ThreadId => Thread.ThreadId;
-
-        public HThread(AThread Thread, int ProcessorId, int Priority)
-        {
-            this.Thread      = Thread;
-            this.ProcessorId = ProcessorId;
-            this.Priority    = Priority;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Handles/KEvent.cs b/Ryujinx.Core/OsHle/Handles/KEvent.cs
new file mode 100644
index 000000000..96ff01f7f
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Handles/KEvent.cs
@@ -0,0 +1,4 @@
+namespace Ryujinx.Core.OsHle.Handles
+{
+    class KEvent : KSynchronizationObject { }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs b/Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs
index 1156e035f..2c8098834 100644
--- a/Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs
+++ b/Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs
@@ -1,8 +1,8 @@
-using System;
+using System.Collections.Generic;
 
 namespace Ryujinx.Core.OsHle.Handles
 {
-    class KProcessHandleTable : IDisposable
+    class KProcessHandleTable
     {
         private IdDictionary Handles;
 
@@ -21,43 +21,14 @@ namespace Ryujinx.Core.OsHle.Handles
             return Handles.GetData<T>(Handle);
         }
 
-        public bool ReplaceData(int Id, object Data)
+        public object CloseHandle(int Handle)
         {
-            return Handles.ReplaceData(Id, Data);
-        }
-
-        public bool CloseHandle(int Handle)
-        {
-            object Data = Handles.GetData(Handle);
-
-            if (Data is HTransferMem TMem)
-            {
-                TMem.Memory.Manager.Reprotect(
-                    TMem.Position,
-                    TMem.Size,
-                    TMem.Perm);
-            }
-
             return Handles.Delete(Handle);
         }
 
-        public void Dispose()
+        public ICollection<object> Clear()
         {
-            Dispose(true);
-        }
-
-        protected virtual void Dispose(bool Disposing)
-        {
-            if (Disposing)
-            {
-                foreach (object Obj in Handles)
-                {
-                    if (Obj is IDisposable DisposableObj)
-                    {
-                        DisposableObj.Dispose();
-                    }
-                }
-            }
+            return Handles.Clear();
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs
index b11160781..81aa3fdd5 100644
--- a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs
+++ b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs
@@ -5,19 +5,31 @@ using System.Threading;
 
 namespace Ryujinx.Core.OsHle.Handles
 {
-    public class KProcessScheduler : IDisposable
+    class KProcessScheduler : IDisposable
     {
+        private const int LowestPriority = 0x40;
+
         private class SchedulerThread : IDisposable
         {
-            public HThread Thread { get; private set; }
+            public KThread Thread { get; private set; }
 
-            public AutoResetEvent WaitEvent { get; private set; }
+            public ManualResetEvent SyncWaitEvent  { get; private set; }
+            public AutoResetEvent   SchedWaitEvent { get; private set; }
 
-            public SchedulerThread(HThread Thread)
+            public bool Active { get; set; }
+
+            public int SyncTimeout { get; set; }
+
+            public SchedulerThread(KThread Thread)
             {
                 this.Thread = Thread;
 
-                WaitEvent = new AutoResetEvent(false);
+                SyncWaitEvent  = new ManualResetEvent(true);
+                SchedWaitEvent = new AutoResetEvent(false);
+
+                Active = true;
+
+                SyncTimeout = 0;
             }
 
             public void Dispose()
@@ -29,7 +41,8 @@ namespace Ryujinx.Core.OsHle.Handles
             {
                 if (Disposing)
                 {
-                    WaitEvent.Dispose();
+                    SyncWaitEvent.Dispose();
+                    SchedWaitEvent.Dispose();
                 }
             }
         }
@@ -51,7 +64,7 @@ namespace Ryujinx.Core.OsHle.Handles
                 }
             }
 
-            public SchedulerThread Pop(int MinPriority = 0x40)
+            public SchedulerThread Pop(int MinPriority = LowestPriority)
             {
                 lock (Threads)
                 {
@@ -65,9 +78,9 @@ namespace Ryujinx.Core.OsHle.Handles
                     {
                         SchedThread = Threads[Index];
 
-                        if (HighestPriority > SchedThread.Thread.Priority)
+                        if (HighestPriority > SchedThread.Thread.ActualPriority)
                         {
-                            HighestPriority = SchedThread.Thread.Priority;
+                            HighestPriority = SchedThread.Thread.ActualPriority;
 
                             HighestPrioIndex = Index;
                         }
@@ -93,9 +106,17 @@ namespace Ryujinx.Core.OsHle.Handles
                     return Threads.Contains(SchedThread);
                 }
             }
+
+            public bool Remove(SchedulerThread SchedThread)
+            {
+                lock (Threads)
+                {
+                    return Threads.Remove(SchedThread);
+                }
+            }
         }
 
-        private ConcurrentDictionary<HThread, SchedulerThread> AllThreads;
+        private ConcurrentDictionary<KThread, SchedulerThread> AllThreads;
 
         private ThreadQueue[] WaitingToRun;
 
@@ -105,7 +126,7 @@ namespace Ryujinx.Core.OsHle.Handles
 
         public KProcessScheduler()
         {
-            AllThreads = new ConcurrentDictionary<HThread, SchedulerThread>();
+            AllThreads = new ConcurrentDictionary<KThread, SchedulerThread>();
 
             WaitingToRun = new ThreadQueue[4];
 
@@ -119,7 +140,7 @@ namespace Ryujinx.Core.OsHle.Handles
             SchedLock = new object();
         }
 
-        public void StartThread(HThread Thread)
+        public void StartThread(KThread Thread)
         {
             lock (SchedLock)
             {
@@ -130,23 +151,119 @@ namespace Ryujinx.Core.OsHle.Handles
                     return;
                 }
 
-                if (!ActiveProcessors.Contains(Thread.ProcessorId))
+                if (ActiveProcessors.Add(Thread.ProcessorId))
                 {
-                    ActiveProcessors.Add(Thread.ProcessorId);
-
                     Thread.Thread.Execute();
 
-                    Logging.Debug($"{GetDbgThreadInfo(Thread)} running.");
+                    PrintDbgThreadInfo(Thread, "running.");
                 }
                 else
                 {
                     WaitingToRun[Thread.ProcessorId].Push(SchedThread);
 
-                    Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
+                    PrintDbgThreadInfo(Thread, "waiting to run.");
                 }
             }
         }
 
+        public void RemoveThread(KThread Thread)
+        {
+            PrintDbgThreadInfo(Thread, "exited.");
+
+            lock (SchedLock)
+            {
+                if (AllThreads.TryRemove(Thread, out SchedulerThread SchedThread))
+                {
+                    WaitingToRun[Thread.ProcessorId].Remove(SchedThread);
+
+                    SchedThread.Dispose();
+                }
+
+                SchedulerThread NewThread = WaitingToRun[Thread.ProcessorId].Pop();
+
+                if (NewThread == null)
+                {
+                    Logging.Debug(LogClass.KernelScheduler, $"Nothing to run on core {Thread.ProcessorId}!");
+
+                    ActiveProcessors.Remove(Thread.ProcessorId);
+
+                    return;
+                }
+
+                RunThread(NewThread);
+            }
+        }
+
+        public void SetThreadActivity(KThread Thread, bool Active)
+        {
+            if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
+            {
+                throw new InvalidOperationException();
+            }
+
+            SchedThread.Active = Active;
+
+            UpdateSyncWaitEvent(SchedThread);
+
+            WaitIfNeeded(SchedThread);
+        }
+
+        public bool EnterWait(KThread Thread, int Timeout = -1)
+        {
+            if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
+            {
+                throw new InvalidOperationException();
+            }
+
+            SchedThread.SyncTimeout = Timeout;
+
+            UpdateSyncWaitEvent(SchedThread);
+
+            return WaitIfNeeded(SchedThread);
+        }
+
+        public void WakeUp(KThread Thread)
+        {
+            if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
+            {
+                throw new InvalidOperationException();
+            }
+
+            SchedThread.SyncTimeout = 0;
+
+            UpdateSyncWaitEvent(SchedThread);
+
+            WaitIfNeeded(SchedThread);
+        }
+
+        private void UpdateSyncWaitEvent(SchedulerThread SchedThread)
+        {
+            if (SchedThread.Active && SchedThread.SyncTimeout == 0)
+            {
+                SchedThread.SyncWaitEvent.Set();
+            }
+            else
+            {
+                SchedThread.SyncWaitEvent.Reset();
+            }
+        }
+
+        private bool WaitIfNeeded(SchedulerThread SchedThread)
+        {
+            KThread Thread = SchedThread.Thread;
+
+            if (!IsActive(SchedThread) && Thread.Thread.IsCurrentThread())
+            {
+                Suspend(Thread.ProcessorId);
+
+                return Resume(Thread);
+            }
+            else
+            {
+                return false;
+            }
+        }
+
         public void Suspend(int ProcessorId)
         {
             lock (SchedLock)
@@ -159,164 +276,120 @@ namespace Ryujinx.Core.OsHle.Handles
                 }
                 else
                 {
+                    Logging.Debug(LogClass.KernelScheduler, $"Nothing to run on core {ProcessorId}!");
+
                     ActiveProcessors.Remove(ProcessorId);
                 }
             }
         }
 
-        public void Resume(HThread CurrThread)
+        public void Yield(KThread Thread)
         {
-            SchedulerThread SchedThread;
-
-            Logging.Debug($"{GetDbgThreadInfo(CurrThread)} entering ipc delay wait state.");
+            PrintDbgThreadInfo(Thread, "yielded execution.");
 
             lock (SchedLock)
             {
-                if (!AllThreads.TryGetValue(CurrThread, out SchedThread))
+                SchedulerThread SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.ActualPriority);
+
+                if (IsActive(Thread) && SchedThread == null)
                 {
-                    Logging.Error($"{GetDbgThreadInfo(CurrThread)} was not found on the scheduler queue!");
+                    PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run.");
 
                     return;
                 }
-            }
-
-            TryResumingExecution(SchedThread);
-        }
-
-        public bool WaitForSignal(HThread Thread, int Timeout = -1)
-        {
-            SchedulerThread SchedThread;
-
-            Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state.");
-
-            lock (SchedLock)
-            {
-                SchedThread = WaitingToRun[Thread.ProcessorId].Pop();
 
                 if (SchedThread != null)
                 {
                     RunThread(SchedThread);
                 }
-                else
-                {
-                    ActiveProcessors.Remove(Thread.ProcessorId);
-                }
-
-                if (!AllThreads.TryGetValue(Thread, out SchedThread))
-                {
-                    Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
-
-                    return false;
-                }
             }
 
-            bool Result;
-
-            if (Timeout >= 0)
-            {
-                Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
-
-                Result = SchedThread.WaitEvent.WaitOne(Timeout);
-            }
-            else
-            {
-                Result = SchedThread.WaitEvent.WaitOne();
-            }
-
-            TryResumingExecution(SchedThread);
-
-            return Result;
+            Resume(Thread);
         }
 
-        private void TryResumingExecution(SchedulerThread SchedThread)
+        public bool Resume(KThread Thread)
         {
-            HThread Thread = SchedThread.Thread;
+            if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
+            {
+                throw new InvalidOperationException();
+            }
+
+            return TryResumingExecution(SchedThread);
+        }
+
+        private bool TryResumingExecution(SchedulerThread SchedThread)
+        {
+            KThread Thread = SchedThread.Thread;
+
+            if (!SchedThread.Active || SchedThread.SyncTimeout != 0)
+            {
+                PrintDbgThreadInfo(Thread, "entering inactive wait state...");
+            }
+
+            bool Result = false;
+
+            if (SchedThread.SyncTimeout != 0)
+            {
+                Result = SchedThread.SyncWaitEvent.WaitOne(SchedThread.SyncTimeout);
+
+                SchedThread.SyncTimeout = 0;
+            }
 
             lock (SchedLock)
             {
                 if (ActiveProcessors.Add(Thread.ProcessorId))
                 {
-                    Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
+                    PrintDbgThreadInfo(Thread, "resuming execution...");
 
-                    return;
+                    return Result;
                 }
 
                 WaitingToRun[Thread.ProcessorId].Push(SchedThread);
+
+                PrintDbgThreadInfo(Thread, "entering wait state...");
             }
 
-            SchedThread.WaitEvent.WaitOne();
+            SchedThread.SchedWaitEvent.WaitOne();
 
-            Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
-        }
+            PrintDbgThreadInfo(Thread, "resuming execution...");
 
-        public void Yield(HThread Thread)
-        {
-            SchedulerThread SchedThread;
-
-            Logging.Debug($"{GetDbgThreadInfo(Thread)} yielded execution.");
-
-            lock (SchedLock)
-            {
-                SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.Priority);
-
-                if (SchedThread == null)
-                {
-                    Logging.Debug($"{GetDbgThreadInfo(Thread)} resumed because theres nothing better to run.");
-
-                    return;
-                }
-                
-                RunThread(SchedThread);
-
-                if (!AllThreads.TryGetValue(Thread, out SchedThread))
-                {
-                    Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
-
-                    return;
-                }
-
-                WaitingToRun[Thread.ProcessorId].Push(SchedThread);
-            }
-
-            SchedThread.WaitEvent.WaitOne();
-
-            Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
+            return Result;
         }
 
         private void RunThread(SchedulerThread SchedThread)
         {
             if (!SchedThread.Thread.Thread.Execute())
             {
-                SchedThread.WaitEvent.Set();
+                SchedThread.SchedWaitEvent.Set();
             }
             else
             {
-                Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} running.");
+                PrintDbgThreadInfo(SchedThread.Thread, "running.");
             }
         }
 
-        public void Signal(params HThread[] Threads)
+        private bool IsActive(KThread Thread)
         {
-            lock (SchedLock)
+            if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
             {
-                foreach (HThread Thread in Threads)
-                {
-                    if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
-                    {
-                        if (!WaitingToRun[Thread.ProcessorId].HasThread(SchedThread))
-                        {
-                            Logging.Debug($"{GetDbgThreadInfo(Thread)} signaled.");
-
-                            SchedThread.WaitEvent.Set();
-                        }
-                    }
-                }
+                throw new InvalidOperationException();
             }
+
+            return IsActive(SchedThread);
         }
 
-        private string GetDbgThreadInfo(HThread Thread)
+        private bool IsActive(SchedulerThread SchedThread)
         {
-            return $"Thread {Thread.ThreadId} (core {Thread.ProcessorId}) prio {Thread.Priority}";
+            return SchedThread.Active && SchedThread.SyncTimeout == 0;
+        }
+
+        private void PrintDbgThreadInfo(KThread Thread, string Message)
+        {
+            Logging.Debug(LogClass.KernelScheduler, "(" +
+                "ThreadId: "       + Thread.ThreadId       + ", " +
+                "ProcessorId: "    + Thread.ProcessorId    + ", " +
+                "ActualPriority: " + Thread.ActualPriority + ", " +
+                "WantedPriority: " + Thread.WantedPriority + ") " + Message);
         }
 
         public void Dispose()
diff --git a/Ryujinx.Core/OsHle/Handles/KSession.cs b/Ryujinx.Core/OsHle/Handles/KSession.cs
new file mode 100644
index 000000000..de3f9efaa
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Handles/KSession.cs
@@ -0,0 +1,31 @@
+using Ryujinx.Core.OsHle.Services;
+using System;
+
+namespace Ryujinx.Core.OsHle.Handles
+{
+    class KSession : IDisposable
+    {
+        public IpcService Service { get; private set; }
+
+        public string ServiceName { get; private set; }
+
+        public KSession(IpcService Service, string ServiceName)
+        {
+            this.Service     = Service;
+            this.ServiceName = ServiceName;
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        protected virtual void Dispose(bool Disposing)
+        {
+            if (Disposing && Service is IDisposable DisposableService)
+            {
+                DisposableService.Dispose();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Handles/KSynchronizationObject.cs b/Ryujinx.Core/OsHle/Handles/KSynchronizationObject.cs
new file mode 100644
index 000000000..3f78b9655
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Handles/KSynchronizationObject.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Threading;
+
+namespace Ryujinx.Core.OsHle.Handles
+{
+    class KSynchronizationObject : IDisposable
+    {
+        public ManualResetEvent WaitEvent { get; private set; }
+
+        public KSynchronizationObject()
+        {
+            WaitEvent = new ManualResetEvent(false);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        protected virtual void Dispose(bool Disposing)
+        {
+            if (Disposing)
+            {
+                WaitEvent.Dispose();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Handles/KThread.cs b/Ryujinx.Core/OsHle/Handles/KThread.cs
new file mode 100644
index 000000000..4286984e2
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Handles/KThread.cs
@@ -0,0 +1,113 @@
+using ChocolArm64;
+using System;
+
+namespace Ryujinx.Core.OsHle.Handles
+{
+    class KThread : KSynchronizationObject
+    {
+        public AThread Thread { get; private set; }
+
+        public KThread MutexOwner { get; set; }
+
+        public KThread NextMutexThread   { get; set; }
+        public KThread NextCondVarThread { get; set; }
+
+        public long MutexAddress   { get; set; }
+        public long CondVarAddress { get; set; }
+
+        public int ActualPriority { get; private set; }
+        public int WantedPriority { get; private set; }
+
+        public int ProcessorId  { get; private set; }
+
+        public int WaitHandle { get; set; }
+
+        public int ThreadId => Thread.ThreadId;
+
+        public KThread(AThread Thread, int ProcessorId, int Priority)
+        {
+            this.Thread      = Thread;
+            this.ProcessorId = ProcessorId;
+
+            ActualPriority = WantedPriority = Priority;
+        }
+
+        public void SetPriority(int Priority)
+        {
+            WantedPriority = Priority;
+
+            UpdatePriority();
+        }
+
+        public void UpdatePriority()
+        {
+            int OldPriority = ActualPriority;
+
+            int CurrPriority = WantedPriority;
+
+            if (NextMutexThread != null && CurrPriority > NextMutexThread.WantedPriority)
+            {
+                CurrPriority = NextMutexThread.WantedPriority;
+            }
+
+            if (CurrPriority != OldPriority)
+            {
+                ActualPriority = CurrPriority;
+
+                UpdateWaitList();
+
+                MutexOwner?.UpdatePriority();
+            }
+        }
+
+        private void UpdateWaitList()
+        {
+            KThread OwnerThread = MutexOwner;
+
+            if (OwnerThread != null)
+            {
+                //The MutexOwner field should only be non null when the thread is
+                //waiting for the lock, and the lock belongs to another thread.
+                if (OwnerThread == this)
+                {
+                    throw new InvalidOperationException();
+                }
+
+                lock (OwnerThread)
+                {
+                    //Remove itself from the list.
+                    KThread CurrThread = OwnerThread;
+
+                    while (CurrThread.NextMutexThread != null)
+                    {
+                        if (CurrThread.NextMutexThread == this)
+                        {
+                            CurrThread.NextMutexThread = NextMutexThread;
+
+                            break;
+                        }
+
+                        CurrThread = CurrThread.NextMutexThread;
+                    }
+
+                    //Re-add taking new priority into account.
+                    CurrThread = OwnerThread;
+
+                    while (CurrThread.NextMutexThread != null)
+                    {
+                        if (CurrThread.NextMutexThread.ActualPriority < ActualPriority)
+                        {
+                            break;
+                        }
+
+                        CurrThread = CurrThread.NextMutexThread;
+                    }
+
+                    NextMutexThread = CurrThread.NextMutexThread;
+
+                    CurrThread.NextMutexThread = this;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Homebrew.cs b/Ryujinx.Core/OsHle/Homebrew.cs
index 2a717ca73..873dda025 100644
--- a/Ryujinx.Core/OsHle/Homebrew.cs
+++ b/Ryujinx.Core/OsHle/Homebrew.cs
@@ -51,7 +51,7 @@ namespace Ryujinx.Core.OsHle
                     long Value0 = Memory.ReadInt64(Position + 0x08);
                     long Value1 = Memory.ReadInt64(Position + 0x10);
 
-                    FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, (int)(Value1 - Value0));
+                    FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, Value1 - Value0);
 
                     break;
                 }
diff --git a/Ryujinx.Core/OsHle/Horizon.cs b/Ryujinx.Core/OsHle/Horizon.cs
index c3f8cd8b0..6c625b09d 100644
--- a/Ryujinx.Core/OsHle/Horizon.cs
+++ b/Ryujinx.Core/OsHle/Horizon.cs
@@ -11,13 +11,14 @@ namespace Ryujinx.Core.OsHle
         internal const int HidSize  = 0x40000;
         internal const int FontSize = 0x50;
 
-        internal ConcurrentDictionary<long, Mutex>   Mutexes  { get; private set; }
-        internal ConcurrentDictionary<long, CondVar> CondVars { get; private set; }
+        private KProcessScheduler Scheduler;
 
         private ConcurrentDictionary<int, Process> Processes;
 
-        internal HSharedMem HidSharedMem;
-        internal HSharedMem FontSharedMem;
+        internal HSharedMem HidSharedMem  { get; private set; }
+        internal HSharedMem FontSharedMem { get; private set; }
+
+        internal KEvent VsyncEvent { get; private set; }
 
         private Switch Ns;
 
@@ -25,13 +26,14 @@ namespace Ryujinx.Core.OsHle
         {
             this.Ns = Ns;
 
-            Mutexes  = new ConcurrentDictionary<long, Mutex>();
-            CondVars = new ConcurrentDictionary<long, CondVar>();
+            Scheduler = new KProcessScheduler();
 
             Processes = new ConcurrentDictionary<int, Process>();
 
             HidSharedMem  = new HSharedMem();
             FontSharedMem = new HSharedMem();
+
+            VsyncEvent = new KEvent();
         }
 
         public void LoadCart(string ExeFsDir, string RomFsFile = null)
@@ -52,11 +54,13 @@ namespace Ryujinx.Core.OsHle
                         continue;
                     }
 
-                    Logging.Info($"Loading {Path.GetFileNameWithoutExtension(File)}...");
+                    Logging.Info(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}...");
 
                     using (FileStream Input = new FileStream(File, FileMode.Open))
                     {
-                        Nso Program = new Nso(Input);
+                        string Name = Path.GetFileNameWithoutExtension(File);
+
+                        Nso Program = new Nso(Input, Name);
 
                         MainProcess.LoadProgram(Program);
                     }
@@ -78,19 +82,23 @@ namespace Ryujinx.Core.OsHle
         {
             bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
 
+            string Name = Path.GetFileNameWithoutExtension(FileName);
+
             Process MainProcess = MakeProcess();
 
             using (FileStream Input = new FileStream(FileName, FileMode.Open))
             {
                 MainProcess.LoadProgram(IsNro
-                    ? (IExecutable)new Nro(Input)
-                    : (IExecutable)new Nso(Input));
+                    ? (IExecutable)new Nro(Input, Name)
+                    : (IExecutable)new Nso(Input, Name));
             }
 
             MainProcess.SetEmptyArgs();
             MainProcess.Run(IsNro);
         }
 
+        public void SignalVsync() => VsyncEvent.WaitEvent.Set();
+
         private Process MakeProcess()
         {
             Process Process;
@@ -104,21 +112,28 @@ namespace Ryujinx.Core.OsHle
                     ProcessId++;
                 }
 
-                Process = new Process(Ns, ProcessId);
+                Process = new Process(Ns, Scheduler, ProcessId);
 
                 Processes.TryAdd(ProcessId, Process);
             }
 
+            InitializeProcess(Process);
+
             return Process;
         }
 
+        private void InitializeProcess(Process Process)
+        {
+            Process.AppletState.SetFocus(true);
+        }
+
         internal void ExitProcess(int ProcessId)
         {
             if (Processes.TryGetValue(ProcessId, out Process Process) && Process.NeedsHbAbi)
             {
                 string NextNro = Homebrew.ReadHbAbiNextLoadPath(Process.Memory, Process.HbAbiDataPosition);
 
-                Logging.Info($"HbAbi NextLoadPath {NextNro}");
+                Logging.Info(LogClass.Loader, $"HbAbi NextLoadPath {NextNro}");
 
                 if (NextNro == string.Empty)
                 {
@@ -131,11 +146,6 @@ namespace Ryujinx.Core.OsHle
 
                 if (File.Exists(NextNro))
                 {
-                    //TODO: Those dictionaries shouldn't even exist,
-                    //the Mutex and CondVar helper classes should be static.
-                    Mutexes.Clear();
-                    CondVars.Clear();
-
                     LoadProgram(NextNro);
                 }
             }
@@ -171,6 +181,10 @@ namespace Ryujinx.Core.OsHle
                     Process.StopAllThreadsAsync();
                     Process.Dispose();
                 }
+
+                VsyncEvent.Dispose();
+
+                Scheduler.Dispose();
             }
         }
     }
diff --git a/Ryujinx.Core/OsHle/IdDictionary.cs b/Ryujinx.Core/OsHle/IdDictionary.cs
index 0b9092461..2a498e7f6 100644
--- a/Ryujinx.Core/OsHle/IdDictionary.cs
+++ b/Ryujinx.Core/OsHle/IdDictionary.cs
@@ -1,11 +1,10 @@
 using System;
-using System.Collections;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 
 namespace Ryujinx.Core.OsHle
 {
-    class IdDictionary : IEnumerable<object>
+    class IdDictionary
     {
         private ConcurrentDictionary<int, object> Objs;
 
@@ -16,6 +15,11 @@ namespace Ryujinx.Core.OsHle
             Objs = new ConcurrentDictionary<int, object>();
         }
 
+        public bool Add(int Id, object Data)
+        {
+            return Objs.TryAdd(Id, Data);
+        }
+
         public int Add(object Data)
         {
             if (Objs.TryAdd(FreeIdHint, Data))
@@ -39,18 +43,6 @@ namespace Ryujinx.Core.OsHle
             throw new InvalidOperationException();
         }
 
-        public bool ReplaceData(int Id, object Data)
-        {
-            if (Objs.ContainsKey(Id))
-            {
-                Objs[Id] = Data;
-
-                return true;
-            }
-
-            return false;
-        }
-
         public object GetData(int Id)
         {
             if (Objs.TryGetValue(Id, out object Data))
@@ -71,31 +63,25 @@ namespace Ryujinx.Core.OsHle
             return default(T);
         }
 
-        public bool Delete(int Id)
+        public object Delete(int Id)
         {
             if (Objs.TryRemove(Id, out object Obj))
             {
-                if (Obj is IDisposable DisposableObj)
-                {
-                    DisposableObj.Dispose();
-                }
-
                 FreeIdHint = Id;
 
-                return true;
+                return Obj;
             }
 
-            return false;
+            return null;
         }
 
-        IEnumerator<object> IEnumerable<object>.GetEnumerator()
+        public ICollection<object> Clear()
         {
-            return Objs.Values.GetEnumerator();
-        }
+            ICollection<object> Values = Objs.Values;
 
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return Objs.Values.GetEnumerator();
+            Objs.Clear();
+
+            return Values;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Ipc/IpcDomCmd.cs b/Ryujinx.Core/OsHle/Ipc/IpcDomCmd.cs
deleted file mode 100644
index 1ef0c4082..000000000
--- a/Ryujinx.Core/OsHle/Ipc/IpcDomCmd.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Ryujinx.Core.OsHle.Ipc
-{
-    enum IpcDomCmd
-    {
-        SendMsg   = 1,
-        DeleteObj = 2
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs
index f2179a962..42322d41a 100644
--- a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs
+++ b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs
@@ -1,6 +1,5 @@
 using ChocolArm64.Memory;
 using Ryujinx.Core.OsHle.Handles;
-using Ryujinx.Core.OsHle.IpcServices;
 using System;
 using System.IO;
 
@@ -8,20 +7,15 @@ namespace Ryujinx.Core.OsHle.Ipc
 {
     static class IpcHandler
     {
-        private const long SfciMagic = 'S' << 0 | 'F' << 8 | 'C' << 16 | 'I' << 24;
-        private const long SfcoMagic = 'S' << 0 | 'F' << 8 | 'C' << 16 | 'O' << 24;
-
         public static void IpcCall(
             Switch     Ns,
             Process    Process,
             AMemory    Memory,
-            HSession   Session,
+            KSession   Session,
             IpcMessage Request,
-            int        ThreadId,
-            long       CmdPtr,
-            int        HndId)
+            long       CmdPtr)
         {
-            IpcMessage Response = new IpcMessage(Request.IsDomain && Request.Type == IpcMessageType.Request);
+            IpcMessage Response = new IpcMessage();
 
             using (MemoryStream Raw = new MemoryStream(Request.RawData))
             {
@@ -29,94 +23,25 @@ namespace Ryujinx.Core.OsHle.Ipc
 
                 if (Request.Type == IpcMessageType.Request)
                 {
-                    string ServiceName = Session.Service.GetType().Name;
+                    Response.Type = IpcMessageType.Response;
 
-                    ServiceProcessRequest ProcReq = null;
-
-                    bool IgnoreNullPR = false;
-
-                    string DbgServiceName = string.Empty;
-
-                    if (Session is HDomain Dom)
+                    using (MemoryStream ResMS = new MemoryStream())
                     {
-                        if (Request.DomCmd == IpcDomCmd.SendMsg)
-                        {
-                            long Magic =      ReqReader.ReadInt64();
-                            int  CmdId = (int)ReqReader.ReadInt64();
+                        BinaryWriter ResWriter = new BinaryWriter(ResMS);
 
-                            object Obj = Dom.GetObject(Request.DomObjId);
+                        ServiceCtx Context = new ServiceCtx(
+                            Ns,
+                            Process,
+                            Memory,
+                            Session,
+                            Request,
+                            Response,
+                            ReqReader,
+                            ResWriter);
 
-                            if (Obj is HDomain)
-                            {
-                                Session.Service.Commands.TryGetValue(CmdId, out ProcReq);
+                        Session.Service.CallMethod(Context);
 
-                                DbgServiceName = $"{ProcReq?.Method.Name ?? CmdId.ToString()}";
-                            }
-                            else if (Obj != null)
-                            {
-                                ((IIpcService)Obj).Commands.TryGetValue(CmdId, out ProcReq);
-
-                                DbgServiceName = $"{Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}";
-                            }
-                        }
-                        else if (Request.DomCmd == IpcDomCmd.DeleteObj)
-                        {
-                            Dom.Delete(Request.DomObjId);
-
-                            Response = FillResponse(Response, 0);
-
-                            IgnoreNullPR = true;
-                        }
-                    }
-                    else
-                    {
-                        long Magic =      ReqReader.ReadInt64();
-                        int  CmdId = (int)ReqReader.ReadInt64();
-
-                        if (Session is HSessionObj)
-                        {
-                            object Obj = ((HSessionObj)Session).Obj;
-
-                            ((IIpcService)Obj).Commands.TryGetValue(CmdId, out ProcReq);
-
-                            DbgServiceName = $"{Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}";
-                        }
-                        else
-                        {
-                            Session.Service.Commands.TryGetValue(CmdId, out ProcReq);
-
-                            DbgServiceName = $"{ProcReq?.Method.Name ?? CmdId.ToString()}";
-                        }
-                    }
-
-                    DbgServiceName = $"Tid {ThreadId} {ServiceName} {DbgServiceName}";
-
-                    Logging.Debug($"IpcMessage: {DbgServiceName}");
-
-                    if (ProcReq != null)
-                    {
-                        using (MemoryStream ResMS = new MemoryStream())
-                        {
-                            BinaryWriter ResWriter = new BinaryWriter(ResMS);
-
-                            ServiceCtx Context = new ServiceCtx(
-                                Ns,
-                                Process,
-                                Memory,
-                                Session,
-                                Request,
-                                Response,
-                                ReqReader,
-                                ResWriter);
-
-                            long Result = ProcReq(Context);
-
-                            Response = FillResponse(Response, Result, ResMS.ToArray());
-                        }
-                    }
-                    else if (!IgnoreNullPR)
-                    {   
-                        throw new NotImplementedException(DbgServiceName);
+                        Response.RawData = ResMS.ToArray();
                     }
                 }
                 else if (Request.Type == IpcMessageType.Control)
@@ -128,11 +53,7 @@ namespace Ryujinx.Core.OsHle.Ipc
                     {
                         case 0:
                         {
-                            HDomain Dom = new HDomain(Session);
-
-                            Process.HandleTable.ReplaceData(HndId, Dom);
-
-                            Request = FillResponse(Response, 0, Dom.Add(Dom));
+                            Request = FillResponse(Response, 0, Session.Service.ConvertToDomain());
 
                             break;
                         }
@@ -140,12 +61,12 @@ namespace Ryujinx.Core.OsHle.Ipc
                         case 3:
                         {
                             Request = FillResponse(Response, 0, 0x500);
-                            
+
                             break;
                         }
 
-                        //TODO: Whats the difference between IpcDuplicateSession/Ex? 
-                        case 2: 
+                        //TODO: Whats the difference between IpcDuplicateSession/Ex?
+                        case 2:
                         case 4:
                         {
                             int Unknown = ReqReader.ReadInt32();
@@ -198,7 +119,7 @@ namespace Ryujinx.Core.OsHle.Ipc
             {
                 BinaryWriter Writer = new BinaryWriter(MS);
 
-                Writer.Write(SfcoMagic);
+                Writer.Write(IpcMagic.Sfco);
                 Writer.Write(Result);
 
                 if (Data != null)
diff --git a/Ryujinx.Core/OsHle/Ipc/IpcLog.cs b/Ryujinx.Core/OsHle/Ipc/IpcLog.cs
index dfec7ccfd..01915d91a 100644
--- a/Ryujinx.Core/OsHle/Ipc/IpcLog.cs
+++ b/Ryujinx.Core/OsHle/Ipc/IpcLog.cs
@@ -125,8 +125,7 @@ namespace Ryujinx.Core.OsHle.Ipc
 
                     Reader.ReadInt64(); //Padding
 
-                    IpcMessage += Environment.NewLine + $"    Domain:" + Environment.NewLine +
-                                  $"      DomCmd: {Enum.GetName(typeof(IpcDomCmd), DomCmd)}" + Environment.NewLine +
+                    IpcMessage += Environment.NewLine + $"    Domain:" + Environment.NewLine + Environment.NewLine +
                                   $"      DomObjId: {DomObjId.ToString()}" + Environment.NewLine;
                 }
 
diff --git a/Ryujinx.Core/OsHle/Ipc/IpcMagic.cs b/Ryujinx.Core/OsHle/Ipc/IpcMagic.cs
new file mode 100644
index 000000000..e3b8c74eb
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Ipc/IpcMagic.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Core.OsHle.Ipc
+{
+    abstract class IpcMagic
+    {
+        public const long Sfci = 'S' << 0 | 'F' << 8 | 'C' << 16 | 'I' << 24;
+        public const long Sfco = 'S' << 0 | 'F' << 8 | 'C' << 16 | 'O' << 24;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs
index ebb3dbca0..a99242465 100644
--- a/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs
+++ b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs
@@ -17,10 +17,6 @@ namespace Ryujinx.Core.OsHle.Ipc
 
         public List<int> ResponseObjIds { get; private set; }    
 
-        public bool      IsDomain { get; private set; }
-        public IpcDomCmd DomCmd   { get; private set; }
-        public int       DomObjId { get; private set; }
-
         public byte[] RawData { get; set; }
 
         public IpcMessage()
@@ -34,27 +30,18 @@ namespace Ryujinx.Core.OsHle.Ipc
             ResponseObjIds = new List<int>();
         }
 
-        public IpcMessage(bool Domain) : this()
+        public IpcMessage(byte[] Data, long CmdPtr) : this()
         {
-            IsDomain = Domain;
-        }
-
-        public IpcMessage(byte[] Data, long CmdPtr, bool Domain) : this()
-        {
-            Logging.Ipc(Data, CmdPtr, Domain);
-
             using (MemoryStream MS = new MemoryStream(Data))
             {
                 BinaryReader Reader = new BinaryReader(MS);
 
-                Initialize(Reader, CmdPtr, Domain);
+                Initialize(Reader, CmdPtr);
             }
         }
 
-        private void Initialize(BinaryReader Reader, long CmdPtr, bool Domain)
+        private void Initialize(BinaryReader Reader, long CmdPtr)
         {
-            IsDomain = Domain;
-
             int Word0 = Reader.ReadInt32();
             int Word1 = Reader.ReadInt32();
 
@@ -110,19 +97,6 @@ namespace Ryujinx.Core.OsHle.Ipc
                 RecvListCount = 0;
             }
 
-            if (Domain && Type == IpcMessageType.Request)
-            {
-                int DomWord0 = Reader.ReadInt32();
-
-                DomCmd = (IpcDomCmd)(DomWord0 & 0xff);
-
-                RawDataSize = (DomWord0 >> 16) & 0xffff;
-
-                DomObjId = Reader.ReadInt32();
-
-                Reader.ReadInt64(); //Padding
-            }
-
             RawData = Reader.ReadBytes(RawDataSize);
 
             Reader.BaseStream.Seek(RecvListPos, SeekOrigin.Begin);
@@ -165,9 +139,7 @@ namespace Ryujinx.Core.OsHle.Ipc
                 //This is the weirdest padding I've seen so far...
                 int Pad1 = 0x10 - Pad0;
 
-                DataLength = (DataLength + Pad0 + Pad1 + (IsDomain ? 0x10 : 0)) / 4;
-
-                DataLength += ResponseObjIds.Count;
+                DataLength = (DataLength + Pad0 + Pad1) / 4;
 
                 Word1 = DataLength & 0x3ff;
 
@@ -182,23 +154,11 @@ namespace Ryujinx.Core.OsHle.Ipc
 
                 MS.Seek(Pad0, SeekOrigin.Current);
 
-                if (IsDomain)
-                {
-                    Writer.Write(ResponseObjIds.Count);
-                    Writer.Write(0);
-                    Writer.Write(0L);
-                }
-
                 if (RawData != null)
                 {
                     Writer.Write(RawData);
                 }
 
-                foreach (int Id in ResponseObjIds)
-                {
-                    Writer.Write(Id);
-                }
-
                 Writer.Write(new byte[Pad1]);
 
                 return MS.ToArray();
diff --git a/Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs b/Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs
index d39f78db8..4eb15b9d7 100644
--- a/Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs
+++ b/Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs
@@ -6,7 +6,7 @@ namespace Ryujinx.Core.OsHle.Ipc
     {
         public long  Position { get; private set; }
         public int   Index    { get; private set; }
-        public short Size     { get; private set; }
+        public long  Size     { get; private set; }
 
         public IpcPtrBuffDesc(BinaryReader Reader)
         {
@@ -20,7 +20,7 @@ namespace Ryujinx.Core.OsHle.Ipc
             Index  = ((int)Word0 >> 0) & 0x03f;
             Index |= ((int)Word0 >> 3) & 0x1c0;
 
-            Size = (short)(Word0 >> 16);
+            Size = (ushort)(Word0 >> 16);
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Kernel/KernelErr.cs b/Ryujinx.Core/OsHle/Kernel/KernelErr.cs
new file mode 100644
index 000000000..b568405b7
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Kernel/KernelErr.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Core.OsHle.Kernel
+{
+    static class KernelErr
+    {
+        public const int InvalidAlignment = 102;
+        public const int InvalidAddress   = 106;
+        public const int InvalidMemRange  = 110;
+        public const int InvalidHandle    = 114;
+        public const int Timeout          = 117;
+        public const int InvalidInfo      = 120;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Kernel/NsTimeConverter.cs b/Ryujinx.Core/OsHle/Kernel/NsTimeConverter.cs
new file mode 100644
index 000000000..84fb0b85e
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Kernel/NsTimeConverter.cs
@@ -0,0 +1,19 @@
+namespace Ryujinx.Core.OsHle.Kernel
+{
+    static class NsTimeConverter
+    {
+        public static int GetTimeMs(ulong Ns)
+        {
+            ulong Ms = Ns / 1_000_000;
+
+            if (Ms < int.MaxValue)
+            {
+                return (int)Ms;
+            }
+            else
+            {
+                return int.MaxValue;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs
similarity index 86%
rename from Ryujinx.Core/OsHle/Svc/SvcHandler.cs
rename to Ryujinx.Core/OsHle/Kernel/SvcHandler.cs
index 3ce56a3cd..25d2767e3 100644
--- a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs
+++ b/Ryujinx.Core/OsHle/Kernel/SvcHandler.cs
@@ -5,7 +5,7 @@ using Ryujinx.Core.OsHle.Handles;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.Svc
+namespace Ryujinx.Core.OsHle.Kernel
 {
     partial class SvcHandler : IDisposable
     {
@@ -17,6 +17,8 @@ namespace Ryujinx.Core.OsHle.Svc
         private Process Process;
         private AMemory Memory;
 
+        private object CondVarLock;
+
         private HashSet<(HSharedMem, long)> MappedSharedMems;
 
         private ulong CurrentHeapSize;
@@ -40,6 +42,7 @@ namespace Ryujinx.Core.OsHle.Svc
                 { 0x0c, SvcGetThreadPriority             },
                 { 0x0d, SvcSetThreadPriority             },
                 { 0x0f, SvcSetThreadCoreMask             },
+                { 0x10, SvcGetCurrentProcessorNumber     },
                 { 0x12, SvcClearEvent                    },
                 { 0x13, SvcMapSharedMemory               },
                 { 0x14, SvcUnmapSharedMemory             },
@@ -58,13 +61,16 @@ namespace Ryujinx.Core.OsHle.Svc
                 { 0x25, SvcGetThreadId                   },
                 { 0x26, SvcBreak                         },
                 { 0x27, SvcOutputDebugString             },
-                { 0x29, SvcGetInfo                       }
+                { 0x29, SvcGetInfo                       },
+                { 0x32, SvcSetThreadActivity             }
             };
 
             this.Ns      = Ns;
             this.Process = Process;
             this.Memory  = Process.Memory;
 
+            CondVarLock = new object();
+
             MappedSharedMems = new HashSet<(HSharedMem, long)>();
         }
 
@@ -79,14 +85,16 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
             {
-                Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} called.");
+                Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} called.");
 
                 Func(ThreadState);
 
-                Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended.");
+                Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended.");
             }
             else
             {
+                Process.PrintStackTrace(ThreadState);
+
                 throw new NotImplementedException(e.Id.ToString("x4"));
             }
         }
diff --git a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs b/Ryujinx.Core/OsHle/Kernel/SvcMemory.cs
similarity index 89%
rename from Ryujinx.Core/OsHle/Svc/SvcMemory.cs
rename to Ryujinx.Core/OsHle/Kernel/SvcMemory.cs
index 80f24d2bd..c8aedcff3 100644
--- a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs
+++ b/Ryujinx.Core/OsHle/Kernel/SvcMemory.cs
@@ -4,7 +4,7 @@ using Ryujinx.Core.OsHle.Handles;
 
 using static Ryujinx.Core.OsHle.ErrorCode;
 
-namespace Ryujinx.Core.OsHle.Svc
+namespace Ryujinx.Core.OsHle.Kernel
 {
     partial class SvcHandler
     {
@@ -57,7 +57,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidPosition(Src))
             {
-                Logging.Warn($"Tried to map Memory at invalid src address {Src:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to map Memory at invalid src address {Src:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -66,7 +66,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidMapPosition(Dst))
             {
-                Logging.Warn($"Tried to map Memory at invalid dst address {Dst:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to map Memory at invalid dst address {Dst:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -92,7 +92,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidPosition(Src))
             {
-                Logging.Warn($"Tried to unmap Memory at invalid src address {Src:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to unmap Memory at invalid src address {Src:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -101,7 +101,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidMapPosition(Dst))
             {
-                Logging.Warn($"Tried to unmap Memory at invalid dst address {Dst:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to unmap Memory at invalid dst address {Dst:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -158,7 +158,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidPosition(Src))
             {
-                Logging.Warn($"Tried to map SharedMemory at invalid address {Src:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to map SharedMemory at invalid address {Src:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -196,7 +196,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidPosition(Src))
             {
-                Logging.Warn($"Tried to unmap SharedMemory at invalid address {Src:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to unmap SharedMemory at invalid address {Src:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -230,7 +230,7 @@ namespace Ryujinx.Core.OsHle.Svc
 
             if (!IsValidPosition(Src))
             {
-                Logging.Warn($"Tried to create TransferMemory at invalid address {Src:x16}!");
+                Logging.Warn(LogClass.KernelSvc, $"Tried to create TransferMemory at invalid address {Src:x16}!");
 
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
 
@@ -244,7 +244,7 @@ namespace Ryujinx.Core.OsHle.Svc
             HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size);
 
             ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem);
-            
+
             ThreadState.X0 = 0;
             ThreadState.X1 = Handle;
         }
@@ -252,13 +252,13 @@ namespace Ryujinx.Core.OsHle.Svc
         private static bool IsValidPosition(long Position)
         {
             return Position >= MemoryRegions.AddrSpaceStart &&
-                   Position <  MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; 
+                   Position <  MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
         }
 
         private static bool IsValidMapPosition(long Position)
         {
             return Position >= MemoryRegions.MapRegionAddress &&
-                   Position <  MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize; 
+                   Position <  MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs
similarity index 56%
rename from Ryujinx.Core/OsHle/Svc/SvcSystem.cs
rename to Ryujinx.Core/OsHle/Kernel/SvcSystem.cs
index 671a32d36..056b5059d 100644
--- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs
+++ b/Ryujinx.Core/OsHle/Kernel/SvcSystem.cs
@@ -3,12 +3,13 @@ using ChocolArm64.State;
 using Ryujinx.Core.OsHle.Exceptions;
 using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Ipc;
+using Ryujinx.Core.OsHle.Services;
 using System;
 using System.Threading;
 
 using static Ryujinx.Core.OsHle.ErrorCode;
 
-namespace Ryujinx.Core.OsHle.Svc
+namespace Ryujinx.Core.OsHle.Kernel
 {
     partial class SvcHandler
     {
@@ -34,7 +35,28 @@ namespace Ryujinx.Core.OsHle.Svc
         {
             int Handle = (int)ThreadState.X0;
 
-            Process.HandleTable.CloseHandle(Handle);
+            object Obj = Process.HandleTable.CloseHandle(Handle);
+
+            if (Obj == null)
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Tried to CloseHandle on invalid handle 0x{Handle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+                return;
+            }
+
+            if (Obj is KSession Session)
+            {
+                Session.Dispose();
+            }
+            else if (Obj is HTransferMem TMem)
+            {
+                TMem.Memory.Manager.Reprotect(
+                    TMem.Position,
+                    TMem.Size,
+                    TMem.Perm);
+            }
 
             ThreadState.X0 = 0;
         }
@@ -43,25 +65,78 @@ namespace Ryujinx.Core.OsHle.Svc
         {
             int Handle = (int)ThreadState.X0;
 
-            //TODO: Implement events.
+            KEvent Event = Process.HandleTable.GetData<KEvent>(Handle);
 
-            ThreadState.X0 = 0;
+            if (Event != null)
+            {
+                Event.WaitEvent.Reset();
+
+                ThreadState.X0 = 0;
+            }
+            else
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Tried to ResetSignal on invalid event handle 0x{Handle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+            }
         }
 
         private void SvcWaitSynchronization(AThreadState ThreadState)
         {
-            long HandlesPtr   = (long)ThreadState.X0;
-            int  HandlesCount =  (int)ThreadState.X2;
-            long Timeout      = (long)ThreadState.X3;
+            long  HandlesPtr   = (long)ThreadState.X1;
+            int   HandlesCount =  (int)ThreadState.X2;
+            ulong Timeout      =       ThreadState.X3;
 
-            //TODO: Implement events.
+            KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
 
-            HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
+            WaitHandle[] Handles = new WaitHandle[HandlesCount];
+
+            for (int Index = 0; Index < HandlesCount; Index++)
+            {
+                int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
+
+                KSynchronizationObject SyncObj = Process.HandleTable.GetData<KSynchronizationObject>(Handle);
+
+                if (SyncObj == null)
+                {
+                    Logging.Warn(LogClass.KernelSvc, $"Tried to WaitSynchronization on invalid handle 0x{Handle:x8}!");
+
+                    ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+                    return;
+                }
+
+                Handles[Index] = SyncObj.WaitEvent;
+            }
 
             Process.Scheduler.Suspend(CurrThread.ProcessorId);
+
+            int HandleIndex;
+
+            ulong Result = 0;
+
+            if (Timeout != ulong.MaxValue)
+            {
+                HandleIndex = WaitHandle.WaitAny(Handles, NsTimeConverter.GetTimeMs(Timeout));
+
+                if (HandleIndex == WaitHandle.WaitTimeout)
+                {
+                    Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
+                }
+            }
+            else
+            {
+                HandleIndex = WaitHandle.WaitAny(Handles);
+            }
+
             Process.Scheduler.Resume(CurrThread);
 
-            ThreadState.X0 = 0;
+            ThreadState.X0 = Result;
+
+            if (Result == 0)
+            {
+                ThreadState.X1 = (ulong)HandleIndex;
+            }
         }
 
         private void SvcGetSystemTick(AThreadState ThreadState)
@@ -78,76 +153,56 @@ namespace Ryujinx.Core.OsHle.Svc
 
             //TODO: Validate that app has perms to access the service, and that the service
             //actually exists, return error codes otherwise.
-
-            HSession Session = new HSession(Process.Services.GetService(Name));
+            KSession Session = new KSession(ServiceFactory.MakeService(Name), Name);
 
             ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session);
-            
+
             ThreadState.X0 = 0;
             ThreadState.X1 = Handle;
         }
 
         private void SvcSendSyncRequest(AThreadState ThreadState)
         {
-            SendSyncRequest(ThreadState, false);
+            SendSyncRequest(ThreadState, ThreadState.Tpidr, 0x100, (int)ThreadState.X0);
         }
 
         private void SvcSendSyncRequestWithUserBuffer(AThreadState ThreadState)
         {
-            SendSyncRequest(ThreadState, true);
+            SendSyncRequest(
+                      ThreadState,
+                (long)ThreadState.X0,
+                (long)ThreadState.X1,
+                 (int)ThreadState.X2);
         }
 
-        private void SendSyncRequest(AThreadState ThreadState, bool UserBuffer)
+        private void SendSyncRequest(AThreadState ThreadState, long CmdPtr, long Size, int Handle)
         {
-            long CmdPtr = ThreadState.Tpidr;
-            long Size   = 0x100;
-            int  Handle = 0;
+            KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
 
-            if (UserBuffer)
-            {
-                CmdPtr = (long)ThreadState.X0;
-                Size   = (long)ThreadState.X1;
-                Handle =  (int)ThreadState.X2;
-            }
-            else
-            {
-                Handle = (int)ThreadState.X0;
-            }
+            byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, Size);
 
-            HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
-
-            Process.Scheduler.Suspend(CurrThread.ProcessorId);
-
-            byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
-
-            HSession Session = Process.HandleTable.GetData<HSession>(Handle);
-
-            IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr, Session is HDomain);
+            KSession Session = Process.HandleTable.GetData<KSession>(Handle);
 
             if (Session != null)
             {
-                IpcHandler.IpcCall(
-                    Ns,
-                    Process,
-                    Memory,
-                    Session,
-                    Cmd,
-                    ThreadState.ThreadId,
-                    CmdPtr,
-                    Handle);
+                Process.Scheduler.Suspend(CurrThread.ProcessorId);
 
-                byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
+                IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr);
+
+                IpcHandler.IpcCall(Ns, Process, Memory, Session, Cmd, CmdPtr);
+
+                Thread.Yield();
+
+                Process.Scheduler.Resume(CurrThread);
 
                 ThreadState.X0 = 0;
             }
             else
             {
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidIpcReq);
+                Logging.Warn(LogClass.KernelSvc, $"Tried to SendSyncRequest on invalid session handle 0x{Handle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
             }
-
-            Thread.Yield();
-
-            Process.Scheduler.Resume(CurrThread);
         }
 
         private void SvcBreak(AThreadState ThreadState)
@@ -156,6 +211,8 @@ namespace Ryujinx.Core.OsHle.Svc
             long Unknown = (long)ThreadState.X1;
             long Info    = (long)ThreadState.X2;
 
+            Process.PrintStackTrace(ThreadState);
+
             throw new GuestBrokeExecutionException();
         }
 
@@ -164,9 +221,9 @@ namespace Ryujinx.Core.OsHle.Svc
             long Position = (long)ThreadState.X0;
             long Size     = (long)ThreadState.X1;
 
-            string Str = AMemoryHelper.ReadAsciiString(Memory, Position, (int)Size);
+            string Str = AMemoryHelper.ReadAsciiString(Memory, Position, Size);
 
-            Logging.Info($"SvcOutputDebugString: {Str}");
+            Logging.Info(LogClass.KernelSvc, Str);
 
             ThreadState.X0 = 0;
         }
@@ -180,7 +237,8 @@ namespace Ryujinx.Core.OsHle.Svc
 
             //Fail for info not available on older Kernel versions.
             if (InfoType == 18 ||
-                InfoType == 19)
+                InfoType == 19 ||
+                InfoType == 20)
             {
                 ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo);
 
@@ -212,7 +270,7 @@ namespace Ryujinx.Core.OsHle.Svc
                 case 6:
                     ThreadState.X1 = MemoryRegions.TotalMemoryAvailable;
                     break;
-    
+
                 case 7:
                     ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize;
                     break;
@@ -241,7 +299,10 @@ namespace Ryujinx.Core.OsHle.Svc
                     ThreadState.X1 = MemoryRegions.MapRegionSize;
                     break;
 
-                default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}");
+                default:
+                    Process.PrintStackTrace(ThreadState);
+
+                    throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle:x8} {InfoId}");
             }
 
             ThreadState.X0 = 0;
diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThread.cs b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs
new file mode 100644
index 000000000..e1300b739
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Kernel/SvcThread.cs
@@ -0,0 +1,177 @@
+using ChocolArm64.State;
+using Ryujinx.Core.OsHle.Handles;
+using System.Threading;
+
+using static Ryujinx.Core.OsHle.ErrorCode;
+
+namespace Ryujinx.Core.OsHle.Kernel
+{
+    partial class SvcHandler
+    {
+        private void SvcCreateThread(AThreadState ThreadState)
+        {
+            long EntryPoint  = (long)ThreadState.X1;
+            long ArgsPtr     = (long)ThreadState.X2;
+            long StackTop    = (long)ThreadState.X3;
+            int  Priority    =  (int)ThreadState.X4;
+            int  ProcessorId =  (int)ThreadState.X5;
+
+            if (ProcessorId == -2)
+            {
+                //TODO: Get this value from the NPDM file.
+                ProcessorId = 0;
+            }
+
+            int Handle = Process.MakeThread(
+                EntryPoint,
+                StackTop,
+                ArgsPtr,
+                Priority,
+                ProcessorId);
+
+            ThreadState.X0 = 0;
+            ThreadState.X1 = (ulong)Handle;
+        }
+
+        private void SvcStartThread(AThreadState ThreadState)
+        {
+            int Handle = (int)ThreadState.X0;
+
+            KThread CurrThread = Process.HandleTable.GetData<KThread>(Handle);
+
+            if (CurrThread != null)
+            {
+                Process.Scheduler.StartThread(CurrThread);
+
+                Process.Scheduler.Yield(Process.GetThread(ThreadState.Tpidr));
+
+                ThreadState.X0 = 0;
+            }
+            else
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+            }
+        }
+
+        private void SvcExitThread(AThreadState ThreadState)
+        {
+            KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
+
+            CurrThread.Thread.StopExecution();
+        }
+
+        private void SvcSleepThread(AThreadState ThreadState)
+        {
+            ulong Ns = ThreadState.X0;
+
+            KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
+
+            if (Ns == 0)
+            {
+                Process.Scheduler.Yield(CurrThread);
+            }
+            else
+            {
+                Process.Scheduler.Suspend(CurrThread.ProcessorId);
+
+                Thread.Sleep(NsTimeConverter.GetTimeMs(Ns));
+
+                Process.Scheduler.Resume(CurrThread);
+            }
+        }
+
+        private void SvcGetThreadPriority(AThreadState ThreadState)
+        {
+            int Handle = (int)ThreadState.X1;
+
+            KThread CurrThread = Process.HandleTable.GetData<KThread>(Handle);
+
+            if (CurrThread != null)
+            {
+                ThreadState.X0 = 0;
+                ThreadState.X1 = (ulong)CurrThread.ActualPriority;
+            }
+            else
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+            }
+        }
+
+        private void SvcSetThreadPriority(AThreadState ThreadState)
+        {
+            int Handle   = (int)ThreadState.X0;
+            int Priority = (int)ThreadState.X1;
+
+            KThread CurrThread = Process.HandleTable.GetData<KThread>(Handle);
+
+            if (CurrThread != null)
+            {
+                CurrThread.SetPriority(Priority);
+
+                ThreadState.X0 = 0;
+            }
+            else
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+            }
+        }
+
+        private void SvcSetThreadCoreMask(AThreadState ThreadState)
+        {
+            ThreadState.X0 = 0;
+
+            //TODO: Error codes.
+        }
+
+        private void SvcGetCurrentProcessorNumber(AThreadState ThreadState)
+        {
+            ThreadState.X0 = (ulong)Process.GetThread(ThreadState.Tpidr).ProcessorId;
+        }
+
+        private void SvcGetThreadId(AThreadState ThreadState)
+        {
+            int Handle = (int)ThreadState.X1;
+
+            KThread CurrThread = Process.HandleTable.GetData<KThread>(Handle);
+
+            if (CurrThread != null)
+            {
+                ThreadState.X0 = 0;
+                ThreadState.X1 = (ulong)CurrThread.ThreadId;
+            }
+            else
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+            }
+        }
+
+        private void SvcSetThreadActivity(AThreadState ThreadState)
+        {
+            int  Handle = (int)ThreadState.X0;
+            bool Active = (int)ThreadState.X1 == 0;
+
+            KThread Thread = Process.HandleTable.GetData<KThread>(Handle);
+
+            if (Thread != null)
+            {
+                Process.Scheduler.SetThreadActivity(Thread, Active);
+
+                ThreadState.X0 = 0;
+            }
+            else
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs
new file mode 100644
index 000000000..6502e4c98
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs
@@ -0,0 +1,424 @@
+using ChocolArm64.State;
+using Ryujinx.Core.OsHle.Handles;
+using System;
+using System.Threading;
+
+using static Ryujinx.Core.OsHle.ErrorCode;
+
+namespace Ryujinx.Core.OsHle.Kernel
+{
+    partial class SvcHandler
+    {
+        private const int MutexHasListenersMask = 0x40000000;
+
+        private void SvcArbitrateLock(AThreadState ThreadState)
+        {
+            int  OwnerThreadHandle =  (int)ThreadState.X0;
+            long MutexAddress      = (long)ThreadState.X1;
+            int  WaitThreadHandle  =  (int)ThreadState.X2;
+
+            if (IsPointingInsideKernel(MutexAddress))
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
+
+                return;
+            }
+
+            if (IsWordAddressUnaligned(MutexAddress))
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
+
+                return;
+            }
+
+            KThread OwnerThread = Process.HandleTable.GetData<KThread>(OwnerThreadHandle);
+
+            if (OwnerThread == null)
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid owner thread handle 0x{OwnerThreadHandle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+                return;
+            }
+
+            KThread WaitThread = Process.HandleTable.GetData<KThread>(WaitThreadHandle);
+
+            if (WaitThread == null)
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid requesting thread handle 0x{WaitThreadHandle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+                return;
+            }
+
+            KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
+
+            MutexLock(CurrThread, WaitThread, OwnerThreadHandle, WaitThreadHandle, MutexAddress);
+
+            ThreadState.X0 = 0;
+        }
+
+        private void SvcArbitrateUnlock(AThreadState ThreadState)
+        {
+            long MutexAddress = (long)ThreadState.X0;
+
+            if (IsPointingInsideKernel(MutexAddress))
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
+
+                return;
+            }
+
+            if (IsWordAddressUnaligned(MutexAddress))
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
+
+                return;
+            }
+
+            if (MutexUnlock(Process.GetThread(ThreadState.Tpidr), MutexAddress))
+            {
+                Process.Scheduler.Yield(Process.GetThread(ThreadState.Tpidr));
+            }
+
+            ThreadState.X0 = 0;
+        }
+
+        private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState)
+        {
+            long  MutexAddress   = (long)ThreadState.X0;
+            long  CondVarAddress = (long)ThreadState.X1;
+            int   ThreadHandle   =  (int)ThreadState.X2;
+            ulong Timeout        =       ThreadState.X3;
+
+            if (IsPointingInsideKernel(MutexAddress))
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
+
+                return;
+            }
+
+            if (IsWordAddressUnaligned(MutexAddress))
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAlignment);
+
+                return;
+            }
+
+            KThread Thread = Process.HandleTable.GetData<KThread>(ThreadHandle);
+
+            if (Thread == null)
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
+
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+
+                return;
+            }
+
+            KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
+
+            MutexUnlock(CurrThread, MutexAddress);
+
+            if (!CondVarWait(CurrThread, ThreadHandle, MutexAddress, CondVarAddress, Timeout))
+            {
+                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
+
+                return;
+            }
+
+            Process.Scheduler.Yield(Process.GetThread(ThreadState.Tpidr));
+
+            ThreadState.X0 = 0;
+        }
+
+        private void SvcSignalProcessWideKey(AThreadState ThreadState)
+        {
+            long CondVarAddress = (long)ThreadState.X0;
+            int  Count          =  (int)ThreadState.X1;
+
+            CondVarSignal(CondVarAddress, Count);
+
+            ThreadState.X0 = 0;
+        }
+
+        private void MutexLock(
+            KThread CurrThread,
+            KThread WaitThread,
+            int     OwnerThreadHandle,
+            int     WaitThreadHandle,
+            long    MutexAddress)
+        {
+            int MutexValue = Process.Memory.ReadInt32(MutexAddress);
+
+            if (MutexValue != (OwnerThreadHandle | MutexHasListenersMask))
+            {
+                return;
+            }
+
+            CurrThread.WaitHandle   = WaitThreadHandle;
+            CurrThread.MutexAddress = MutexAddress;
+
+            InsertWaitingMutexThread(OwnerThreadHandle, WaitThread);
+
+            Process.Scheduler.EnterWait(WaitThread);
+        }
+
+        private bool MutexUnlock(KThread CurrThread, long MutexAddress)
+        {
+            if (CurrThread == null)
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid mutex 0x{MutexAddress:x16}!");
+
+                return false;
+            }
+
+            lock (CurrThread)
+            {
+                //This is the new thread that will not own the mutex.
+                //If no threads are waiting for the lock, then it should be null.
+                KThread OwnerThread = CurrThread.NextMutexThread;
+
+                while (OwnerThread != null && OwnerThread.MutexAddress != MutexAddress)
+                {
+                    OwnerThread = OwnerThread.NextMutexThread;
+                }
+
+                CurrThread.NextMutexThread = null;
+
+                if (OwnerThread != null)
+                {
+                    int HasListeners = OwnerThread.NextMutexThread != null ? MutexHasListenersMask : 0;
+
+                    Process.Memory.WriteInt32(MutexAddress, HasListeners | OwnerThread.WaitHandle);
+
+                    OwnerThread.WaitHandle     = 0;
+                    OwnerThread.MutexAddress   = 0;
+                    OwnerThread.CondVarAddress = 0;
+
+                    OwnerThread.MutexOwner = null;
+
+                    OwnerThread.UpdatePriority();
+
+                    Process.Scheduler.WakeUp(OwnerThread);
+
+                    return true;
+                }
+                else
+                {
+                    Process.Memory.WriteInt32(MutexAddress, 0);
+
+                    return false;
+                }
+            }
+        }
+
+        private bool CondVarWait(
+            KThread WaitThread,
+            int     WaitThreadHandle,
+            long    MutexAddress,
+            long    CondVarAddress,
+            ulong   Timeout)
+        {
+            WaitThread.WaitHandle     = WaitThreadHandle;
+            WaitThread.MutexAddress   = MutexAddress;
+            WaitThread.CondVarAddress = CondVarAddress;
+
+            lock (CondVarLock)
+            {
+                KThread CurrThread = Process.ThreadArbiterList;
+
+                if (CurrThread != null)
+                {
+                    bool DoInsert = CurrThread != WaitThread;
+
+                    while (CurrThread.NextCondVarThread != null)
+                    {
+                        if (CurrThread.NextCondVarThread.ActualPriority < WaitThread.ActualPriority)
+                        {
+                            break;
+                        }
+
+                        CurrThread = CurrThread.NextCondVarThread;
+
+                        DoInsert &= CurrThread != WaitThread;
+                    }
+
+                    //Only insert if the node doesn't already exist in the list.
+                    //This prevents circular references.
+                    if (DoInsert)
+                    {
+                        if (WaitThread.NextCondVarThread != null)
+                        {
+                            throw new InvalidOperationException();
+                        }
+
+                        WaitThread.NextCondVarThread = CurrThread.NextCondVarThread;
+                        CurrThread.NextCondVarThread = WaitThread;
+                    }
+                }
+                else
+                {
+                    Process.ThreadArbiterList = WaitThread;
+                }
+            }
+
+            if (Timeout != ulong.MaxValue)
+            {
+                return Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout));
+            }
+            else
+            {
+                return Process.Scheduler.EnterWait(WaitThread);
+            }
+        }
+
+        private void CondVarSignal(long CondVarAddress, int Count)
+        {
+            lock (CondVarLock)
+            {
+                KThread PrevThread = null;
+                KThread CurrThread = Process.ThreadArbiterList;
+
+                while (CurrThread != null && (Count == -1 || Count > 0))
+                {
+                    if (CurrThread.CondVarAddress == CondVarAddress)
+                    {
+                        if (PrevThread != null)
+                        {
+                            PrevThread.NextCondVarThread = CurrThread.NextCondVarThread;
+                        }
+                        else
+                        {
+                            Process.ThreadArbiterList = CurrThread.NextCondVarThread;
+                        }
+
+                        CurrThread.NextCondVarThread = null;
+
+                        AcquireMutexValue(CurrThread.MutexAddress);
+
+                        int MutexValue = Process.Memory.ReadInt32(CurrThread.MutexAddress);
+
+                        MutexValue &= ~MutexHasListenersMask;
+
+                        if (MutexValue == 0)
+                        {
+                            //Give the lock to this thread.
+                            Process.Memory.WriteInt32(CurrThread.MutexAddress, CurrThread.WaitHandle);
+
+                            CurrThread.WaitHandle     = 0;
+                            CurrThread.MutexAddress   = 0;
+                            CurrThread.CondVarAddress = 0;
+
+                            CurrThread.MutexOwner = null;
+
+                            CurrThread.UpdatePriority();
+
+                            Process.Scheduler.WakeUp(CurrThread);
+                        }
+                        else
+                        {
+                            //Wait until the lock is released.
+                            InsertWaitingMutexThread(MutexValue, CurrThread);
+
+                            MutexValue |= MutexHasListenersMask;
+
+                            Process.Memory.WriteInt32(CurrThread.MutexAddress, MutexValue);
+                        }
+
+                        ReleaseMutexValue(CurrThread.MutexAddress);
+
+                        Count--;
+                    }
+
+                    PrevThread = CurrThread;
+                    CurrThread = CurrThread.NextCondVarThread;
+                }
+            }
+        }
+
+        private void InsertWaitingMutexThread(int OwnerThreadHandle, KThread WaitThread)
+        {
+            KThread OwnerThread = Process.HandleTable.GetData<KThread>(OwnerThreadHandle);
+
+            if (OwnerThread == null)
+            {
+                Logging.Warn(LogClass.KernelSvc, $"Invalid thread handle 0x{OwnerThreadHandle:x8}!");
+
+                return;
+            }
+
+            WaitThread.MutexOwner = OwnerThread;
+
+            lock (OwnerThread)
+            {
+                KThread CurrThread = OwnerThread;
+
+                while (CurrThread.NextMutexThread != null)
+                {
+                    if (CurrThread == WaitThread)
+                    {
+                        return;
+                    }
+
+                    if (CurrThread.NextMutexThread.ActualPriority < WaitThread.ActualPriority)
+                    {
+                        break;
+                    }
+
+                    CurrThread = CurrThread.NextMutexThread;
+                }
+
+                if (CurrThread != WaitThread)
+                {
+                    if (WaitThread.NextCondVarThread != null)
+                    {
+                        throw new InvalidOperationException();
+                    }
+
+                    WaitThread.NextMutexThread = CurrThread.NextMutexThread;
+                    CurrThread.NextMutexThread = WaitThread;
+                }
+            }
+
+            OwnerThread.UpdatePriority();
+        }
+
+        private void AcquireMutexValue(long MutexAddress)
+        {
+            while (!Process.Memory.AcquireAddress(MutexAddress))
+            {
+                Thread.Yield();
+            }
+        }
+
+        private void ReleaseMutexValue(long MutexAddress)
+        {
+            Process.Memory.ReleaseAddress(MutexAddress);
+        }
+
+        private bool IsPointingInsideKernel(long Address)
+        {
+            return ((ulong)Address + 0x1000000000) < 0xffffff000;
+        }
+
+        private bool IsWordAddressUnaligned(long Address)
+        {
+            return (Address & 3) != 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/KernelErr.cs b/Ryujinx.Core/OsHle/KernelErr.cs
deleted file mode 100644
index 19983af19..000000000
--- a/Ryujinx.Core/OsHle/KernelErr.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Ryujinx.Core.OsHle
-{
-    static class KernelErr
-    {
-        public const int InvalidMemRange = 110;
-        public const int InvalidHandle   = 114;
-        public const int Timeout         = 117;
-        public const int InvalidInfo     = 120;
-        public const int InvalidIpcReq   = 123;
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/MemoryRegions.cs b/Ryujinx.Core/OsHle/MemoryRegions.cs
index 75b97b1f2..f7ef47f46 100644
--- a/Ryujinx.Core/OsHle/MemoryRegions.cs
+++ b/Ryujinx.Core/OsHle/MemoryRegions.cs
@@ -16,7 +16,7 @@ namespace Ryujinx.Core.OsHle
 
         public const long MainStackAddress = AMemoryMgr.AddrSize - MainStackSize;
 
-        public const long TlsPagesSize = 0x4000;
+        public const long TlsPagesSize = 0x20000;
 
         public const long TlsPagesAddress = MainStackAddress - TlsPagesSize;
 
diff --git a/Ryujinx.Core/OsHle/Mutex.cs b/Ryujinx.Core/OsHle/Mutex.cs
deleted file mode 100644
index c619e1217..000000000
--- a/Ryujinx.Core/OsHle/Mutex.cs
+++ /dev/null
@@ -1,122 +0,0 @@
-using Ryujinx.Core.OsHle.Handles;
-using System.Collections.Concurrent;
-using System.Threading;
-
-namespace Ryujinx.Core.OsHle
-{
-    class Mutex
-    {
-        private const int MutexHasListenersMask = 0x40000000;
-
-        private Process Process;
-
-        private long MutexAddress;
-
-        private bool OwnsMutexValue;
-
-        private object EnterWaitLock;
-
-        private ConcurrentQueue<HThread> WaitingThreads;
-
-        public Mutex(Process Process, long MutexAddress, int OwnerThreadHandle)
-        {
-            this.Process      = Process;
-            this.MutexAddress = MutexAddress;
-
-            //Process.Memory.WriteInt32(MutexAddress, OwnerThreadHandle);
-
-            EnterWaitLock = new object();
-
-            WaitingThreads = new ConcurrentQueue<HThread>();
-        }
-
-        public void WaitForLock(HThread RequestingThread, int RequestingThreadHandle)
-        {
-            AcquireMutexValue();
-
-            lock (EnterWaitLock)
-            {
-                int CurrentThreadHandle = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask;
-
-                if (CurrentThreadHandle == RequestingThreadHandle ||
-                    CurrentThreadHandle == 0)
-                {
-                    return;
-                }
-
-                Process.Memory.WriteInt32(MutexAddress, CurrentThreadHandle | MutexHasListenersMask);
-
-                ReleaseMutexValue();
-
-                WaitingThreads.Enqueue(RequestingThread);
-            }
-
-            Process.Scheduler.WaitForSignal(RequestingThread);
-        }
-
-        public void GiveUpLock(int ThreadHandle)
-        {
-            AcquireMutexValue();
-
-            lock (EnterWaitLock)
-            {
-                int CurrentThread = Process.Memory.ReadInt32(MutexAddress) & ~MutexHasListenersMask;
-
-                if (CurrentThread == ThreadHandle)
-                {
-                    Unlock();
-                }
-            }
-
-            ReleaseMutexValue();
-        }
-
-        public void Unlock()
-        {
-            AcquireMutexValue();
-
-            lock (EnterWaitLock)
-            {
-                int HasListeners = WaitingThreads.Count > 1 ? MutexHasListenersMask : 0;
-
-                Process.Memory.WriteInt32(MutexAddress, HasListeners);
-
-                ReleaseMutexValue();
-
-                HThread[] UnlockedThreads = new HThread[WaitingThreads.Count];
-
-                int Index = 0;
-
-                while (WaitingThreads.TryDequeue(out HThread Thread))
-                {
-                    UnlockedThreads[Index++] = Thread;
-                }
-
-                Process.Scheduler.Signal(UnlockedThreads);
-            }
-        }
-
-        private void AcquireMutexValue()
-        {
-            if (!OwnsMutexValue)
-            {
-                while (!Process.Memory.AcquireAddress(MutexAddress))
-                {
-                    Thread.Yield();
-                }
-
-                OwnsMutexValue = true;
-            }
-        }
-
-        private void ReleaseMutexValue()
-        {
-            if (OwnsMutexValue)
-            {
-                OwnsMutexValue = false;
-
-                Process.Memory.ReleaseAddress(MutexAddress);
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs
index 239b19803..6591ed5aa 100644
--- a/Ryujinx.Core/OsHle/Process.cs
+++ b/Ryujinx.Core/OsHle/Process.cs
@@ -1,21 +1,25 @@
 using ChocolArm64;
 using ChocolArm64.Events;
 using ChocolArm64.Memory;
+using ChocolArm64.State;
 using Ryujinx.Core.Loaders;
 using Ryujinx.Core.Loaders.Executables;
 using Ryujinx.Core.OsHle.Exceptions;
 using Ryujinx.Core.OsHle.Handles;
-using Ryujinx.Core.OsHle.Svc;
+using Ryujinx.Core.OsHle.Kernel;
+using Ryujinx.Core.OsHle.Services.Nv;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
+using System.Text;
 
 namespace Ryujinx.Core.OsHle
 {
     class Process : IDisposable
     {
-        private const int TlsSize       = 0x200;
-        private const int TotalTlsSlots = 32;
+        private const int TlsSize = 0x200;
+
+        private const int TotalTlsSlots = (int)MemoryRegions.TlsPagesSize / TlsSize;
 
         private const int TickFreq = 19_200_000;
 
@@ -31,21 +35,25 @@ namespace Ryujinx.Core.OsHle
 
         public AMemory Memory { get; private set; }
 
-        public ServiceMgr Services { get; private set; }
-
         public KProcessScheduler Scheduler { get; private set; }
 
+        public KThread ThreadArbiterList { get; set; }
+
         public KProcessHandleTable HandleTable { get; private set; }
 
+        public AppletStateMgr AppletState { get; private set; }
+
         private SvcHandler SvcHandler;
 
         private ConcurrentDictionary<int, AThread> TlsSlots;
 
-        private ConcurrentDictionary<long, HThread> ThreadsByTpidr;
+        private ConcurrentDictionary<long, KThread> Threads;
+
+        private KThread MainThread;
 
         private List<Executable> Executables;
 
-        private HThread MainThread;
+        private Dictionary<long, string> SymbolTable;
 
         private long ImageBase;
 
@@ -53,24 +61,23 @@ namespace Ryujinx.Core.OsHle
 
         private bool Disposed;
 
-        public Process(Switch Ns, int ProcessId)
+        public Process(Switch Ns, KProcessScheduler Scheduler, int ProcessId)
         {
             this.Ns        = Ns;
+            this.Scheduler = Scheduler;
             this.ProcessId = ProcessId;
 
             Memory = new AMemory();
 
-            Services = new ServiceMgr();
-
             HandleTable = new KProcessHandleTable();
 
-            Scheduler = new KProcessScheduler();            
+            AppletState = new AppletStateMgr();
 
             SvcHandler = new SvcHandler(Ns, this);
 
             TlsSlots = new ConcurrentDictionary<int, AThread>();
 
-            ThreadsByTpidr = new ConcurrentDictionary<long, HThread>();
+            Threads = new ConcurrentDictionary<long, KThread>();
 
             Executables = new List<Executable>();
 
@@ -89,7 +96,7 @@ namespace Ryujinx.Core.OsHle
                 throw new ObjectDisposedException(nameof(Process));
             }
 
-            Logging.Info($"Image base at 0x{ImageBase:x16}.");
+            Logging.Info(LogClass.Loader, $"Image base at 0x{ImageBase:x16}.");
 
             Executable Executable = new Executable(Program, Memory, ImageBase);
 
@@ -118,21 +125,23 @@ namespace Ryujinx.Core.OsHle
                 return false;
             }
 
+            MakeSymbolTable();
+
             MapRWMemRegion(
                 MemoryRegions.MainStackAddress,
                 MemoryRegions.MainStackSize,
                 MemoryType.Normal);
-            
+
             long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize;
 
-            int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 0, 0);
+            int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 44, 0);
 
             if (Handle == -1)
             {
                 return false;
             }
 
-            MainThread = HandleTable.GetData<HThread>(Handle);
+            MainThread = HandleTable.GetData<KThread>(Handle);
 
             if (NeedsHbAbi)
             {
@@ -184,30 +193,32 @@ namespace Ryujinx.Core.OsHle
                 throw new ObjectDisposedException(nameof(Process));
             }
 
-            AThread Thread = new AThread(GetTranslator(), Memory, EntryPoint);
+            AThread CpuThread = new AThread(GetTranslator(), Memory, EntryPoint);
 
-            HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority);
+            KThread Thread = new KThread(CpuThread, ProcessorId, Priority);
 
-            int Handle = HandleTable.OpenHandle(ThreadHnd);
+            int Handle = HandleTable.OpenHandle(Thread);
 
-            int ThreadId = GetFreeTlsSlot(Thread);
+            int ThreadId = GetFreeTlsSlot(CpuThread);
 
             long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize;
 
-            Thread.ThreadState.Break     += BreakHandler;
-            Thread.ThreadState.SvcCall   += SvcHandler.SvcCall;
-            Thread.ThreadState.Undefined += UndefinedHandler;
-            Thread.ThreadState.ProcessId  = ProcessId;
-            Thread.ThreadState.ThreadId   = ThreadId;
-            Thread.ThreadState.CntfrqEl0  = TickFreq;
-            Thread.ThreadState.Tpidr      = Tpidr;
-            Thread.ThreadState.X0         = (ulong)ArgsPtr;
-            Thread.ThreadState.X1         = (ulong)Handle;
-            Thread.ThreadState.X31        = (ulong)StackTop;
+            CpuThread.ThreadState.ProcessId = ProcessId;
+            CpuThread.ThreadState.ThreadId  = ThreadId;
+            CpuThread.ThreadState.CntfrqEl0 = TickFreq;
+            CpuThread.ThreadState.Tpidr     = Tpidr;
 
-            Thread.WorkFinished += ThreadFinished;
+            CpuThread.ThreadState.X0  = (ulong)ArgsPtr;
+            CpuThread.ThreadState.X1  = (ulong)Handle;
+            CpuThread.ThreadState.X31 = (ulong)StackTop;
 
-            ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, ThreadHnd);
+            CpuThread.ThreadState.Break     += BreakHandler;
+            CpuThread.ThreadState.SvcCall   += SvcHandler.SvcCall;
+            CpuThread.ThreadState.Undefined += UndefinedHandler;
+
+            CpuThread.WorkFinished += ThreadFinished;
+
+            Threads.TryAdd(CpuThread.ThreadState.Tpidr, Thread);
 
             return Handle;
         }
@@ -222,20 +233,23 @@ namespace Ryujinx.Core.OsHle
             throw new UndefinedInstructionException(e.Position, e.RawOpCode);
         }
 
+        private void MakeSymbolTable()
+        {
+            SymbolTable = new Dictionary<long, string>();
+
+            foreach (Executable Exe in Executables)
+            {
+                foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
+                {
+                    SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
+                }
+            }
+        }
+
         private ATranslator GetTranslator()
         {
             if (Translator == null)
             {
-                Dictionary<long, string> SymbolTable = new Dictionary<long, string>();
-
-                foreach (Executable Exe in Executables)
-                {
-                    foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
-                    {
-                        SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
-                    }
-                }
-
                 Translator = new ATranslator(SymbolTable);
 
                 Translator.CpuTrace += CpuTraceHandler;
@@ -244,6 +258,16 @@ namespace Ryujinx.Core.OsHle
             return Translator;
         }
 
+        public void EnableCpuTracing()
+        {
+            Translator.EnableCpuTrace = true;
+        }
+
+        public void DisableCpuTracing()
+        {
+            Translator.EnableCpuTrace = false;
+        }
+
         private void CpuTraceHandler(object sender, ACpuTraceEventArgs e)
         {
             string NsoName = string.Empty;
@@ -253,22 +277,52 @@ namespace Ryujinx.Core.OsHle
                 if (e.Position >= Executables[Index].ImageBase)
                 {
                     NsoName = $"{(e.Position - Executables[Index].ImageBase):x16}";
-                    
+
                     break;
                 }
             }
 
-            Logging.Trace($"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
+            Logging.Trace(LogClass.CPU, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
         }
 
-        public void EnableCpuTracing()
+        public void PrintStackTrace(AThreadState ThreadState)
         {
-            Translator.EnableCpuTrace = true;
+            long[] Positions = ThreadState.GetCallStack();
+
+            StringBuilder Trace = new StringBuilder();
+
+            Trace.AppendLine("Guest stack trace:");
+
+            foreach (long Position in Positions)
+            {
+                if (!SymbolTable.TryGetValue(Position, out string SubName))
+                {
+                    SubName = $"Sub{Position:x16}";
+                }
+
+                Trace.AppendLine(" " + SubName + " (" + GetNsoNameAndAddress(Position) + ")");
+            }
+
+            Logging.Info(LogClass.CPU, Trace.ToString());
         }
 
-        public void DisableCpuTracing()
+        private string GetNsoNameAndAddress(long Position)
         {
-            Translator.EnableCpuTrace = false;
+            string Name = string.Empty;
+
+            for (int Index = Executables.Count - 1; Index >= 0; Index--)
+            {
+                if (Position >= Executables[Index].ImageBase)
+                {
+                    long Offset = Position - Executables[Index].ImageBase;
+
+                    Name = $"{Executables[Index].Name}:{Offset:x8}";
+
+                    break;
+                }
+            }
+
+            return Name;
         }
 
         private int GetFreeTlsSlot(AThread Thread)
@@ -288,9 +342,15 @@ namespace Ryujinx.Core.OsHle
         {
             if (sender is AThread Thread)
             {
-                Logging.Info($"Thread {Thread.ThreadId} exiting...");
+                Logging.Info(LogClass.KernelScheduler, $"Thread {Thread.ThreadId} exiting...");
 
                 TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
+
+                KThread KernelThread = GetThread(Thread.ThreadState.Tpidr);
+
+                Scheduler.RemoveThread(KernelThread);
+
+                KernelThread.WaitEvent.Set();
             }
 
             if (TlsSlots.Count == 0)
@@ -300,7 +360,7 @@ namespace Ryujinx.Core.OsHle
                     Dispose();
                 }
 
-                Logging.Info($"No threads running, now exiting Process {ProcessId}...");
+                Logging.Info(LogClass.KernelScheduler, $"No threads running, now exiting Process {ProcessId}...");
 
                 Ns.Os.ExitProcess(ProcessId);
             }
@@ -311,11 +371,11 @@ namespace Ryujinx.Core.OsHle
             return (int)((Position - MemoryRegions.TlsPagesAddress) / TlsSize);
         }
 
-        public HThread GetThread(long Tpidr)
+        public KThread GetThread(long Tpidr)
         {
-            if (!ThreadsByTpidr.TryGetValue(Tpidr, out HThread Thread))
+            if (!Threads.TryGetValue(Tpidr, out KThread Thread))
             {
-                Logging.Error($"Thread with TPIDR 0x{Tpidr:x16} not found!");
+                Logging.Error(LogClass.KernelScheduler, $"Thread with TPIDR 0x{Tpidr:x16} not found!");
             }
 
             return Thread;
@@ -338,20 +398,34 @@ namespace Ryujinx.Core.OsHle
                 {
                     ShouldDispose = true;
 
-                    Logging.Info($"Process {ProcessId} waiting all threads terminate...");
+                    Logging.Info(LogClass.KernelScheduler, $"Process {ProcessId} waiting all threads terminate...");
 
                     return;
                 }
 
                 Disposed = true;
-                
-                Services.Dispose();
-                HandleTable.Dispose();
-                Scheduler.Dispose();
+
+                foreach (object Obj in HandleTable.Clear())
+                {
+                    if (Obj is KSession Session)
+                    {
+                        Session.Dispose();
+                    }
+                }
+
+                INvDrvServices.Fds.DeleteProcess(this);
+
+                INvDrvServices.NvMaps    .DeleteProcess(this);
+                INvDrvServices.NvMapsById.DeleteProcess(this);
+                INvDrvServices.NvMapsFb  .DeleteProcess(this);
+
+                AppletState.Dispose();
+
                 SvcHandler.Dispose();
+
                 Memory.Dispose();
 
-                Logging.Info($"Process {ProcessId} exiting...");
+                Logging.Info(LogClass.KernelScheduler, $"Process {ProcessId} exiting...");
             }
         }
     }
diff --git a/Ryujinx.Core/OsHle/ServiceCtx.cs b/Ryujinx.Core/OsHle/ServiceCtx.cs
index 60c378d5a..7716507fe 100644
--- a/Ryujinx.Core/OsHle/ServiceCtx.cs
+++ b/Ryujinx.Core/OsHle/ServiceCtx.cs
@@ -10,7 +10,7 @@ namespace Ryujinx.Core.OsHle
         public Switch       Ns           { get; private set; }
         public Process      Process      { get; private set; }
         public AMemory      Memory       { get; private set; }
-        public HSession     Session      { get; private set; }
+        public KSession     Session      { get; private set; }
         public IpcMessage   Request      { get; private set; }
         public IpcMessage   Response     { get; private set; }
         public BinaryReader RequestData  { get; private set; }
@@ -20,7 +20,7 @@ namespace Ryujinx.Core.OsHle
             Switch       Ns,
             Process      Process,
             AMemory      Memory,
-            HSession     Session,
+            KSession     Session,
             IpcMessage   Request,
             IpcMessage   Response,
             BinaryReader RequestData,
diff --git a/Ryujinx.Core/OsHle/ServiceMgr.cs b/Ryujinx.Core/OsHle/ServiceMgr.cs
deleted file mode 100644
index f59647afe..000000000
--- a/Ryujinx.Core/OsHle/ServiceMgr.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using Ryujinx.Core.OsHle.IpcServices;
-using Ryujinx.Core.OsHle.IpcServices.Acc;
-using Ryujinx.Core.OsHle.IpcServices.Am;
-using Ryujinx.Core.OsHle.IpcServices.Apm;
-using Ryujinx.Core.OsHle.IpcServices.Aud;
-using Ryujinx.Core.OsHle.IpcServices.Bsd;
-using Ryujinx.Core.OsHle.IpcServices.Friend;
-using Ryujinx.Core.OsHle.IpcServices.FspSrv;
-using Ryujinx.Core.OsHle.IpcServices.Hid;
-using Ryujinx.Core.OsHle.IpcServices.Lm;
-using Ryujinx.Core.OsHle.IpcServices.Nifm;
-using Ryujinx.Core.OsHle.IpcServices.Ns;
-using Ryujinx.Core.OsHle.IpcServices.NvServices;
-using Ryujinx.Core.OsHle.IpcServices.Pctl;
-using Ryujinx.Core.OsHle.IpcServices.Pl;
-using Ryujinx.Core.OsHle.IpcServices.Set;
-using Ryujinx.Core.OsHle.IpcServices.Sfdnsres;
-using Ryujinx.Core.OsHle.IpcServices.Sm;
-using Ryujinx.Core.OsHle.IpcServices.Ssl;
-using Ryujinx.Core.OsHle.IpcServices.Time;
-using Ryujinx.Core.OsHle.IpcServices.Vi;
-using System;
-using System.Collections.Generic;
-
-namespace Ryujinx.Core.OsHle
-{
-    class ServiceMgr : IDisposable
-    {
-        private Dictionary<string, IIpcService> Services;
-
-        public ServiceMgr()
-        {
-            Services = new Dictionary<string, IIpcService>();
-        }
-
-        public IIpcService GetService(string Name)
-        {
-            lock (Services)
-            {
-                if (!Services.TryGetValue(Name, out IIpcService Service))
-                {
-                    switch (Name)
-                    {
-                        case "acc:u0":   Service = new ServiceAcc();      break;
-                        case "aoc:u":    Service = new ServiceNs();       break;
-                        case "apm":      Service = new ServiceApm();      break;
-                        case "apm:p":    Service = new ServiceApm();      break;
-                        case "appletOE": Service = new ServiceAppletOE(); break;
-                        case "audout:u": Service = new ServiceAudOut();   break;
-                        case "audren:u": Service = new ServiceAudRen();   break;
-                        case "bsd:s":    Service = new ServiceBsd();      break;
-                        case "bsd:u":    Service = new ServiceBsd();      break;
-                        case "friend:a": Service = new ServiceFriend();   break;
-                        case "fsp-srv":  Service = new ServiceFspSrv();   break;
-                        case "hid":      Service = new ServiceHid();      break;
-                        case "lm":       Service = new ServiceLm();       break;
-                        case "nifm:u":   Service = new ServiceNifm();     break;
-                        case "nvdrv":    Service = new ServiceNvDrv();    break;
-                        case "nvdrv:a":  Service = new ServiceNvDrv();    break;
-                        case "pctl:a":   Service = new ServicePctl();     break;
-                        case "pl:u":     Service = new ServicePl();       break;
-                        case "set":      Service = new ServiceSet();      break;
-                        case "set:sys":  Service = new ServiceSetSys();   break;
-                        case "sfdnsres": Service = new ServiceSfdnsres(); break;
-                        case "sm:":      Service = new ServiceSm();       break;
-                        case "ssl":      Service = new ServiceSsl();      break;
-                        case "time:s":   Service = new ServiceTime();     break;
-                        case "time:u":   Service = new ServiceTime();     break;
-                        case "vi:m":     Service = new ServiceVi();       break;
-                        case "vi:s":     Service = new ServiceVi();       break;
-                        case "vi:u":     Service = new ServiceVi();       break;
-                    }
-
-                    if (Service == null)
-                    {
-                        throw new NotImplementedException(Name);
-                    }
-
-                    Services.Add(Name, Service);
-                }
-
-                return Service;
-            }
-        }
-
-        public void Dispose()
-        {
-            Dispose(true);
-        }
-
-        protected virtual void Dispose(bool Disposing)
-        {
-            if (Disposing)
-            {
-                lock (Services)
-                {
-                    foreach (IIpcService Service in Services.Values)
-                    {
-                        if (Service is IDisposable DisposableSrv)
-                        {
-                            DisposableSrv.Dispose();
-                        }
-                    }
-
-                    Services.Clear();
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Acc/ServiceAcc.cs b/Ryujinx.Core/OsHle/Services/Acc/IAccountServiceForApplication.cs
similarity index 62%
rename from Ryujinx.Core/OsHle/Services/Acc/ServiceAcc.cs
rename to Ryujinx.Core/OsHle/Services/Acc/IAccountServiceForApplication.cs
index 8844bb5d1..3ecdf15c5 100644
--- a/Ryujinx.Core/OsHle/Services/Acc/ServiceAcc.cs
+++ b/Ryujinx.Core/OsHle/Services/Acc/IAccountServiceForApplication.cs
@@ -1,20 +1,19 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Acc
+namespace Ryujinx.Core.OsHle.Services.Acc
 {
-    class ServiceAcc : IIpcService
+    class IAccountServiceForApplication : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceAcc()
+        public IAccountServiceForApplication()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
+                {   0, GetUserCount                        },
                 {   3, ListOpenUsers                       },
                 {   5, GetProfile                          },
                 { 100, InitializeApplicationInfo           },
@@ -22,8 +21,19 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
             };
         }
 
+        public long GetUserCount(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(0);
+
+            Logging.Stub(LogClass.ServiceAcc, "Stubbed");
+
+            return 0;
+        }
+
         public long ListOpenUsers(ServiceCtx Context)
         {
+            Logging.Stub(LogClass.ServiceAcc, "Stubbed");
+
             return 0;
         }
 
@@ -36,6 +46,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
 
         public long InitializeApplicationInfo(ServiceCtx Context)
         {
+            Logging.Stub(LogClass.ServiceAcc, "Stubbed");
+
             return 0;
         }
 
@@ -46,4 +58,4 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
             return 0;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/Ryujinx.Core/OsHle/Services/Acc/IManagerForApplication.cs b/Ryujinx.Core/OsHle/Services/Acc/IManagerForApplication.cs
index ab491eac7..cc72a64c0 100644
--- a/Ryujinx.Core/OsHle/Services/Acc/IManagerForApplication.cs
+++ b/Ryujinx.Core/OsHle/Services/Acc/IManagerForApplication.cs
@@ -1,13 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Acc
+namespace Ryujinx.Core.OsHle.Services.Acc
 {
-    class IManagerForApplication : IIpcService
+    class IManagerForApplication : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IManagerForApplication()
         {
@@ -19,12 +19,16 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
         }
 
         public long CheckAvailability(ServiceCtx Context)
-        {           
+        {
+            Logging.Stub(LogClass.ServiceAcc, "Stubbed");
+
             return 0;
         }
 
         public long GetAccountId(ServiceCtx Context)
         {
+            Logging.Stub(LogClass.ServiceAcc, "AccountId = 0xcafeL");
+
             Context.ResponseData.Write(0xcafeL);
 
             return 0;
diff --git a/Ryujinx.Core/OsHle/Services/Acc/IProfile.cs b/Ryujinx.Core/OsHle/Services/Acc/IProfile.cs
index 77fe2b48c..6f316b1c4 100644
--- a/Ryujinx.Core/OsHle/Services/Acc/IProfile.cs
+++ b/Ryujinx.Core/OsHle/Services/Acc/IProfile.cs
@@ -1,13 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Acc
+namespace Ryujinx.Core.OsHle.Services.Acc
 {
-    class IProfile : IIpcService
+    class IProfile : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IProfile()
         {
@@ -19,6 +19,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
 
         public long GetBase(ServiceCtx Context)
         {
+            Logging.Stub(LogClass.ServiceAcc, "Stubbed");
+
             Context.ResponseData.Write(0L);
             Context.ResponseData.Write(0L);
             Context.ResponseData.Write(0L);
diff --git a/Ryujinx.Core/OsHle/Services/Am/AmErr.cs b/Ryujinx.Core/OsHle/Services/Am/AmErr.cs
new file mode 100644
index 000000000..cb41c2d0f
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Am/AmErr.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Core.OsHle.Services.Am
+{
+    static class AmErr
+    {
+        public const int NoMessages = 3;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Am/FocusState.cs b/Ryujinx.Core/OsHle/Services/Am/FocusState.cs
new file mode 100644
index 000000000..2585cf2cc
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Am/FocusState.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Core.OsHle.Services.Am
+{
+    enum FocusState
+    {
+        InFocus    = 1,
+        OutOfFocus = 2
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Am/IAllSystemAppletProxiesService.cs b/Ryujinx.Core/OsHle/Services/Am/IAllSystemAppletProxiesService.cs
new file mode 100644
index 000000000..0d20f94cb
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Am/IAllSystemAppletProxiesService.cs
@@ -0,0 +1,27 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Am
+{
+    class IAllSystemAppletProxiesService : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IAllSystemAppletProxiesService()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                { 100, OpenSystemAppletProxy }
+            };
+        }
+
+        public long OpenSystemAppletProxy(ServiceCtx Context)
+        {
+            MakeObject(Context, new ISystemAppletProxy());
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Am/IApplicationCreator.cs b/Ryujinx.Core/OsHle/Services/Am/IApplicationCreator.cs
new file mode 100644
index 000000000..1114897b3
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Am/IApplicationCreator.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Am
+{
+    class IApplicationCreator : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IApplicationCreator()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                //...
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs b/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs
index c989cdd44..f1c63fac5 100644
--- a/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs
@@ -2,15 +2,13 @@ using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 using System.IO;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Am
+namespace Ryujinx.Core.OsHle.Services.Am
 {
-    class IApplicationFunctions : IIpcService
+    class IApplicationFunctions : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IApplicationFunctions()
         {
@@ -20,6 +18,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
                 { 20, EnsureSaveData     },
                 { 21, GetDesiredLanguage },
                 { 22, SetTerminateResult },
+                { 23, GetDisplayVersion  },
                 { 40, NotifyRunning      }
             };
         }
@@ -39,6 +38,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
             long UIdLow  = Context.RequestData.ReadInt64();
             long UIdHigh = Context.RequestData.ReadInt64();
 
+            Logging.Stub(LogClass.ServiceAm, $"UidLow = {UIdLow}, UidHigh = {UIdHigh}");
+
             Context.ResponseData.Write(0L);
 
             return 0;
@@ -46,6 +47,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
 
         public long GetDesiredLanguage(ServiceCtx Context)
         {
+            Logging.Stub(LogClass.ServiceAm, "LanguageId = 1");
+
             //This is an enumerator where each number is a differnet language.
             //0 is Japanese and 1 is English, need to figure out the other codes.
             Context.ResponseData.Write(1L);
@@ -60,7 +63,16 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
             int Module = ErrorCode & 0xFF;
             int Description = (ErrorCode >> 9) & 0xFFF;
 
-            Logging.Info($"({(ErrorModule)Module}){2000 + Module}-{Description}");
+            Logging.Info(LogClass.ServiceAm, $"({(ErrorModule)Module}){2000 + Module}-{Description}");
+
+            return 0;
+        }
+
+        public long GetDisplayVersion(ServiceCtx Context)
+        {
+            //FIXME: Need to check correct version on a switch.
+            Context.ResponseData.Write(1L);
+            Context.ResponseData.Write(0L);
 
             return 0;
         }
diff --git a/Ryujinx.Core/OsHle/Services/Am/IApplicationProxy.cs b/Ryujinx.Core/OsHle/Services/Am/IApplicationProxy.cs
index 5417d7f04..d9d91600e 100644
--- a/Ryujinx.Core/OsHle/Services/Am/IApplicationProxy.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/IApplicationProxy.cs
@@ -1,15 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Am
+namespace Ryujinx.Core.OsHle.Services.Am
 {
-    class IApplicationProxy : IIpcService
+    class IApplicationProxy : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IApplicationProxy()
         {
diff --git a/Ryujinx.Core/OsHle/Services/Am/ServiceAppletOE.cs b/Ryujinx.Core/OsHle/Services/Am/IApplicationProxyService.cs
similarity index 64%
rename from Ryujinx.Core/OsHle/Services/Am/ServiceAppletOE.cs
rename to Ryujinx.Core/OsHle/Services/Am/IApplicationProxyService.cs
index b60c93dd5..6e33a1de8 100644
--- a/Ryujinx.Core/OsHle/Services/Am/ServiceAppletOE.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/IApplicationProxyService.cs
@@ -1,17 +1,15 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Am
+namespace Ryujinx.Core.OsHle.Services.Am
 {
-    class ServiceAppletOE : IIpcService
+    class IApplicationProxyService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceAppletOE()
+        public IApplicationProxyService()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
diff --git a/Ryujinx.Core/OsHle/Services/Am/IAudioController.cs b/Ryujinx.Core/OsHle/Services/Am/IAudioController.cs
index 1212f1e24..3b2a69513 100644
--- a/Ryujinx.Core/OsHle/Services/Am/IAudioController.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/IAudioController.cs
@@ -1,20 +1,71 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Am
+namespace Ryujinx.Core.OsHle.Services.Am
 {
-    class IAudioController : IIpcService
+    class IAudioController : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IAudioController()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                //...
+                { 0, SetExpectedMasterVolume              },
+                { 1, GetMainAppletExpectedMasterVolume    },
+                { 2, GetLibraryAppletExpectedMasterVolume },
+                { 3, ChangeMainAppletMasterVolume         }, 
+                { 4, SetTransparentVolumeRate             }
             };
         }
+
+        public long SetExpectedMasterVolume(ServiceCtx Context)
+        {
+            float AppletVolume        = Context.RequestData.ReadSingle();
+            float LibraryAppletVolume = Context.RequestData.ReadSingle();
+
+            Logging.Stub(LogClass.ServiceAm, "Stubbed");
+
+            return 0;
+        }
+
+        public long GetMainAppletExpectedMasterVolume(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(1f);
+
+            Logging.Stub(LogClass.ServiceAm, "Stubbed");
+
+            return 0;
+        }
+
+        public long GetLibraryAppletExpectedMasterVolume(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(1f);
+
+            Logging.Stub(LogClass.ServiceAm, "Stubbed");
+
+            return 0;
+        }
+
+        public long ChangeMainAppletMasterVolume(ServiceCtx Context)
+        {
+            float Unknown0 = Context.RequestData.ReadSingle();
+            long  Unknown1 = Context.RequestData.ReadInt64();
+
+            Logging.Stub(LogClass.ServiceAm, "Stubbed");
+
+            return 0;
+        }
+
+        public long SetTransparentVolumeRate(ServiceCtx Context)
+        {
+            float Unknown0 = Context.RequestData.ReadSingle();
+
+            Logging.Stub(LogClass.ServiceAm, "Stubbed");
+
+            return 0;
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/Ryujinx.Core/OsHle/Services/Am/ICommonStateGetter.cs b/Ryujinx.Core/OsHle/Services/Am/ICommonStateGetter.cs
index 2999bbbae..c1b78e834 100644
--- a/Ryujinx.Core/OsHle/Services/Am/ICommonStateGetter.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/ICommonStateGetter.cs
@@ -1,13 +1,16 @@
+using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Am
+using static Ryujinx.Core.OsHle.ErrorCode;
+
+namespace Ryujinx.Core.OsHle.Services.Am
 {
-    class ICommonStateGetter : IIpcService
+    class ICommonStateGetter : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public ICommonStateGetter()
         {
@@ -17,37 +20,32 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
                 { 1, ReceiveMessage       },
                 { 5, GetOperationMode     },
                 { 6, GetPerformanceMode   },
-                { 9, GetCurrentFocusState },
+                { 8, GetBootMode          },
+                { 9, GetCurrentFocusState }
             };
         }
 
-        private enum FocusState
-        {
-            InFocus    = 1,
-            OutOfFocus = 2
-        }
-
-        private enum OperationMode
-        {
-            Handheld = 0,
-            Docked   = 1
-        }
-
         public long GetEventHandle(ServiceCtx Context)
         {
-            Context.ResponseData.Write(0L);
+            KEvent Event = Context.Process.AppletState.MessageEvent;
+
+            int Handle = Context.Process.HandleTable.OpenHandle(Event);
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
 
             return 0;
         }
 
         public long ReceiveMessage(ServiceCtx Context)
         {
-            //Program expects 0xF at 0x17ae70 on puyo sdk,
-            //otherwise runs on a infinite loop until it reads said value.
-            //What it means is still unknown.
-            Context.ResponseData.Write(0xfL);
+            if (!Context.Process.AppletState.TryDequeueMessage(out MessageInfo Message))
+            {
+                return MakeError(ErrorModule.Am, AmErr.NoMessages);
+            }
 
-            return 0; //0x680;
+            Context.ResponseData.Write((int)Message);
+
+            return 0;
         }
 
         public long GetOperationMode(ServiceCtx Context)
@@ -59,14 +57,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
 
         public long GetPerformanceMode(ServiceCtx Context)
         {
-            Context.ResponseData.Write((byte)0);
+            Context.ResponseData.Write((byte)Apm.PerformanceMode.Handheld);
+
+            return 0;
+        }
+
+        public long GetBootMode(ServiceCtx Context)
+        {
+            Context.ResponseData.Write((byte)0); //Unknown value.
+
+            Logging.Stub(LogClass.ServiceAm, "Stubbed");
 
             return 0;
         }
 
         public long GetCurrentFocusState(ServiceCtx Context)
         {
-            Context.ResponseData.Write((byte)FocusState.InFocus);
+            Context.ResponseData.Write((byte)Context.Process.AppletState.FocusState);
 
             return 0;
         }
diff --git a/Ryujinx.Core/OsHle/Services/Am/IDebugFunctions.cs b/Ryujinx.Core/OsHle/Services/Am/IDebugFunctions.cs
index 944e58d81..b7c7e9cf6 100644
--- a/Ryujinx.Core/OsHle/Services/Am/IDebugFunctions.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/IDebugFunctions.cs
@@ -1,13 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Am
+namespace Ryujinx.Core.OsHle.Services.Am
 {
-    class IDebugFunctions : IIpcService
+    class IDebugFunctions : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IDebugFunctions()
         {
diff --git a/Ryujinx.Core/OsHle/Services/Am/IDisplayController.cs b/Ryujinx.Core/OsHle/Services/Am/IDisplayController.cs
index 979e842a6..a56d17130 100644
--- a/Ryujinx.Core/OsHle/Services/Am/IDisplayController.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/IDisplayController.cs
@@ -1,13 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Am
+namespace Ryujinx.Core.OsHle.Services.Am
 {
-    class IDisplayController : IIpcService
+    class IDisplayController : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IDisplayController()
         {
diff --git a/Ryujinx.Core/OsHle/Services/Am/IGlobalStateController.cs b/Ryujinx.Core/OsHle/Services/Am/IGlobalStateController.cs
new file mode 100644
index 000000000..d5810154e
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Am/IGlobalStateController.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Am
+{
+    class IGlobalStateController : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IGlobalStateController()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                //...
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Am/IHomeMenuFunctions.cs b/Ryujinx.Core/OsHle/Services/Am/IHomeMenuFunctions.cs
new file mode 100644
index 000000000..2b81eede5
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Am/IHomeMenuFunctions.cs
@@ -0,0 +1,45 @@
+using Ryujinx.Core.OsHle.Handles;
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Am
+{
+    class IHomeMenuFunctions : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        private KEvent ChannelEvent;
+
+        public IHomeMenuFunctions()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                { 10, RequestToGetForeground        },
+                { 21, GetPopFromGeneralChannelEvent }
+            };
+
+            //ToDo: Signal this Event somewhere in future.
+            ChannelEvent = new KEvent();
+        }
+
+        public long RequestToGetForeground(ServiceCtx Context)
+        {
+            Logging.Stub(LogClass.ServiceAm, "Stubbed");
+
+            return 0;
+        }
+
+        public long GetPopFromGeneralChannelEvent(ServiceCtx Context)
+        {
+            int Handle = Context.Process.HandleTable.OpenHandle(ChannelEvent);
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
+
+            Logging.Stub(LogClass.ServiceAm, "Stubbed");
+
+            return 0;
+        }
+    }
+}
diff --git a/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletCreator.cs b/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletCreator.cs
index 9f5b5e69c..7b3e12cc6 100644
--- a/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletCreator.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletCreator.cs
@@ -1,13 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Am
+namespace Ryujinx.Core.OsHle.Services.Am
 {
-    class ILibraryAppletCreator : IIpcService
+    class ILibraryAppletCreator : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public ILibraryAppletCreator()
         {
diff --git a/Ryujinx.Core/OsHle/Services/Am/ISelfController.cs b/Ryujinx.Core/OsHle/Services/Am/ISelfController.cs
index 403e4072d..e508b5f6a 100644
--- a/Ryujinx.Core/OsHle/Services/Am/ISelfController.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/ISelfController.cs
@@ -1,29 +1,30 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Am
+namespace Ryujinx.Core.OsHle.Services.Am
 {
-    class ISelfController : IIpcService
+    class ISelfController : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public ISelfController()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                { 1,  Exit                                  },
+                { 1,  LockExit                              },
                 { 10, SetScreenShotPermission               },
                 { 11, SetOperationModeChangedNotification   },
                 { 12, SetPerformanceModeChangedNotification },
                 { 13, SetFocusHandlingMode                  },
                 { 14, SetRestartMessageEnabled              },
-                { 16, SetOutOfFocusSuspendingEnabled        }
+                { 16, SetOutOfFocusSuspendingEnabled        },
+                { 50, SetHandlesRequestToDisplay            }
             };
         }
 
-        public long Exit(ServiceCtx Context)
+        public long LockExit(ServiceCtx Context)
         {
             return 0;
         }
@@ -32,6 +33,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
         {
             bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
 
+            Logging.Stub(LogClass.ServiceAm, $"ScreenShot Allowed = {Enable}");
+
             return 0;
         }
 
@@ -39,6 +42,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
         {
             bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
 
+            Logging.Stub(LogClass.ServiceAm, $"OperationMode Changed = {Enable}");
+
             return 0;
         }
 
@@ -46,6 +51,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
         {
             bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
 
+            Logging.Stub(LogClass.ServiceAm, $"PerformanceMode Changed = {Enable}");
+
             return 0;
         }
 
@@ -55,6 +62,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
             bool Flag2 = Context.RequestData.ReadByte() != 0 ? true : false;
             bool Flag3 = Context.RequestData.ReadByte() != 0 ? true : false;
 
+            Logging.Stub(LogClass.ServiceAm, $"Focus Handling Mode Flags = {{{Flag1}|{Flag2}|{Flag3}}}");
+
             return 0;
         }
 
@@ -62,6 +71,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
         {
             bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
 
+            Logging.Stub(LogClass.ServiceAm, $"Restart Message Enabled = {Enable}");
+
             return 0;
         }
 
@@ -69,6 +80,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
         {
             bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
 
+            Logging.Stub(LogClass.ServiceAm, $"Out Of Focus Suspending Enabled = {Enable}");
+
+            return 0;
+        }
+
+        public long SetHandlesRequestToDisplay(ServiceCtx Context)
+        {
+            bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
+
+            Logging.Stub(LogClass.ServiceAm, $"HandlesRequestToDisplay Allowed = {Enable}");
+
             return 0;
         }
     }
diff --git a/Ryujinx.Core/OsHle/Services/Am/IStorage.cs b/Ryujinx.Core/OsHle/Services/Am/IStorage.cs
index 375b960b1..fef4a237e 100644
--- a/Ryujinx.Core/OsHle/Services/Am/IStorage.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/IStorage.cs
@@ -1,15 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Am
+namespace Ryujinx.Core.OsHle.Services.Am
 {
-    class IStorage : IIpcService
+    class IStorage : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public byte[] Data { get; private set; }
 
diff --git a/Ryujinx.Core/OsHle/Services/Am/IStorageAccessor.cs b/Ryujinx.Core/OsHle/Services/Am/IStorageAccessor.cs
index 6d83e6f94..1e17d9ba6 100644
--- a/Ryujinx.Core/OsHle/Services/Am/IStorageAccessor.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/IStorageAccessor.cs
@@ -3,13 +3,13 @@ using Ryujinx.Core.OsHle.Ipc;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Am
+namespace Ryujinx.Core.OsHle.Services.Am
 {
-    class IStorageAccessor : IIpcService
+    class IStorageAccessor : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         private IStorage Storage;
 
diff --git a/Ryujinx.Core/OsHle/Services/Am/ISystemAppletProxy.cs b/Ryujinx.Core/OsHle/Services/Am/ISystemAppletProxy.cs
new file mode 100644
index 000000000..2d477b34c
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Am/ISystemAppletProxy.cs
@@ -0,0 +1,99 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Am
+{
+    class ISystemAppletProxy : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public ISystemAppletProxy()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                {    0, GetCommonStateGetter     },
+                {    1, GetSelfController        },
+                {    2, GetWindowController      },
+                {    3, GetAudioController       },
+                {    4, GetDisplayController     },
+                {   11, GetLibraryAppletCreator  },
+                {   20, GetHomeMenuFunctions     },
+                {   21, GetGlobalStateController },
+                {   22, GetApplicationCreator    },
+                { 1000, GetDebugFunctions        }
+            };
+        }
+
+        public long GetCommonStateGetter(ServiceCtx Context)
+        {
+            MakeObject(Context, new ICommonStateGetter());
+
+            return 0;
+        }
+
+        public long GetSelfController(ServiceCtx Context)
+        {
+            MakeObject(Context, new ISelfController());
+
+            return 0;
+        }
+
+        public long GetWindowController(ServiceCtx Context)
+        {
+            MakeObject(Context, new IWindowController());
+
+            return 0;
+        }
+
+        public long GetAudioController(ServiceCtx Context)
+        {
+            MakeObject(Context, new IAudioController());
+
+            return 0;
+        }
+
+        public long GetDisplayController(ServiceCtx Context)
+        {
+            MakeObject(Context, new IDisplayController());
+
+            return 0;
+        }
+
+        public long GetLibraryAppletCreator(ServiceCtx Context)
+        {
+            MakeObject(Context, new ILibraryAppletCreator());
+
+            return 0;
+        }
+
+        public long GetHomeMenuFunctions(ServiceCtx Context)
+        {
+            MakeObject(Context, new IHomeMenuFunctions());
+
+            return 0;
+        }
+
+        public long GetGlobalStateController(ServiceCtx Context)
+        {
+            MakeObject(Context, new IGlobalStateController());
+
+            return 0;
+        }
+
+        public long GetApplicationCreator(ServiceCtx Context)
+        {
+            MakeObject(Context, new IApplicationCreator());
+
+            return 0;
+        }
+
+        public long GetDebugFunctions(ServiceCtx Context)
+        {
+            MakeObject(Context, new IDebugFunctions());
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Am/IWindowController.cs b/Ryujinx.Core/OsHle/Services/Am/IWindowController.cs
index ddc73bced..b494a64bb 100644
--- a/Ryujinx.Core/OsHle/Services/Am/IWindowController.cs
+++ b/Ryujinx.Core/OsHle/Services/Am/IWindowController.cs
@@ -1,13 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Am
+namespace Ryujinx.Core.OsHle.Services.Am
 {
-    class IWindowController : IIpcService
+    class IWindowController : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IWindowController()
         {
@@ -20,6 +20,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
 
         public long GetAppletResourceUserId(ServiceCtx Context)
         {
+            Logging.Stub(LogClass.ServiceAm, $"Applet Resource Id = 0");
+
             Context.ResponseData.Write(0L);
 
             return 0;
@@ -27,6 +29,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
 
         public long AcquireForegroundRights(ServiceCtx Context)
         {
+            Logging.Stub(LogClass.ServiceAm, "Stubbed");
+
             return 0;
         }
     }
diff --git a/Ryujinx.Core/OsHle/Services/Am/MessageInfo.cs b/Ryujinx.Core/OsHle/Services/Am/MessageInfo.cs
new file mode 100644
index 000000000..45bf4326f
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Am/MessageInfo.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Core.OsHle.Services.Am
+{
+    enum MessageInfo
+    {
+        FocusStateChanged      = 0xf,
+        OperationModeChanged   = 0x1e,
+        PerformanceModeChanged = 0x1f
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Am/OperationMode.cs b/Ryujinx.Core/OsHle/Services/Am/OperationMode.cs
new file mode 100644
index 000000000..f82a825b4
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Am/OperationMode.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Core.OsHle.Services.Am
+{
+    enum OperationMode
+    {
+        Handheld = 0,
+        Docked   = 1
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Apm/ServiceApm.cs b/Ryujinx.Core/OsHle/Services/Apm/IManager.cs
similarity index 64%
rename from Ryujinx.Core/OsHle/Services/Apm/ServiceApm.cs
rename to Ryujinx.Core/OsHle/Services/Apm/IManager.cs
index d6c0400ac..7320328e5 100644
--- a/Ryujinx.Core/OsHle/Services/Apm/ServiceApm.cs
+++ b/Ryujinx.Core/OsHle/Services/Apm/IManager.cs
@@ -1,17 +1,15 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Apm
+namespace Ryujinx.Core.OsHle.Services.Apm
 {
-    class ServiceApm : IIpcService
+    class IManager : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceApm()
+        public IManager()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
diff --git a/Ryujinx.Core/OsHle/Services/Apm/ISession.cs b/Ryujinx.Core/OsHle/Services/Apm/ISession.cs
index 500f7596c..bbef100ce 100644
--- a/Ryujinx.Core/OsHle/Services/Apm/ISession.cs
+++ b/Ryujinx.Core/OsHle/Services/Apm/ISession.cs
@@ -1,26 +1,39 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Apm
+namespace Ryujinx.Core.OsHle.Services.Apm
 {
-    class ISession : IIpcService
+    class ISession : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public ISession()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                { 0, SetPerformanceConfiguration }
+                { 0, SetPerformanceConfiguration },
+                { 1, GetPerformanceConfiguration }
             };
         }
 
         public long SetPerformanceConfiguration(ServiceCtx Context)
         {
-            int PerfMode   = Context.RequestData.ReadInt32();
-            int PerfConfig = Context.RequestData.ReadInt32();
+            PerformanceMode          PerfMode   = (PerformanceMode)Context.RequestData.ReadInt32();
+            PerformanceConfiguration PerfConfig = (PerformanceConfiguration)Context.RequestData.ReadInt32();
+
+            return 0;
+        }
+
+        public long GetPerformanceConfiguration(ServiceCtx Context)
+        {
+            PerformanceMode PerfMode = (PerformanceMode)Context.RequestData.ReadInt32();
+
+            Context.ResponseData.Write((uint)PerformanceConfiguration.PerformanceConfiguration1);
+
+            Logging.Stub(LogClass.ServiceApm, $"PerformanceMode = {PerfMode}, PerformanceConfiguration =" +
+                $" {PerformanceConfiguration.PerformanceConfiguration1}");
 
             return 0;
         }
diff --git a/Ryujinx.Core/OsHle/Services/Apm/PerformanceConfiguration.cs b/Ryujinx.Core/OsHle/Services/Apm/PerformanceConfiguration.cs
new file mode 100644
index 000000000..5a4d072e2
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Apm/PerformanceConfiguration.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.Core.OsHle.Services.Apm
+{
+    enum PerformanceConfiguration : uint
+    {
+        PerformanceConfiguration1  = 0x00010000,
+        PerformanceConfiguration2  = 0x00010001,
+        PerformanceConfiguration3  = 0x00010002,
+        PerformanceConfiguration4  = 0x00020000,
+        PerformanceConfiguration5  = 0x00020001,
+        PerformanceConfiguration6  = 0x00020002,
+        PerformanceConfiguration7  = 0x00020003,
+        PerformanceConfiguration8  = 0x00020004,
+        PerformanceConfiguration9  = 0x00020005,
+        PerformanceConfiguration10 = 0x00020006,
+        PerformanceConfiguration11 = 0x92220007,
+        PerformanceConfiguration12 = 0x92220008
+    }
+}
diff --git a/Ryujinx.Core/OsHle/Services/Apm/PerformanceMode.cs b/Ryujinx.Core/OsHle/Services/Apm/PerformanceMode.cs
new file mode 100644
index 000000000..db6ef4072
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Apm/PerformanceMode.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Core.OsHle.Services.Apm
+{
+    enum PerformanceMode
+    {
+        Handheld = 0,
+        Docked   = 1
+    }
+}
diff --git a/Ryujinx.Core/OsHle/Services/Aud/AudioOutData.cs b/Ryujinx.Core/OsHle/Services/Aud/AudioOutData.cs
new file mode 100644
index 000000000..9ba935414
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Aud/AudioOutData.cs
@@ -0,0 +1,14 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Core.OsHle.Services.Aud
+{
+    [StructLayout(LayoutKind.Sequential)]
+    struct AudioOutData
+    {
+        public long NextBufferPtr;
+        public long SampleBufferPtr;
+        public long SampleBufferCapacity;
+        public long SampleBufferSize;
+        public long SampleBufferInnerOffset;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs
index 9ebf140a2..039a4413f 100644
--- a/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs
+++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs
@@ -1,23 +1,34 @@
 using ChocolArm64.Memory;
+using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 using System.Text;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Aud
+namespace Ryujinx.Core.OsHle.Services.Aud
 {
-    class IAudioDevice : IIpcService
+    class IAudioDevice : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        private KEvent SystemEvent;
 
         public IAudioDevice()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                { 0, ListAudioDeviceName        },
-                { 1, SetAudioDeviceOutputVolume },
+                { 0, ListAudioDeviceName         },
+                { 1, SetAudioDeviceOutputVolume  },
+                { 3, GetActiveAudioDeviceName    },
+                { 4, QueryAudioDeviceSystemEvent },
+                { 5, GetActiveChannelCount       }
             };
+
+            SystemEvent = new KEvent();
+
+            //TODO: We shouldn't be signaling this here.
+            SystemEvent.WaitEvent.Set();
         }
 
         public long ListAudioDeviceName(ServiceCtx Context)
@@ -55,9 +66,45 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
             long Position = Context.Request.SendBuff[0].Position;
             long Size     = Context.Request.SendBuff[0].Size;
 
-            string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position, (int)Size);
+            string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position, Size);
+
+            Logging.Stub(LogClass.ServiceAudio, $"Volume = {Volume}, Position = {Position}, Size = {Size}");
+
+            return 0;
+        }
+
+        public long GetActiveAudioDeviceName(ServiceCtx Context)
+        {
+            string Name = "FIXME";
+
+            long Position = Context.Request.ReceiveBuff[0].Position;
+            long Size     = Context.Request.ReceiveBuff[0].Size;
+
+            byte[] Buffer = Encoding.ASCII.GetBytes(Name + '\0');
+
+            AMemoryHelper.WriteBytes(Context.Memory, Position, Buffer);
+
+            return 0;
+        }
+
+        public long QueryAudioDeviceSystemEvent(ServiceCtx Context)
+        {
+            int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent);
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
+
+            Logging.Stub(LogClass.ServiceAudio, "Stubbed");
+
+            return 0;
+        }
+
+        public long GetActiveChannelCount(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(2);
+
+            Logging.Stub(LogClass.ServiceAudio, "Stubbed");
 
             return 0;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs
index 2312920f6..3f7a18c4a 100644
--- a/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs
+++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs
@@ -1,145 +1,86 @@
 using ChocolArm64.Memory;
+using Ryujinx.Audio;
 using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Ipc;
-using OpenTK.Audio;
-using OpenTK.Audio.OpenAL;
 using System;
 using System.Collections.Generic;
-using System.IO;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Aud
+namespace Ryujinx.Core.OsHle.Services.Aud
 {
-    class IAudioOut : IIpcService, IDisposable
+    class IAudioOut : IpcService, IDisposable
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public IAudioOut()
+        private IAalOutput AudioOut;
+
+        private KEvent ReleaseEvent;
+
+        private int Track;
+
+        public IAudioOut(IAalOutput AudioOut, KEvent ReleaseEvent, int Track)
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                { 0, GetAudioOutState             },
-                { 1, StartAudioOut                },
-                { 2, StopAudioOut                 },
-                { 3, AppendAudioOutBuffer         },
-                { 4, RegisterBufferEvent          },
-                { 5, GetReleasedAudioOutBuffer    },
-                { 6, ContainsAudioOutBuffer       },
-                { 7, AppendAudioOutBuffer_ex      },
-                { 8, GetReleasedAudioOutBuffer_ex }
+                { 0, GetAudioOutState            },
+                { 1, StartAudioOut               },
+                { 2, StopAudioOut                },
+                { 3, AppendAudioOutBuffer        },
+                { 4, RegisterBufferEvent         },
+                { 5, GetReleasedAudioOutBuffer   },
+                { 6, ContainsAudioOutBuffer      },
+                { 7, AppendAudioOutBufferEx      },
+                { 8, GetReleasedAudioOutBufferEx }
             };
+
+            this.AudioOut     = AudioOut;
+            this.ReleaseEvent = ReleaseEvent;
+            this.Track        = Track;
         }
 
-        enum AudioOutState
-        {
-            Started,
-            Stopped
-        };
-
-        //IAudioOut
-        private AudioOutState State = AudioOutState.Stopped;
-        private Queue<long> BufferIdQueue = new Queue<long>();
-
-        //OpenAL
-        private bool OpenALInstalled = true;
-        private AudioContext AudioCtx;
-        private int Source;
-        private int Buffer;
-
-        //Return State of IAudioOut
         public long GetAudioOutState(ServiceCtx Context)
         {
-            Context.ResponseData.Write((int)State);
+            Context.ResponseData.Write((int)AudioOut.GetState(Track));
 
             return 0;
         }
 
         public long StartAudioOut(ServiceCtx Context)
         {
-            if (State == AudioOutState.Stopped)
-            {
-                State = AudioOutState.Started;
-
-                try
-                { 
-                    AudioCtx = new AudioContext(); //Create the audio context
-                }
-                catch (Exception)
-                {
-                    Logging.Warn("OpenAL Error! PS: Install OpenAL Core SDK!");
-                    OpenALInstalled = false;
-                }
-
-                if (OpenALInstalled) AL.Listener(ALListenerf.Gain, 8.0f); //Add more gain to it
-            }
+            AudioOut.Start(Track);
 
             return 0;
         }
 
         public long StopAudioOut(ServiceCtx Context)
         {
-            if (State == AudioOutState.Started)
-            {
-                if (OpenALInstalled)
-                { 
-                    if (AudioCtx == null) //Needed to call the instance of AudioContext()
-                        return 0;
-
-                    AL.SourceStop(Source);
-                    AL.DeleteSource(Source);
-                    AL.DeleteBuffers(1, ref Buffer);
-                }
-                State = AudioOutState.Stopped;
-            }
+            AudioOut.Stop(Track);
 
             return 0;
         }
 
         public long AppendAudioOutBuffer(ServiceCtx Context)
         {
-            long BufferId = Context.RequestData.ReadInt64();
+            long Tag = Context.RequestData.ReadInt64();
 
-            byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, sizeof(long) * 5);
+            AudioOutData Data = AMemoryHelper.Read<AudioOutData>(
+                Context.Memory,
+                Context.Request.SendBuff[0].Position);
+            
+            byte[] Buffer = AMemoryHelper.ReadBytes(
+                Context.Memory,
+                Data.SampleBufferPtr,
+                Data.SampleBufferSize);
 
-            using (MemoryStream MS = new MemoryStream(AudioOutBuffer))
-            {
-                BinaryReader Reader = new BinaryReader(MS);
-                long PointerNextBuffer        = Reader.ReadInt64();
-                long PointerSampleBuffer      = Reader.ReadInt64();
-                long CapacitySampleBuffer     = Reader.ReadInt64();
-                long SizeDataInSampleBuffer   = Reader.ReadInt64();
-                long OffsetDataInSampleBuffer = Reader.ReadInt64();
-
-                if (SizeDataInSampleBuffer > 0)
-                {
-                    BufferIdQueue.Enqueue(BufferId);
-
-                    byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerSampleBuffer + OffsetDataInSampleBuffer, (int)SizeDataInSampleBuffer);
-
-                    if (OpenALInstalled)
-                    {
-                        if (AudioCtx == null) //Needed to call the instance of AudioContext()
-                            return 0;
-                        
-                        EnsureAudioFinalized();
-
-                        Source = AL.GenSource();
-                        Buffer = AL.GenBuffer();
-
-                        AL.BufferData(Buffer, ALFormat.Stereo16, AudioSampleBuffer, AudioSampleBuffer.Length, 48000);
-                        AL.SourceQueueBuffer(Source, Buffer);
-                        AL.SourcePlay(Source);
-                    }
-                }
-            }
+            AudioOut.AppendBuffer(Track, Tag, Buffer);
 
             return 0;
         }
 
         public long RegisterBufferEvent(ServiceCtx Context)
         {
-            int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
+            int Handle = Context.Process.HandleTable.OpenHandle(ReleaseEvent);
 
             Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
 
@@ -148,62 +89,65 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
 
         public long GetReleasedAudioOutBuffer(ServiceCtx Context)
         {
-            int ReleasedBuffersCount = 0;
+            long Position = Context.Request.ReceiveBuff[0].Position;
+            long Size     = Context.Request.ReceiveBuff[0].Size;
 
-            for(int i = 0; i < BufferIdQueue.Count; i++)
+            uint Count = (uint)((ulong)Size >> 3);
+
+            long[] ReleasedBuffers = AudioOut.GetReleasedBuffers(Track, (int)Count);
+
+            for (uint Index = 0; Index < Count; Index++)
             {
-                long BufferId = BufferIdQueue.Dequeue();
+                long Tag = 0;
 
-                AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position + (8 * i), BitConverter.GetBytes(BufferId));
+                if (Index < ReleasedBuffers.Length)
+                {
+                    Tag = ReleasedBuffers[Index];
+                }
 
-                ReleasedBuffersCount++;
+                Context.Memory.WriteInt64(Position + Index * 8, Tag);
             }
 
-            Context.ResponseData.Write(ReleasedBuffersCount);
+            Context.ResponseData.Write(ReleasedBuffers.Length);
 
             return 0;
         }
 
         public long ContainsAudioOutBuffer(ServiceCtx Context)
         {
+            long Tag = Context.RequestData.ReadInt64();
+
+            Context.ResponseData.Write(AudioOut.ContainsBuffer(Track, Tag) ? 1 : 0);
+
             return 0;
         }
 
-        public long AppendAudioOutBuffer_ex(ServiceCtx Context)
+        public long AppendAudioOutBufferEx(ServiceCtx Context)
         {
+            Logging.Stub(LogClass.ServiceAudio, "Stubbed");
+
             return 0;
         }
 
-        public long GetReleasedAudioOutBuffer_ex(ServiceCtx Context)
+        public long GetReleasedAudioOutBufferEx(ServiceCtx Context)
         {
+            Logging.Stub(LogClass.ServiceAudio, "Stubbed");
+
             return 0;
         }
 
-        private void EnsureAudioFinalized()
-        {
-            if (Source != 0 ||
-                Buffer != 0)
-            {
-                AL.SourceStop(Source);
-                AL.SourceUnqueueBuffer(Buffer);
-                AL.DeleteSource(Source);
-                AL.DeleteBuffers(1, ref Buffer);
-
-                Source = 0;
-                Buffer = 0;
-            }
-        }
-
         public void Dispose()
         {
             Dispose(true);
         }
 
-        protected virtual void Dispose(bool disposing)
+        protected virtual void Dispose(bool Disposing)
         {
-            if (disposing)
+            if (Disposing)
             {
-                EnsureAudioFinalized();
+                AudioOut.CloseTrack(Track);
+
+                ReleaseEvent.Dispose();
             }
         }
     }
diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioOutManager.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioOutManager.cs
new file mode 100644
index 000000000..b1d20fbe9
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioOutManager.cs
@@ -0,0 +1,94 @@
+using ChocolArm64.Memory;
+using Ryujinx.Audio;
+using Ryujinx.Core.OsHle.Handles;
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Ryujinx.Core.OsHle.Services.Aud
+{
+    class IAudioOutManager : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IAudioOutManager()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                { 0, ListAudioOuts },
+                { 1, OpenAudioOut  }
+            };
+        }
+
+        public long ListAudioOuts(ServiceCtx Context)
+        {
+            long Position = Context.Request.ReceiveBuff[0].Position;
+
+            AMemoryHelper.WriteBytes(Context.Memory, Position, Encoding.ASCII.GetBytes("iface"));
+
+            Context.ResponseData.Write(1);
+
+            return 0;
+        }
+
+        public long OpenAudioOut(ServiceCtx Context)
+        {
+            IAalOutput AudioOut = Context.Ns.AudioOut;
+
+            string DeviceName = AMemoryHelper.ReadAsciiString(
+                Context.Memory,
+                Context.Request.SendBuff[0].Position,
+                Context.Request.SendBuff[0].Size);
+
+            if (DeviceName == string.Empty)
+            {
+                DeviceName = "FIXME";
+            }
+
+            long DeviceNamePosition = Context.Request.ReceiveBuff[0].Position;
+            long DeviceNameSize     = Context.Request.ReceiveBuff[0].Size;
+
+            byte[] DeviceNameBuffer = Encoding.ASCII.GetBytes(DeviceName);
+
+            if (DeviceName.Length <= DeviceNameSize)
+            {
+                AMemoryHelper.WriteBytes(Context.Memory, DeviceNamePosition, DeviceNameBuffer);
+            }
+
+            int SampleRate = Context.RequestData.ReadInt32();
+            int Channels   = Context.RequestData.ReadInt32();
+
+            Channels = (ushort)(Channels >> 16);
+
+            if (SampleRate == 0)
+            {
+                SampleRate = 48000;
+            }
+
+            if (Channels < 1 || Channels > 2)
+            {
+                Channels = 2;
+            }
+
+            KEvent ReleaseEvent = new KEvent();
+
+            ReleaseCallback Callback = () =>
+            {
+                ReleaseEvent.WaitEvent.Set();
+            };
+
+            int Track = AudioOut.OpenTrack(SampleRate, Channels, Callback, out AudioFormat Format);
+
+            MakeObject(Context, new IAudioOut(AudioOut, ReleaseEvent, Track));
+
+            Context.ResponseData.Write(SampleRate);
+            Context.ResponseData.Write(Channels);
+            Context.ResponseData.Write((int)Format);
+            Context.ResponseData.Write((int)PlaybackState.Stopped);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs
index 4d29371fb..d3795b53c 100644
--- a/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs
+++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs
@@ -1,14 +1,17 @@
 using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Ipc;
+using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Aud
+namespace Ryujinx.Core.OsHle.Services.Aud
 {
-    class IAudioRenderer : IIpcService
+    class IAudioRenderer : IpcService, IDisposable
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        private KEvent UpdateEvent;
 
         public IAudioRenderer()
         {
@@ -19,6 +22,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
                 { 6, StopAudioRenderer          },
                 { 7, QuerySystemEvent           }
             };
+
+            UpdateEvent = new KEvent();
         }
 
         public long RequestUpdateAudioRenderer(ServiceCtx Context)
@@ -41,26 +46,46 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
                 Context.Memory.WriteInt32(Position + Offset, 5);
             }
 
+            //TODO: We shouldn't be signaling this here.
+            UpdateEvent.WaitEvent.Set();
+
             return 0;
         }
 
         public long StartAudioRenderer(ServiceCtx Context)
         {
+            Logging.Stub(LogClass.ServiceAudio, "Stubbed");
+
             return 0;
         }
 
         public long StopAudioRenderer(ServiceCtx Context)
         {
+            Logging.Stub(LogClass.ServiceAudio, "Stubbed");
+
             return 0;
         }
 
         public long QuerySystemEvent(ServiceCtx Context)
         {
-            int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
+            int Handle = Context.Process.HandleTable.OpenHandle(UpdateEvent);
 
             Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
 
             return 0;
         }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        protected virtual void Dispose(bool Disposing)
+        {
+            if (Disposing)
+            {
+                UpdateEvent.Dispose();
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioRendererManager.cs
similarity index 85%
rename from Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs
rename to Ryujinx.Core/OsHle/Services/Aud/IAudioRendererManager.cs
index c3a0a8b43..dcf3c7b7c 100644
--- a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs
+++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioRendererManager.cs
@@ -1,17 +1,15 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Aud
+namespace Ryujinx.Core.OsHle.Services.Aud
 {
-    class ServiceAudRen : IIpcService
+    class IAudioRendererManager : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceAudRen()
+        public IAudioRendererManager()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
@@ -44,6 +42,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
             int Unknown2c  = Context.RequestData.ReadInt32();
             int Rev1Magic  = Context.RequestData.ReadInt32();
 
+            Logging.Stub(LogClass.ServiceAudio, "BufferSize = 0x400L");
+
             Context.ResponseData.Write(0x400L);
 
             return 0;
diff --git a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudOut.cs b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudOut.cs
deleted file mode 100644
index eb923562b..000000000
--- a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudOut.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using ChocolArm64.Memory;
-using Ryujinx.Core.OsHle.Ipc;
-using System.Collections.Generic;
-using System.Text;
-
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Aud
-{
-    class ServiceAudOut : IIpcService
-    {
-        private Dictionary<int, ServiceProcessRequest> m_Commands;
-
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
-
-        public ServiceAudOut()
-        {
-            m_Commands = new Dictionary<int, ServiceProcessRequest>()
-            {
-                { 0, ListAudioOuts },
-                { 1, OpenAudioOut  },
-            };
-        }
-
-        public long ListAudioOuts(ServiceCtx Context)
-        {
-            long Position = Context.Request.ReceiveBuff[0].Position;
-
-            AMemoryHelper.WriteBytes(Context.Memory, Position, Encoding.ASCII.GetBytes("iface"));
-
-            Context.ResponseData.Write(1);
-
-            return 0;
-        }
-
-        public long OpenAudioOut(ServiceCtx Context)
-        {
-            MakeObject(Context, new IAudioOut());
-
-            Context.ResponseData.Write(48000); //Sample Rate
-            Context.ResponseData.Write(2); //Channel Count
-            Context.ResponseData.Write(2); //PCM Format
-            /*  
-                0 - Invalid
-                1 - INT8
-                2 - INT16
-                3 - INT24
-                4 - INT32
-                5 - PCM Float
-                6 - ADPCM
-            */
-            Context.ResponseData.Write(0); //Unknown
-
-            return 0;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Bsd/BsdError.cs b/Ryujinx.Core/OsHle/Services/Bsd/BsdError.cs
new file mode 100644
index 000000000..a1ac0433b
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Bsd/BsdError.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Core.OsHle.Services.Bsd
+{
+    //bsd_errno == (SocketException.ErrorCode - 10000)
+    public enum BsdError
+    {
+        Timeout = 60
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Bsd/BsdSocket.cs b/Ryujinx.Core/OsHle/Services/Bsd/BsdSocket.cs
new file mode 100644
index 000000000..592b07d95
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Bsd/BsdSocket.cs
@@ -0,0 +1,18 @@
+using System.Net;
+using System.Net.Sockets;
+
+namespace Ryujinx.Core.OsHle.Services.Bsd
+{
+    class BsdSocket
+    {
+        public int Family;
+        public int Type;
+        public int Protocol;
+
+        public IPAddress IpAddress;
+
+        public IPEndPoint RemoteEP;
+
+        public Socket Handle;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs b/Ryujinx.Core/OsHle/Services/Bsd/IClient.cs
similarity index 66%
rename from Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs
rename to Ryujinx.Core/OsHle/Services/Bsd/IClient.cs
index da1e51e17..ccfb9147c 100644
--- a/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs
+++ b/Ryujinx.Core/OsHle/Services/Bsd/IClient.cs
@@ -1,65 +1,23 @@
 using ChocolArm64.Memory;
 using Ryujinx.Core.OsHle.Ipc;
 using Ryujinx.Core.OsHle.Utilities;
-using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Net;
 using System.Net.Sockets;
 using System.Threading.Tasks;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Bsd
+namespace Ryujinx.Core.OsHle.Services.Bsd
 {
-
-    //bsd_errno == (SocketException.ErrorCode - 10000)
-    //https://github.com/freebsd/freebsd/blob/master/sys/sys/errno.h
-    public enum BsdError
-    {
-        ENOTSOCK        = 38, /* Socket operation on non-socket */
-        EDESTADDRREQ    = 39, /* Destination address required */
-        EMSGSIZE        = 40, /* Message too long */
-        EPROTOTYPE      = 41, /* Protocol wrong type for socket */
-        ENOPROTOOPT     = 42, /* Protocol not available */
-        EPROTONOSUPPORT = 43, /* Protocol not supported */
-        ESOCKTNOSUPPORT = 44, /* Socket type not supported */
-        EOPNOTSUPP      = 45, /* Operation not supported */
-        EPFNOSUPPORT    = 46, /* Protocol family not supported */
-        EAFNOSUPPORT    = 47, /* Address family not supported by protocol family */
-        EADDRINUSE      = 48, /* Address already in use */
-        EADDRNOTAVAIL   = 49, /* Can't assign requested address */
-        ENETDOWN        = 50, /* Network is down */
-        ENETUNREACH     = 51, /* Network is unreachable */
-        ENETRESET       = 52, /* Network dropped connection on reset */
-        ECONNABORTED    = 53, /* Software caused connection abort */
-        ECONNRESET      = 54, /* Connection reset by peer */
-        ENOBUFS         = 55, /* No buffer space available */
-        EISCONN         = 56, /* Socket is already connected */
-        ENOTCONN        = 57, /* Socket is not connected */
-        ESHUTDOWN       = 58, /* Can't send after socket shutdown */
-        ETOOMANYREFS    = 59, /* Too many references: can't splice */
-        ETIMEDOUT       = 60, /* Operation timed out */
-        ECONNREFUSED    = 61  /* Connection refused */
-    }
-
-    class SocketBsd
-    {
-        public int Family;
-        public int Type;
-        public int Protocol;
-        public IPAddress IpAddress;
-        public IPEndPoint RemoteEP;
-        public Socket Handle;
-    }
-
-    class ServiceBsd : IIpcService
+    class IClient : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        private List<SocketBsd> Sockets = new List<SocketBsd>();
+        private List<BsdSocket> Sockets = new List<BsdSocket>();
 
-        public ServiceBsd()
+        public IClient()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
@@ -95,11 +53,6 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
             } BsdBufferConfig;
             */
 
-            long Pid = Context.RequestData.ReadInt64();
-            long TransferMemorySize = Context.RequestData.ReadInt64();
-
-            // Two other args are unknown!
-
             Context.ResponseData.Write(0);
 
             //Todo: Stub
@@ -118,18 +71,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
         //(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno)
         public long Socket(ServiceCtx Context)
         {
-            SocketBsd NewBSDSocket = new SocketBsd
+            BsdSocket NewBsdSocket = new BsdSocket
             {
                 Family   = Context.RequestData.ReadInt32(),
                 Type     = Context.RequestData.ReadInt32(),
                 Protocol = Context.RequestData.ReadInt32()
             };
 
-            Sockets.Add(NewBSDSocket);
+            Sockets.Add(NewBsdSocket);
 
-            Sockets[Sockets.Count - 1].Handle = new Socket((AddressFamily)Sockets[Sockets.Count - 1].Family,
-                                                           (SocketType)Sockets[Sockets.Count - 1].Type,
-                                                           (ProtocolType)Sockets[Sockets.Count - 1].Protocol);
+            NewBsdSocket.Handle = new Socket((AddressFamily)NewBsdSocket.Family,
+                                                (SocketType)NewBsdSocket.Type,
+                                              (ProtocolType)NewBsdSocket.Protocol);
 
             Context.ResponseData.Write(Sockets.Count - 1);
             Context.ResponseData.Write(0);
@@ -149,12 +102,13 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
             //https://github.com/TuxSH/ftpd/blob/switch_pr/source/ftp.c#L1634
             //https://linux.die.net/man/2/poll
 
-            byte[] SentBuffer     = AMemoryHelper.ReadBytes(Context.Memory, 
-                                                            Context.Request.SendBuff[0].Position, 
-                                                            (int)Context.Request.SendBuff[0].Size);
-            int SocketId          = Get32(SentBuffer, 0);
-            short RequestedEvents = (short)Get16(SentBuffer, 4);
-            short ReturnedEvents  = (short)Get16(SentBuffer, 6);
+            byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
+                                                        Context.Request.SendBuff[0].Position,
+                                                        Context.Request.SendBuff[0].Size);
+
+            int SocketId        = Get32(SentBuffer, 0);
+            int RequestedEvents = Get16(SentBuffer, 4);
+            int ReturnedEvents  = Get16(SentBuffer, 6);
 
             //Todo: Stub - Need to implemented the Type-22 buffer.
 
@@ -167,18 +121,20 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
         //(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, buffer<i8, 0x22, 0> message)
         public long Recv(ServiceCtx Context)
         {
+            int SocketId    = Context.RequestData.ReadInt32();
+            int SocketFlags = Context.RequestData.ReadInt32();
+
+            byte[] ReceivedBuffer = new byte[Context.Request.ReceiveBuff[0].Size];
+
             try
             {
-                int SocketId          = Context.RequestData.ReadInt32();
-                int SocketFlags       = Context.RequestData.ReadInt32();
-                byte[] ReceivedBuffer = new byte[Context.Request.ReceiveBuff[0].Size];
-                int ReadedBytes       = Sockets[SocketId].Handle.Receive(ReceivedBuffer);
+                int BytesRead = Sockets[SocketId].Handle.Receive(ReceivedBuffer);
 
                 //Logging.Debug("Received Buffer:" + Environment.NewLine + Logging.HexDump(ReceivedBuffer));
 
                 AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, ReceivedBuffer);
 
-                Context.ResponseData.Write(ReadedBytes);
+                Context.ResponseData.Write(BytesRead);
                 Context.ResponseData.Write(0);
             }
             catch (SocketException Ex)
@@ -193,15 +149,16 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
         //(u32 socket, u32 flags, buffer<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
         public long Send(ServiceCtx Context)
         {
-            int SocketId      = Context.RequestData.ReadInt32();
-            int SocketFlags   = Context.RequestData.ReadInt32();
-            byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory, 
-                                                        Context.Request.SendBuff[0].Position, 
-                                                        (int)Context.Request.SendBuff[0].Size);
+            int SocketId    = Context.RequestData.ReadInt32();
+            int SocketFlags = Context.RequestData.ReadInt32();
+
+            byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
+                                                        Context.Request.SendBuff[0].Position,
+                                                        Context.Request.SendBuff[0].Size);
 
             try
             {
-                //Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer));
+                //Logging.Debug("Sent Buffer:" + Environment.NewLine + Logging.HexDump(SentBuffer));
 
                 int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
 
@@ -220,14 +177,16 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
         //(u32 socket, u32 flags, buffer<i8, 0x21, 0>, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
         public long SendTo(ServiceCtx Context)
         {
-            int SocketId         = Context.RequestData.ReadInt32();
-            int SocketFlags      = Context.RequestData.ReadInt32();
-            byte[] SentBuffer    = AMemoryHelper.ReadBytes(Context.Memory, 
-                                                           Context.Request.SendBuff[0].Position, 
-                                                           (int)Context.Request.SendBuff[0].Size);
-            byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory, 
-                                                           Context.Request.SendBuff[1].Position, 
-                                                           (int)Context.Request.SendBuff[1].Size);
+            int SocketId    = Context.RequestData.ReadInt32();
+            int SocketFlags = Context.RequestData.ReadInt32();
+
+            byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
+                                                        Context.Request.SendBuff[0].Position,
+                                                        Context.Request.SendBuff[0].Size);
+
+            byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
+                                                           Context.Request.SendBuff[1].Position,
+                                                           Context.Request.SendBuff[1].Size);
 
             if (!Sockets[SocketId].Handle.Connected)
             {
@@ -246,7 +205,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
 
             try
             {
-                //Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer));
+                //Logging.Debug("Sent Buffer:" + Environment.NewLine + Logging.HexDump(SentBuffer));
 
                 int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
 
@@ -265,12 +224,13 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
         //(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<sockaddr, 0x22, 0> addr)
         public long Accept(ServiceCtx Context)
         {
-            int SocketId       = Context.RequestData.ReadInt32();
+            int SocketId = Context.RequestData.ReadInt32();
+
             long AddrBufferPtr = Context.Request.ReceiveBuff[0].Position;
 
             Socket HandleAccept = null;
 
-            var TimeOut = Task.Factory.StartNew(() =>
+            Task TimeOut = Task.Factory.StartNew(() =>
             {
                 try
                 {
@@ -287,28 +247,28 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
 
             if (HandleAccept != null)
             {
-                SocketBsd NewBSDSocket = new SocketBsd
+                BsdSocket NewBsdSocket = new BsdSocket
                 {
                     IpAddress = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint).Address,
                     RemoteEP  = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint),
                     Handle    = HandleAccept
                 };
 
-                Sockets.Add(NewBSDSocket);
+                Sockets.Add(NewBsdSocket);
 
                 using (MemoryStream MS = new MemoryStream())
                 {
                     BinaryWriter Writer = new BinaryWriter(MS);
 
                     Writer.Write((byte)0);
-                    Writer.Write((byte)Sockets[Sockets.Count - 1].Handle.AddressFamily);
-                    Writer.Write((Int16)((IPEndPoint)Sockets[Sockets.Count - 1].Handle.LocalEndPoint).Port);
 
-                    string[] IpAdress = Sockets[Sockets.Count - 1].IpAddress.ToString().Split('.');
-                    Writer.Write(byte.Parse(IpAdress[0]));
-                    Writer.Write(byte.Parse(IpAdress[1]));
-                    Writer.Write(byte.Parse(IpAdress[2]));
-                    Writer.Write(byte.Parse(IpAdress[3]));
+                    Writer.Write((byte)NewBsdSocket.Handle.AddressFamily);
+
+                    Writer.Write((short)((IPEndPoint)NewBsdSocket.Handle.LocalEndPoint).Port);
+
+                    byte[] IpAddress = NewBsdSocket.IpAddress.GetAddressBytes();
+
+                    Writer.Write(IpAddress);
 
                     AMemoryHelper.WriteBytes(Context.Memory, AddrBufferPtr, MS.ToArray());
 
@@ -320,7 +280,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
             else
             {
                 Context.ResponseData.Write(-1);
-                Context.ResponseData.Write((int)BsdError.ETIMEDOUT);
+                Context.ResponseData.Write((int)BsdError.Timeout);
             }
 
             return 0;
@@ -331,9 +291,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
         {
             int SocketId = Context.RequestData.ReadInt32();
 
-            byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory, 
-                                                           Context.Request.SendBuff[0].Position, 
-                                                           (int)Context.Request.SendBuff[0].Size);
+            byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
+                                                           Context.Request.SendBuff[0].Position,
+                                                           Context.Request.SendBuff[0].Size);
 
             try
             {
@@ -356,9 +316,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
         {
             int SocketId = Context.RequestData.ReadInt32();
 
-            byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory, 
-                                                           Context.Request.SendBuff[0].Position, 
-                                                           (int)Context.Request.SendBuff[0].Size);
+            byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
+                                                           Context.Request.SendBuff[0].Position,
+                                                           Context.Request.SendBuff[0].Size);
 
             try
             {
@@ -404,19 +364,20 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
         //(u32 socket, u32 level, u32 option_name, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
         public long SetSockOpt(ServiceCtx Context)
         {
-            int SocketId         = Context.RequestData.ReadInt32();
-            int SocketLevel      = Context.RequestData.ReadInt32();
-            int SocketOptionName = Context.RequestData.ReadInt32();
+            int SocketId = Context.RequestData.ReadInt32();
 
-            byte[] SocketOptionValue = AMemoryHelper.ReadBytes(Context.Memory, 
-                                                               Context.Request.PtrBuff[0].Position, 
+            SocketOptionLevel SocketLevel      = (SocketOptionLevel)Context.RequestData.ReadInt32();
+            SocketOptionName  SocketOptionName =  (SocketOptionName)Context.RequestData.ReadInt32();
+
+            byte[] SocketOptionValue = AMemoryHelper.ReadBytes(Context.Memory,
+                                                               Context.Request.PtrBuff[0].Position,
                                                                Context.Request.PtrBuff[0].Size);
 
+            int OptionValue = Get32(SocketOptionValue, 0);
+
             try
             {
-                Sockets[SocketId].Handle.SetSocketOption((SocketOptionLevel)SocketLevel, 
-                                                         (SocketOptionName)SocketOptionName,
-                                                         Get32(SocketOptionValue, 0));
+                Sockets[SocketId].Handle.SetSocketOption(SocketLevel, SocketOptionName, OptionValue);
 
                 Context.ResponseData.Write(0);
                 Context.ResponseData.Write(0);
@@ -461,12 +422,13 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
                 int Size   = Reader.ReadByte();
                 int Family = Reader.ReadByte();
                 int Port   = EndianSwap.Swap16(Reader.ReadInt16());
-                string IpAddress = Reader.ReadByte().ToString() +
-                                   "." + Reader.ReadByte().ToString() +
-                                   "." + Reader.ReadByte().ToString() +
-                                   "." + Reader.ReadByte().ToString();
 
-                Logging.Debug($"Try to connect to {IpAddress}:{Port}");
+                string IpAddress = Reader.ReadByte().ToString() + "." +
+                                   Reader.ReadByte().ToString() + "." +
+                                   Reader.ReadByte().ToString() + "." +
+                                   Reader.ReadByte().ToString();
+
+                Logging.Debug(LogClass.ServiceBsd, $"Try to connect to {IpAddress}:{Port}");
 
                 Sockets[SocketId].IpAddress = IPAddress.Parse(IpAddress);
                 Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port);
diff --git a/Ryujinx.Core/OsHle/Services/Caps/IAlbumAccessorService.cs b/Ryujinx.Core/OsHle/Services/Caps/IAlbumAccessorService.cs
new file mode 100644
index 000000000..d92f3e53c
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Caps/IAlbumAccessorService.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Caps
+{
+    class IAlbumAccessorService : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IAlbumAccessorService()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                //...
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Caps/IScreenshotService.cs b/Ryujinx.Core/OsHle/Services/Caps/IScreenshotService.cs
new file mode 100644
index 000000000..af9b53a8b
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Caps/IScreenshotService.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Caps
+{
+    class IScreenshotService : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IScreenshotService()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                //...
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Friend/IFriendService.cs b/Ryujinx.Core/OsHle/Services/Friend/IFriendService.cs
index e3e03da85..c6e29f860 100644
--- a/Ryujinx.Core/OsHle/Services/Friend/IFriendService.cs
+++ b/Ryujinx.Core/OsHle/Services/Friend/IFriendService.cs
@@ -1,13 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Friend
+namespace Ryujinx.Core.OsHle.Services.Friend
 {
-    class IFriendService : IIpcService
+    class IFriendService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IFriendService()
         {
diff --git a/Ryujinx.Core/OsHle/Services/Friend/ServiceFriend.cs b/Ryujinx.Core/OsHle/Services/Friend/IServiceCreator.cs
similarity index 64%
rename from Ryujinx.Core/OsHle/Services/Friend/ServiceFriend.cs
rename to Ryujinx.Core/OsHle/Services/Friend/IServiceCreator.cs
index 674877f67..2c66d9658 100644
--- a/Ryujinx.Core/OsHle/Services/Friend/ServiceFriend.cs
+++ b/Ryujinx.Core/OsHle/Services/Friend/IServiceCreator.cs
@@ -1,17 +1,15 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Friend
+namespace Ryujinx.Core.OsHle.Services.Friend
 {
-    class ServiceFriend : IIpcService
+    class IServiceCreator : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceFriend()
+        public IServiceCreator()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/FsErr.cs b/Ryujinx.Core/OsHle/Services/FspSrv/FsErr.cs
index 656d529f2..762f65511 100644
--- a/Ryujinx.Core/OsHle/Services/FspSrv/FsErr.cs
+++ b/Ryujinx.Core/OsHle/Services/FspSrv/FsErr.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
+namespace Ryujinx.Core.OsHle.Services.FspSrv
 {
     static class FsErr
     {
diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/IDirectory.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IDirectory.cs
index 54dbec746..fab8c676d 100644
--- a/Ryujinx.Core/OsHle/Services/FspSrv/IDirectory.cs
+++ b/Ryujinx.Core/OsHle/Services/FspSrv/IDirectory.cs
@@ -5,15 +5,15 @@ using System.Collections.Generic;
 using System.IO;
 using System.Text;
 
-namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
+namespace Ryujinx.Core.OsHle.Services.FspSrv
 {
-    class IDirectory : IIpcService, IDisposable
+    class IDirectory : IpcService, IDisposable
     {
         private const int DirectoryEntrySize = 0x310;
 
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         private List<string> DirectoryEntries;
 
diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/IFile.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IFile.cs
index ac2100f29..bd7d138fd 100644
--- a/Ryujinx.Core/OsHle/Services/FspSrv/IFile.cs
+++ b/Ryujinx.Core/OsHle/Services/FspSrv/IFile.cs
@@ -4,13 +4,13 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 
-namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
+namespace Ryujinx.Core.OsHle.Services.FspSrv
 {
-    class IFile : IIpcService, IDisposable
+    class IFile : IpcService, IDisposable
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         private Stream BaseStream;
 
@@ -62,7 +62,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
             long Offset = Context.RequestData.ReadInt64();
             long Size   = Context.RequestData.ReadInt64();
 
-            byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, Position, (int)Size);
+            byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, Position, Size);
 
             BaseStream.Seek(Offset, SeekOrigin.Begin);
             BaseStream.Write(Data, 0, (int)Size);
diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs
index 62bcb8e8b..f65b89ef6 100644
--- a/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs
+++ b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs
@@ -5,15 +5,14 @@ using System.IO;
 using System.Text;
 
 using static Ryujinx.Core.OsHle.ErrorCode;
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
 
-namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
+namespace Ryujinx.Core.OsHle.Services.FspSrv
 {
-    class IFileSystem : IIpcService
+    class IFileSystem : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         private HashSet<string> OpenPaths;
 
diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/ServiceFspSrv.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystemProxy.cs
similarity index 53%
rename from Ryujinx.Core/OsHle/Services/FspSrv/ServiceFspSrv.cs
rename to Ryujinx.Core/OsHle/Services/FspSrv/IFileSystemProxy.cs
index 991f40272..3afee1a16 100644
--- a/Ryujinx.Core/OsHle/Services/FspSrv/ServiceFspSrv.cs
+++ b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystemProxy.cs
@@ -1,42 +1,40 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
+namespace Ryujinx.Core.OsHle.Services.FspSrv
 {
-    class ServiceFspSrv : IIpcService
+    class IFileSystemProxy : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceFspSrv()
+        public IFileSystemProxy()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                {    1, Initialize                      },
-                {   18, MountSdCard                     },
-                {   51, MountSaveData                   },
-                {  200, OpenDataStorageByCurrentProcess },
-                {  203, OpenRomStorage                  },
-                { 1005, GetGlobalAccessLogMode          }
+                {    1, SetCurrentProcess                    },
+                {   18, OpenSdCardFileSystem                 },
+                {   51, OpenSaveDataFileSystem               },
+                {  200, OpenDataStorageByCurrentProcess      },
+                {  203, OpenPatchDataStorageByCurrentProcess },
+                { 1005, GetGlobalAccessLogMode               }
             };
         }
 
-        public long Initialize(ServiceCtx Context)
+        public long SetCurrentProcess(ServiceCtx Context)
         {
             return 0;
         }
 
-        public long MountSdCard(ServiceCtx Context)
+        public long OpenSdCardFileSystem(ServiceCtx Context)
         {
             MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetSdCardPath()));
 
             return 0;
         }
 
-        public long MountSaveData(ServiceCtx Context)
+        public long OpenSaveDataFileSystem(ServiceCtx Context)
         {
             MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetGameSavesPath()));
 
@@ -50,7 +48,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
             return 0;
         }
 
-        public long OpenRomStorage(ServiceCtx Context)
+        public long OpenPatchDataStorageByCurrentProcess(ServiceCtx Context)
         {
             MakeObject(Context, new IStorage(Context.Ns.VFs.RomFs));
 
@@ -62,6 +60,6 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
             Context.ResponseData.Write(0);
 
             return 0;
-        }        
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/IStorage.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IStorage.cs
index 297461a04..05181c649 100644
--- a/Ryujinx.Core/OsHle/Services/FspSrv/IStorage.cs
+++ b/Ryujinx.Core/OsHle/Services/FspSrv/IStorage.cs
@@ -3,13 +3,13 @@ using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 using System.IO;
 
-namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
+namespace Ryujinx.Core.OsHle.Services.FspSrv
 {
-    class IStorage : IIpcService
+    class IStorage : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         private Stream BaseStream;
 
diff --git a/Ryujinx.Core/OsHle/Services/Hid/IActiveVibrationDeviceList.cs b/Ryujinx.Core/OsHle/Services/Hid/IActiveVibrationDeviceList.cs
index f6596f429..d02fa17e7 100644
--- a/Ryujinx.Core/OsHle/Services/Hid/IActiveVibrationDeviceList.cs
+++ b/Ryujinx.Core/OsHle/Services/Hid/IActiveVibrationDeviceList.cs
@@ -1,13 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Hid
+namespace Ryujinx.Core.OsHle.Services.Hid
 {
-    class IActiveApplicationDeviceList : IIpcService
+    class IActiveApplicationDeviceList : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IActiveApplicationDeviceList()
         {
diff --git a/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs b/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs
index ef437c022..23dfd7a22 100644
--- a/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs
+++ b/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs
@@ -2,13 +2,13 @@ using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Hid
+namespace Ryujinx.Core.OsHle.Services.Hid
 {
-    class IAppletResource : IIpcService
+    class IAppletResource : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         private HSharedMem HidSharedMem;
 
diff --git a/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs b/Ryujinx.Core/OsHle/Services/Hid/IHidServer.cs
similarity index 52%
rename from Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs
rename to Ryujinx.Core/OsHle/Services/Hid/IHidServer.cs
index 6735c6ad2..0b401e208 100644
--- a/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs
+++ b/Ryujinx.Core/OsHle/Services/Hid/IHidServer.cs
@@ -1,34 +1,40 @@
+using Ryujinx.Core.Input;
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
-using Ryujinx.Core.Input;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Hid
+namespace Ryujinx.Core.OsHle.Services.Hid
 {
-    class ServiceHid : IIpcService
+    class IHidServer : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceHid()
+        public IHidServer()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
                 {   0, CreateAppletResource                    },
+                {   1, ActivateDebugPad                        },
                 {  11, ActivateTouchScreen                     },
+                {  21, ActivateMouse                           },
+                {  31, ActivateKeyboard                        },
                 {  66, StartSixAxisSensor                      },
+                {  79, SetGyroscopeZeroDriftMode               },
                 { 100, SetSupportedNpadStyleSet                },
                 { 101, GetSupportedNpadStyleSet                },
                 { 102, SetSupportedNpadIdType                  },
                 { 103, ActivateNpad                            },
+                { 108, GetPlayerLedPattern                     },
                 { 120, SetNpadJoyHoldType                      },
+                { 121, GetNpadJoyHoldType                      },
                 { 122, SetNpadJoyAssignmentModeSingleByDefault },
                 { 123, SetNpadJoyAssignmentModeSingle          },
                 { 124, SetNpadJoyAssignmentModeDual            },
                 { 125, MergeSingleJoyAsDualJoy                 },
+                { 128, SetNpadHandheldActivationMode           },
                 { 200, GetVibrationDeviceInfo                  },
+                { 201, SendVibrationValue                      },
                 { 203, CreateActiveVibrationDeviceList         },
                 { 206, SendVibrationValues                     }
             };
@@ -41,9 +47,36 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
             return 0;
         }
 
+        public long ActivateDebugPad(ServiceCtx Context)
+        {
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
+            return 0;
+        }
+
         public long ActivateTouchScreen(ServiceCtx Context)
         {
-            long Unknown = Context.RequestData.ReadInt64();
+            long AppletResourceUserId = Context.RequestData.ReadInt64();
+
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
+            return 0;
+        }
+
+        public long ActivateMouse(ServiceCtx Context)
+        {
+            long AppletResourceUserId = Context.RequestData.ReadInt64();
+
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
+            return 0;
+        }
+
+        public long ActivateKeyboard(ServiceCtx Context)
+        {
+            long AppletResourceUserId = Context.RequestData.ReadInt64();
+
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
 
             return 0;
         }
@@ -54,6 +87,19 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
 
             long AppletResourceUserId = Context.RequestData.ReadInt64();
 
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
+            return 0;
+        }
+
+        public long SetGyroscopeZeroDriftMode(ServiceCtx Context)
+        {
+            int Handle = Context.RequestData.ReadInt32();
+            int Unknown = Context.RequestData.ReadInt32();
+            long AppletResourceUserId = Context.RequestData.ReadInt64();
+
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
             return 0;
         }
 
@@ -61,6 +107,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
         {
             Context.ResponseData.Write(0);
 
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
             return 0;
         }
 
@@ -69,6 +117,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
             long Unknown0 = Context.RequestData.ReadInt64();
             long Unknown8 = Context.RequestData.ReadInt64();
 
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
             return 0;
         }
 
@@ -76,6 +126,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
         {
             long Unknown = Context.RequestData.ReadInt64();
 
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
             return 0;
         }
 
@@ -83,6 +135,19 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
         {
             long Unknown = Context.RequestData.ReadInt64();
 
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
+            return 0;
+        }
+
+        public long GetPlayerLedPattern(ServiceCtx Context)
+        {
+            long Unknown = Context.RequestData.ReadInt32();
+
+            Context.ResponseData.Write(0L);
+
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
             return 0;
         }
 
@@ -91,6 +156,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
             long Unknown0 = Context.RequestData.ReadInt64();
             long Unknown8 = Context.RequestData.ReadInt64();
 
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
             return 0;
         }
 
@@ -98,13 +165,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
         {
             Context.ResponseData.Write(0L);
 
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
             return 0;
         }
 
         public long SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx Context)
         {
             HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32();
-            long AppletUserResourseId = Context.RequestData.ReadInt64();
+            long AppletUserResourceId = Context.RequestData.ReadInt64();
+
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
 
             return 0;
         }
@@ -112,16 +183,20 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
         public long SetNpadJoyAssignmentModeSingle(ServiceCtx Context)
         {
             HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32();
-            long AppletUserResourseId = Context.RequestData.ReadInt64();
+            long AppletUserResourceId = Context.RequestData.ReadInt64();
             long NpadJoyDeviceType = Context.RequestData.ReadInt64();
-            
+
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
             return 0;
         }
 
         public long SetNpadJoyAssignmentModeDual(ServiceCtx Context)
         {
             HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32();
-            long AppletUserResourseId = Context.RequestData.ReadInt64();
+            long AppletUserResourceId = Context.RequestData.ReadInt64();
+
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
 
             return 0;
         }
@@ -130,7 +205,19 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
         {
             long Unknown0 = Context.RequestData.ReadInt32();
             long Unknown8 = Context.RequestData.ReadInt32();
-            long AppletUserResourseId = Context.RequestData.ReadInt64();
+            long AppletUserResourceId = Context.RequestData.ReadInt64();
+
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
+            return 0;
+        }
+
+        public long SetNpadHandheldActivationMode(ServiceCtx Context)
+        {
+            long AppletUserResourceId = Context.RequestData.ReadInt64();
+            long Unknown = Context.RequestData.ReadInt64();
+
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
 
             return 0;
         }
@@ -139,11 +226,29 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
         {
             int VibrationDeviceHandle = Context.RequestData.ReadInt32();
 
+            Logging.Stub(LogClass.ServiceHid, $"VibrationDeviceHandle = {VibrationDeviceHandle}, VibrationDeviceInfo = 0");
+
             Context.ResponseData.Write(0L); //VibrationDeviceInfoForIpc
 
             return 0;
         }
 
+        public long SendVibrationValue(ServiceCtx Context)
+        {
+            int VibrationDeviceHandle = Context.RequestData.ReadInt32();
+
+            int VibrationValue1 = Context.RequestData.ReadInt32();
+            int VibrationValue2 = Context.RequestData.ReadInt32();
+            int VibrationValue3 = Context.RequestData.ReadInt32();
+            int VibrationValue4 = Context.RequestData.ReadInt32();
+
+            long AppletUserResourceId = Context.RequestData.ReadInt64();
+
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
+            return 0;
+        }
+
         public long CreateActiveVibrationDeviceList(ServiceCtx Context)
         {
             MakeObject(Context, new IActiveApplicationDeviceList());
@@ -153,7 +258,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
 
         public long SendVibrationValues(ServiceCtx Context)
         {
+            Logging.Stub(LogClass.ServiceHid, "Stubbed");
+
             return 0;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/Ryujinx.Core/OsHle/Services/IIpcService.cs b/Ryujinx.Core/OsHle/Services/IIpcService.cs
index eebcdfbe5..98b5c2390 100644
--- a/Ryujinx.Core/OsHle/Services/IIpcService.cs
+++ b/Ryujinx.Core/OsHle/Services/IIpcService.cs
@@ -1,7 +1,7 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices
+namespace Ryujinx.Core.OsHle.Services
 {
     interface IIpcService
     {
diff --git a/Ryujinx.Core/OsHle/Services/IpcService.cs b/Ryujinx.Core/OsHle/Services/IpcService.cs
new file mode 100644
index 000000000..f39adb7a3
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/IpcService.cs
@@ -0,0 +1,153 @@
+using Ryujinx.Core.OsHle.Handles;
+using Ryujinx.Core.OsHle.Ipc;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Ryujinx.Core.OsHle.Services
+{
+    abstract class IpcService : IIpcService
+    {
+        public abstract IReadOnlyDictionary<int, ServiceProcessRequest> Commands { get; }
+
+        private IdDictionary DomainObjects;
+
+        private int SelfId;
+
+        private bool IsDomain;
+
+        public IpcService()
+        {
+            DomainObjects = new IdDictionary();
+
+            SelfId = -1;
+        }
+
+        public int ConvertToDomain()
+        {
+            if (SelfId == -1)
+            {
+                SelfId = DomainObjects.Add(this);
+            }
+
+            IsDomain = true;
+
+            return SelfId;
+        }
+
+        public void ConvertToSession()
+        {
+            IsDomain = false;
+        }
+
+        public void CallMethod(ServiceCtx Context)
+        {
+            IIpcService Service = this;
+
+            if (IsDomain)
+            {
+                int DomainWord0 = Context.RequestData.ReadInt32();
+                int DomainObjId = Context.RequestData.ReadInt32();
+
+                long Padding = Context.RequestData.ReadInt64();
+
+                int DomainCmd = DomainWord0 & 0xff;
+
+                if (DomainCmd == 1)
+                {
+                    Service = GetObject(DomainObjId);
+
+                    Context.ResponseData.Write(0L);
+                    Context.ResponseData.Write(0L);
+                }
+                else if (DomainCmd == 2)
+                {
+                    Delete(DomainObjId);
+
+                    Context.ResponseData.Write(0L);
+
+                    return;
+                }
+                else
+                {
+                    throw new NotImplementedException($"Domain command: {DomainCmd}");
+                }
+            }
+
+            long SfciMagic =      Context.RequestData.ReadInt64();
+            int  CommandId = (int)Context.RequestData.ReadInt64();
+
+            if (Service.Commands.TryGetValue(CommandId, out ServiceProcessRequest ProcessRequest))
+            {
+                Context.ResponseData.BaseStream.Seek(IsDomain ? 0x20 : 0x10, SeekOrigin.Begin);
+
+                Logging.Trace(LogClass.KernelIpc, $"{Service.GetType().Name}: {ProcessRequest.Method.Name}");
+
+                long Result = ProcessRequest(Context);
+
+                if (IsDomain)
+                {
+                    foreach (int Id in Context.Response.ResponseObjIds)
+                    {
+                        Context.ResponseData.Write(Id);
+                    }
+
+                    Context.ResponseData.BaseStream.Seek(0, SeekOrigin.Begin);
+
+                    Context.ResponseData.Write(Context.Response.ResponseObjIds.Count);
+                }
+
+                Context.ResponseData.BaseStream.Seek(IsDomain ? 0x10 : 0, SeekOrigin.Begin);
+
+                Context.ResponseData.Write(IpcMagic.Sfco);
+                Context.ResponseData.Write(Result);
+            }
+            else
+            {
+                string DbgMessage = $"{Context.Session.ServiceName} {Service.GetType().Name}: {CommandId}";
+
+                throw new NotImplementedException(DbgMessage);
+            }
+        }
+
+        protected static void MakeObject(ServiceCtx Context, IpcService Obj)
+        {
+            IpcService Service = Context.Session.Service;
+
+            if (Service.IsDomain)
+            {
+                Context.Response.ResponseObjIds.Add(Service.Add(Obj));
+            }
+            else
+            {
+                KSession Session = new KSession(Obj, Context.Session.ServiceName);
+
+                int Handle = Context.Process.HandleTable.OpenHandle(Session);
+
+                Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
+            }
+        }
+
+        private int Add(IIpcService Obj)
+        {
+            return DomainObjects.Add(Obj);
+        }
+
+        private bool Delete(int Id)
+        {
+            object Obj = DomainObjects.Delete(Id);
+
+            if (Obj is IDisposable DisposableObj)
+            {
+                DisposableObj.Dispose();
+            }
+
+            return Obj != null;
+        }
+
+        private IIpcService GetObject(int Id)
+        {
+            return DomainObjects.GetData<IIpcService>(Id);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Lm/ServiceLm.cs b/Ryujinx.Core/OsHle/Services/Lm/ILogService.cs
similarity index 60%
rename from Ryujinx.Core/OsHle/Services/Lm/ServiceLm.cs
rename to Ryujinx.Core/OsHle/Services/Lm/ILogService.cs
index ca3fe35e8..3315cf4cd 100644
--- a/Ryujinx.Core/OsHle/Services/Lm/ServiceLm.cs
+++ b/Ryujinx.Core/OsHle/Services/Lm/ILogService.cs
@@ -1,17 +1,15 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Lm
+namespace Ryujinx.Core.OsHle.Services.Lm
 {
-    class ServiceLm : IIpcService
+    class ILogService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceLm()
+        public ILogService()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
@@ -21,8 +19,6 @@ namespace Ryujinx.Core.OsHle.IpcServices.Lm
 
         public long Initialize(ServiceCtx Context)
         {
-            Context.Session.Initialize();
-
             MakeObject(Context, new ILogger());
 
             return 0;
diff --git a/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs b/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs
index 5ee097b6f..6d3de79b7 100644
--- a/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs
+++ b/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs
@@ -5,13 +5,13 @@ using System.Collections.Generic;
 using System.IO;
 using System.Text;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Lm
+namespace Ryujinx.Core.OsHle.Services.Lm
 {
-    class ILogger : IIpcService
+    class ILogger : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public ILogger()
         {
@@ -54,7 +54,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Lm
             long BufferPosition = Context.Request.PtrBuff[0].Position;
             long BufferLen      = Context.Request.PtrBuff[0].Size;
 
-            byte[] LogBuffer = AMemoryHelper.ReadBytes(Context.Memory, BufferPosition, (int)BufferLen);
+            byte[] LogBuffer = AMemoryHelper.ReadBytes(Context.Memory, BufferPosition, BufferLen);
 
             MemoryStream LogMessage = new MemoryStream(LogBuffer);
             BinaryReader bReader = new BinaryReader(LogMessage);
@@ -129,11 +129,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Lm
 
             switch((Severity)iSeverity)
             {
-                case Severity.Trace:    Logging.Trace(LogString); break;
-                case Severity.Info:     Logging.Info(LogString);  break;
-                case Severity.Warning:  Logging.Warn(LogString);  break;
-                case Severity.Error:    Logging.Error(LogString); break;
-                case Severity.Critical: Logging.Fatal(LogString); break;
+                case Severity.Trace:    Logging.Trace(LogClass.ServiceLm, LogString); break;
+                case Severity.Info:     Logging.Info(LogClass.ServiceLm, LogString);  break;
+                case Severity.Warning:  Logging.Warn(LogClass.ServiceLm, LogString);  break;
+                case Severity.Error:    Logging.Error(LogClass.ServiceLm, LogString); break;
+                case Severity.Critical: Logging.Fatal(LogClass.ServiceLm, LogString); break;
             }
 
             return 0;
diff --git a/Ryujinx.Core/OsHle/Services/Nifm/IGeneralService.cs b/Ryujinx.Core/OsHle/Services/Nifm/IGeneralService.cs
index c31ee36b2..e40ad9f01 100644
--- a/Ryujinx.Core/OsHle/Services/Nifm/IGeneralService.cs
+++ b/Ryujinx.Core/OsHle/Services/Nifm/IGeneralService.cs
@@ -1,15 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Nifm
+namespace Ryujinx.Core.OsHle.Services.Nifm
 {
-    class IGeneralService : IIpcService
+    class IGeneralService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IGeneralService()
         {
@@ -26,7 +24,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Nifm
 
             MakeObject(Context, new IRequest());
 
-            //Todo: Stub
+            Logging.Stub(LogClass.ServiceNifm, "Stubbed");
 
             return 0;
         }
diff --git a/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs b/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs
index 6110e5fbe..276183cd4 100644
--- a/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs
+++ b/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs
@@ -1,13 +1,17 @@
+using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Ipc;
+using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Nifm
+namespace Ryujinx.Core.OsHle.Services.Nifm
 {
-    class IRequest : IIpcService
+    class IRequest : IpcService, IDisposable
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        private KEvent Event;
 
         public IRequest()
         {
@@ -15,23 +19,26 @@ namespace Ryujinx.Core.OsHle.IpcServices.Nifm
             {
                 { 0, GetRequestState               },
                 { 1, GetResult                     },
-                { 2, GetSystemEventReadableHandles }
+                { 2, GetSystemEventReadableHandles },
+                { 3, Cancel                        },
+                { 4, Submit                        },
             };
+
+            Event = new KEvent();
         }
 
-        // -> i32
         public long GetRequestState(ServiceCtx Context)
         {
             Context.ResponseData.Write(0);
 
-            //Todo: Stub
+            Logging.Stub(LogClass.ServiceNifm, "Stubbed");
 
             return 0;
         }
 
         public long GetResult(ServiceCtx Context)
         {
-            //Todo: Stub
+            Logging.Stub(LogClass.ServiceNifm, "Stubbed");
 
             return 0;
         }
@@ -39,11 +46,39 @@ namespace Ryujinx.Core.OsHle.IpcServices.Nifm
         //GetSystemEventReadableHandles() -> (KObject, KObject)
         public long GetSystemEventReadableHandles(ServiceCtx Context)
         {
-            Context.Response.HandleDesc = IpcHandleDesc.MakeMove(0xbadcafe);
+            //FIXME: Is this supposed to return 2 events?
+            int Handle = Context.Process.HandleTable.OpenHandle(Event);
 
-            //Todo: Stub
+            Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
 
             return 0;
         }
+
+        public long Cancel(ServiceCtx Context)
+        {
+            Logging.Stub(LogClass.ServiceNifm, "Stubbed");
+
+            return 0;
+        }
+
+        public long Submit(ServiceCtx Context)
+        {
+            Logging.Stub(LogClass.ServiceNifm, "Stubbed");
+
+            return 0;
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        protected virtual void Dispose(bool Disposing)
+        {
+            if (Disposing)
+            {
+                Event.Dispose();
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Nifm/ServiceNifm.cs b/Ryujinx.Core/OsHle/Services/Nifm/IStaticService.cs
similarity index 65%
rename from Ryujinx.Core/OsHle/Services/Nifm/ServiceNifm.cs
rename to Ryujinx.Core/OsHle/Services/Nifm/IStaticService.cs
index 7e183389b..2129ce43a 100644
--- a/Ryujinx.Core/OsHle/Services/Nifm/ServiceNifm.cs
+++ b/Ryujinx.Core/OsHle/Services/Nifm/IStaticService.cs
@@ -1,17 +1,15 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Nifm
+namespace Ryujinx.Core.OsHle.Services.Nifm
 {
-    class ServiceNifm : IIpcService
+    class IStaticService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceNifm()
+        public IStaticService()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
diff --git a/Ryujinx.Core/OsHle/Services/Ns/IAddOnContentManager.cs b/Ryujinx.Core/OsHle/Services/Ns/IAddOnContentManager.cs
new file mode 100644
index 000000000..5c08cd628
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Ns/IAddOnContentManager.cs
@@ -0,0 +1,41 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Ns
+{
+    class IAddOnContentManager : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IAddOnContentManager()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                { 2, CountAddOnContent },
+                { 3, ListAddOnContent  }
+            };
+        }
+
+        public static long CountAddOnContent(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(0);
+
+            Logging.Stub(LogClass.ServiceNs, "Stubbed");
+
+            return 0;
+        }
+
+        public static long ListAddOnContent(ServiceCtx Context)
+        {
+            Logging.Stub(LogClass.ServiceNs, "Stubbed");
+
+            //TODO: This is supposed to write a u32 array aswell.
+            //It's unknown what it contains.
+            Context.ResponseData.Write(0);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Ns/IServiceGetterInterface.cs b/Ryujinx.Core/OsHle/Services/Ns/IServiceGetterInterface.cs
new file mode 100644
index 000000000..603445f99
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Ns/IServiceGetterInterface.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Ns
+{
+    class IServiceGetterInterface : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IServiceGetterInterface()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                //...
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Ns/ISystemUpdateInterface.cs b/Ryujinx.Core/OsHle/Services/Ns/ISystemUpdateInterface.cs
new file mode 100644
index 000000000..4d9895ed9
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Ns/ISystemUpdateInterface.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Ns
+{
+    class ISystemUpdateInterface : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public ISystemUpdateInterface()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                //...
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Ns/IVulnerabilityManagerInterface.cs b/Ryujinx.Core/OsHle/Services/Ns/IVulnerabilityManagerInterface.cs
new file mode 100644
index 000000000..1091da0d3
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Ns/IVulnerabilityManagerInterface.cs
@@ -0,0 +1,20 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Ns
+{
+    class IVulnerabilityManagerInterface : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IVulnerabilityManagerInterface()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                //...
+            };
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs
similarity index 79%
rename from Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs
rename to Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs
index 67ad44919..cc5f95cd3 100644
--- a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs
+++ b/Ryujinx.Core/OsHle/Services/Nv/INvDrvServices.cs
@@ -1,28 +1,33 @@
 using ChocolArm64.Memory;
+using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Ipc;
 using Ryujinx.Core.OsHle.Utilities;
 using Ryujinx.Graphics.Gpu;
 using System;
 using System.Collections.Generic;
+using System.IO;
 
-namespace Ryujinx.Core.OsHle.IpcServices.NvServices
+namespace Ryujinx.Core.OsHle.Services.Nv
 {
-    class ServiceNvDrv : IIpcService
+    class INvDrvServices : IpcService, IDisposable
     {
         private delegate long ServiceProcessIoctl(ServiceCtx Context);
 
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         private Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds;
 
-        private IdDictionary Fds;
+        public static GlobalStateTable Fds { get; private set; }
 
-        private IdDictionary NvMaps;
-        private IdDictionary NvMapsById;
+        public static GlobalStateTable NvMaps     { get; private set; }
+        public static GlobalStateTable NvMapsById { get; private set; }
+        public static GlobalStateTable NvMapsFb   { get; private set; }
 
-        public ServiceNvDrv()
+        private KEvent Event;
+
+        public INvDrvServices()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
@@ -41,10 +46,12 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
                 { ("/dev/nvhost-as-gpu",   0x4106), NvGpuAsIoctlMapBufferEx           },
                 { ("/dev/nvhost-as-gpu",   0x4108), NvGpuAsIoctlGetVaRegions          },
                 { ("/dev/nvhost-as-gpu",   0x4109), NvGpuAsIoctlInitializeEx          },
+                { ("/dev/nvhost-as-gpu",   0x4114), NvGpuAsIoctlRemap                 },
                 { ("/dev/nvhost-ctrl",     0x001b), NvHostIoctlCtrlGetConfig          },
                 { ("/dev/nvhost-ctrl",     0x001d), NvHostIoctlCtrlEventWait          },
                 { ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize         },
                 { ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo            },
+                { ("/dev/nvhost-ctrl-gpu", 0x4703), NvGpuIoctlZbcSetTable             },
                 { ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics      },
                 { ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks             },
                 { ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask    },
@@ -64,10 +71,16 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
                 { ("/dev/nvmap",           0x010e), NvMapIocGetId                     },
             };
 
-            Fds = new IdDictionary();
+            Event = new KEvent();
+        }
 
-            NvMaps     = new IdDictionary();
-            NvMapsById = new IdDictionary();
+        static INvDrvServices()
+        {
+            Fds = new GlobalStateTable();
+
+            NvMaps     = new GlobalStateTable();
+            NvMapsById = new GlobalStateTable();
+            NvMapsFb   = new GlobalStateTable();
         }
 
         public long Open(ServiceCtx Context)
@@ -76,7 +89,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
 
             string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr);
 
-            int Fd = Fds.Add(new NvFd(Name));
+            int Fd = Fds.Add(Context.Process, new NvFd(Name));
 
             Context.ResponseData.Write(Fd);
             Context.ResponseData.Write(0);
@@ -88,8 +101,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
         {
             int Fd  = Context.RequestData.ReadInt32();
             int Cmd = Context.RequestData.ReadInt32() & 0xffff;
-            
-            NvFd FdData = Fds.GetData<NvFd>(Fd);
+
+            NvFd FdData = Fds.GetData<NvFd>(Context.Process, Fd);
 
             long Position = Context.Request.GetSendBuffPtr();
 
@@ -109,7 +122,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
         {
             int Fd = Context.RequestData.ReadInt32();
 
-            Fds.Delete(Fd);
+            Fds.Delete(Context.Process, Fd);
 
             Context.ResponseData.Write(0);
 
@@ -123,6 +136,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
 
             Context.ResponseData.Write(0);
 
+            NvMapsFb.Add(Context.Process, 0, new NvMapFb());
+
             return 0;
         }
 
@@ -131,7 +146,10 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
             int Fd      = Context.RequestData.ReadInt32();
             int EventId = Context.RequestData.ReadInt32();
 
-            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(0xcafe);
+            //TODO: Use Fd/EventId, different channels have different events.
+            int Handle = Context.Process.HandleTable.OpenHandle(Event);
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
 
             Context.ResponseData.Write(0);
 
@@ -191,38 +209,43 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
             int  Flags    = Reader.ReadInt32();
             int  Kind     = Reader.ReadInt32();
             int  Handle   = Reader.ReadInt32();
-            int  PageSize = Reader.ReadInt32();            
+            int  PageSize = Reader.ReadInt32();
             long BuffAddr = Reader.ReadInt64();
             long MapSize  = Reader.ReadInt64();
             long Offset   = Reader.ReadInt64();
 
             if (Handle == 0)
             {
-                //Handle 0 is valid here, but it refers to something else.
-                //TODO: Figure out what, for now just return success.
+                //This is used to store offsets for the Framebuffer(s);
+                NvMapFb MapFb = (NvMapFb)NvMapsFb.GetData(Context.Process, 0);
+
+                MapFb.AddBufferOffset(BuffAddr);
+
                 return 0;
             }
 
-            NvMap Map = NvMaps.GetData<NvMap>(Handle);
+            NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle);
 
             if (Map == null)
             {
-                Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
-                
+                Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
+
                 return -1; //TODO: Corrent error code.
             }
 
             if ((Flags & 1) != 0)
             {
-                Offset = Context.Ns.Gpu.MapMemory(Map.Address, Offset, Map.Size);
+                Offset = Context.Ns.Gpu.MapMemory(Map.CpuAddress, Offset, Map.Size);
             }
             else
             {
-                Offset = Context.Ns.Gpu.MapMemory(Map.Address, Map.Size);
+                Offset = Context.Ns.Gpu.MapMemory(Map.CpuAddress, Map.Size);
             }
 
             Context.Memory.WriteInt64(Position + 0x20, Offset);
 
+            Map.GpuAddress = Offset;
+
             return 0;
         }
 
@@ -273,6 +296,35 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
             return 0;
         }
 
+        private long NvGpuAsIoctlRemap(ServiceCtx Context)
+        {
+            Context.RequestData.BaseStream.Seek(-4, SeekOrigin.Current);
+
+            int Cmd = Context.RequestData.ReadInt32();
+
+            int Size = (Cmd >> 16) & 0xff;
+
+            int Count = Size / 0x18;
+
+            long Position = Context.Request.GetSendBuffPtr();
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            for (int Index = 0; Index < Count; Index++)
+            {
+                int Flags   = Reader.ReadInt32();
+                int Kind    = Reader.ReadInt32();
+                int Handle  = Reader.ReadInt32();
+                int Padding = Reader.ReadInt32();
+                int Offset  = Reader.ReadInt32();
+                int Pages   = Reader.ReadInt32();
+            }
+
+            //TODO
+
+            return 0;
+        }
+
         private long NvHostIoctlCtrlGetConfig(ServiceCtx Context)
         {
             long Position = Context.Request.GetSendBuffPtr();
@@ -333,6 +385,32 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
             return 0;
         }
 
+        private long NvGpuIoctlZbcSetTable(ServiceCtx Context)
+        {
+            long Position = Context.Request.GetSendBuffPtr();
+
+            MemReader Reader = new MemReader(Context.Memory, Position);
+
+            int[] ColorDs = new int[4];
+            int[] ColorL2 = new int[4];
+
+            ColorDs[0] = Reader.ReadInt32();
+            ColorDs[1] = Reader.ReadInt32();
+            ColorDs[2] = Reader.ReadInt32();
+            ColorDs[3] = Reader.ReadInt32();
+
+            ColorL2[0] = Reader.ReadInt32();
+            ColorL2[1] = Reader.ReadInt32();
+            ColorL2[2] = Reader.ReadInt32();
+            ColorL2[3] = Reader.ReadInt32();
+
+            int Depth  = Reader.ReadInt32();
+            int Format = Reader.ReadInt32();
+            int Type   = Reader.ReadInt32();
+
+            return 0;
+        }
+
         private long NvGpuIoctlGetCharacteristics(ServiceCtx Context)
         {
             long Position = Context.Request.GetSendBuffPtr();
@@ -462,9 +540,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
                 {
                     byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, CpuAddr, Size);
 
-                    NsGpuPBEntry[] PushBuffer = NsGpuPBEntry.DecodePushBuffer(Data);
+                    NsGpuPBEntry[] PushBuffer = NvGpuPushBuffer.Decode(Data);
 
-                    Context.Ns.Gpu.ProcessPushBuffer(PushBuffer, Context.Memory);
+                    Context.Ns.Gpu.Fifo.PushBuffer(Context.Memory, PushBuffer);
                 }
             }
 
@@ -550,13 +628,13 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
 
             NvMap Map = new NvMap() { Size = Size };
 
-            Map.Handle = NvMaps.Add(Map);
+            Map.Handle = NvMaps.Add(Context.Process, Map);
 
-            Map.Id = NvMapsById.Add(Map);
+            Map.Id = NvMapsById.Add(Context.Process, Map);
 
             Context.Memory.WriteInt32(Position + 4, Map.Handle);
 
-            Logging.Info($"NvMap {Map.Id} created with size {Size:x8}!");
+            Logging.Info(LogClass.ServiceNv, $"NvMap {Map.Id} created with size {Size:x8}!");
 
             return 0;
         }
@@ -567,12 +645,12 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
 
             int Id = Context.Memory.ReadInt32(Position);
 
-            NvMap Map = NvMapsById.GetData<NvMap>(Id);
+            NvMap Map = NvMapsById.GetData<NvMap>(Context.Process, Id);
 
             if (Map == null)
             {
-                Logging.Warn($"Trying to use invalid NvMap Id {Id}!");
-                
+                Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Id {Id}!");
+
                 return -1; //TODO: Corrent error code.
             }
 
@@ -594,18 +672,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
             byte Kind     = (byte)Reader.ReadInt64();
             long Addr     =       Reader.ReadInt64();
 
-            NvMap Map = NvMaps.GetData<NvMap>(Handle);
+            NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle);
 
             if (Map == null)
             {
-                Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
-                
+                Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
+
                 return -1; //TODO: Corrent error code.
             }
 
-            Map.Address = Addr;
-            Map.Align   = Align;
-            Map.Kind    = Kind;
+            Map.CpuAddress = Addr;
+            Map.Align      = Align;
+            Map.Kind       = Kind;
 
             return 0;
         }
@@ -620,12 +698,12 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
             int  Handle  = Reader.ReadInt32();
             int  Padding = Reader.ReadInt32();
 
-            NvMap Map = NvMaps.GetData<NvMap>(Handle);
+            NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle);
 
             if (Map == null)
             {
-                Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
-                
+                Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
+
                 return -1; //TODO: Corrent error code.
             }
 
@@ -645,12 +723,12 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
             int Handle = Reader.ReadInt32();
             int Param  = Reader.ReadInt32();
 
-            NvMap Map = NvMaps.GetData<NvMap>(Handle);
+            NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle);
 
             if (Map == null)
             {
-                Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
-                
+                Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
+
                 return -1; //TODO: Corrent error code.
             }
 
@@ -675,12 +753,12 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
 
             int Handle = Context.Memory.ReadInt32(Position + 4);
 
-            NvMap Map = NvMaps.GetData<NvMap>(Handle);
+            NvMap Map = NvMaps.GetData<NvMap>(Context.Process, Handle);
 
             if (Map == null)
             {
-                Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
-                
+                Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
+
                 return -1; //TODO: Corrent error code.
             }
 
@@ -689,9 +767,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
             return 0;
         }
 
-        public NvMap GetNvMap(int Handle)
+        public void Dispose()
         {
-            return NvMaps.GetData<NvMap>(Handle);
+            Dispose(true);
+        }
+
+        protected virtual void Dispose(bool Disposing)
+        {
+            if (Disposing)
+            {
+                Event.Dispose();
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvFd.cs b/Ryujinx.Core/OsHle/Services/Nv/NvFd.cs
index dbce74adf..1fdbd1a17 100644
--- a/Ryujinx.Core/OsHle/Services/Nv/NvFd.cs
+++ b/Ryujinx.Core/OsHle/Services/Nv/NvFd.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Core.OsHle.IpcServices.NvServices
+namespace Ryujinx.Core.OsHle.Services.Nv
 {
     class NvFd
     {
diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs
index ca844f9f2..f3dd1f471 100644
--- a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs
+++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Core.OsHle.IpcServices.NvServices
+namespace Ryujinx.Core.OsHle.Services.Nv
 {
     class NvMap
     {
@@ -7,6 +7,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
         public int  Size;
         public int  Align;
         public int  Kind;
-        public long Address;
+        public long CpuAddress;
+        public long GpuAddress;
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs
new file mode 100644
index 000000000..d8a47418b
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Nv/NvMapFb.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Nv
+{
+    class NvMapFb
+    {
+        private List<long> BufferOffs;
+
+        public NvMapFb()
+        {
+            BufferOffs = new List<long>();
+        }
+
+        public void AddBufferOffset(long Offset)
+        {
+            BufferOffs.Add(Offset);
+        }
+
+        public bool HasBufferOffset(int Index)
+        {
+            if ((uint)Index >= BufferOffs.Count)
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        public long GetBufferOffset(int Index)
+        {
+            if ((uint)Index >= BufferOffs.Count)
+            {
+                throw new ArgumentOutOfRangeException(nameof(Index));
+            }
+
+            return BufferOffs[Index];
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/ObjHelper.cs b/Ryujinx.Core/OsHle/Services/ObjHelper.cs
deleted file mode 100644
index 89d986aeb..000000000
--- a/Ryujinx.Core/OsHle/Services/ObjHelper.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using Ryujinx.Core.OsHle.Handles;
-using Ryujinx.Core.OsHle.Ipc;
-
-namespace Ryujinx.Core.OsHle.IpcServices
-{
-    static class ObjHelper
-    {
-        public static void MakeObject(ServiceCtx Context, object Obj)
-        {
-            if (Context.Session is HDomain Dom)
-            {
-                Context.Response.ResponseObjIds.Add(Dom.Add(Obj));
-            }
-            else
-            {
-                HSessionObj HndData = new HSessionObj(Context.Session, Obj);
-
-                int VHandle = Context.Process.HandleTable.OpenHandle(HndData);
-
-                Context.Response.HandleDesc = IpcHandleDesc.MakeMove(VHandle);
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Pctl/IParentalControlService.cs b/Ryujinx.Core/OsHle/Services/Pctl/IParentalControlService.cs
index 4eb92d31d..28b35b0a3 100644
--- a/Ryujinx.Core/OsHle/Services/Pctl/IParentalControlService.cs
+++ b/Ryujinx.Core/OsHle/Services/Pctl/IParentalControlService.cs
@@ -1,13 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Pctl
+namespace Ryujinx.Core.OsHle.Services.Pctl
 {
-    class IParentalControlService : IIpcService
+    class IParentalControlService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IParentalControlService()
         {
diff --git a/Ryujinx.Core/OsHle/Services/Pctl/ServicePctl.cs b/Ryujinx.Core/OsHle/Services/Pctl/IParentalControlServiceFactory.cs
similarity index 65%
rename from Ryujinx.Core/OsHle/Services/Pctl/ServicePctl.cs
rename to Ryujinx.Core/OsHle/Services/Pctl/IParentalControlServiceFactory.cs
index 2d5e22a46..5421f1655 100644
--- a/Ryujinx.Core/OsHle/Services/Pctl/ServicePctl.cs
+++ b/Ryujinx.Core/OsHle/Services/Pctl/IParentalControlServiceFactory.cs
@@ -1,17 +1,15 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Pctl
+namespace Ryujinx.Core.OsHle.Services.Pctl
 {
-    class ServicePctl : IIpcService
+    class IParentalControlServiceFactory : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServicePctl()
+        public IParentalControlServiceFactory()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
diff --git a/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs b/Ryujinx.Core/OsHle/Services/Pl/ISharedFontManager.cs
similarity index 87%
rename from Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs
rename to Ryujinx.Core/OsHle/Services/Pl/ISharedFontManager.cs
index 9a6177993..2872577f0 100644
--- a/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs
+++ b/Ryujinx.Core/OsHle/Services/Pl/ISharedFontManager.cs
@@ -1,15 +1,15 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Pl
+namespace Ryujinx.Core.OsHle.Services.Pl
 {
-    class ServicePl : IIpcService
+    class ISharedFontManager : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServicePl()
+        public ISharedFontManager()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
diff --git a/Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs b/Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs
index 29fe02b8a..2318b1729 100644
--- a/Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs
+++ b/Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Core.OsHle.IpcServices.Pl
+namespace Ryujinx.Core.OsHle.Services.Pl
 {
     enum SharedFontType
     {
diff --git a/Ryujinx.Core/OsHle/Services/Ssl/ServiceSsl.cs b/Ryujinx.Core/OsHle/Services/Prepo/IPrepoService.cs
similarity index 53%
rename from Ryujinx.Core/OsHle/Services/Ssl/ServiceSsl.cs
rename to Ryujinx.Core/OsHle/Services/Prepo/IPrepoService.cs
index 23934b140..42e438414 100644
--- a/Ryujinx.Core/OsHle/Services/Ssl/ServiceSsl.cs
+++ b/Ryujinx.Core/OsHle/Services/Prepo/IPrepoService.cs
@@ -1,19 +1,19 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Ssl
+namespace Ryujinx.Core.OsHle.Services.Prepo
 {
-    class ServiceSsl : IIpcService
+    class IPrepoService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceSsl()
+        public IPrepoService()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                //{ 0, Function }
+                //...
             };
         }
     }
diff --git a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs b/Ryujinx.Core/OsHle/Services/ServiceFactory.cs
new file mode 100644
index 000000000..8e639b949
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/ServiceFactory.cs
@@ -0,0 +1,146 @@
+using Ryujinx.Core.OsHle.Services.Acc;
+using Ryujinx.Core.OsHle.Services.Am;
+using Ryujinx.Core.OsHle.Services.Apm;
+using Ryujinx.Core.OsHle.Services.Aud;
+using Ryujinx.Core.OsHle.Services.Bsd;
+using Ryujinx.Core.OsHle.Services.Caps;
+using Ryujinx.Core.OsHle.Services.Friend;
+using Ryujinx.Core.OsHle.Services.FspSrv;
+using Ryujinx.Core.OsHle.Services.Hid;
+using Ryujinx.Core.OsHle.Services.Lm;
+using Ryujinx.Core.OsHle.Services.Ns;
+using Ryujinx.Core.OsHle.Services.Nv;
+using Ryujinx.Core.OsHle.Services.Pctl;
+using Ryujinx.Core.OsHle.Services.Pl;
+using Ryujinx.Core.OsHle.Services.Prepo;
+using Ryujinx.Core.OsHle.Services.Set;
+using Ryujinx.Core.OsHle.Services.Sfdnsres;
+using Ryujinx.Core.OsHle.Services.Sm;
+using Ryujinx.Core.OsHle.Services.Ssl;
+using Ryujinx.Core.OsHle.Services.Vi;
+using System;
+
+namespace Ryujinx.Core.OsHle.Services
+{
+    static class ServiceFactory
+    {
+        public static IpcService MakeService(string Name)
+        {
+            switch (Name)
+            {
+                case "acc:u0":
+                    return new IAccountServiceForApplication();
+
+                case "aoc:u":
+                    return new IAddOnContentManager();
+
+                case "apm":
+                    return new IManager();
+
+                case "apm:p":
+                    return new IManager();
+
+                case "appletAE":
+                    return new IAllSystemAppletProxiesService();
+
+                case "appletOE":
+                    return new IApplicationProxyService();
+
+                case "audout:u":
+                    return new IAudioOutManager();
+
+                case "audren:u":
+                    return new IAudioRendererManager();
+
+                case "bsd:s":
+                    return new IClient();
+
+                case "bsd:u":
+                    return new IClient();
+
+                case "caps:a":
+                    return new IAlbumAccessorService();
+
+                case "caps:ss":
+                    return new IScreenshotService();
+
+                case "friend:a":
+                    return new IServiceCreator();
+
+                case "friend:u":
+                    return new IServiceCreator();
+
+                case "fsp-srv":
+                    return new IFileSystemProxy();
+
+                case "hid":
+                    return new IHidServer();
+
+                case "lm":
+                    return new ILogService();
+
+                case "nifm:u":
+                    return new Nifm.IStaticService();
+
+                case "ns:ec":
+                    return new IServiceGetterInterface();
+
+                case "ns:su":
+                    return new ISystemUpdateInterface();
+
+                case "ns:vm":
+                    return new IVulnerabilityManagerInterface();
+
+                case "nvdrv":
+                    return new INvDrvServices();
+
+                case "nvdrv:a":
+                    return new INvDrvServices();
+
+                case "pctl:a":
+                    return new IParentalControlServiceFactory();
+
+                case "pl:u":
+                    return new ISharedFontManager();
+
+                case "prepo:u":
+                    return new IPrepoService();
+
+                case "set":
+                    return new ISettingsServer();
+
+                case "set:sys":
+                    return new ISystemSettingsServer();
+
+                case "sfdnsres":
+                    return new IResolver();
+
+                case "sm:":
+                    return new IUserInterface();
+
+                case "ssl":
+                    return new ISslService();
+
+                case "time:a":
+                    return new Time.IStaticService();
+
+                case "time:s":
+                    return new Time.IStaticService();
+
+                case "time:u":
+                    return new Time.IStaticService();
+
+                case "vi:m":
+                    return new IManagerRootService();
+
+                case "vi:s":
+                    return new ISystemRootService();
+
+                case "vi:u":
+                    return new IApplicationRootService();
+            }
+
+            throw new NotImplementedException(Name);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Set/ISettingsServer.cs b/Ryujinx.Core/OsHle/Services/Set/ISettingsServer.cs
new file mode 100644
index 000000000..ea0303f0a
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Set/ISettingsServer.cs
@@ -0,0 +1,109 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Ryujinx.Core.OsHle.Services.Set
+{
+    class ISettingsServer : IpcService
+    {
+        private static string[] LanguageCodes = new string[]
+        {
+            "ja",
+            "en-US",
+            "fr",
+            "de",
+            "it",
+            "es",
+            "zh-CN",
+            "ko",
+            "nl",
+            "pt",
+            "ru",
+            "zh-TW",
+            "en-GB",
+            "fr-CA",
+            "es-419",
+            "zh-Hans",
+            "zh-Hant"
+        };
+
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public ISettingsServer()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                { 0, GetLanguageCode               },
+                { 1, GetAvailableLanguageCodes     },
+                { 3, GetAvailableLanguageCodeCount }
+            };
+        }
+
+        public static long GetLanguageCode(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(LanguageCodetoLongBE(LanguageCodes[1]));
+
+            return 0;
+        }
+
+        private static long LanguageCodetoLongBE(string LanguageCode)
+        {
+            using (MemoryStream MS = new MemoryStream())
+            {
+                foreach (char Chr in LanguageCode)
+                {
+                    MS.WriteByte((byte)Chr);
+                }
+
+                for (int Offs = 0; Offs < (8 - LanguageCode.Length); Offs++)
+                {
+                    MS.WriteByte(0);
+                }
+
+                return BitConverter.ToInt64(MS.ToArray(), 0);
+            }
+        }
+
+        public static long GetAvailableLanguageCodes(ServiceCtx Context)
+        {
+            long  Position = Context.Request.RecvListBuff[0].Position;
+            short Size     = Context.Request.RecvListBuff[0].Size;
+
+            int Count = (int)((uint)Size / 8);
+
+            if (Count > LanguageCodes.Length)
+            {
+                Count = LanguageCodes.Length;
+            }
+
+            for (int Index = 0; Index < Count; Index++)
+            {
+                string LanguageCode = LanguageCodes[Index];
+
+                foreach (char Chr in LanguageCode)
+                {
+                    Context.Memory.WriteByte(Position++, (byte)Chr);
+                }
+
+                for (int Offs = 0; Offs < (8 - LanguageCode.Length); Offs++)
+                {
+                    Context.Memory.WriteByte(Position++, 0);
+                }
+            }
+
+            Context.ResponseData.Write(Count);
+
+            return 0;
+        }
+
+        public static long GetAvailableLanguageCodeCount(ServiceCtx Context)
+        {
+            Context.ResponseData.Write(LanguageCodes.Length);
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Set/ISystemSettingsServer.cs b/Ryujinx.Core/OsHle/Services/Set/ISystemSettingsServer.cs
new file mode 100644
index 000000000..bdf5c7b40
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Set/ISystemSettingsServer.cs
@@ -0,0 +1,87 @@
+using ChocolArm64.Memory;
+using Ryujinx.Core.OsHle.Ipc;
+using Ryujinx.Core.Settings;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace Ryujinx.Core.OsHle.Services.Set
+{
+    class ISystemSettingsServer : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public ISystemSettingsServer()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                {  4, GetFirmwareVersion2 },
+                { 23, GetColorSetId       },
+                { 24, SetColorSetId       }
+            };
+        }
+
+        public static long GetFirmwareVersion2(ServiceCtx Context)
+        {
+            long ReplyPos  = Context.Request.RecvListBuff[0].Position;
+            long ReplySize = Context.Request.RecvListBuff[0].Size;
+
+            byte MajorFWVersion = 0x03;
+            byte MinorFWVersion = 0x00;
+            byte MicroFWVersion = 0x00;
+            byte Unknown        = 0x00; //Build?
+
+            int RevisionNumber  = 0x0A;
+
+            string Platform     = "NX";
+            string UnknownHex   = "7fbde2b0bba4d14107bf836e4643043d9f6c8e47";
+            string Version      = "3.0.0";
+            string Build        = "NintendoSDK Firmware for NX 3.0.0-10.0";
+
+            //http://switchbrew.org/index.php?title=System_Version_Title
+            using (MemoryStream MS = new MemoryStream(0x100))
+            {
+                BinaryWriter Writer = new BinaryWriter(MS);
+
+                Writer.Write(MajorFWVersion);
+                Writer.Write(MinorFWVersion);
+                Writer.Write(MicroFWVersion);
+                Writer.Write(Unknown);
+
+                Writer.Write(RevisionNumber);
+
+                Writer.Write(Encoding.ASCII.GetBytes(Platform));
+
+                MS.Seek(0x28, SeekOrigin.Begin);
+                Writer.Write(Encoding.ASCII.GetBytes(UnknownHex));
+
+                MS.Seek(0x68, SeekOrigin.Begin);
+                Writer.Write(Encoding.ASCII.GetBytes(Version));
+
+                MS.Seek(0x80, SeekOrigin.Begin);
+                Writer.Write(Encoding.ASCII.GetBytes(Build));
+
+                AMemoryHelper.WriteBytes(Context.Memory, ReplyPos, MS.ToArray());
+            }
+
+            return 0;
+        }
+
+        public static long GetColorSetId(ServiceCtx Context)
+        {
+            Context.ResponseData.Write((int)Context.Ns.Settings.ThemeColor);
+
+            return 0;
+        }
+
+        public static long SetColorSetId(ServiceCtx Context)
+        {
+            int ColorSetId = Context.RequestData.ReadInt32();
+
+            Context.Ns.Settings.ThemeColor = (ColorSet)ColorSetId;
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Set/ServiceSet.cs b/Ryujinx.Core/OsHle/Services/Set/ServiceSet.cs
deleted file mode 100644
index c60e1712a..000000000
--- a/Ryujinx.Core/OsHle/Services/Set/ServiceSet.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using ChocolArm64.Memory;
-using Ryujinx.Core.OsHle.Ipc;
-using System;
-using System.Collections.Generic;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Set
-{
-    class ServiceSet : IIpcService
-    {
-        private Dictionary<int, ServiceProcessRequest> m_Commands;
-
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
-
-        public ServiceSet()
-        {
-            m_Commands = new Dictionary<int, ServiceProcessRequest>()
-            {
-                { 1, GetAvailableLanguageCodes }
-            };
-        }
-
-        private const int LangCodesCount = 13;
-
-        public static long GetAvailableLanguageCodes(ServiceCtx Context)
-        {
-            int PtrBuffSize = Context.RequestData.ReadInt32();
-
-            if (Context.Request.RecvListBuff.Count > 0)
-            {
-                long  Position = Context.Request.RecvListBuff[0].Position;
-                short Size     = Context.Request.RecvListBuff[0].Size;
-
-                //This should return an array of ints with values matching the LanguageCode enum.
-                foreach (long value in new long[] { 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L })
-                {
-                    AMemoryHelper.WriteBytes(Context.Memory, Position += 8, BitConverter.GetBytes(value));
-                }
-            }
-
-            Context.ResponseData.Write(LangCodesCount);
-
-            return 0;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Set/ServiceSetSys.cs b/Ryujinx.Core/OsHle/Services/Set/ServiceSetSys.cs
deleted file mode 100644
index dee6573d3..000000000
--- a/Ryujinx.Core/OsHle/Services/Set/ServiceSetSys.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Ryujinx.Core.OsHle.Ipc;
-using System.Collections.Generic;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Set
-{
-    class ServiceSetSys : IIpcService
-    {
-        private Dictionary<int, ServiceProcessRequest> m_Commands;
-
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
-
-        public ServiceSetSys()
-        {
-            m_Commands = new Dictionary<int, ServiceProcessRequest>()
-            {
-                { 23, GetColorSetId },
-                { 24, SetColorSetId }
-            };
-        }
-
-        public static long GetColorSetId(ServiceCtx Context)
-        {
-            Context.ResponseData.Write((int)Context.Ns.Settings.ThemeColor);
-
-            return 0;
-        }
-
-        public static long SetColorSetId(ServiceCtx Context)
-        {            
-            return 0;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Sfdnsres/ServiceSfdnsres.cs b/Ryujinx.Core/OsHle/Services/Sfdnsres/IResolver.cs
similarity index 52%
rename from Ryujinx.Core/OsHle/Services/Sfdnsres/ServiceSfdnsres.cs
rename to Ryujinx.Core/OsHle/Services/Sfdnsres/IResolver.cs
index f110ae736..e8d48ceeb 100644
--- a/Ryujinx.Core/OsHle/Services/Sfdnsres/ServiceSfdnsres.cs
+++ b/Ryujinx.Core/OsHle/Services/Sfdnsres/IResolver.cs
@@ -1,19 +1,19 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Sfdnsres
+namespace Ryujinx.Core.OsHle.Services.Sfdnsres
 {
-    class ServiceSfdnsres : IIpcService
+    class IResolver : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceSfdnsres()
+        public IResolver()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                //{ 0, Function }
+                //...
             };
         }
     }
diff --git a/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs b/Ryujinx.Core/OsHle/Services/Sm/IUserInterface.cs
similarity index 77%
rename from Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs
rename to Ryujinx.Core/OsHle/Services/Sm/IUserInterface.cs
index cb745e373..f7c0f1076 100644
--- a/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs
+++ b/Ryujinx.Core/OsHle/Services/Sm/IUserInterface.cs
@@ -2,15 +2,17 @@ using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Sm
+namespace Ryujinx.Core.OsHle.Services.Sm
 {
-    class ServiceSm : IIpcService
+    class IUserInterface : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceSm()
+        private bool IsInitialized;
+
+        public IUserInterface()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
@@ -23,7 +25,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Sm
 
         public long Initialize(ServiceCtx Context)
         {
-            Context.Session.Initialize();
+            IsInitialized = true;
 
             return 0;
         }
@@ -31,7 +33,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Sm
         public long GetService(ServiceCtx Context)
         {
             //Only for kernel version > 3.0.0.
-            if (!Context.Session.IsInitialized)
+            if (!IsInitialized)
             {
                 //return SmNotInitialized;
             }
@@ -55,7 +57,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Sm
                 return 0;
             }
 
-            HSession Session = new HSession(Context.Process.Services.GetService(Name));
+            KSession Session = new KSession(ServiceFactory.MakeService(Name), Name);
 
             int Handle = Context.Process.HandleTable.OpenHandle(Session);
 
diff --git a/Ryujinx.Core/OsHle/Services/Ns/ServiceNs.cs b/Ryujinx.Core/OsHle/Services/Ssl/ISslService.cs
similarity index 54%
rename from Ryujinx.Core/OsHle/Services/Ns/ServiceNs.cs
rename to Ryujinx.Core/OsHle/Services/Ssl/ISslService.cs
index 720baa6ec..825e33639 100644
--- a/Ryujinx.Core/OsHle/Services/Ns/ServiceNs.cs
+++ b/Ryujinx.Core/OsHle/Services/Ssl/ISslService.cs
@@ -1,19 +1,19 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Ns
+namespace Ryujinx.Core.OsHle.Services.Ssl
 {
-    class ServiceNs : IIpcService
+    class ISslService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceNs()
+        public ISslService()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                //{ 1, Function }
+                //...
             };
         }
     }
diff --git a/Ryujinx.Core/OsHle/Services/Time/ServiceTime.cs b/Ryujinx.Core/OsHle/Services/Time/IStaticService.cs
similarity index 84%
rename from Ryujinx.Core/OsHle/Services/Time/ServiceTime.cs
rename to Ryujinx.Core/OsHle/Services/Time/IStaticService.cs
index 43f28bb80..94d9ae741 100644
--- a/Ryujinx.Core/OsHle/Services/Time/ServiceTime.cs
+++ b/Ryujinx.Core/OsHle/Services/Time/IStaticService.cs
@@ -1,17 +1,15 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Time
+namespace Ryujinx.Core.OsHle.Services.Time
 {
-    class ServiceTime : IIpcService
+    class IStaticService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceTime()
+        public IStaticService()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
diff --git a/Ryujinx.Core/OsHle/Services/Time/ISteadyClock.cs b/Ryujinx.Core/OsHle/Services/Time/ISteadyClock.cs
index d20e4378a..187d7a063 100644
--- a/Ryujinx.Core/OsHle/Services/Time/ISteadyClock.cs
+++ b/Ryujinx.Core/OsHle/Services/Time/ISteadyClock.cs
@@ -1,13 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Time
+namespace Ryujinx.Core.OsHle.Services.Time
 {
-    class ISteadyClock : IIpcService
+    class ISteadyClock : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public ISteadyClock()
         {
diff --git a/Ryujinx.Core/OsHle/Services/Time/ISystemClock.cs b/Ryujinx.Core/OsHle/Services/Time/ISystemClock.cs
index 4d4493dad..9cfdcc875 100644
--- a/Ryujinx.Core/OsHle/Services/Time/ISystemClock.cs
+++ b/Ryujinx.Core/OsHle/Services/Time/ISystemClock.cs
@@ -2,15 +2,15 @@ using Ryujinx.Core.OsHle.Ipc;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Time
+namespace Ryujinx.Core.OsHle.Services.Time
 {
-    class ISystemClock : IIpcService
+    class ISystemClock : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        private static DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+        private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
 
         private SystemClockType ClockType;
 
diff --git a/Ryujinx.Core/OsHle/Services/Time/ITimeZoneService.cs b/Ryujinx.Core/OsHle/Services/Time/ITimeZoneService.cs
index d220824c5..ec50c82f4 100644
--- a/Ryujinx.Core/OsHle/Services/Time/ITimeZoneService.cs
+++ b/Ryujinx.Core/OsHle/Services/Time/ITimeZoneService.cs
@@ -2,48 +2,49 @@ using Ryujinx.Core.OsHle.Ipc;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Time
+namespace Ryujinx.Core.OsHle.Services.Time
 {
-    class ITimeZoneService : IIpcService
+    class ITimeZoneService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        private static DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local);
+        private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
 
         public ITimeZoneService()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                { 101,  ToCalendarTimeWithMyRule }
+                {   0, GetDeviceLocationName    },
+                { 101, ToCalendarTimeWithMyRule }
             };
         }
 
-        //(nn::time::PosixTime)-> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
+        public long GetDeviceLocationName(ServiceCtx Context)
+        {
+            Logging.Stub(LogClass.ServiceTime, "Stubbed");
+
+            for (int Index = 0; Index < 0x24; Index++)
+            {
+                Context.ResponseData.Write((byte)0);
+            }
+
+            return 0;
+        }
+
         public long ToCalendarTimeWithMyRule(ServiceCtx Context)
         {
             long PosixTime = Context.RequestData.ReadInt64();
 
-            Epoch = Epoch.AddSeconds(PosixTime).ToLocalTime();
+            DateTime CurrentTime = Epoch.AddSeconds(PosixTime).ToLocalTime();
 
-            /*
-                struct CalendarTime {
-                    u16_le year;
-                    u8 month; // Starts at 1
-                    u8 day;   // Starts at 1
-                    u8 hour;
-                    u8 minute;
-                    u8 second;
-                    INSERT_PADDING_BYTES(1);
-                };
-            */
-            Context.ResponseData.Write((short)Epoch.Year);
-            Context.ResponseData.Write((byte)Epoch.Month);
-            Context.ResponseData.Write((byte)Epoch.Day);
-            Context.ResponseData.Write((byte)Epoch.Hour);
-            Context.ResponseData.Write((byte)Epoch.Minute);
-            Context.ResponseData.Write((byte)Epoch.Second);
+            Context.ResponseData.Write((ushort)CurrentTime.Year);
+            Context.ResponseData.Write((byte)CurrentTime.Month);
+            Context.ResponseData.Write((byte)CurrentTime.Day);
+            Context.ResponseData.Write((byte)CurrentTime.Hour);
+            Context.ResponseData.Write((byte)CurrentTime.Minute);
+            Context.ResponseData.Write((byte)CurrentTime.Second);
             Context.ResponseData.Write((byte)0);
 
             /* Thanks to TuxSH
@@ -57,11 +58,16 @@ namespace Ryujinx.Core.OsHle.IpcServices.Time
 	                };
                 };
             */
-            Context.ResponseData.Write((int)Epoch.DayOfWeek);
-            Context.ResponseData.Write(Epoch.DayOfYear);
+            Context.ResponseData.Write((int)CurrentTime.DayOfWeek);
+
+            Context.ResponseData.Write(CurrentTime.DayOfYear);
+
+            //TODO: Find out the names used.
             Context.ResponseData.Write(new byte[8]);
-            Context.ResponseData.Write(Convert.ToByte(Epoch.IsDaylightSavingTime()));
-            Context.ResponseData.Write(0);
+
+            Context.ResponseData.Write((byte)(CurrentTime.IsDaylightSavingTime() ? 1 : 0));
+
+            Context.ResponseData.Write((int)TimeZoneInfo.Local.GetUtcOffset(CurrentTime).TotalSeconds);
 
             return 0;
         }
diff --git a/Ryujinx.Core/OsHle/Services/Time/SystemClockType.cs b/Ryujinx.Core/OsHle/Services/Time/SystemClockType.cs
index 2314942a3..7b5074ba7 100644
--- a/Ryujinx.Core/OsHle/Services/Time/SystemClockType.cs
+++ b/Ryujinx.Core/OsHle/Services/Time/SystemClockType.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Core.OsHle.IpcServices.Time
+namespace Ryujinx.Core.OsHle.Services.Time
 {
     enum SystemClockType
     {
diff --git a/Ryujinx.Core/OsHle/Services/Vi/Display.cs b/Ryujinx.Core/OsHle/Services/Vi/Display.cs
index ceadc3931..0dbb2eda0 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/Display.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/Display.cs
@@ -1,4 +1,4 @@
-namespace Ryujinx.Core.OsHle.IpcServices.Vi
+namespace Ryujinx.Core.OsHle.Services.Vi
 {
     class Display
     {
diff --git a/Ryujinx.Core/OsHle/Services/Vi/GbpBuffer.cs b/Ryujinx.Core/OsHle/Services/Vi/GbpBuffer.cs
index 5fe585d06..cae310544 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/GbpBuffer.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/GbpBuffer.cs
@@ -1,6 +1,6 @@
 using System.IO;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Android
+namespace Ryujinx.Core.OsHle.Services.Android
 {
     struct GbpBuffer
     {
diff --git a/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs b/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs
index 0ff1f9099..b92dc16c4 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs
@@ -1,19 +1,17 @@
 using ChocolArm64.Memory;
-using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 using System.IO;
 
-using static Ryujinx.Core.OsHle.IpcServices.Android.Parcel;
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
+using static Ryujinx.Core.OsHle.Services.Android.Parcel;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Vi
+namespace Ryujinx.Core.OsHle.Services.Vi
 {
-    class IApplicationDisplayService : IIpcService
+    class IApplicationDisplayService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         private IdDictionary Displays;
 
@@ -27,6 +25,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
                 {  103, GetIndirectDisplayTransactionService },
                 { 1010, OpenDisplay                          },
                 { 1020, CloseDisplay                         },
+                { 1102, GetDisplayResolution                 },
                 { 2020, OpenLayer                            },
                 { 2021, CloseLayer                           },
                 { 2030, CreateStrayLayer                     },
@@ -86,6 +85,16 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
             return 0;
         }
 
+        public long GetDisplayResolution(ServiceCtx Context)
+        {
+            long DisplayId = Context.RequestData.ReadInt32();
+
+            Context.ResponseData.Write(1280);
+            Context.ResponseData.Write(720);
+
+            return 0;
+        }
+
         public long OpenLayer(ServiceCtx Context)
         {
             long LayerId = Context.RequestData.ReadInt64();
@@ -145,7 +154,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
         {
             string Name = GetDisplayName(Context);
 
-            int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
+            int Handle = Context.Process.HandleTable.OpenHandle(Context.Ns.Os.VsyncEvent);
 
             Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
 
diff --git a/Ryujinx.Core/OsHle/Services/Vi/IApplicationRootService.cs b/Ryujinx.Core/OsHle/Services/Vi/IApplicationRootService.cs
new file mode 100644
index 000000000..d92b2d9d3
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Vi/IApplicationRootService.cs
@@ -0,0 +1,29 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Vi
+{
+    class IApplicationRootService : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public IApplicationRootService()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                { 0, GetDisplayService }
+            };
+        }
+
+        public long GetDisplayService(ServiceCtx Context)
+        {
+            int ServiceType = Context.RequestData.ReadInt32();
+
+            MakeObject(Context, new IApplicationDisplayService());
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs b/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs
index b24a773bf..72e8ef198 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs
@@ -1,17 +1,20 @@
 using ChocolArm64.Memory;
+using Ryujinx.Core.OsHle.Handles;
 using Ryujinx.Core.OsHle.Ipc;
-using Ryujinx.Core.OsHle.IpcServices.Android;
+using Ryujinx.Core.OsHle.Services.Android;
 using Ryujinx.Graphics.Gal;
 using System;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Vi
+namespace Ryujinx.Core.OsHle.Services.Vi
 {
-    class IHOSBinderDriver : IIpcService, IDisposable
+    class IHOSBinderDriver : IpcService, IDisposable
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        private KEvent ReleaseEvent;
 
         private NvFlinger Flinger;
 
@@ -19,12 +22,15 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                { 0, TransactParcel  },
-                { 1, AdjustRefcount  },
-                { 2, GetNativeHandle }
+                { 0, TransactParcel     },
+                { 1, AdjustRefcount     },
+                { 2, GetNativeHandle    },
+                { 3, TransactParcelAuto }
             };
 
-            Flinger = new NvFlinger(Renderer);
+            ReleaseEvent = new KEvent();
+
+            Flinger = new NvFlinger(Renderer, ReleaseEvent);
         }
 
         public long TransactParcel(ServiceCtx Context)
@@ -35,7 +41,25 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
             long DataPos  = Context.Request.SendBuff[0].Position;
             long DataSize = Context.Request.SendBuff[0].Size;
 
-            byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, (int)DataSize);
+            byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, DataSize);
+
+            Data = Parcel.GetParcelData(Data);
+
+            return Flinger.ProcessParcelRequest(Context, Data, Code);
+        }
+
+        //TransactParcelAuto(i32, u32, u32, buffer<unknown, 0x21, 0>) -> buffer<unknown, 0x22, 0>
+        //Buffer C (PtrBuff) and X (ReceiveListBuff) can be used here...
+        //But they are all null during all my tests.
+        public long TransactParcelAuto(ServiceCtx Context)
+        {
+            int Id   = Context.RequestData.ReadInt32();
+            int Code = Context.RequestData.ReadInt32();
+
+            long DataPos  = Context.Request.SendBuff[0].Position;
+            long DataSize = Context.Request.SendBuff[0].Size;
+
+            byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, DataSize);
 
             Data = Parcel.GetParcelData(Data);
 
@@ -56,7 +80,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
             int  Id  = Context.RequestData.ReadInt32();
             uint Unk = Context.RequestData.ReadUInt32();
 
-            Context.Response.HandleDesc = IpcHandleDesc.MakeMove(0xbadcafe);
+            int Handle = Context.Process.HandleTable.OpenHandle(ReleaseEvent);
+
+            Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
 
             return 0;
         }
@@ -66,10 +92,12 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
             Dispose(true);
         }
 
-        protected virtual void Dispose(bool disposing)
+        protected virtual void Dispose(bool Disposing)
         {
-            if (disposing)
+            if (Disposing)
             {
+                ReleaseEvent.Dispose();
+
                 Flinger.Dispose();
             }
         }
diff --git a/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs b/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs
index 69dbff47a..3c7925010 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs
@@ -1,13 +1,13 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Vi
+namespace Ryujinx.Core.OsHle.Services.Vi
 {
-    class IManagerDisplayService : IIpcService
+    class IManagerDisplayService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public IManagerDisplayService()
         {
diff --git a/Ryujinx.Core/OsHle/Services/Vi/ServiceVi.cs b/Ryujinx.Core/OsHle/Services/Vi/IManagerRootService.cs
similarity index 60%
rename from Ryujinx.Core/OsHle/Services/Vi/ServiceVi.cs
rename to Ryujinx.Core/OsHle/Services/Vi/IManagerRootService.cs
index 2c3dd2a3c..177e5e666 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/ServiceVi.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/IManagerRootService.cs
@@ -1,17 +1,15 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Vi
+namespace Ryujinx.Core.OsHle.Services.Vi
 {
-    class ServiceVi : IIpcService
+    class IManagerRootService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
-        public ServiceVi()
+        public IManagerRootService()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
@@ -21,7 +19,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
 
         public long GetDisplayService(ServiceCtx Context)
         {
-            int Unknown = Context.RequestData.ReadInt32();
+            int ServiceType = Context.RequestData.ReadInt32();
 
             MakeObject(Context, new IApplicationDisplayService());
 
diff --git a/Ryujinx.Core/OsHle/Services/Vi/ISystemDisplayService.cs b/Ryujinx.Core/OsHle/Services/Vi/ISystemDisplayService.cs
index d87fcbf6e..3bdeb32a6 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/ISystemDisplayService.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/ISystemDisplayService.cs
@@ -1,19 +1,20 @@
 using Ryujinx.Core.OsHle.Ipc;
 using System.Collections.Generic;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Vi
+namespace Ryujinx.Core.OsHle.Services.Vi
 {
-    class ISystemDisplayService : IIpcService
+    class ISystemDisplayService : IpcService
     {
         private Dictionary<int, ServiceProcessRequest> m_Commands;
 
-        public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
 
         public ISystemDisplayService()
         {
             m_Commands = new Dictionary<int, ServiceProcessRequest>()
             {
-                { 2205, SetLayerZ }
+                { 2205, SetLayerZ },
+                { 2207, SetLayerVisibility }
             };
         }
 
@@ -21,5 +22,10 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
         {
             return 0;
         }
+
+        public static long SetLayerVisibility(ServiceCtx Context)
+        {
+            return 0;
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Vi/ISystemRootService.cs b/Ryujinx.Core/OsHle/Services/Vi/ISystemRootService.cs
new file mode 100644
index 000000000..47123a556
--- /dev/null
+++ b/Ryujinx.Core/OsHle/Services/Vi/ISystemRootService.cs
@@ -0,0 +1,29 @@
+using Ryujinx.Core.OsHle.Ipc;
+using System.Collections.Generic;
+
+namespace Ryujinx.Core.OsHle.Services.Vi
+{
+    class ISystemRootService : IpcService
+    {
+        private Dictionary<int, ServiceProcessRequest> m_Commands;
+
+        public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
+
+        public ISystemRootService()
+        {
+            m_Commands = new Dictionary<int, ServiceProcessRequest>()
+            {
+                { 1, GetDisplayService }
+            };
+        }
+
+        public long GetDisplayService(ServiceCtx Context)
+        {
+            int ServiceType = Context.RequestData.ReadInt32();
+
+            MakeObject(Context, new IApplicationDisplayService());
+
+            return 0;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
index 5309dcabb..4dc019971 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs
@@ -1,15 +1,16 @@
 using ChocolArm64.Memory;
-using Ryujinx.Core.OsHle.IpcServices.NvServices;
+using Ryujinx.Core.OsHle.Handles;
+using Ryujinx.Core.OsHle.Services.Nv;
 using Ryujinx.Graphics.Gal;
+using Ryujinx.Graphics.Gpu;
 using System;
-using System.IO;
 using System.Collections.Generic;
+using System.IO;
 using System.Text;
 using System.Threading;
+using static Ryujinx.Core.OsHle.Services.Android.Parcel;
 
-using static Ryujinx.Core.OsHle.IpcServices.Android.Parcel;
-
-namespace Ryujinx.Core.OsHle.IpcServices.Android
+namespace Ryujinx.Core.OsHle.Services.Android
 {
     class NvFlinger : IDisposable
     {
@@ -17,6 +18,10 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
 
         private Dictionary<(string, int), ServiceProcessParcel> Commands;
 
+        private KEvent ReleaseEvent;
+
+        private IGalRenderer Renderer;
+
         private const int BufferQueueCount = 0x40;
         private const int BufferQueueMask  = BufferQueueCount - 1;
 
@@ -55,21 +60,13 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
             public GbpBuffer Data;
         }
 
-        private IGalRenderer Renderer;
-
         private BufferEntry[] BufferQueue;
 
         private ManualResetEvent WaitBufferFree;
-        
-        private object RenderQueueLock;
 
-        private int RenderQueueCount;
+        private bool Disposed;
 
-        private bool NvFlingerDisposed;
-
-        private bool KeepRunning;
-
-        public NvFlinger(IGalRenderer Renderer)
+        public NvFlinger(IGalRenderer Renderer, KEvent ReleaseEvent)
         {
             Commands = new Dictionary<(string, int), ServiceProcessParcel>()
             {
@@ -84,15 +81,12 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
                 { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
             };
 
-            this.Renderer = Renderer;
+            this.Renderer     = Renderer;
+            this.ReleaseEvent = ReleaseEvent;
 
             BufferQueue = new BufferEntry[0x40];
 
             WaitBufferFree = new ManualResetEvent(false);
-
-            RenderQueueLock = new object();
-
-            KeepRunning = true;
         }
 
         public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code)
@@ -118,7 +112,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
 
                 if (Commands.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq))
                 {
-                    Logging.Debug($"{InterfaceName} {ProcReq.Method.Name}");
+                    Logging.Debug(LogClass.ServiceNv, $"{InterfaceName} {ProcReq.Method.Name}");
 
                     return ProcReq(Context, Reader);
                 }
@@ -136,7 +130,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
             using (MemoryStream MS = new MemoryStream())
             {
                 BinaryWriter Writer = new BinaryWriter(MS);
-                
+
                 BufferEntry Entry = BufferQueue[Slot];
 
                 int  BufferCount = 1; //?
@@ -240,13 +234,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
         private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader)
         {
             int Slot = ParcelReader.ReadInt32();
-            
-            int  BufferCount = ParcelReader.ReadInt32();
-            long BufferSize  = ParcelReader.ReadInt64();
 
-            BufferQueue[Slot].State = BufferState.Free;
+            int BufferCount = ParcelReader.ReadInt32();
 
-            BufferQueue[Slot].Data = new GbpBuffer(ParcelReader);
+            if (BufferCount > 0)
+            {
+                long BufferSize = ParcelReader.ReadInt64();
+
+                BufferQueue[Slot].State = BufferState.Free;
+
+                BufferQueue[Slot].Data = new GbpBuffer(ParcelReader);
+            }
 
             return MakeReplyParcel(Context, 0);
         }
@@ -278,24 +276,24 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
             return 0;
         }
 
-        private unsafe void SendFrameBuffer(ServiceCtx Context, int Slot)
+        private void SendFrameBuffer(ServiceCtx Context, int Slot)
         {
-            int FbWidth  = BufferQueue[Slot].Data.Width;
-            int FbHeight = BufferQueue[Slot].Data.Height;
+            int FbWidth  = 1280;
+            int FbHeight = 720;
 
-            long FbSize = (uint)FbWidth * FbHeight * 4;
+            NvMap Map = GetNvMap(Context, Slot);
 
-            NvMap NvMap = GetNvMap(Context, Slot);
+            NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0);
 
-            if ((ulong)(NvMap.Address + FbSize) > AMemoryMgr.AddrSize)
+            long CpuAddr = Map.CpuAddress;
+            long GpuAddr = Map.GpuAddress;
+
+            if (MapFb.HasBufferOffset(Slot))
             {
-                Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!");
+                CpuAddr += MapFb.GetBufferOffset(Slot);
 
-                BufferQueue[Slot].State = BufferState.Free;
-
-                WaitBufferFree.Set();
-
-                return;
+                //TODO: Enable once the frame buffers problems are fixed.
+                //GpuAddr += MapFb.GetBufferOffset(Slot);
             }
 
             BufferQueue[Slot].State = BufferState.Acquired;
@@ -349,39 +347,28 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
                 Rotate = -MathF.PI * 0.5f;
             }
 
-            lock (RenderQueueLock)
-            {
-                if (NvFlingerDisposed)
-                {
-                    return;
-                }
+            Renderer.SetFrameBufferTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY);
 
-                Interlocked.Increment(ref RenderQueueCount);
+            //TODO: Support double buffering here aswell, it is broken for GPU
+            //frame buffers because it seems to be completely out of sync.
+            if (Context.Ns.Gpu.Engine3d.IsFrameBufferPosition(GpuAddr))
+            {
+                //Frame buffer is rendered to by the GPU, we can just
+                //bind the frame buffer texture, it's not necessary to read anything.
+                Renderer.SetFrameBuffer(GpuAddr);
+            }
+            else
+            {
+                //Frame buffer is not set on the GPU registers, in this case
+                //assume that the app is manually writing to it.
+                Texture Texture = new Texture(CpuAddr, FbWidth, FbHeight);
+
+                byte[] Data = TextureReader.Read(Context.Memory, Texture);
+
+                Renderer.SetFrameBuffer(Data, FbWidth, FbHeight);
             }
 
-            byte* Fb = (byte*)Context.Memory.Ram + NvMap.Address;
-
-            Context.Ns.Gpu.Renderer.QueueAction(delegate()
-            {
-                Context.Ns.Gpu.Renderer.SetFrameBuffer(
-                    Fb,
-                    FbWidth,
-                    FbHeight,
-                    ScaleX,
-                    ScaleY,
-                    OffsX,
-                    OffsY,
-                    Rotate);
-
-                BufferQueue[Slot].State = BufferState.Free;
-
-                Interlocked.Decrement(ref RenderQueueCount);
-
-                lock (WaitBufferFree)
-                {
-                    WaitBufferFree.Set();
-                }
-            });
+            Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot));
         }
 
         private NvMap GetNvMap(ServiceCtx Context, int Slot)
@@ -397,9 +384,19 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
                 NvMapHandle = BitConverter.ToInt32(RawValue, 0);
             }
 
-            ServiceNvDrv NvDrv = (ServiceNvDrv)Context.Process.Services.GetService("nvdrv");
+            return INvDrvServices.NvMaps.GetData<NvMap>(Context.Process, NvMapHandle);
+        }
 
-            return NvDrv.GetNvMap(NvMapHandle);
+        private void ReleaseBuffer(int Slot)
+        {
+            BufferQueue[Slot].State = BufferState.Free;
+
+            ReleaseEvent.WaitEvent.Set();
+
+            lock (WaitBufferFree)
+            {
+                WaitBufferFree.Set();
+            }
         }
 
         private int GetFreeSlotBlocking(int Width, int Height)
@@ -415,9 +412,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
                         break;
                     }
 
-                    Logging.Debug("Waiting for a free BufferQueue slot...");
+                    Logging.Debug(LogClass.ServiceNv, "Waiting for a free BufferQueue slot...");
 
-                    if (!KeepRunning)
+                    if (Disposed)
                     {
                         break;
                     }
@@ -427,9 +424,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
 
                 WaitBufferFree.WaitOne();
             }
-            while (KeepRunning);
+            while (!Disposed);
 
-            Logging.Debug($"Found free BufferQueue slot {Slot}!");
+            Logging.Debug(LogClass.ServiceNv, $"Found free BufferQueue slot {Slot}!");
 
             return Slot;
         }
@@ -467,26 +464,12 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
 
         protected virtual void Dispose(bool Disposing)
         {
-            if (Disposing && !NvFlingerDisposed)
+            if (Disposing && !Disposed)
             {
-                lock (RenderQueueLock)
-                {
-                    NvFlingerDisposed = true;
-                }
-
-                //Ensure that all pending actions was sent before
-                //we can safely assume that the class was disposed.
-                while (RenderQueueCount > 0)
-                {
-                    Thread.Yield();
-                }
-
-                Renderer.ResetFrameBuffer();
+                Disposed = true;
 
                 lock (WaitBufferFree)
                 {
-                    KeepRunning = false;
-
                     WaitBufferFree.Set();
                 }
 
diff --git a/Ryujinx.Core/OsHle/Services/Vi/Parcel.cs b/Ryujinx.Core/OsHle/Services/Vi/Parcel.cs
index 3404c227e..1300a741d 100644
--- a/Ryujinx.Core/OsHle/Services/Vi/Parcel.cs
+++ b/Ryujinx.Core/OsHle/Services/Vi/Parcel.cs
@@ -1,7 +1,7 @@
 using System;
 using System.IO;
 
-namespace Ryujinx.Core.OsHle.IpcServices.Android
+namespace Ryujinx.Core.OsHle.Services.Android
 {
     static class Parcel
     {
diff --git a/Ryujinx.Core/OsHle/Svc/SvcThread.cs b/Ryujinx.Core/OsHle/Svc/SvcThread.cs
deleted file mode 100644
index 231ee2a23..000000000
--- a/Ryujinx.Core/OsHle/Svc/SvcThread.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-using ChocolArm64.State;
-using Ryujinx.Core.OsHle.Handles;
-
-namespace Ryujinx.Core.OsHle.Svc
-{
-    partial class SvcHandler
-    {
-        private void SvcCreateThread(AThreadState ThreadState)
-        {
-            long EntryPoint  = (long)ThreadState.X1;
-            long ArgsPtr     = (long)ThreadState.X2;
-            long StackTop    = (long)ThreadState.X3;
-            int  Priority    =  (int)ThreadState.X4;
-            int  ProcessorId =  (int)ThreadState.X5;
-
-            if (Ns.Os.TryGetProcess(ThreadState.ProcessId, out Process Process))
-            {
-                if (ProcessorId == -2)
-                {
-                    //TODO: Get this value from the NPDM file.
-                    ProcessorId = 0;
-                }
-
-                int Handle = Process.MakeThread(
-                    EntryPoint,
-                    StackTop,
-                    ArgsPtr,
-                    Priority,
-                    ProcessorId);
-
-                ThreadState.X0 = 0;
-                ThreadState.X1 = (ulong)Handle;
-            }
-
-            //TODO: Error codes.
-        }
-
-        private void SvcStartThread(AThreadState ThreadState)
-        {
-            int Handle = (int)ThreadState.X0;
-
-            HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
-
-            if (Thread != null)
-            {
-                Process.Scheduler.StartThread(Thread);
-
-                ThreadState.X0 = 0;
-            }
-
-            //TODO: Error codes.
-        }
-
-        private void SvcExitThread(AThreadState ThreadState)
-        {
-            HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
-            
-            CurrThread.Thread.StopExecution();
-        }
-
-        private void SvcSleepThread(AThreadState ThreadState)
-        {           
-            ulong NanoSecs = ThreadState.X0;
-
-            HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
-            
-            if (NanoSecs == 0)
-            {
-                Process.Scheduler.Yield(CurrThread);
-            }
-            else
-            {
-                Process.Scheduler.WaitForSignal(CurrThread, (int)(NanoSecs / 1000000));
-            }
-        }
-
-        private void SvcGetThreadPriority(AThreadState ThreadState)
-        {
-            int Handle = (int)ThreadState.X1;
-
-            HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
-
-            if (Thread != null)
-            {
-                ThreadState.X0 = 0;
-                ThreadState.X1 = (ulong)Thread.Priority;
-            }
-
-            //TODO: Error codes.
-        }
-
-        private void SvcSetThreadPriority(AThreadState ThreadState)
-        {
-            int Handle = (int)ThreadState.X1;
-            int Prio = (int)ThreadState.X0;
-
-            HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
-
-            if (Thread != null)
-            {
-                Thread.Priority = Prio;
-
-                ThreadState.X0 = 0;
-            }
-
-            //TODO: Error codes.
-        }
-
-        private void SvcSetThreadCoreMask(AThreadState ThreadState)
-        {
-            ThreadState.X0 = 0;
-
-            //TODO: Error codes.
-        }
-
-        private void SvcGetThreadId(AThreadState ThreadState)
-        {
-            int Handle = (int)ThreadState.X0;
-
-            HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
-
-            if (Thread != null)
-            {
-                ThreadState.X0 = 0;
-                ThreadState.X1 = (ulong)Thread.ThreadId;
-            }
-
-            //TODO: Error codes.
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs
deleted file mode 100644
index 38356073e..000000000
--- a/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using ChocolArm64.State;
-using Ryujinx.Core.OsHle.Handles;
-
-using static Ryujinx.Core.OsHle.ErrorCode;
-
-namespace Ryujinx.Core.OsHle.Svc
-{
-    partial class SvcHandler
-    {
-        private void SvcArbitrateLock(AThreadState ThreadState)
-        {
-            int  OwnerThreadHandle      =  (int)ThreadState.X0;
-            long MutexAddress           = (long)ThreadState.X1;
-            int  RequestingThreadHandle =  (int)ThreadState.X2;
-
-            HThread RequestingThread = Process.HandleTable.GetData<HThread>(RequestingThreadHandle);
-
-            Mutex M = new Mutex(Process, MutexAddress, OwnerThreadHandle);
-
-            M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M);
-
-            M.WaitForLock(RequestingThread, RequestingThreadHandle);
-
-            ThreadState.X0 = 0;
-        }
-
-        private void SvcArbitrateUnlock(AThreadState ThreadState)
-        {
-            long MutexAddress = (long)ThreadState.X0;
-
-            if (Ns.Os.Mutexes.TryGetValue(MutexAddress, out Mutex M))
-            {
-                M.Unlock();
-            }
-
-            ThreadState.X0 = 0;
-        }
-
-        private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState)
-        {
-            long MutexAddress   = (long)ThreadState.X0;
-            long CondVarAddress = (long)ThreadState.X1;
-            int  ThreadHandle   =  (int)ThreadState.X2;
-            long Timeout        = (long)ThreadState.X3;
-
-            HThread Thread = Process.HandleTable.GetData<HThread>(ThreadHandle);
-
-            Mutex M = new Mutex(Process, MutexAddress, ThreadHandle);
-
-            M = Ns.Os.Mutexes.GetOrAdd(MutexAddress, M);
-
-            M.GiveUpLock(ThreadHandle);
-
-            CondVar Cv = new CondVar(Process, CondVarAddress, Timeout);
-
-            Cv = Ns.Os.CondVars.GetOrAdd(CondVarAddress, Cv);
-
-            if (!Cv.WaitForSignal(Thread))
-            {
-                ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
-
-                return;
-            }
-
-            M.WaitForLock(Thread, ThreadHandle);
-
-            ThreadState.X0 = 0;
-        }
-
-        private void SvcSignalProcessWideKey(AThreadState ThreadState)
-        {
-            long CondVarAddress = (long)ThreadState.X0;
-            int  Count          =  (int)ThreadState.X1;
-
-            HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
-
-            if (Ns.Os.CondVars.TryGetValue(CondVarAddress, out CondVar Cv))
-            {
-                Cv.SetSignal(CurrThread, Count);
-            }
-
-            ThreadState.X0 = 0;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Core/Ryujinx.Core.csproj b/Ryujinx.Core/Ryujinx.Core.csproj
index 7d5ad7185..b9374af1d 100644
--- a/Ryujinx.Core/Ryujinx.Core.csproj
+++ b/Ryujinx.Core/Ryujinx.Core.csproj
@@ -14,6 +14,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
+    <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
     <ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
   </ItemGroup>
 
diff --git a/Ryujinx.Core/Settings/SetSys.cs b/Ryujinx.Core/Settings/SystemSettings.cs
similarity index 72%
rename from Ryujinx.Core/Settings/SetSys.cs
rename to Ryujinx.Core/Settings/SystemSettings.cs
index d8b6eb6ef..0f56ef3ae 100644
--- a/Ryujinx.Core/Settings/SetSys.cs
+++ b/Ryujinx.Core/Settings/SystemSettings.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.Core.Settings
 {
-    public class SetSys
+    public class SystemSettings
     {
         public ColorSet ThemeColor;
     }
diff --git a/Ryujinx.Core/Switch.cs b/Ryujinx.Core/Switch.cs
index 487f3bdb9..0df8b1260 100644
--- a/Ryujinx.Core/Switch.cs
+++ b/Ryujinx.Core/Switch.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Audio;
 using Ryujinx.Core.Input;
 using Ryujinx.Core.OsHle;
 using Ryujinx.Core.Settings;
@@ -9,32 +10,50 @@ namespace Ryujinx.Core
 {
     public class Switch : IDisposable
     {
-        internal NsGpu     Gpu { get; private set; }
-        internal Horizon   Os  { get; private set; }
-        internal VirtualFs VFs { get; private set; }
+        internal IAalOutput AudioOut { get; private set; }
+
+        internal NsGpu Gpu { get; private set; }
+
+        internal VirtualFileSystem VFs { get; private set; }
+
+        public Horizon Os { get; private set; }
+
+        public SystemSettings Settings { get; private set; }
 
-        public Hid    Hid                       { get; private set; }        
-        public SetSys Settings                  { get; private set; }
         public PerformanceStatistics Statistics { get; private set; }
 
+        public Hid Hid { get; private set; }
+
         public event EventHandler Finish;
 
-        public Switch(IGalRenderer Renderer)
+        public Switch(IGalRenderer Renderer, IAalOutput AudioOut)
         {
-            Gpu = new NsGpu(Renderer);
+            if (Renderer == null)
+            {
+                throw new ArgumentNullException(nameof(Renderer));
+            }
 
-            VFs = new VirtualFs();
+            if (AudioOut == null)
+            {
+                throw new ArgumentNullException(nameof(AudioOut));
+            }
 
-            Hid = new Hid();
+            this.AudioOut = AudioOut;
 
-            Statistics = new PerformanceStatistics();
+            Gpu = new NsGpu(Renderer);            
+
+            VFs = new VirtualFileSystem();
 
             Os = new Horizon(this);
 
+            Settings = new SystemSettings();
+
+            Statistics = new PerformanceStatistics();
+
+            Hid = new Hid();
+
             Os.HidSharedMem.MemoryMapped   += Hid.ShMemMap;
             Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap;
-
-            Settings = new SetSys();
         }
 
         public void LoadCart(string ExeFsDir, string RomFsFile = null)
diff --git a/Ryujinx.Core/VirtualFs.cs b/Ryujinx.Core/VirtualFileSystem.cs
similarity index 98%
rename from Ryujinx.Core/VirtualFs.cs
rename to Ryujinx.Core/VirtualFileSystem.cs
index c0858e0ee..1c717b2ca 100644
--- a/Ryujinx.Core/VirtualFs.cs
+++ b/Ryujinx.Core/VirtualFileSystem.cs
@@ -3,7 +3,7 @@ using System.IO;
 
 namespace Ryujinx.Core
 {
-    class VirtualFs : IDisposable
+    class VirtualFileSystem : IDisposable
     {
         private const string BasePath   = "RyuFs";
         private const string NandPath   = "nand";
diff --git a/Ryujinx.Graphics/Gal/GalBlendEquation.cs b/Ryujinx.Graphics/Gal/GalBlendEquation.cs
new file mode 100644
index 000000000..7fd4ba5fa
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalBlendEquation.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public enum GalBlendEquation
+    {
+        FuncAdd             = 1,
+        FuncSubtract        = 2,
+        FuncReverseSubtract = 3,
+        Min                 = 4,
+        Max                 = 5
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalBlendFactor.cs b/Ryujinx.Graphics/Gal/GalBlendFactor.cs
new file mode 100644
index 000000000..7237c4eda
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalBlendFactor.cs
@@ -0,0 +1,25 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public enum GalBlendFactor
+    {
+        Zero                  = 0x1,
+        One                   = 0x2,
+        SrcColor              = 0x3,
+        OneMinusSrcColor      = 0x4,
+        SrcAlpha              = 0x5,
+        OneMinusSrcAlpha      = 0x6,
+        DstAlpha              = 0x7,
+        OneMinusDstAlpha      = 0x8,
+        DstColor              = 0x9,
+        OneMinusDstColor      = 0xa,
+        SrcAlphaSaturate      = 0xb,
+        Src1Color             = 0x10,
+        OneMinusSrc1Color     = 0x11,
+        Src1Alpha             = 0x12,
+        OneMinusSrc1Alpha     = 0x13,
+        ConstantColor         = 0x61,
+        OneMinusConstantColor = 0x62,
+        ConstantAlpha         = 0x63,
+        OneMinusConstantAlpha = 0x64
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalClearBufferFlags.cs b/Ryujinx.Graphics/Gal/GalClearBufferFlags.cs
new file mode 100644
index 000000000..8565051ca
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalClearBufferFlags.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Ryujinx.Graphics.Gal
+{
+    [Flags]
+    public enum GalClearBufferFlags
+    {
+        Depth      = 1 << 0,
+        Stencil    = 1 << 1,
+        ColorRed   = 1 << 2,
+        ColorGreen = 1 << 3,
+        ColorBlue  = 1 << 4,
+        ColorAlpha = 1 << 5
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalColorF.cs b/Ryujinx.Graphics/Gal/GalColorF.cs
new file mode 100644
index 000000000..7cfb171dc
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalColorF.cs
@@ -0,0 +1,22 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public struct GalColorF
+    {
+        public float Red   { get; private set; }
+        public float Green { get; private set; }
+        public float Blue  { get; private set; }
+        public float Alpha { get; private set; }
+
+        public GalColorF(
+            float Red,
+            float Green,
+            float Blue,
+            float Alpha)
+        {
+            this.Red   = Red;
+            this.Green = Green;
+            this.Blue  = Blue;
+            this.Alpha = Alpha;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalConsts.cs b/Ryujinx.Graphics/Gal/GalConsts.cs
new file mode 100644
index 000000000..6c8857c6e
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalConsts.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public static class GalConsts
+    {
+        public const string FlipUniformName = "flip";
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalIndexFormat.cs b/Ryujinx.Graphics/Gal/GalIndexFormat.cs
new file mode 100644
index 000000000..71a50cdba
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalIndexFormat.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public enum GalIndexFormat
+    {
+        Byte  = 0,
+        Int16 = 1,
+        Int32 = 2
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalShaderType.cs b/Ryujinx.Graphics/Gal/GalShaderType.cs
new file mode 100644
index 000000000..eb5aaf889
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalShaderType.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public enum GalShaderType
+    {
+        Vertex         = 0,
+        TessControl    = 1,
+        TessEvaluation = 2,
+        Geometry       = 3,
+        Fragment       = 4
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalTexture.cs b/Ryujinx.Graphics/Gal/GalTexture.cs
new file mode 100644
index 000000000..fcf1f1ad2
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalTexture.cs
@@ -0,0 +1,20 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public struct GalTexture
+    {
+        public byte[] Data;
+
+        public int Width;
+        public int Height;
+
+        public GalTextureFormat Format;
+
+        public GalTexture(byte[] Data, int Width, int Height, GalTextureFormat Format)
+        {
+            this.Data   = Data;
+            this.Width  = Width;
+            this.Height = Height;
+            this.Format = Format;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalTextureFilter.cs b/Ryujinx.Graphics/Gal/GalTextureFilter.cs
new file mode 100644
index 000000000..8e9669f00
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalTextureFilter.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public enum GalTextureFilter
+    {
+        Nearest = 1,
+        Linear  = 2
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs
new file mode 100644
index 000000000..37291e18b
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalTextureFormat.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public enum GalTextureFormat
+    {
+        A8B8G8R8 = 0x8,
+        A1B5G5R5 = 0x14,
+        B5G6R5   = 0x15,
+        BC1      = 0x24,
+        BC2      = 0x25,
+        BC3      = 0x26,
+        BC4      = 0x27,
+        BC5      = 0x28
+    }
+}
diff --git a/Ryujinx.Graphics/Gal/GalTextureMipFilter.cs b/Ryujinx.Graphics/Gal/GalTextureMipFilter.cs
new file mode 100644
index 000000000..2123ec7d2
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalTextureMipFilter.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public enum GalTextureMipFilter
+    {
+        None    = 1,
+        Nearest = 2,
+        Linear  = 3
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalTextureSampler.cs b/Ryujinx.Graphics/Gal/GalTextureSampler.cs
new file mode 100644
index 000000000..b9e5c7658
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalTextureSampler.cs
@@ -0,0 +1,33 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public struct GalTextureSampler
+    {
+        public GalTextureWrap AddressU { get; private set; }
+        public GalTextureWrap AddressV { get; private set; }
+        public GalTextureWrap AddressP { get; private set; }
+
+        public GalTextureFilter    MinFilter { get; private set; }
+        public GalTextureFilter    MagFilter { get; private set; }
+        public GalTextureMipFilter MipFilter { get; private set; }
+
+        public GalColorF BorderColor { get; private set; }
+
+        public GalTextureSampler(
+            GalTextureWrap      AddressU,
+            GalTextureWrap      AddressV,
+            GalTextureWrap      AddressP,
+            GalTextureFilter    MinFilter,
+            GalTextureFilter    MagFilter,
+            GalTextureMipFilter MipFilter,
+            GalColorF           BorderColor)
+        {
+            this.AddressU    = AddressU;
+            this.AddressV    = AddressV;
+            this.AddressP    = AddressP;
+            this.MinFilter   = MinFilter;
+            this.MagFilter   = MagFilter;
+            this.MipFilter   = MipFilter;
+            this.BorderColor = BorderColor;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalTextureWrap.cs b/Ryujinx.Graphics/Gal/GalTextureWrap.cs
new file mode 100644
index 000000000..66e531540
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/GalTextureWrap.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public enum GalTextureWrap
+    {
+        Repeat              = 0,
+        MirroredRepeat      = 1,
+        ClampToEdge         = 2,
+        ClampToBorder       = 3,
+        Clamp               = 4,
+        MirrorClampToEdge   = 5,
+        MirrorClampToBorder = 6,
+        MirrorClamp         = 7
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
index dc38c5934..563e624d5 100644
--- a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
+++ b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs
@@ -2,8 +2,6 @@ namespace Ryujinx.Graphics.Gal
 {
     public struct GalVertexAttrib
     {
-        public int  Index   { get; private set; }
-        public int  Buffer  { get; private set; }
         public bool IsConst { get; private set; }
         public int  Offset  { get; private set; }
 
@@ -13,16 +11,12 @@ namespace Ryujinx.Graphics.Gal
         public bool IsBgra { get; private set; }
 
         public GalVertexAttrib(
-            int                 Index,
-            int                 Buffer,
             bool                IsConst,
             int                 Offset,
             GalVertexAttribSize Size,
             GalVertexAttribType Type,
             bool                IsBgra)
         {
-            this.Index   = Index;
-            this.Buffer  = Buffer;
             this.IsConst = IsConst;
             this.Offset  = Offset;
             this.Size    = Size;
diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs
index aa4eac4e1..af88412a2 100644
--- a/Ryujinx.Graphics/Gal/IGalRenderer.cs
+++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs
@@ -1,29 +1,77 @@
 using System;
+using System.Collections.Generic;
 
 namespace Ryujinx.Graphics.Gal
 {
     public unsafe interface IGalRenderer
     {
         void QueueAction(Action ActionMthd);
+
         void RunActions();
 
-        void InitializeFrameBuffer();
-        void ResetFrameBuffer();
         void Render();
+
         void SetWindowSize(int Width, int Height);
-        void SetFrameBuffer(
-            byte* Fb,
-            int   Width,
-            int   Height,
-            float ScaleX,
-            float ScaleY,
-            float OffsX,
-            float OffsY,
-            float Rotate);
 
-        void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs);
+        //Blend
+        void SetBlendEnable(bool Enable);
 
-        void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height);
+        void SetBlend(
+            GalBlendEquation Equation,
+            GalBlendFactor   FuncSrc,
+            GalBlendFactor   FuncDst);
+
+        void SetBlendSeparate(
+            GalBlendEquation EquationRgb,
+            GalBlendEquation EquationAlpha,
+            GalBlendFactor   FuncSrcRgb,
+            GalBlendFactor   FuncDstRgb,
+            GalBlendFactor   FuncSrcAlpha,
+            GalBlendFactor   FuncDstAlpha);
+
+        //Frame Buffer
+        void CreateFrameBuffer(long Tag, int Width, int Height);
+
+        void BindFrameBuffer(long Tag);
+
+        void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler);
+
+        void SetFrameBuffer(long Tag);
+
+        void SetFrameBuffer(byte[] Data, int Width, int Height);
+
+        void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY);
+
+        void SetViewport(int X, int Y, int Width, int Height);
+
+        //Rasterizer
+        void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
+
+        void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs);
+
+        void SetIndexArray(byte[] Buffer, GalIndexFormat Format);
+
+        void DrawArrays(int VbIndex, GalPrimitiveType PrimType);
+
+        void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType);
+
+        //Shader
+        void CreateShader(long Tag, GalShaderType Type, byte[] Data);
+
+        IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag);
+
+        void SetConstBuffer(long Tag, int Cbuf, byte[] Data);
+
+        void SetUniform1(string UniformName, int Value);
+
+        void SetUniform2F(string UniformName, float X, float Y);
+
+        void BindShader(long Tag);
+
+        void BindProgram();
+
+        //Texture
+        void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler);
 
         void BindTexture(int Index);
     }
diff --git a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs
deleted file mode 100644
index b761811f6..000000000
--- a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs
+++ /dev/null
@@ -1,250 +0,0 @@
-using OpenTK;
-using OpenTK.Graphics.OpenGL;
-using System;
-
-namespace Ryujinx.Graphics.Gal.OpenGL
-{
-    unsafe class FrameBuffer
-    {
-        public int WindowWidth  { get; set; }
-        public int WindowHeight { get; set; }
-
-        private int VtxShaderHandle;
-        private int FragShaderHandle;
-        private int PrgShaderHandle;
-
-        private int TexHandle;
-        private int TexWidth;
-        private int TexHeight;
-
-        private int VaoHandle;
-        private int VboHandle;
-
-        private int[] Pixels;
-
-        private byte* FbPtr;
-
-        private object FbPtrLock;
-
-        public FrameBuffer(int Width, int Height)
-        {
-            if (Width < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Width));
-            }
-
-            if (Height < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Height));
-            }
-
-            FbPtrLock = new object();
-
-            TexWidth  = Width;
-            TexHeight = Height;
-
-            WindowWidth  = Width;
-            WindowHeight = Height;
-
-            SetupShaders();
-            SetupTexture();
-            SetupVertex();
-        }
-
-        private void SetupShaders()
-        {
-            VtxShaderHandle  = GL.CreateShader(ShaderType.VertexShader);
-            FragShaderHandle = GL.CreateShader(ShaderType.FragmentShader);
-
-            string VtxShaderSource  = EmbeddedResource.GetString("GlFbVtxShader");
-            string FragShaderSource = EmbeddedResource.GetString("GlFbFragShader");
-
-            GL.ShaderSource(VtxShaderHandle, VtxShaderSource);
-            GL.ShaderSource(FragShaderHandle, FragShaderSource);
-            GL.CompileShader(VtxShaderHandle);
-            GL.CompileShader(FragShaderHandle);
-
-            PrgShaderHandle = GL.CreateProgram();
-
-            GL.AttachShader(PrgShaderHandle, VtxShaderHandle);
-            GL.AttachShader(PrgShaderHandle, FragShaderHandle);
-            GL.LinkProgram(PrgShaderHandle);
-            GL.UseProgram(PrgShaderHandle);
-
-            int TexUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "tex");
-
-            GL.Uniform1(TexUniformLocation, 0);
-
-            int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size");
-
-            GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f));
-        }
-
-        private void SetupTexture()
-        {
-            Pixels = new int[TexWidth * TexHeight];
-
-            if (TexHandle == 0)
-            {
-                TexHandle = GL.GenTexture();
-            }
-
-            GL.BindTexture(TextureTarget.Texture2D, TexHandle);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
-            GL.TexImage2D(TextureTarget.Texture2D,
-                0,
-                PixelInternalFormat.Rgba,
-                TexWidth,
-                TexHeight,
-                0,
-                PixelFormat.Rgba,
-                PixelType.UnsignedByte,
-                IntPtr.Zero);
-        }
-
-        private void SetupVertex()
-        {
-            VaoHandle = GL.GenVertexArray();
-            VboHandle = GL.GenBuffer();
-
-            float[] Buffer = new float[]
-            {
-                -1,  1,  0,  0,
-                 1,  1,  1,  0,
-                -1, -1,  0,  1,
-                 1, -1,  1,  1
-            };
-
-            IntPtr Length = new IntPtr(Buffer.Length * 4);
-
-            GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
-            GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
-            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
-
-            GL.BindVertexArray(VaoHandle);
-
-            GL.EnableVertexAttribArray(0);
-
-            GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
-
-            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 16, 0);
-
-            GL.EnableVertexAttribArray(1);
-
-            GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
-
-            GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8);
-
-            GL.BindVertexArray(0);
-        }
-
-        public unsafe void Set(byte* Fb, int Width, int Height, Matrix2 Transform, Vector2 Offs)
-        {
-            if (Fb == null)
-            {
-                throw new ArgumentNullException(nameof(Fb));
-            }
-
-            if (Width < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Width));
-            }
-
-            if (Height < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Height));
-            }
-
-            lock (FbPtrLock)
-            {
-                FbPtr = Fb;
-            }
-
-            if (Width  != TexWidth ||
-                Height != TexHeight)
-            {
-                TexWidth  = Width;
-                TexHeight = Height;
-
-                SetupTexture();
-            }
-
-            GL.UseProgram(PrgShaderHandle);
-
-            int TransformUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "transform");
-
-            GL.UniformMatrix2(TransformUniformLocation, false, ref Transform);
-
-            int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size");
-
-            GL.Uniform2(WindowSizeUniformLocation, new Vector2(WindowWidth, WindowHeight));
-
-            int OffsetUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "offset");
-
-            GL.Uniform2(OffsetUniformLocation, Offs);
-        }
-
-        public void Reset()
-        {
-            lock (FbPtrLock)
-            {
-                FbPtr = null;
-            }
-        }
-
-        public void Render()
-        {
-            lock (FbPtrLock)
-            {
-                if (FbPtr == null)
-                {
-                    return;
-                }
-
-                for (int Y = 0; Y < TexHeight; Y++)
-                for (int X = 0; X < TexWidth;  X++)
-                {
-                    Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y)));
-                }
-            }
-
-            GL.BindTexture(TextureTarget.Texture2D, TexHandle);
-            GL.TexSubImage2D(TextureTarget.Texture2D,
-                0,
-                0,
-                0,
-                TexWidth,
-                TexHeight,
-                PixelFormat.Rgba,
-                PixelType.UnsignedByte,
-                Pixels);
-            
-            GL.ActiveTexture(TextureUnit.Texture0);
-
-            GL.BindVertexArray(VaoHandle);
-
-            GL.UseProgram(PrgShaderHandle);
-
-            GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
-        }
-
-        private int GetSwizzleOffset(int X, int Y)
-        {
-            int Pos;
-
-            Pos  = (Y & 0x7f) >> 4;
-            Pos += (X >> 4) << 3;
-            Pos += (Y >> 7) * ((TexWidth >> 4) << 3);
-            Pos *= 1024;
-            Pos += ((Y & 0xf) >> 3) << 9;
-            Pos += ((X & 0xf) >> 3) << 8;
-            Pos += ((Y & 0x7) >> 1) << 6;
-            Pos += ((X & 0x7) >> 2) << 5;
-            Pos += ((Y & 0x1) >> 0) << 4;
-            Pos += ((X & 0x3) >> 0) << 2;
-
-            return Pos;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs
new file mode 100644
index 000000000..97ff8e13b
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs
@@ -0,0 +1,49 @@
+using OpenTK.Graphics.OpenGL;
+
+namespace Ryujinx.Graphics.Gal.OpenGL
+{
+    class OGLBlend
+    {
+        public void Enable()
+        {
+            GL.Enable(EnableCap.Blend);
+        }
+
+        public void Disable()
+        {
+            GL.Disable(EnableCap.Blend);
+        }
+
+        public void Set(
+            GalBlendEquation Equation,
+            GalBlendFactor   FuncSrc,
+            GalBlendFactor   FuncDst)
+        {
+            GL.BlendEquation(
+                OGLEnumConverter.GetBlendEquation(Equation));
+
+            GL.BlendFunc(
+                OGLEnumConverter.GetBlendFactorSrc(FuncSrc),
+                OGLEnumConverter.GetBlendFactorDst(FuncDst));
+        }
+
+        public void SetSeparate(
+            GalBlendEquation EquationRgb,
+            GalBlendEquation EquationAlpha,
+            GalBlendFactor   FuncSrcRgb,
+            GalBlendFactor   FuncDstRgb,
+            GalBlendFactor   FuncSrcAlpha,
+            GalBlendFactor   FuncDstAlpha)
+        {
+            GL.BlendEquationSeparate(
+                OGLEnumConverter.GetBlendEquation(EquationRgb),
+                OGLEnumConverter.GetBlendEquation(EquationAlpha));
+
+            GL.BlendFuncSeparate(
+                OGLEnumConverter.GetBlendFactorSrc(FuncSrcRgb),
+                OGLEnumConverter.GetBlendFactorDst(FuncDstRgb),
+                OGLEnumConverter.GetBlendFactorSrc(FuncSrcAlpha),
+                OGLEnumConverter.GetBlendFactorDst(FuncDstAlpha));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
new file mode 100644
index 000000000..4cc0a0397
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs
@@ -0,0 +1,198 @@
+using OpenTK.Graphics.OpenGL;
+using System;
+
+namespace Ryujinx.Graphics.Gal.OpenGL
+{
+    static class OGLEnumConverter
+    {
+        public static DrawElementsType GetDrawElementsType(GalIndexFormat Format)
+        {
+            switch (Format)
+            {
+                case GalIndexFormat.Byte:  return DrawElementsType.UnsignedByte;
+                case GalIndexFormat.Int16: return DrawElementsType.UnsignedShort;
+                case GalIndexFormat.Int32: return DrawElementsType.UnsignedInt;
+            }
+
+            throw new ArgumentException(nameof(Format));
+        }
+
+        public static PrimitiveType GetPrimitiveType(GalPrimitiveType Type)
+        {
+            switch (Type)
+            {
+                case GalPrimitiveType.Points:                 return PrimitiveType.Points;
+                case GalPrimitiveType.Lines:                  return PrimitiveType.Lines;
+                case GalPrimitiveType.LineLoop:               return PrimitiveType.LineLoop;
+                case GalPrimitiveType.LineStrip:              return PrimitiveType.LineStrip;
+                case GalPrimitiveType.Triangles:              return PrimitiveType.Triangles;
+                case GalPrimitiveType.TriangleStrip:          return PrimitiveType.TriangleStrip;
+                case GalPrimitiveType.TriangleFan:            return PrimitiveType.TriangleFan;
+                case GalPrimitiveType.Quads:                  return PrimitiveType.Quads;
+                case GalPrimitiveType.QuadStrip:              return PrimitiveType.QuadStrip;
+                case GalPrimitiveType.Polygon:                return PrimitiveType.Polygon;
+                case GalPrimitiveType.LinesAdjacency:         return PrimitiveType.LinesAdjacency;
+                case GalPrimitiveType.LineStripAdjacency:     return PrimitiveType.LineStripAdjacency;
+                case GalPrimitiveType.TrianglesAdjacency:     return PrimitiveType.TrianglesAdjacency;
+                case GalPrimitiveType.TriangleStripAdjacency: return PrimitiveType.TriangleStripAdjacency;
+                case GalPrimitiveType.Patches:                return PrimitiveType.Patches;
+            }
+
+            throw new ArgumentException(nameof(Type));
+        }
+
+        public static ShaderType GetShaderType(GalShaderType Type)
+        {
+            switch (Type)
+            {
+                case GalShaderType.Vertex:         return ShaderType.VertexShader;
+                case GalShaderType.TessControl:    return ShaderType.TessControlShader;
+                case GalShaderType.TessEvaluation: return ShaderType.TessEvaluationShader;
+                case GalShaderType.Geometry:       return ShaderType.GeometryShader;
+                case GalShaderType.Fragment:       return ShaderType.FragmentShader;
+            }
+
+            throw new ArgumentException(nameof(Type));
+        }
+
+        public static (PixelFormat, PixelType) GetTextureFormat(GalTextureFormat Format)
+        {
+            switch (Format)
+            {
+                case GalTextureFormat.A8B8G8R8: return (PixelFormat.Rgba, PixelType.UnsignedByte);
+                case GalTextureFormat.A1B5G5R5: return (PixelFormat.Rgba, PixelType.UnsignedShort5551);
+                case GalTextureFormat.B5G6R5:   return (PixelFormat.Rgb,  PixelType.UnsignedShort565);
+            }
+
+            throw new NotImplementedException(Format.ToString());
+        }
+
+        public static PixelInternalFormat GetCompressedTextureFormat(GalTextureFormat Format)
+        {
+            switch (Format)
+            {
+                case GalTextureFormat.BC1: return PixelInternalFormat.CompressedRgbaS3tcDxt1Ext;
+                case GalTextureFormat.BC2: return PixelInternalFormat.CompressedRgbaS3tcDxt3Ext;
+                case GalTextureFormat.BC3: return PixelInternalFormat.CompressedRgbaS3tcDxt5Ext;
+                case GalTextureFormat.BC4: return PixelInternalFormat.CompressedRedRgtc1;
+                case GalTextureFormat.BC5: return PixelInternalFormat.CompressedRgRgtc2;
+            }
+
+            throw new NotImplementedException(Format.ToString());
+        }
+
+        public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap)
+        {
+            switch (Wrap)
+            {
+                case GalTextureWrap.Repeat:              return TextureWrapMode.Repeat;
+                case GalTextureWrap.MirroredRepeat:      return TextureWrapMode.MirroredRepeat;
+                case GalTextureWrap.ClampToEdge:         return TextureWrapMode.ClampToEdge;
+                case GalTextureWrap.ClampToBorder:       return TextureWrapMode.ClampToBorder;
+                case GalTextureWrap.Clamp:               return TextureWrapMode.Clamp;
+
+                //TODO: Those needs extensions (and are currently wrong).
+                case GalTextureWrap.MirrorClampToEdge:   return TextureWrapMode.ClampToEdge;
+                case GalTextureWrap.MirrorClampToBorder: return TextureWrapMode.ClampToBorder;
+                case GalTextureWrap.MirrorClamp:         return TextureWrapMode.Clamp;
+            }
+
+            throw new ArgumentException(nameof(Wrap));
+        }
+
+        public static TextureMinFilter GetTextureMinFilter(
+            GalTextureFilter    MinFilter,
+            GalTextureMipFilter MipFilter)
+        {
+            //TODO: Mip (needs mipmap support first).
+            switch (MinFilter)
+            {
+                case GalTextureFilter.Nearest: return TextureMinFilter.Nearest;
+                case GalTextureFilter.Linear:  return TextureMinFilter.Linear;
+            }
+
+            throw new ArgumentException(nameof(MinFilter));
+        }
+
+        public static TextureMagFilter GetTextureMagFilter(GalTextureFilter Filter)
+        {
+            switch (Filter)
+            {
+                case GalTextureFilter.Nearest: return TextureMagFilter.Nearest;
+                case GalTextureFilter.Linear:  return TextureMagFilter.Linear;
+            }
+
+            throw new ArgumentException(nameof(Filter));
+        }
+
+        public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
+        {
+            switch (BlendEquation)
+            {
+                case GalBlendEquation.FuncAdd:             return BlendEquationMode.FuncAdd;
+                case GalBlendEquation.FuncSubtract:        return BlendEquationMode.FuncSubtract;
+                case GalBlendEquation.FuncReverseSubtract: return BlendEquationMode.FuncReverseSubtract;
+                case GalBlendEquation.Min:                 return BlendEquationMode.Min;
+                case GalBlendEquation.Max:                 return BlendEquationMode.Max;
+            }
+
+            throw new ArgumentException(nameof(BlendEquation));
+        }
+
+        public static BlendingFactorSrc GetBlendFactorSrc(GalBlendFactor BlendFactor)
+        {
+            switch (BlendFactor)
+            {
+                case GalBlendFactor.Zero:                  return BlendingFactorSrc.Zero;
+                case GalBlendFactor.One:                   return BlendingFactorSrc.One;
+                case GalBlendFactor.SrcColor:              return BlendingFactorSrc.SrcColor;
+                case GalBlendFactor.OneMinusSrcColor:      return BlendingFactorSrc.OneMinusSrcColor;
+                case GalBlendFactor.DstColor:              return BlendingFactorSrc.DstColor;
+                case GalBlendFactor.OneMinusDstColor:      return BlendingFactorSrc.OneMinusDstColor;
+                case GalBlendFactor.SrcAlpha:              return BlendingFactorSrc.SrcAlpha;
+                case GalBlendFactor.OneMinusSrcAlpha:      return BlendingFactorSrc.OneMinusSrcAlpha;
+                case GalBlendFactor.DstAlpha:              return BlendingFactorSrc.DstAlpha;
+                case GalBlendFactor.OneMinusDstAlpha:      return BlendingFactorSrc.OneMinusDstAlpha;
+                case GalBlendFactor.ConstantColor:         return BlendingFactorSrc.ConstantColor;
+                case GalBlendFactor.OneMinusConstantColor: return BlendingFactorSrc.OneMinusConstantColor;
+                case GalBlendFactor.ConstantAlpha:         return BlendingFactorSrc.ConstantAlpha;
+                case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactorSrc.OneMinusConstantAlpha;
+                case GalBlendFactor.SrcAlphaSaturate:      return BlendingFactorSrc.SrcAlphaSaturate;
+                case GalBlendFactor.Src1Color:             return BlendingFactorSrc.Src1Color;
+                case GalBlendFactor.OneMinusSrc1Color:     return BlendingFactorSrc.OneMinusSrc1Color;
+                case GalBlendFactor.Src1Alpha:             return BlendingFactorSrc.Src1Alpha;
+                case GalBlendFactor.OneMinusSrc1Alpha:     return BlendingFactorSrc.OneMinusSrc1Alpha;
+            }
+
+            throw new ArgumentException(nameof(BlendFactor));
+        }
+
+        public static BlendingFactorDest GetBlendFactorDst(GalBlendFactor BlendFactor)
+        {
+            switch (BlendFactor)
+            {
+                case GalBlendFactor.Zero:                  return BlendingFactorDest.Zero;
+                case GalBlendFactor.One:                   return BlendingFactorDest.One;
+                case GalBlendFactor.SrcColor:              return BlendingFactorDest.SrcColor;
+                case GalBlendFactor.OneMinusSrcColor:      return BlendingFactorDest.OneMinusSrcColor;
+                case GalBlendFactor.DstColor:              return BlendingFactorDest.DstColor;
+                case GalBlendFactor.OneMinusDstColor:      return BlendingFactorDest.OneMinusDstColor;
+                case GalBlendFactor.SrcAlpha:              return BlendingFactorDest.SrcAlpha;
+                case GalBlendFactor.OneMinusSrcAlpha:      return BlendingFactorDest.OneMinusSrcAlpha;
+                case GalBlendFactor.DstAlpha:              return BlendingFactorDest.DstAlpha;
+                case GalBlendFactor.OneMinusDstAlpha:      return BlendingFactorDest.OneMinusDstAlpha;
+                case GalBlendFactor.ConstantColor:         return BlendingFactorDest.ConstantColor;
+                case GalBlendFactor.OneMinusConstantColor: return BlendingFactorDest.OneMinusConstantColor;
+                case GalBlendFactor.ConstantAlpha:         return BlendingFactorDest.ConstantAlpha;
+                case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactorDest.OneMinusConstantAlpha;
+                case GalBlendFactor.SrcAlphaSaturate:      return BlendingFactorDest.SrcAlphaSaturate;
+                case GalBlendFactor.Src1Color:             return BlendingFactorDest.Src1Color;
+                case GalBlendFactor.OneMinusSrc1Color:     return BlendingFactorDest.OneMinusSrc1Color;
+                case GalBlendFactor.Src1Alpha:             return BlendingFactorDest.Src1Alpha;
+                case GalBlendFactor.OneMinusSrc1Alpha:     return BlendingFactorDest.OneMinusSrc1Alpha;
+            }
+
+            throw new ArgumentException(nameof(BlendFactor));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
new file mode 100644
index 000000000..05a7288a8
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLFrameBuffer.cs
@@ -0,0 +1,391 @@
+using OpenTK;
+using OpenTK.Graphics.OpenGL;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Gal.OpenGL
+{
+    class OGLFrameBuffer
+    {
+        private struct Rect
+        {
+            public int X      { get; private set; }
+            public int Y      { get; private set; }
+            public int Width  { get; private set; }
+            public int Height { get; private set; }
+
+            public Rect(int X, int Y, int Width, int Height)
+            {
+                this.X     = X;
+                this.Y     = Y;
+                this.Width = Width;
+                this.Height = Height;
+            }
+        }
+
+        private class FrameBuffer
+        {
+            public int Width  { get; set; }
+            public int Height { get; set; }
+
+            public int Handle    { get; private set; }
+            public int RbHandle  { get; private set; }
+            public int TexHandle { get; private set; }
+
+            public FrameBuffer(int Width, int Height)
+            {
+                this.Width  = Width;
+                this.Height = Height;
+
+                Handle    = GL.GenFramebuffer();
+                RbHandle  = GL.GenRenderbuffer();
+                TexHandle = GL.GenTexture();
+            }
+        }
+
+        private struct ShaderProgram
+        {
+            public int Handle;
+            public int VpHandle;
+            public int FpHandle;
+        }
+
+        private Dictionary<long, FrameBuffer> Fbs;
+
+        private ShaderProgram Shader;
+
+        private Rect Viewport;
+        private Rect Window;
+
+        private bool IsInitialized;
+
+        private int RawFbTexWidth;
+        private int RawFbTexHeight;
+        private int RawFbTexHandle;
+
+        private int CurrFbHandle;
+        private int CurrTexHandle;
+
+        private int VaoHandle;
+        private int VboHandle;
+
+        public OGLFrameBuffer()
+        {
+            Fbs = new Dictionary<long, FrameBuffer>();
+
+            Shader = new ShaderProgram();
+        }
+
+        public void Create(long Tag, int Width, int Height)
+        {
+            //TODO: We should either use the original frame buffer size,
+            //or just remove the Width/Height arguments.
+            Width  = Window.Width;
+            Height = Window.Height;
+
+            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
+            {
+                if (Fb.Width  != Width ||
+                    Fb.Height != Height)
+                {
+                    SetupTexture(Fb.TexHandle, Width, Height);
+
+                    Fb.Width  = Width;
+                    Fb.Height = Height;
+                }
+
+                return;
+            }
+
+            Fb = new FrameBuffer(Width, Height);
+
+            SetupTexture(Fb.TexHandle, Width, Height);
+
+            GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
+
+            GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Fb.RbHandle);
+
+            GL.RenderbufferStorage(
+                RenderbufferTarget.Renderbuffer,
+                RenderbufferStorage.Depth24Stencil8,
+                Width,
+                Height);
+
+            GL.FramebufferRenderbuffer(
+                FramebufferTarget.Framebuffer,
+                FramebufferAttachment.DepthStencilAttachment,
+                RenderbufferTarget.Renderbuffer,
+                Fb.RbHandle);
+
+            GL.FramebufferTexture(
+                FramebufferTarget.Framebuffer,
+                FramebufferAttachment.ColorAttachment0,
+                Fb.TexHandle,
+                0);
+
+            GL.DrawBuffer(DrawBufferMode.ColorAttachment0);
+
+            GL.Viewport(0, 0, Width, Height);
+
+            Fbs.Add(Tag, Fb);
+        }
+
+        public void Bind(long Tag)
+        {
+            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
+            {
+                GL.BindFramebuffer(FramebufferTarget.Framebuffer, Fb.Handle);
+
+                CurrFbHandle = Fb.Handle;
+            }
+        }
+
+        public void BindTexture(long Tag, int Index)
+        {
+            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
+            {
+                GL.ActiveTexture(TextureUnit.Texture0 + Index);
+
+                GL.BindTexture(TextureTarget.Texture2D, Fb.TexHandle);
+            }
+        }
+
+        public void Set(long Tag)
+        {
+            if (Fbs.TryGetValue(Tag, out FrameBuffer Fb))
+            {
+                CurrTexHandle = Fb.TexHandle;
+            }
+        }
+
+        public void Set(byte[] Data, int Width, int Height)
+        {
+            if (RawFbTexHandle == 0)
+            {
+                RawFbTexHandle = GL.GenTexture();
+            }
+
+            if (RawFbTexWidth  != Width ||
+                RawFbTexHeight != Height)
+            {
+                SetupTexture(RawFbTexHandle, Width, Height);
+
+                RawFbTexWidth  = Width;
+                RawFbTexHeight = Height;
+            }
+
+            GL.ActiveTexture(TextureUnit.Texture0);
+
+            GL.BindTexture(TextureTarget.Texture2D, RawFbTexHandle);
+
+            (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
+
+            GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, Width, Height, Format, Type, Data);
+
+            CurrTexHandle = RawFbTexHandle;
+        }
+
+        public void SetTransform(Matrix2 Transform, Vector2 Offs)
+        {
+            EnsureInitialized();
+
+            int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
+
+            GL.UseProgram(Shader.Handle);
+
+            int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform");
+
+            GL.UniformMatrix2(TransformUniformLocation, false, ref Transform);
+
+            int OffsetUniformLocation = GL.GetUniformLocation(Shader.Handle, "offset");
+
+            GL.Uniform2(OffsetUniformLocation, ref Offs);
+
+            GL.UseProgram(CurrentProgram);
+        }
+
+        public void SetWindowSize(int Width, int Height)
+        {
+            int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
+
+            GL.UseProgram(Shader.Handle);
+
+            int WindowSizeUniformLocation = GL.GetUniformLocation(Shader.Handle, "window_size");
+
+            GL.Uniform2(WindowSizeUniformLocation, new Vector2(Width, Height));
+
+            GL.UseProgram(CurrentProgram);
+
+            Window = new Rect(0, 0, Width, Height);
+        }
+
+        public void SetViewport(int X, int Y, int Width, int Height)
+        {
+            Viewport = new Rect(X, Y, Width, Height);
+
+            //TODO
+        }
+
+        public void Render()
+        {
+            if (CurrTexHandle != 0)
+            {
+                EnsureInitialized();
+
+                bool AlphaBlendEnable = GL.GetInteger(GetPName.Blend) != 0;
+
+                GL.Disable(EnableCap.Blend);
+
+                GL.ActiveTexture(TextureUnit.Texture0);
+
+                GL.BindTexture(TextureTarget.Texture2D, CurrTexHandle);
+
+                int CurrentProgram = GL.GetInteger(GetPName.CurrentProgram);
+
+                GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
+
+                SetViewport(Window);
+
+                GL.Clear(
+                    ClearBufferMask.ColorBufferBit |
+                    ClearBufferMask.DepthBufferBit);
+
+                GL.BindVertexArray(VaoHandle);
+
+                GL.UseProgram(Shader.Handle);
+
+                GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
+
+                //Restore the original state.
+                GL.BindFramebuffer(FramebufferTarget.Framebuffer, CurrFbHandle);
+
+                GL.UseProgram(CurrentProgram);
+
+                if (AlphaBlendEnable)
+                {
+                    GL.Enable(EnableCap.Blend);
+                }
+
+                //GL.Viewport(0, 0, 1280, 720);
+            }
+        }
+
+        private void SetViewport(Rect Viewport)
+        {
+            GL.Viewport(
+                Viewport.X,
+                Viewport.Y,
+                Viewport.Width,
+                Viewport.Height);
+        }
+
+        private void EnsureInitialized()
+        {
+            if (!IsInitialized)
+            {
+                IsInitialized = true;
+
+                SetupShader();
+                SetupVertex();
+            }
+        }
+
+        private void SetupShader()
+        {
+            Shader.VpHandle = GL.CreateShader(ShaderType.VertexShader);
+            Shader.FpHandle = GL.CreateShader(ShaderType.FragmentShader);
+
+            string VpSource = EmbeddedResource.GetString("GlFbVtxShader");
+            string FpSource = EmbeddedResource.GetString("GlFbFragShader");
+
+            GL.ShaderSource(Shader.VpHandle, VpSource);
+            GL.ShaderSource(Shader.FpHandle, FpSource);
+            GL.CompileShader(Shader.VpHandle);
+            GL.CompileShader(Shader.FpHandle);
+
+            Shader.Handle = GL.CreateProgram();
+
+            GL.AttachShader(Shader.Handle, Shader.VpHandle);
+            GL.AttachShader(Shader.Handle, Shader.FpHandle);
+            GL.LinkProgram(Shader.Handle);
+            GL.UseProgram(Shader.Handle);
+
+            Matrix2 Transform = Matrix2.Identity;
+
+            int TexUniformLocation = GL.GetUniformLocation(Shader.Handle, "tex");
+
+            GL.Uniform1(TexUniformLocation, 0);
+
+            int WindowSizeUniformLocation = GL.GetUniformLocation(Shader.Handle, "window_size");
+
+            GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f));
+
+            int TransformUniformLocation = GL.GetUniformLocation(Shader.Handle, "transform");
+
+            GL.UniformMatrix2(TransformUniformLocation, false, ref Transform);
+        }
+
+        private void SetupVertex()
+        {
+            VaoHandle = GL.GenVertexArray();
+            VboHandle = GL.GenBuffer();
+
+            float[] Buffer = new float[]
+            {
+                -1,  1,  0,  0,
+                 1,  1,  1,  0,
+                -1, -1,  0,  1,
+                 1, -1,  1,  1
+            };
+
+            IntPtr Length = new IntPtr(Buffer.Length * 4);
+
+            GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
+            GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
+            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
+
+            GL.BindVertexArray(VaoHandle);
+
+            GL.EnableVertexAttribArray(0);
+
+            GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
+
+            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 16, 0);
+
+            GL.EnableVertexAttribArray(1);
+
+            GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
+
+            GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8);
+        }
+
+        private void SetupTexture(int Handle, int Width, int Height)
+        {
+            GL.BindTexture(TextureTarget.Texture2D, Handle);
+
+            const int MinFilter = (int)TextureMinFilter.Linear;
+            const int MagFilter = (int)TextureMagFilter.Linear;
+
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);
+
+            (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(GalTextureFormat.A8B8G8R8);
+
+            const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
+
+            const int Level  = 0;
+            const int Border = 0;
+
+            GL.TexImage2D(
+                TextureTarget.Texture2D,
+                Level,
+                InternalFmt,
+                Width,
+                Height,
+                Border,
+                Format,
+                Type,
+                IntPtr.Zero);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
new file mode 100644
index 000000000..9e0d45233
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs
@@ -0,0 +1,231 @@
+using OpenTK.Graphics.OpenGL;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Gal.OpenGL
+{
+    class OGLRasterizer
+    {
+        private static Dictionary<GalVertexAttribSize, int> AttribElements =
+                   new Dictionary<GalVertexAttribSize, int>()
+        {
+            { GalVertexAttribSize._32_32_32_32, 4 },
+            { GalVertexAttribSize._32_32_32,    3 },
+            { GalVertexAttribSize._16_16_16_16, 4 },
+            { GalVertexAttribSize._32_32,       2 },
+            { GalVertexAttribSize._16_16_16,    3 },
+            { GalVertexAttribSize._8_8_8_8,     4 },
+            { GalVertexAttribSize._16_16,       2 },
+            { GalVertexAttribSize._32,          1 },
+            { GalVertexAttribSize._8_8_8,       3 },
+            { GalVertexAttribSize._8_8,         2 },
+            { GalVertexAttribSize._16,          1 },
+            { GalVertexAttribSize._8,           1 },
+            { GalVertexAttribSize._10_10_10_2,  4 },
+            { GalVertexAttribSize._11_11_10,    3 }
+        };
+
+        private static Dictionary<GalVertexAttribSize, VertexAttribPointerType> AttribTypes =
+                   new Dictionary<GalVertexAttribSize, VertexAttribPointerType>()
+        {
+            { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int   },
+            { GalVertexAttribSize._32_32_32,    VertexAttribPointerType.Int   },
+            { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.Short },
+            { GalVertexAttribSize._32_32,       VertexAttribPointerType.Int   },
+            { GalVertexAttribSize._16_16_16,    VertexAttribPointerType.Short },
+            { GalVertexAttribSize._8_8_8_8,     VertexAttribPointerType.Byte  },
+            { GalVertexAttribSize._16_16,       VertexAttribPointerType.Short },
+            { GalVertexAttribSize._32,          VertexAttribPointerType.Int   },
+            { GalVertexAttribSize._8_8_8,       VertexAttribPointerType.Byte  },
+            { GalVertexAttribSize._8_8,         VertexAttribPointerType.Byte  },
+            { GalVertexAttribSize._16,          VertexAttribPointerType.Short },
+            { GalVertexAttribSize._8,           VertexAttribPointerType.Byte  },
+            { GalVertexAttribSize._10_10_10_2,  VertexAttribPointerType.Int   }, //?
+            { GalVertexAttribSize._11_11_10,    VertexAttribPointerType.Int   }  //?
+        };
+
+        private struct VbInfo
+        {
+            public int VaoHandle;
+            public int VboHandle;
+
+            public int PrimCount;
+        }
+
+        private struct IbInfo
+        {
+            public int IboHandle;
+            public int Count;
+
+            public DrawElementsType Type;
+        }
+
+        private VbInfo[] VertexBuffers;
+
+        private IbInfo IndexBuffer;
+
+        public OGLRasterizer()
+        {
+            VertexBuffers = new VbInfo[32];
+
+            IndexBuffer = new IbInfo();
+        }
+
+        public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
+        {
+            ClearBufferMask Mask = 0;
+
+            //OpenGL doesn't support clearing just a single color channel,
+            //so we can't just clear all channels...
+            if (Flags.HasFlag(GalClearBufferFlags.ColorRed)   &&
+                Flags.HasFlag(GalClearBufferFlags.ColorGreen) &&
+                Flags.HasFlag(GalClearBufferFlags.ColorBlue)  &&
+                Flags.HasFlag(GalClearBufferFlags.ColorAlpha))
+            {
+                Mask = ClearBufferMask.ColorBufferBit;
+            }
+
+            if (Flags.HasFlag(GalClearBufferFlags.Depth))
+            {
+                Mask |= ClearBufferMask.DepthBufferBit;
+            }
+
+            if (Flags.HasFlag(GalClearBufferFlags.Stencil))
+            {
+                Mask |= ClearBufferMask.StencilBufferBit;
+            }
+
+            GL.Clear(Mask);
+        }
+
+        public void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs)
+        {
+            EnsureVbInitialized(VbIndex);
+
+            VertexBuffers[VbIndex].PrimCount = Buffer.Length / Stride;
+
+            VbInfo Vb = VertexBuffers[VbIndex];
+
+            IntPtr Length = new IntPtr(Buffer.Length);
+
+            GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
+            GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
+            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
+
+            GL.BindVertexArray(Vb.VaoHandle);
+
+            for (int Attr = 0; Attr < 16; Attr++)
+            {
+                GL.DisableVertexAttribArray(Attr);
+            }
+
+            for (int Index = 0; Index < Attribs.Length; Index++)
+            {
+                GalVertexAttrib Attrib = Attribs[Index];
+
+                GL.EnableVertexAttribArray(Index);
+
+                GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
+
+                bool Unsigned =
+                    Attrib.Type == GalVertexAttribType.Unorm ||
+                    Attrib.Type == GalVertexAttribType.Uint  ||
+                    Attrib.Type == GalVertexAttribType.Uscaled;
+
+                bool Normalize =
+                    Attrib.Type == GalVertexAttribType.Snorm ||
+                    Attrib.Type == GalVertexAttribType.Unorm;
+
+                VertexAttribPointerType Type = 0;
+
+                if (Attrib.Type == GalVertexAttribType.Float)
+                {
+                    Type = VertexAttribPointerType.Float;
+                }
+                else
+                {
+                    Type = AttribTypes[Attrib.Size] + (Unsigned ? 1 : 0);
+                }
+
+                int Size   = AttribElements[Attrib.Size];
+                int Offset = Attrib.Offset;
+
+                GL.VertexAttribPointer(Index, Size, Type, Normalize, Stride, Offset);
+            }
+
+            GL.BindVertexArray(0);
+        }
+
+        public void SetIndexArray(byte[] Buffer, GalIndexFormat Format)
+        {
+            EnsureIbInitialized();
+
+            IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format);
+
+            IndexBuffer.Count = Buffer.Length >> (int)Format;
+
+            IntPtr Length = new IntPtr(Buffer.Length);
+
+            GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexBuffer.IboHandle);
+            GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
+            GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
+        }
+
+        public void DrawArrays(int VbIndex, GalPrimitiveType PrimType)
+        {
+            VbInfo Vb = VertexBuffers[VbIndex];
+
+            if (Vb.PrimCount == 0)
+            {
+                return;
+            }
+
+            GL.BindVertexArray(Vb.VaoHandle);
+
+            GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), 0, Vb.PrimCount);
+        }
+
+        public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
+        {
+            VbInfo Vb = VertexBuffers[VbIndex];
+
+            if (Vb.PrimCount == 0)
+            {
+                return;
+            }
+
+            PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType);
+
+            GL.BindVertexArray(Vb.VaoHandle);
+
+            GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexBuffer.IboHandle);
+
+            GL.DrawElements(Mode, IndexBuffer.Count, IndexBuffer.Type, First);
+        }
+
+        private void EnsureVbInitialized(int VbIndex)
+        {
+            VbInfo Vb = VertexBuffers[VbIndex];
+
+            if (Vb.VaoHandle == 0)
+            {
+                Vb.VaoHandle = GL.GenVertexArray();
+            }
+
+            if (Vb.VboHandle == 0)
+            {
+                Vb.VboHandle = GL.GenBuffer();
+            }
+
+            VertexBuffers[VbIndex] = Vb;
+        }
+
+        private void EnsureIbInitialized()
+        {
+            if (IndexBuffer.IboHandle == 0)
+            {
+                IndexBuffer.IboHandle = GL.GenBuffer();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
new file mode 100644
index 000000000..fff6362b4
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs
@@ -0,0 +1,262 @@
+using OpenTK.Graphics.OpenGL;
+using Ryujinx.Graphics.Gal.Shader;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace Ryujinx.Graphics.Gal.OpenGL
+{
+    class OGLShader
+    {
+        private class ShaderStage : IDisposable
+        {
+            public int Handle { get; private set; }
+
+            public bool IsCompiled { get; private set; }
+
+            public GalShaderType Type { get; private set; }
+
+            public string Code { get; private set; }
+
+            public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
+            public IEnumerable<ShaderDeclInfo> UniformUsage { get; private set; }
+
+            public ShaderStage(
+                GalShaderType               Type,
+                string                      Code,
+                IEnumerable<ShaderDeclInfo> TextureUsage,
+                IEnumerable<ShaderDeclInfo> UniformUsage)
+            {
+                this.Type         = Type;
+                this.Code         = Code;
+                this.TextureUsage = TextureUsage;
+                this.UniformUsage = UniformUsage;
+            }
+
+            public void Compile()
+            {
+                if (Handle == 0)
+                {
+                    Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type));
+
+                    CompileAndCheck(Handle, Code);
+                }
+            }
+
+            public void Dispose()
+            {
+                Dispose(true);
+            }
+
+            protected virtual void Dispose(bool Disposing)
+            {
+                if (Disposing && Handle != 0)
+                {
+                    GL.DeleteShader(Handle);
+
+                    Handle = 0;
+                }
+            }
+        }
+
+        private struct ShaderProgram
+        {
+            public ShaderStage Vertex;
+            public ShaderStage TessControl;
+            public ShaderStage TessEvaluation;
+            public ShaderStage Geometry;
+            public ShaderStage Fragment;
+        }
+
+        private ShaderProgram Current;
+
+        private ConcurrentDictionary<long, ShaderStage> Stages;
+
+        private Dictionary<ShaderProgram, int> Programs;
+
+        public int CurrentProgramHandle { get; private set; }
+
+        public OGLShader()
+        {
+            Stages = new ConcurrentDictionary<long, ShaderStage>();
+
+            Programs = new Dictionary<ShaderProgram, int>();
+        }
+
+        public void Create(long Tag, GalShaderType Type, byte[] Data)
+        {
+            Stages.GetOrAdd(Tag, (Key) => ShaderStageFactory(Type, Data));
+        }
+
+        private ShaderStage ShaderStageFactory(GalShaderType Type, byte[] Data)
+        {
+            GlslProgram Program = GetGlslProgram(Data, Type);
+
+            return new ShaderStage(
+                Type,
+                Program.Code,
+                Program.Textures,
+                Program.Uniforms);
+        }
+
+        private GlslProgram GetGlslProgram(byte[] Data, GalShaderType Type)
+        {
+            int[] Code = new int[(Data.Length - 0x50) >> 2];
+
+            using (MemoryStream MS = new MemoryStream(Data))
+            {
+                MS.Seek(0x50, SeekOrigin.Begin);
+
+                BinaryReader Reader = new BinaryReader(MS);
+
+                for (int Index = 0; Index < Code.Length; Index++)
+                {
+                    Code[Index] = Reader.ReadInt32();
+                }
+            }
+
+            GlslDecompiler Decompiler = new GlslDecompiler();
+
+            return Decompiler.Decompile(Code, Type);
+        }
+
+        public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag)
+        {
+            if (Stages.TryGetValue(Tag, out ShaderStage Stage))
+            {
+                return Stage.TextureUsage;
+            }
+
+            return Enumerable.Empty<ShaderDeclInfo>();
+        }
+
+        public void SetConstBuffer(long Tag, int Cbuf, byte[] Data)
+        {
+            BindProgram();
+
+            if (Stages.TryGetValue(Tag, out ShaderStage Stage))
+            {
+                foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf))
+                {
+                    float Value = BitConverter.ToSingle(Data, DeclInfo.Index * 4);
+
+                    int Location = GL.GetUniformLocation(CurrentProgramHandle, DeclInfo.Name);
+
+                    GL.Uniform1(Location, Value);
+                }
+            }
+        }
+
+        public void SetUniform1(string UniformName, int Value)
+        {
+            BindProgram();
+
+            int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName);
+
+            GL.Uniform1(Location, Value);
+        }
+
+        public void SetUniform2F(string UniformName, float X, float Y)
+        {
+            BindProgram();
+
+            int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName);
+
+            GL.Uniform2(Location, X, Y);
+        }
+
+        public void Bind(long Tag)
+        {
+            if (Stages.TryGetValue(Tag, out ShaderStage Stage))
+            {
+                Bind(Stage);
+            }
+        }
+
+        private void Bind(ShaderStage Stage)
+        {
+            switch (Stage.Type)
+            {
+                case GalShaderType.Vertex:         Current.Vertex         = Stage; break;
+                case GalShaderType.TessControl:    Current.TessControl    = Stage; break;
+                case GalShaderType.TessEvaluation: Current.TessEvaluation = Stage; break;
+                case GalShaderType.Geometry:       Current.Geometry       = Stage; break;
+                case GalShaderType.Fragment:       Current.Fragment       = Stage; break;
+            }
+        }
+
+        public void BindProgram()
+        {
+            if (Current.Vertex   == null ||
+                Current.Fragment == null)
+            {
+                return;
+            }
+
+            if (!Programs.TryGetValue(Current, out int Handle))
+            {
+                Handle = GL.CreateProgram();
+
+                AttachIfNotNull(Handle, Current.Vertex);
+                AttachIfNotNull(Handle, Current.TessControl);
+                AttachIfNotNull(Handle, Current.TessEvaluation);
+                AttachIfNotNull(Handle, Current.Geometry);
+                AttachIfNotNull(Handle, Current.Fragment);
+
+                GL.LinkProgram(Handle);
+
+                CheckProgramLink(Handle);
+
+                Programs.Add(Current, Handle);
+            }
+
+            GL.UseProgram(Handle);
+
+            CurrentProgramHandle = Handle;
+        }
+
+        private void AttachIfNotNull(int ProgramHandle, ShaderStage Stage)
+        {
+            if (Stage != null)
+            {
+                Stage.Compile();
+
+                GL.AttachShader(ProgramHandle, Stage.Handle);
+            }
+        }
+
+        public static void CompileAndCheck(int Handle, string Code)
+        {
+            GL.ShaderSource(Handle, Code);
+            GL.CompileShader(Handle);
+
+            CheckCompilation(Handle);
+        }
+
+        private static void CheckCompilation(int Handle)
+        {
+            int Status = 0;
+
+            GL.GetShader(Handle, ShaderParameter.CompileStatus, out Status);
+
+            if (Status == 0)
+            {
+                throw new ShaderException(GL.GetShaderInfoLog(Handle));
+            }
+        }
+
+        private static void CheckProgramLink(int Handle)
+        {
+            int Status = 0;
+
+            GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out Status);
+
+            if (Status == 0)
+            {
+                throw new ShaderException(GL.GetProgramInfoLog(Handle));
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
new file mode 100644
index 000000000..9ea25056c
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs
@@ -0,0 +1,115 @@
+using OpenTK.Graphics.OpenGL;
+
+namespace Ryujinx.Graphics.Gal.OpenGL
+{
+    class OGLTexture
+    {
+        private int[] Textures;
+
+        public OGLTexture()
+        {
+            Textures = new int[80];
+        }
+
+        public void Set(int Index, GalTexture Texture)
+        {
+            GL.ActiveTexture(TextureUnit.Texture0 + Index);
+
+            Bind(Index);
+
+            const int Level  = 0; //TODO: Support mipmap textures.
+            const int Border = 0;
+
+            if (IsCompressedTextureFormat(Texture.Format))
+            {
+                PixelInternalFormat InternalFmt = OGLEnumConverter.GetCompressedTextureFormat(Texture.Format);
+
+                GL.CompressedTexImage2D(
+                    TextureTarget.Texture2D,
+                    Level,
+                    InternalFmt,
+                    Texture.Width,
+                    Texture.Height,
+                    Border,
+                    Texture.Data.Length,
+                    Texture.Data);
+            }
+            else
+            {
+                const PixelInternalFormat InternalFmt = PixelInternalFormat.Rgba;
+
+                (PixelFormat Format, PixelType Type) = OGLEnumConverter.GetTextureFormat(Texture.Format);
+
+                GL.TexImage2D(
+                    TextureTarget.Texture2D,
+                    Level,
+                    InternalFmt,
+                    Texture.Width,
+                    Texture.Height,
+                    Border,
+                    Format,
+                    Type,
+                    Texture.Data);
+            }
+        }
+
+        public void Bind(int Index)
+        {
+            int Handle = EnsureTextureInitialized(Index);
+
+            GL.BindTexture(TextureTarget.Texture2D, Handle);
+        }
+
+        public static void Set(GalTextureSampler Sampler)
+        {
+            int WrapS = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressU);
+            int WrapT = (int)OGLEnumConverter.GetTextureWrapMode(Sampler.AddressV);
+
+            int MinFilter = (int)OGLEnumConverter.GetTextureMinFilter(Sampler.MinFilter, Sampler.MipFilter);
+            int MagFilter = (int)OGLEnumConverter.GetTextureMagFilter(Sampler.MagFilter);
+
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, WrapS);
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, WrapT);
+
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, MinFilter);
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, MagFilter);
+
+            float[] Color = new float[]
+            {
+                Sampler.BorderColor.Red,
+                Sampler.BorderColor.Green,
+                Sampler.BorderColor.Blue,
+                Sampler.BorderColor.Alpha
+            };
+
+            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBorderColor, Color);
+        }
+
+        private static bool IsCompressedTextureFormat(GalTextureFormat Format)
+        {
+            switch (Format)
+            {
+                case GalTextureFormat.BC1:
+                case GalTextureFormat.BC2:
+                case GalTextureFormat.BC3:
+                case GalTextureFormat.BC4:
+                case GalTextureFormat.BC5:
+                    return true;
+            }
+
+            return false;
+        }
+
+        private int EnsureTextureInitialized(int TexIndex)
+        {
+            int Handle = Textures[TexIndex];
+
+            if (Handle == 0)
+            {
+                Handle = Textures[TexIndex] = GL.GenTexture();
+            }
+
+            return Handle;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
index 002e54dad..cf2da91c5 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs
@@ -1,5 +1,4 @@
 using OpenTK;
-using OpenTK.Graphics.OpenGL;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
@@ -8,46 +7,33 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 {
     public class OpenGLRenderer : IGalRenderer
     {
-        private struct VertexBuffer
-        {
-            public int VaoHandle;
-            public int VboHandle;
+        private OGLBlend Blend;
 
-            public int PrimCount;
-        }
+        private OGLFrameBuffer FrameBuffer;
 
-        private struct Texture
-        {
-            public int Handle;
-        }
+        private OGLRasterizer Rasterizer;
 
-        private List<VertexBuffer> VertexBuffers;
+        private OGLShader Shader;
 
-        private Texture[] Textures;
+        private OGLTexture Texture;
 
         private ConcurrentQueue<Action> ActionsQueue;
 
-        private FrameBuffer FbRenderer;
-
         public OpenGLRenderer()
         {
-            VertexBuffers = new List<VertexBuffer>();
+            Blend = new OGLBlend();
 
-            Textures = new Texture[8];
+            FrameBuffer = new OGLFrameBuffer();
+
+            Rasterizer = new OGLRasterizer();
+
+            Shader = new OGLShader();
+
+            Texture = new OGLTexture();
 
             ActionsQueue = new ConcurrentQueue<Action>();
         }
 
-        public void InitializeFrameBuffer()
-        {
-            FbRenderer = new FrameBuffer(1280, 720);
-        }
-
-        public void ResetFrameBuffer()
-        {
-            FbRenderer.Reset();
-        }
-
         public void QueueAction(Action ActionMthd)
         {
             ActionsQueue.Enqueue(ActionMthd);
@@ -65,259 +51,216 @@ namespace Ryujinx.Graphics.Gal.OpenGL
 
         public void Render()
         {
-            FbRenderer.Render();
-
-            for (int Index = 0; Index < VertexBuffers.Count; Index++)
-            {
-                VertexBuffer Vb = VertexBuffers[Index];
-
-                if (Vb.VaoHandle != 0 &&
-                    Vb.PrimCount != 0)
-                {
-                    GL.BindVertexArray(Vb.VaoHandle);
-                    GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount);
-                }
-            }
+            FrameBuffer.Render();
         }
 
         public void SetWindowSize(int Width, int Height)
         {
-            FbRenderer.WindowWidth  = Width;
-            FbRenderer.WindowHeight = Height;
+            FrameBuffer.SetWindowSize(Width, Height);
         }
 
-        public unsafe void SetFrameBuffer(
-            byte* Fb,
-            int   Width,
-            int   Height,
-            float ScaleX,
-            float ScaleY,
-            float OffsX,
-            float OffsY,
-            float Rotate)
+        public void SetBlendEnable(bool Enable)
+        {
+            if (Enable)
+            {
+                ActionsQueue.Enqueue(() => Blend.Enable());
+            }
+            else
+            {
+                ActionsQueue.Enqueue(() => Blend.Disable());
+            }
+        }
+
+        public void SetBlend(
+            GalBlendEquation Equation,
+            GalBlendFactor   FuncSrc,
+            GalBlendFactor   FuncDst)
+        {
+            ActionsQueue.Enqueue(() => Blend.Set(Equation, FuncSrc, FuncDst));
+        }
+
+        public void SetBlendSeparate(
+            GalBlendEquation EquationRgb,
+            GalBlendEquation EquationAlpha,
+            GalBlendFactor   FuncSrcRgb,
+            GalBlendFactor   FuncDstRgb,
+            GalBlendFactor   FuncSrcAlpha,
+            GalBlendFactor   FuncDstAlpha)
+        {
+            ActionsQueue.Enqueue(() =>
+            {
+                Blend.SetSeparate(
+                    EquationRgb,
+                    EquationAlpha,
+                    FuncSrcRgb,
+                    FuncDstRgb,
+                    FuncSrcAlpha,
+                    FuncDstAlpha);
+            });
+        }
+
+        public void CreateFrameBuffer(long Tag, int Width, int Height)
+        {
+            ActionsQueue.Enqueue(() => FrameBuffer.Create(Tag, Width, Height));
+        }
+
+        public void BindFrameBuffer(long Tag)
+        {
+            ActionsQueue.Enqueue(() => FrameBuffer.Bind(Tag));
+        }
+
+        public void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler)
+        {
+            ActionsQueue.Enqueue(() =>
+            {
+                FrameBuffer.BindTexture(Tag, Index);
+
+                OGLTexture.Set(Sampler);
+            });
+        }
+
+        public void SetFrameBuffer(long Tag)
+        {
+            ActionsQueue.Enqueue(() => FrameBuffer.Set(Tag));
+        }
+
+        public void SetFrameBuffer(byte[] Data, int Width, int Height)
+        {
+            ActionsQueue.Enqueue(() => FrameBuffer.Set(Data, Width, Height));
+        }
+
+        public void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY)
         {
             Matrix2 Transform;
 
-            Transform  = Matrix2.CreateScale(ScaleX, ScaleY);
+            Transform  = Matrix2.CreateScale(SX, SY);
             Transform *= Matrix2.CreateRotation(Rotate);
 
-            Vector2 Offs = new Vector2(OffsX, OffsY);
+            Vector2 Offs = new Vector2(TX, TY);
 
-            FbRenderer.Set(Fb, Width, Height, Transform, Offs);
+            ActionsQueue.Enqueue(() => FrameBuffer.SetTransform(Transform, Offs));
         }
 
-        public void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs)
+        public void SetViewport(int X, int Y, int Width, int Height)
         {
-            if (Index < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(Index));
-            }
-
-            if (Buffer.Length == 0 || Stride == 0)
-            {
-                return;
-            }
-
-            EnsureVbInitialized(Index);
-
-            VertexBuffer Vb = VertexBuffers[Index];
-
-            Vb.PrimCount = Buffer.Length / Stride;
-
-            VertexBuffers[Index] = Vb;
-
-            IntPtr Length = new IntPtr(Buffer.Length);
-
-            GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
-            GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
-            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
-
-            GL.BindVertexArray(Vb.VaoHandle);
-
-            for (int Attr = 0; Attr < 16; Attr++)
-            {
-                GL.DisableVertexAttribArray(Attr);
-            }
-
-            foreach (GalVertexAttrib Attrib in Attribs)
-            {
-                if (Attrib.Index >= 3) break;
-
-                GL.EnableVertexAttribArray(Attrib.Index);
-
-                GL.BindBuffer(BufferTarget.ArrayBuffer, Vb.VboHandle);
-
-                int Size = 0;
-
-                switch (Attrib.Size)
-                {
-                    case GalVertexAttribSize._8:
-                    case GalVertexAttribSize._16:
-                    case GalVertexAttribSize._32:
-                        Size = 1;
-                        break;
-                    case GalVertexAttribSize._8_8:
-                    case GalVertexAttribSize._16_16:
-                    case GalVertexAttribSize._32_32:
-                        Size = 2;
-                        break;
-                    case GalVertexAttribSize._8_8_8:
-                    case GalVertexAttribSize._11_11_10:
-                    case GalVertexAttribSize._16_16_16:
-                    case GalVertexAttribSize._32_32_32:
-                        Size = 3;
-                        break;
-                    case GalVertexAttribSize._8_8_8_8:
-                    case GalVertexAttribSize._10_10_10_2:
-                    case GalVertexAttribSize._16_16_16_16:
-                    case GalVertexAttribSize._32_32_32_32:
-                        Size = 4;
-                        break;
-                }
-
-                bool Signed =
-                    Attrib.Type == GalVertexAttribType.Snorm ||
-                    Attrib.Type == GalVertexAttribType.Sint  ||
-                    Attrib.Type == GalVertexAttribType.Sscaled;
-
-                bool Normalize =
-                    Attrib.Type == GalVertexAttribType.Snorm ||
-                    Attrib.Type == GalVertexAttribType.Unorm;
-
-                VertexAttribPointerType Type = 0;
-
-                switch (Attrib.Type)
-                {
-                    case GalVertexAttribType.Snorm:
-                    case GalVertexAttribType.Unorm:
-                    case GalVertexAttribType.Sint:
-                    case GalVertexAttribType.Uint:
-                    case GalVertexAttribType.Uscaled:
-                    case GalVertexAttribType.Sscaled:                    
-                    {
-                        switch (Attrib.Size)
-                        {
-                            case GalVertexAttribSize._8:
-                            case GalVertexAttribSize._8_8:
-                            case GalVertexAttribSize._8_8_8:
-                            case GalVertexAttribSize._8_8_8_8:
-                            {
-                                Type = Signed
-                                    ? VertexAttribPointerType.Byte
-                                    : VertexAttribPointerType.UnsignedByte;
-
-                                break;
-                            }
-
-                            case GalVertexAttribSize._16:
-                            case GalVertexAttribSize._16_16:
-                            case GalVertexAttribSize._16_16_16:
-                            case GalVertexAttribSize._16_16_16_16:
-                            {
-                                Type = Signed
-                                    ? VertexAttribPointerType.Short
-                                    : VertexAttribPointerType.UnsignedShort;
-
-                                break;
-                            }
-
-                            case GalVertexAttribSize._10_10_10_2:
-                            case GalVertexAttribSize._11_11_10:
-                            case GalVertexAttribSize._32:
-                            case GalVertexAttribSize._32_32:
-                            case GalVertexAttribSize._32_32_32:
-                            case GalVertexAttribSize._32_32_32_32:
-                            {
-                                Type = Signed
-                                    ? VertexAttribPointerType.Int
-                                    : VertexAttribPointerType.UnsignedInt;
-
-                                break;
-                            }
-                        }
-
-                        break;
-                    }
-
-                    case GalVertexAttribType.Float:
-                    {
-                        Type = VertexAttribPointerType.Float;
-
-                        break;
-                    }
-                }
-
-                GL.VertexAttribPointer(
-                    Attrib.Index,
-                    Size,
-                    Type,
-                    Normalize,
-                    Stride,
-                    Attrib.Offset);
-            }
-
-            GL.BindVertexArray(0);
+            ActionsQueue.Enqueue(() => FrameBuffer.SetViewport(X, Y, Width, Height));
         }
 
-        public void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height)
+        public void ClearBuffers(int RtIndex, GalClearBufferFlags Flags)
         {
-            EnsureTexInitialized(Index);
+            ActionsQueue.Enqueue(() => Rasterizer.ClearBuffers(RtIndex, Flags));
+        }
 
-            GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
-            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
-            GL.TexImage2D(TextureTarget.Texture2D,
-                0,
-                PixelInternalFormat.Rgba,
-                Width,
-                Height,
-                0,
-                PixelFormat.Rgba,
-                PixelType.UnsignedByte,
-                Buffer);
+        public void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs)
+        {
+            if ((uint)VbIndex > 31)
+            {
+                throw new ArgumentOutOfRangeException(nameof(VbIndex));
+            }
+
+            ActionsQueue.Enqueue(() => Rasterizer.SetVertexArray(VbIndex, Stride,
+                Buffer  ?? throw new ArgumentNullException(nameof(Buffer)),
+                Attribs ?? throw new ArgumentNullException(nameof(Attribs))));
+        }
+
+        public void SetIndexArray(byte[] Buffer, GalIndexFormat Format)
+        {
+            if (Buffer == null)
+            {
+                throw new ArgumentNullException(nameof(Buffer));
+            }
+
+            ActionsQueue.Enqueue(() => Rasterizer.SetIndexArray(Buffer, Format));
+        }
+
+        public void DrawArrays(int VbIndex, GalPrimitiveType PrimType)
+        {
+            if ((uint)VbIndex > 31)
+            {
+                throw new ArgumentOutOfRangeException(nameof(VbIndex));
+            }
+
+            ActionsQueue.Enqueue(() => Rasterizer.DrawArrays(VbIndex, PrimType));
+        }
+
+        public void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType)
+        {
+            if ((uint)VbIndex > 31)
+            {
+                throw new ArgumentOutOfRangeException(nameof(VbIndex));
+            }
+
+            ActionsQueue.Enqueue(() => Rasterizer.DrawElements(VbIndex, First, PrimType));
+        }
+
+        public void CreateShader(long Tag, GalShaderType Type, byte[] Data)
+        {
+            if (Data == null)
+            {
+                throw new ArgumentNullException(nameof(Data));
+            }
+
+            Shader.Create(Tag, Type, Data);
+        }
+
+        public void SetConstBuffer(long Tag, int Cbuf, byte[] Data)
+        {
+            if (Data == null)
+            {
+                throw new ArgumentNullException(nameof(Data));
+            }
+
+            ActionsQueue.Enqueue(() => Shader.SetConstBuffer(Tag, Cbuf, Data));
+        }
+
+        public void SetUniform1(string UniformName, int Value)
+        {
+            if (UniformName == null)
+            {
+                throw new ArgumentNullException(nameof(UniformName));
+            }
+
+            ActionsQueue.Enqueue(() => Shader.SetUniform1(UniformName, Value));
+        }
+
+        public void SetUniform2F(string UniformName, float X, float Y)
+        {
+            if (UniformName == null)
+            {
+                throw new ArgumentNullException(nameof(UniformName));
+            }
+
+            ActionsQueue.Enqueue(() => Shader.SetUniform2F(UniformName, X, Y));
+        }
+
+        public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag)
+        {
+            return Shader.GetTextureUsage(Tag);
+        }
+
+        public void BindShader(long Tag)
+        {
+            ActionsQueue.Enqueue(() => Shader.Bind(Tag));
+        }
+
+        public void BindProgram()
+        {
+            ActionsQueue.Enqueue(() => Shader.BindProgram());
+        }
+
+        public void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler)
+        {
+            ActionsQueue.Enqueue(() =>
+            {
+                this.Texture.Set(Index, Texture);
+
+                OGLTexture.Set(Sampler);
+            });
         }
 
         public void BindTexture(int Index)
         {
-            GL.ActiveTexture(TextureUnit.Texture0 + Index);
-
-            GL.BindTexture(TextureTarget.Texture2D, Textures[Index].Handle);            
-        }
-
-        private void EnsureVbInitialized(int VbIndex)
-        {
-            while (VbIndex >= VertexBuffers.Count)
-            {
-                VertexBuffers.Add(new VertexBuffer());
-            }
-
-            VertexBuffer Vb = VertexBuffers[VbIndex];
-
-            if (Vb.VaoHandle == 0)
-            {
-                Vb.VaoHandle = GL.GenVertexArray();
-            }
-
-            if (Vb.VboHandle == 0)
-            {
-                Vb.VboHandle = GL.GenBuffer();
-            }
-
-            VertexBuffers[VbIndex] = Vb;
-        }
-
-        private void EnsureTexInitialized(int TexIndex)
-        {
-            Texture Tex = Textures[TexIndex];
-
-            if (Tex.Handle == 0)
-            {
-                Tex.Handle = GL.GenTexture();
-            }
-
-            Textures[TexIndex] = Tex;
+            ActionsQueue.Enqueue(() => Texture.Bind(Index));
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
new file mode 100644
index 000000000..cd901747d
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs
@@ -0,0 +1,214 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class GlslDecl
+    {
+        public const int VertexIdAttr    = 0x2fc;
+        public const int GlPositionWAttr = 0x7c;
+
+        private const int AttrStartIndex = 8;
+        private const int TexStartIndex = 8;
+
+        public const string PositionOutAttrName = "position";
+
+        private const string InAttrName  = "in_attr";
+        private const string OutAttrName = "out_attr";
+        private const string UniformName = "c";
+
+        private const string GprName     = "gpr";
+        private const string PredName    = "pred";
+        private const string TextureName = "tex";
+
+        public const string FragmentOutputName = "FragColor";
+
+        private string[] StagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" };
+
+        private string StagePrefix;
+
+        private Dictionary<int, ShaderDeclInfo> m_Textures;
+
+        private Dictionary<(int, int), ShaderDeclInfo> m_Uniforms;
+
+        private Dictionary<int, ShaderDeclInfo> m_InAttributes;
+        private Dictionary<int, ShaderDeclInfo> m_OutAttributes;
+
+        private Dictionary<int, ShaderDeclInfo> m_Gprs;
+        private Dictionary<int, ShaderDeclInfo> m_Preds;
+
+        public IReadOnlyDictionary<int, ShaderDeclInfo> Textures => m_Textures;
+
+        public IReadOnlyDictionary<(int, int), ShaderDeclInfo> Uniforms => m_Uniforms;
+
+        public IReadOnlyDictionary<int, ShaderDeclInfo> InAttributes  => m_InAttributes;
+        public IReadOnlyDictionary<int, ShaderDeclInfo> OutAttributes => m_OutAttributes;
+
+        public IReadOnlyDictionary<int, ShaderDeclInfo> Gprs  => m_Gprs;
+        public IReadOnlyDictionary<int, ShaderDeclInfo> Preds => m_Preds;
+
+        public GalShaderType ShaderType { get; private set; }
+
+        public GlslDecl(ShaderIrNode[] Nodes, GalShaderType ShaderType)
+        {
+            this.ShaderType = ShaderType;
+
+            StagePrefix = StagePrefixes[(int)ShaderType] + "_";
+
+            m_Uniforms = new Dictionary<(int, int), ShaderDeclInfo>();
+
+            m_Textures = new Dictionary<int, ShaderDeclInfo>();
+
+            m_InAttributes  = new Dictionary<int, ShaderDeclInfo>();
+            m_OutAttributes = new Dictionary<int, ShaderDeclInfo>();
+
+            m_Gprs  = new Dictionary<int, ShaderDeclInfo>();
+            m_Preds = new Dictionary<int, ShaderDeclInfo>();
+
+            if (ShaderType == GalShaderType.Fragment)
+            {
+                m_Gprs.Add(0, new ShaderDeclInfo(FragmentOutputName, 0, 0, 4));
+
+                m_InAttributes.Add(7, new ShaderDeclInfo(PositionOutAttrName, -1, 0, 4));
+            }
+            else
+            {
+                m_OutAttributes.Add(7, new ShaderDeclInfo("gl_Position", -1, 0, 4));
+            }
+
+            foreach (ShaderIrNode Node in Nodes)
+            {
+                Traverse(null, Node);
+            }
+        }
+
+        private void Traverse(ShaderIrNode Parent, ShaderIrNode Node)
+        {
+            switch (Node)
+            {
+                case ShaderIrAsg Asg:
+                {
+                    Traverse(Asg, Asg.Dst);
+                    Traverse(Asg, Asg.Src);
+
+                    break;
+                }
+
+                case ShaderIrCond Cond:
+                {
+                    Traverse(Cond, Cond.Pred);
+                    Traverse(Cond, Cond.Child);
+
+                    break;
+                }
+
+                case ShaderIrOp Op:
+                {
+                    Traverse(Op, Op.OperandA);
+                    Traverse(Op, Op.OperandB);
+                    Traverse(Op, Op.OperandC);
+
+                    if (Op.Inst == ShaderIrInst.Texq ||
+                        Op.Inst == ShaderIrInst.Texs ||
+                        Op.Inst == ShaderIrInst.Txlf)
+                    {
+                        int Handle = ((ShaderIrOperImm)Op.OperandC).Value;
+
+                        int Index = Handle - TexStartIndex;
+
+                        string Name = StagePrefix + TextureName + Index;
+
+                        m_Textures.TryAdd(Handle, new ShaderDeclInfo(Name, Handle));
+                    }
+                    break;
+                }
+
+                case ShaderIrOperCbuf Cbuf:
+                {
+                    string Name = StagePrefix + UniformName + Cbuf.Index + "_" + Cbuf.Offs;
+
+                    ShaderDeclInfo DeclInfo = new ShaderDeclInfo(Name, Cbuf.Offs, Cbuf.Index);
+
+                    m_Uniforms.TryAdd((Cbuf.Index, Cbuf.Offs), DeclInfo);
+
+                    break;
+                }
+
+                case ShaderIrOperAbuf Abuf:
+                {
+                    //This is a built-in input variable.
+                    if (Abuf.Offs == VertexIdAttr)
+                    {
+                        break;
+                    }
+
+                    int Index =  Abuf.Offs >> 4;
+                    int Elem  = (Abuf.Offs >> 2) & 3;
+
+                    int GlslIndex = Index - AttrStartIndex;
+
+                    ShaderDeclInfo DeclInfo;
+
+                    if (Parent is ShaderIrAsg Asg && Asg.Dst == Node)
+                    {
+                        if (!m_OutAttributes.TryGetValue(Index, out DeclInfo))
+                        {
+                            DeclInfo = new ShaderDeclInfo(OutAttrName + GlslIndex, GlslIndex);
+
+                            m_OutAttributes.Add(Index, DeclInfo);
+                        }
+                    }
+                    else
+                    {
+                        if (!m_InAttributes.TryGetValue(Index, out DeclInfo))
+                        {
+                            DeclInfo = new ShaderDeclInfo(InAttrName + GlslIndex, GlslIndex);
+
+                            m_InAttributes.Add(Index, DeclInfo);
+                        }
+                    }
+
+                    DeclInfo.Enlarge(Elem + 1);
+
+                    break;
+                }
+
+                case ShaderIrOperGpr Gpr:
+                {
+                    if (!Gpr.IsConst && !HasName(m_Gprs, Gpr.Index))
+                    {
+                        string Name = GprName + Gpr.Index;
+
+                        m_Gprs.TryAdd(Gpr.Index, new ShaderDeclInfo(Name, Gpr.Index));
+                    }
+                    break;
+                }
+
+                case ShaderIrOperPred Pred:
+                {
+                    if (!Pred.IsConst && !HasName(m_Preds, Pred.Index))
+                    {
+                        string Name = PredName + Pred.Index;
+
+                        m_Preds.TryAdd(Pred.Index, new ShaderDeclInfo(Name, Pred.Index));
+                    }
+                    break;
+                }
+            }
+        }
+
+        private bool HasName(Dictionary<int, ShaderDeclInfo> Decls, int Index)
+        {
+            int VecIndex = Index >> 2;
+
+            if (Decls.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo))
+            {
+                if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size)
+                {
+                    return true;
+                }
+            }
+
+            return Decls.ContainsKey(Index);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
new file mode 100644
index 000000000..d7173bcd6
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs
@@ -0,0 +1,776 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class GlslDecompiler
+    {
+        private delegate string GetInstExpr(ShaderIrOp Op);
+
+        private Dictionary<ShaderIrInst, GetInstExpr> InstsExpr;
+
+        private enum OperType
+        {
+            Bool,
+            F32,
+            I32
+        }
+
+        private const string IdentationStr = "    ";
+
+        private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" };
+
+        private GlslDecl Decl;
+
+        private StringBuilder SB;
+
+        public GlslDecompiler()
+        {
+            InstsExpr = new Dictionary<ShaderIrInst, GetInstExpr>()
+            {
+                { ShaderIrInst.And,   GetAndExpr   },
+                { ShaderIrInst.Asr,   GetAsrExpr   },
+                { ShaderIrInst.Band,  GetBandExpr  },
+                { ShaderIrInst.Bnot,  GetBnotExpr  },
+                { ShaderIrInst.Ceil,  GetCeilExpr  },
+                { ShaderIrInst.Ceq,   GetCeqExpr   },
+                { ShaderIrInst.Cge,   GetCgeExpr   },
+                { ShaderIrInst.Cgt,   GetCgtExpr   },
+                { ShaderIrInst.Clamp, GetClampExpr },
+                { ShaderIrInst.Cle,   GetCleExpr   },
+                { ShaderIrInst.Clt,   GetCltExpr   },
+                { ShaderIrInst.Cne,   GetCneExpr   },
+                { ShaderIrInst.Exit,  GetExitExpr  },
+                { ShaderIrInst.Fabs,  GetFabsExpr  },
+                { ShaderIrInst.Fadd,  GetFaddExpr  },
+                { ShaderIrInst.Fceq,  GetCeqExpr   },
+                { ShaderIrInst.Fcge,  GetCgeExpr   },
+                { ShaderIrInst.Fcgt,  GetCgtExpr   },
+                { ShaderIrInst.Fcle,  GetCleExpr   },
+                { ShaderIrInst.Fclt,  GetCltExpr   },
+                { ShaderIrInst.Fcne,  GetCneExpr   },
+                { ShaderIrInst.Fcos,  GetFcosExpr  },
+                { ShaderIrInst.Fex2,  GetFex2Expr  },
+                { ShaderIrInst.Ffma,  GetFfmaExpr  },
+                { ShaderIrInst.Flg2,  GetFlg2Expr  },
+                { ShaderIrInst.Floor, GetFloorExpr },
+                { ShaderIrInst.Fmul,  GetFmulExpr  },
+                { ShaderIrInst.Fneg,  GetFnegExpr  },
+                { ShaderIrInst.Frcp,  GetFrcpExpr  },
+                { ShaderIrInst.Frsq,  GetFrsqExpr  },
+                { ShaderIrInst.Fsin,  GetFsinExpr  },
+                { ShaderIrInst.Ftos,  GetFtosExpr  },
+                { ShaderIrInst.Ftou,  GetFtouExpr  },
+                { ShaderIrInst.Ipa,   GetIpaExpr   },
+                { ShaderIrInst.Kil,   GetKilExpr   },
+                { ShaderIrInst.Lsr,   GetLsrExpr   },
+                { ShaderIrInst.Not,   GetNotExpr   },
+                { ShaderIrInst.Or,    GetOrExpr    },
+                { ShaderIrInst.Stof,  GetStofExpr  },
+                { ShaderIrInst.Texq,  GetTexqExpr  },
+                { ShaderIrInst.Texs,  GetTexsExpr  },
+                { ShaderIrInst.Trunc, GetTruncExpr },
+                { ShaderIrInst.Txlf,  GetTxlfExpr  },
+                { ShaderIrInst.Utof,  GetUtofExpr  },
+                { ShaderIrInst.Xor,   GetXorExpr   }
+            };
+        }
+
+        public GlslProgram Decompile(int[] Code, GalShaderType ShaderType)
+        {
+            ShaderIrBlock Block = ShaderDecoder.DecodeBasicBlock(Code, 0);
+
+            ShaderIrNode[] Nodes = Block.GetNodes();
+
+            Decl = new GlslDecl(Nodes, ShaderType);
+
+            SB = new StringBuilder();
+
+            SB.AppendLine("#version 410 core");
+
+            PrintDeclTextures();
+            PrintDeclUniforms();
+            PrintDeclInAttributes();
+            PrintDeclOutAttributes();
+            PrintDeclGprs();
+            PrintDeclPreds();
+
+            PrintBlockScope("void main()", 1, Nodes);
+
+            string GlslCode = SB.ToString();
+
+            return new GlslProgram(
+                GlslCode,
+                Decl.Textures.Values,
+                Decl.Uniforms.Values);
+        }
+
+        private void PrintDeclTextures()
+        {
+            PrintDecls(Decl.Textures, "uniform sampler2D");
+        }
+
+        private void PrintDeclUniforms()
+        {
+            if (Decl.ShaderType == GalShaderType.Vertex)
+            {
+                SB.AppendLine("uniform vec2 " + GalConsts.FlipUniformName + ";");
+            }
+
+            foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector))
+            {
+                SB.AppendLine($"uniform {GetDecl(DeclInfo)};");
+            }
+
+            if (Decl.Uniforms.Count > 0)
+            {
+                SB.AppendLine();
+            }
+        }
+
+        private void PrintDeclInAttributes()
+        {
+            if (Decl.ShaderType == GalShaderType.Fragment)
+            {
+                SB.AppendLine("in vec4 " + GlslDecl.PositionOutAttrName + ";");
+            }
+
+            PrintDeclAttributes(Decl.InAttributes.Values, "in");
+        }
+
+        private void PrintDeclOutAttributes()
+        {
+            if (Decl.ShaderType == GalShaderType.Vertex)
+            {
+                SB.AppendLine("out vec4 " + GlslDecl.PositionOutAttrName + ";");
+            }
+
+            PrintDeclAttributes(Decl.OutAttributes.Values, "out");
+        }
+
+        private void PrintDeclAttributes(IEnumerable<ShaderDeclInfo> Decls, string InOut)
+        {
+            int Count = 0;
+
+            foreach (ShaderDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector))
+            {
+                if (DeclInfo.Index >= 0)
+                {
+                    SB.AppendLine("layout (location = " + DeclInfo.Index + ") " + InOut + " " + GetDecl(DeclInfo) + ";");
+
+                    Count++;
+                }
+            }
+
+            if (Count > 0)
+            {
+                SB.AppendLine();
+            }
+        }
+
+        private void PrintDeclGprs()
+        {
+            PrintDecls(Decl.Gprs);
+        }
+
+        private void PrintDeclPreds()
+        {
+            PrintDecls(Decl.Preds, "bool");
+        }
+
+        private void PrintDecls(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, string CustomType = null)
+        {
+            foreach (ShaderDeclInfo DeclInfo in Dict.Values.OrderBy(DeclKeySelector))
+            {
+                string Name;
+
+                if (CustomType != null)
+                {
+                    Name = CustomType + " " + DeclInfo.Name + ";";
+                }
+                else if (DeclInfo.Name == GlslDecl.FragmentOutputName)
+                {
+                    Name = "layout (location = 0) out " + GetDecl(DeclInfo) + ";" + Environment.NewLine;
+                }
+                else
+                {
+                    Name = GetDecl(DeclInfo) + ";";
+                }
+
+                SB.AppendLine(Name);
+            }
+
+            if (Dict.Count > 0)
+            {
+                SB.AppendLine();
+            }
+        }
+
+        private int DeclKeySelector(ShaderDeclInfo DeclInfo)
+        {
+            return DeclInfo.Cbuf << 24 | DeclInfo.Index;
+        }
+
+        private string GetDecl(ShaderDeclInfo DeclInfo)
+        {
+            return ElemTypes[DeclInfo.Size - 1] + " " + DeclInfo.Name;
+        }
+
+        private void PrintBlockScope(string ScopeName, int IdentationLevel, params ShaderIrNode[] Nodes)
+        {
+            string Identation = string.Empty;
+
+            for (int Index = 0; Index < IdentationLevel - 1; Index++)
+            {
+                Identation += IdentationStr;
+            }
+
+            if (ScopeName != string.Empty)
+            {
+                ScopeName += " ";
+            }
+
+            SB.AppendLine(Identation + ScopeName + "{");
+
+            string LastLine = Identation + "}";
+
+            if (IdentationLevel > 0)
+            {
+                Identation += IdentationStr;
+            }
+
+            for (int Index = 0; Index < Nodes.Length; Index++)
+            {
+                ShaderIrNode Node = Nodes[Index];
+
+                if (Node is ShaderIrCond Cond)
+                {
+                    string IfExpr = GetSrcExpr(Cond.Pred, true);
+
+                    if (Cond.Not)
+                    {
+                        IfExpr = "!(" + IfExpr + ")";
+                    }
+
+                    string SubScopeName = "if (" + IfExpr + ")";
+
+                    PrintBlockScope(SubScopeName, IdentationLevel + 1, Cond.Child);
+                }
+                else if (Node is ShaderIrAsg Asg && IsValidOutOper(Asg.Dst))
+                {
+                    string Expr = GetSrcExpr(Asg.Src, true);
+
+                    Expr = GetExprWithCast(Asg.Dst, Asg.Src, Expr);
+
+                    SB.AppendLine(Identation + GetDstOperName(Asg.Dst) + " = " + Expr + ";");
+                }
+                else if (Node is ShaderIrOp Op)
+                {
+                    if (Op.Inst == ShaderIrInst.Exit)
+                    {
+                        //Do everything that needs to be done before
+                        //the shader ends here.
+                        if (Decl.ShaderType == GalShaderType.Vertex)
+                        {
+                            SB.AppendLine(Identation + "gl_Position.xy *= flip;");
+
+                            SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + " = gl_Position;");
+                        }
+                    }
+
+                    SB.AppendLine(Identation + GetSrcExpr(Op, true) + ";");
+                }
+                else
+                {
+                    throw new InvalidOperationException();
+                }
+            }
+
+            SB.AppendLine(LastLine);
+        }
+
+        private bool IsValidOutOper(ShaderIrNode Node)
+        {
+            if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst)
+            {
+                return false;
+            }
+            else if (Node is ShaderIrOperPred Pred && Pred.IsConst)
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        private string GetDstOperName(ShaderIrNode Node)
+        {
+            if (Node is ShaderIrOperAbuf Abuf)
+            {
+                return GetOutAbufName(Abuf);
+            }
+            else if (Node is ShaderIrOperGpr Gpr)
+            {
+                return GetName(Gpr);
+            }
+            else if (Node is ShaderIrOperPred Pred)
+            {
+                return GetName(Pred);
+            }
+
+            throw new ArgumentException(nameof(Node));
+        }
+
+        private string GetSrcExpr(ShaderIrNode Node, bool Entry = false)
+        {
+            switch (Node)
+            {
+                case ShaderIrOperAbuf Abuf: return GetName (Abuf);
+                case ShaderIrOperCbuf Cbuf: return GetName (Cbuf);
+                case ShaderIrOperGpr  Gpr:  return GetName (Gpr);
+                case ShaderIrOperImm  Imm:  return GetValue(Imm);
+                case ShaderIrOperImmf Immf: return GetValue(Immf);
+                case ShaderIrOperPred Pred: return GetName (Pred);
+
+                case ShaderIrOp Op:
+                    string Expr;
+
+                    if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr))
+                    {
+                        Expr = GetExpr(Op);
+                    }
+                    else
+                    {
+                        throw new NotImplementedException(Op.Inst.ToString());
+                    }
+
+                    if (!Entry && NeedsParentheses(Op))
+                    {
+                        Expr = "(" + Expr + ")";
+                    }
+
+                    return Expr;
+
+                default: throw new ArgumentException(nameof(Node));
+            }
+        }
+
+        private static bool NeedsParentheses(ShaderIrOp Op)
+        {
+            switch (Op.Inst)
+            {
+                case ShaderIrInst.Frcp:
+                    return true;
+
+                case ShaderIrInst.Ipa:
+                case ShaderIrInst.Texq:
+                case ShaderIrInst.Texs:
+                case ShaderIrInst.Txlf:
+                    return false;
+            }
+
+            return Op.OperandB != null ||
+                   Op.OperandC != null;
+        }
+
+        private string GetName(ShaderIrOperCbuf Cbuf)
+        {
+            if (!Decl.Uniforms.TryGetValue((Cbuf.Index, Cbuf.Offs), out ShaderDeclInfo DeclInfo))
+            {
+                throw new InvalidOperationException();
+            }
+
+            return DeclInfo.Name;
+        }
+
+        private string GetOutAbufName(ShaderIrOperAbuf Abuf)
+        {
+            return GetName(Decl.OutAttributes, Abuf);
+        }
+
+        private string GetName(ShaderIrOperAbuf Abuf)
+        {
+            if (Abuf.Offs == GlslDecl.VertexIdAttr)
+            {
+                return "gl_VertexID";
+            }
+
+            return GetName(Decl.InAttributes, Abuf);
+        }
+
+        private string GetName(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, ShaderIrOperAbuf Abuf)
+        {
+            int Index =  Abuf.Offs >> 4;
+            int Elem  = (Abuf.Offs >> 2) & 3;
+
+            if (!Dict.TryGetValue(Index, out ShaderDeclInfo DeclInfo))
+            {
+                throw new InvalidOperationException();
+            }
+
+            return DeclInfo.Size > 1 ? DeclInfo.Name + "." + GetAttrSwizzle(Elem) : DeclInfo.Name;
+        }
+
+        private string GetName(ShaderIrOperGpr Gpr)
+        {
+            return Gpr.IsConst ? "0" : GetNameWithSwizzle(Decl.Gprs, Gpr.Index);
+        }
+
+        private string GetValue(ShaderIrOperImm Imm)
+        {
+            //Only use hex is the value is too big and would likely be hard to read as int.
+            if (Imm.Value >  0xfff ||
+                Imm.Value < -0xfff)
+            {
+                return "0x" + Imm.Value.ToString("x8", CultureInfo.InvariantCulture);
+            }
+            else
+            {
+                return Imm.Value.ToString(CultureInfo.InvariantCulture);
+            }
+        }
+
+        private string GetValue(ShaderIrOperImmf Immf)
+        {
+            return Immf.Value.ToString(CultureInfo.InvariantCulture);
+        }
+
+        private string GetName(ShaderIrOperPred Pred)
+        {
+            return Pred.IsConst ? "true" : GetNameWithSwizzle(Decl.Preds, Pred.Index);
+        }
+
+        private string GetNameWithSwizzle(IReadOnlyDictionary<int, ShaderDeclInfo> Dict, int Index)
+        {
+            int VecIndex = Index >> 2;
+
+            if (Dict.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo))
+            {
+                if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size)
+                {
+                    return DeclInfo.Name + "." + GetAttrSwizzle(Index & 3);
+                }
+            }
+
+            if (!Dict.TryGetValue(Index, out DeclInfo))
+            {
+                throw new InvalidOperationException();
+            }
+
+            return DeclInfo.Name;
+        }
+
+        private string GetAttrSwizzle(int Elem)
+        {
+            return "xyzw".Substring(Elem, 1);
+        }
+
+        private string GetAndExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&");
+
+        private string GetAsrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">>");
+
+        private string GetBandExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "&&");
+
+        private string GetBnotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "!");
+
+        private string GetCeilExpr(ShaderIrOp Op) => GetUnaryCall(Op, "ceil");
+
+        private string GetClampExpr(ShaderIrOp Op) => GetTernaryCall(Op, "clamp");
+
+        private string GetCltExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<");
+        private string GetCeqExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "==");
+        private string GetCleExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "<=");
+        private string GetCgtExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">");
+        private string GetCneExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "!=");
+        private string GetCgeExpr(ShaderIrOp Op) => GetBinaryExpr(Op, ">=");
+
+        private string GetExitExpr(ShaderIrOp Op) => "return";
+
+        private string GetFabsExpr(ShaderIrOp Op) => GetUnaryCall(Op, "abs");
+
+        private string GetFaddExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "+");
+
+        private string GetFcosExpr(ShaderIrOp Op) => GetUnaryCall(Op, "cos");
+
+        private string GetFex2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "exp2");
+
+        private string GetFfmaExpr(ShaderIrOp Op) => GetTernaryExpr(Op, "*", "+");
+
+        private string GetFlg2Expr(ShaderIrOp Op) => GetUnaryCall(Op, "log2");
+
+        private string GetFloorExpr(ShaderIrOp Op) => GetUnaryCall(Op, "floor");
+
+        private string GetFmulExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "*");
+
+        private string GetFnegExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "-");
+
+        private string GetFrcpExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "1 / ");
+
+        private string GetFrsqExpr(ShaderIrOp Op) => GetUnaryCall(Op, "inversesqrt");
+
+        private string GetFsinExpr(ShaderIrOp Op) => GetUnaryCall(Op, "sin");
+
+        private string GetFtosExpr(ShaderIrOp Op)
+        {
+            return "int(" + GetOperExpr(Op, Op.OperandA) + ")";
+        }
+
+        private string GetFtouExpr(ShaderIrOp Op)
+        {
+            return "int(uint(" + GetOperExpr(Op, Op.OperandA) + "))";
+        }
+
+        private string GetIpaExpr(ShaderIrOp Op) => GetSrcExpr(Op.OperandA);
+
+        private string GetKilExpr(ShaderIrOp Op) => "discard";
+
+        private string GetLsrExpr(ShaderIrOp Op)
+        {
+            return "int(uint(" + GetOperExpr(Op, Op.OperandA) + ") >> " +
+                                 GetOperExpr(Op, Op.OperandB) + ")";
+        }
+
+        private string GetNotExpr(ShaderIrOp Op) => GetUnaryExpr(Op, "~");
+
+        private string GetOrExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "|");
+
+        private string GetStofExpr(ShaderIrOp Op)
+        {
+            return "float(" + GetOperExpr(Op, Op.OperandA) + ")";
+        }
+
+        private string GetTexqExpr(ShaderIrOp Op)
+        {
+            ShaderIrMetaTexq Meta = (ShaderIrMetaTexq)Op.MetaData;
+
+            string Ch = "xyzw".Substring(Meta.Elem, 1);
+
+            if (Meta.Info == ShaderTexqInfo.Dimension)
+            {
+                string Sampler = GetTexSamplerName(Op);
+
+                string Lod = GetOperExpr(Op, Op.OperandA); //???
+
+                return "textureSize(" + Sampler + ", " + Lod + ")." + Ch;
+            }
+            else
+            {
+                throw new NotImplementedException(Meta.Info.ToString());
+            }
+        }
+
+        private string GetTexsExpr(ShaderIrOp Op)
+        {
+            ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
+
+            string Sampler = GetTexSamplerName(Op);
+
+            string Coords = GetTexSamplerCoords(Op);
+
+            string Ch = "rgba".Substring(Meta.Elem, 1);
+
+            return "texture(" + Sampler + ", " + Coords + ")." + Ch;
+        }
+
+        private string GetTxlfExpr(ShaderIrOp Op)
+        {
+            ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData;
+
+            string Sampler = GetTexSamplerName(Op);
+
+            string Coords = GetITexSamplerCoords(Op);
+
+            string Ch = "rgba".Substring(Meta.Elem, 1);
+
+            return "texelFetch(" + Sampler + ", " + Coords + ", 0)." + Ch;
+        }
+
+        private string GetTruncExpr(ShaderIrOp Op) => GetUnaryCall(Op, "trunc");
+
+        private string GetUtofExpr(ShaderIrOp Op)
+        {
+            return "float(uint(" + GetOperExpr(Op, Op.OperandA) + "))";
+        }
+
+        private string GetXorExpr(ShaderIrOp Op) => GetBinaryExpr(Op, "^");
+
+        private string GetUnaryCall(ShaderIrOp Op, string FuncName)
+        {
+            return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ")";
+        }
+
+        private string GetTernaryCall(ShaderIrOp Op, string FuncName)
+        {
+            return FuncName + "(" + GetOperExpr(Op, Op.OperandA) + ", " +
+                                    GetOperExpr(Op, Op.OperandB) + ", " +
+                                    GetOperExpr(Op, Op.OperandC) + ")";
+        }
+
+        private string GetUnaryExpr(ShaderIrOp Op, string Opr)
+        {
+            return Opr + GetOperExpr(Op, Op.OperandA);
+        }
+
+        private string GetBinaryExpr(ShaderIrOp Op, string Opr)
+        {
+            return GetOperExpr(Op, Op.OperandA) + " " + Opr + " " +
+                   GetOperExpr(Op, Op.OperandB);
+        }
+
+        private string GetTernaryExpr(ShaderIrOp Op, string Opr1, string Opr2)
+        {
+            return GetOperExpr(Op, Op.OperandA) + " " + Opr1 + " " +
+                   GetOperExpr(Op, Op.OperandB) + " " + Opr2 + " " +
+                   GetOperExpr(Op, Op.OperandC);
+        }
+
+        private string GetTexSamplerName(ShaderIrOp Op)
+        {
+            ShaderIrOperImm Node = (ShaderIrOperImm)Op.OperandC;
+
+            int Handle = ((ShaderIrOperImm)Op.OperandC).Value;
+
+            if (!Decl.Textures.TryGetValue(Handle, out ShaderDeclInfo DeclInfo))
+            {
+                throw new InvalidOperationException();
+            }
+
+            return DeclInfo.Name;
+        }
+
+        private string GetTexSamplerCoords(ShaderIrOp Op)
+        {
+            return "vec2(" + GetOperExpr(Op, Op.OperandA) + ", " +
+                             GetOperExpr(Op, Op.OperandB) + ")";
+        }
+
+        private string GetITexSamplerCoords(ShaderIrOp Op)
+        {
+            return "ivec2(" + GetOperExpr(Op, Op.OperandA) + ", " +
+                              GetOperExpr(Op, Op.OperandB) + ")";
+        }
+
+        private string GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper)
+        {
+            return GetExprWithCast(Op, Oper, GetSrcExpr(Oper));
+        }
+
+        private static string GetExprWithCast(ShaderIrNode Dst, ShaderIrNode Src, string Expr)
+        {
+            //Note: The "DstType" (of the cast) is the type that the operation
+            //uses on the source operands, while the "SrcType" is the destination
+            //type of the operand result (if it is a operation) or just the type
+            //of the variable for registers/uniforms/attributes.
+            OperType DstType = GetSrcNodeType(Dst);
+            OperType SrcType = GetDstNodeType(Src);
+
+            if (DstType != SrcType)
+            {
+                //Check for invalid casts
+                //(like bool to int/float and others).
+                if (SrcType != OperType.F32 &&
+                    SrcType != OperType.I32)
+                {
+                    throw new InvalidOperationException();
+                }
+
+                switch (Src)
+                {
+                    case ShaderIrOperGpr Gpr:
+                    {
+                        //When the Gpr is ZR, just return the 0 value directly,
+                        //since the float encoding for 0 is 0.
+                        if (Gpr.IsConst)
+                        {
+                            return "0";
+                        }
+                        break;
+                    }
+
+                    case ShaderIrOperImm Imm:
+                    {
+                        //For integer immediates being used as float,
+                        //it's better (for readability) to just return the float value.
+                        if (DstType == OperType.F32)
+                        {
+                            float Value = BitConverter.Int32BitsToSingle(Imm.Value);
+
+                            return Value.ToString(CultureInfo.InvariantCulture);
+                        }
+                        break;
+                    }
+                }
+
+                switch (DstType)
+                {
+                    case OperType.F32: Expr = "intBitsToFloat(" + Expr + ")"; break;
+                    case OperType.I32: Expr = "floatBitsToInt(" + Expr + ")"; break;
+                }
+            }
+
+            return Expr;
+        }
+
+        private static OperType GetDstNodeType(ShaderIrNode Node)
+        {
+            //Special case instructions with the result type different
+            //from the input types (like integer <-> float conversion) here.
+            if (Node is ShaderIrOp Op)
+            {
+                switch (Op.Inst)
+                {
+                    case ShaderIrInst.Stof:
+                    case ShaderIrInst.Txlf:
+                    case ShaderIrInst.Utof:
+                        return OperType.F32;
+
+                    case ShaderIrInst.Ftos:
+                    case ShaderIrInst.Ftou:
+                        return OperType.I32;
+                }
+            }
+
+            return GetSrcNodeType(Node);
+        }
+
+        private static OperType GetSrcNodeType(ShaderIrNode Node)
+        {
+            switch (Node)
+            {
+                case ShaderIrOperAbuf Abuf:
+                    return Abuf.Offs == GlslDecl.VertexIdAttr
+                        ? OperType.I32
+                        : OperType.F32;
+
+                case ShaderIrOperCbuf Cbuf: return OperType.F32;
+                case ShaderIrOperGpr  Gpr:  return OperType.F32;
+                case ShaderIrOperImm  Imm:  return OperType.I32;
+                case ShaderIrOperImmf Immf: return OperType.F32;
+                case ShaderIrOperPred Pred: return OperType.Bool;
+
+                case ShaderIrOp Op:
+                    if (Op.Inst > ShaderIrInst.B_Start &&
+                        Op.Inst < ShaderIrInst.B_End)
+                    {
+                        return OperType.Bool;
+                    }
+                    else if (Op.Inst > ShaderIrInst.F_Start &&
+                             Op.Inst < ShaderIrInst.F_End)
+                    {
+                        return OperType.F32;
+                    }
+                    else if (Op.Inst > ShaderIrInst.I_Start &&
+                             Op.Inst < ShaderIrInst.I_End)
+                    {
+                        return OperType.I32;
+                    }
+                    break;
+            }
+
+            throw new ArgumentException(nameof(Node));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs
new file mode 100644
index 000000000..729b6f1de
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs
@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    struct GlslProgram
+    {
+        public string Code { get; private set; }
+
+        public IEnumerable<ShaderDeclInfo> Textures { get; private set; }
+        public IEnumerable<ShaderDeclInfo> Uniforms { get; private set; }
+
+        public GlslProgram(
+            string                      Code,
+            IEnumerable<ShaderDeclInfo> Textures,
+            IEnumerable<ShaderDeclInfo> Uniforms)
+        {
+            this.Code     = Code;
+            this.Textures = Textures;
+            this.Uniforms = Uniforms;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs
new file mode 100644
index 000000000..ef0fd78bd
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecode.cs
@@ -0,0 +1,4 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    delegate void ShaderDecodeFunc(ShaderIrBlock Block, long OpCode);
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
new file mode 100644
index 000000000..830aeb8cb
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs
@@ -0,0 +1,358 @@
+using System;
+
+using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
+
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    static partial class ShaderDecode
+    {
+        public static void Fadd_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluBinaryF(Block, OpCode, ShaderOper.CR, ShaderIrInst.Fadd);
+        }
+
+        public static void Fadd_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluBinaryF(Block, OpCode, ShaderOper.Immf, ShaderIrInst.Fadd);
+        }
+
+        public static void Fadd_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluBinaryF(Block, OpCode, ShaderOper.RR, ShaderIrInst.Fadd);
+        }
+
+        public static void Ffma_CR(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluFfma(Block, OpCode, ShaderOper.CR);
+        }
+
+        public static void Ffma_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluFfma(Block, OpCode, ShaderOper.Immf);
+        }
+
+        public static void Ffma_RC(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluFfma(Block, OpCode, ShaderOper.RC);
+        }
+
+        public static void Ffma_RR(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluFfma(Block, OpCode, ShaderOper.RR);
+        }
+
+        public static void Fmul32i(ShaderIrBlock Block, long OpCode)
+        {
+            ShaderIrNode OperA = GetOperGpr8     (OpCode);
+            ShaderIrNode OperB = GetOperImmf32_20(OpCode);
+
+            ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Fmul, OperA, OperB);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+        }
+
+        public static void Fmul_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluBinaryF(Block, OpCode, ShaderOper.CR, ShaderIrInst.Fmul);
+        }
+
+        public static void Fmul_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluBinaryF(Block, OpCode, ShaderOper.Immf, ShaderIrInst.Fmul);
+        }
+
+        public static void Fmul_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluBinaryF(Block, OpCode, ShaderOper.RR, ShaderIrInst.Fmul);
+        }
+
+        public static void Fsetp_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitFsetp(Block, OpCode, ShaderOper.CR);
+        }
+
+        public static void Fsetp_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitFsetp(Block, OpCode, ShaderOper.Immf);
+        }
+
+        public static void Fsetp_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitFsetp(Block, OpCode, ShaderOper.RR);
+        }
+
+        public static void Ipa(ShaderIrBlock Block, long OpCode)
+        {
+            ShaderIrNode OperA = GetOperAbuf28(OpCode);
+            ShaderIrNode OperB = GetOperGpr20 (OpCode);
+
+            ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ipa, OperA, OperB);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+        }
+
+        public static void Isetp_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitIsetp(Block, OpCode, ShaderOper.CR);
+        }
+
+        public static void Isetp_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitIsetp(Block, OpCode, ShaderOper.Imm);
+        }
+
+        public static void Isetp_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitIsetp(Block, OpCode, ShaderOper.RR);
+        }
+
+        public static void Lop32i(ShaderIrBlock Block, long OpCode)
+        {
+            int SubOp = (int)(OpCode >> 53) & 3;
+
+            bool Ia = ((OpCode >> 55) & 1) != 0;
+            bool Ib = ((OpCode >> 56) & 1) != 0;
+
+            ShaderIrInst Inst = 0;
+
+            switch (SubOp)
+            {
+                case 0: Inst = ShaderIrInst.And; break;
+                case 1: Inst = ShaderIrInst.Or;  break;
+                case 2: Inst = ShaderIrInst.Xor; break;
+            }
+
+            ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), Ia);
+
+            //SubOp == 3 is pass, used by the not instruction
+            //which just moves the inverted register value.
+            if (SubOp < 3)
+            {
+                ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), Ib);
+
+                ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB);
+
+                Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+            }
+            else
+            {
+                Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode));
+            }
+        }
+
+        public static void Mufu(ShaderIrBlock Block, long OpCode)
+        {
+            int SubOp = (int)(OpCode >> 20) & 7;
+
+            bool Aa = ((OpCode >> 46) & 1) != 0;
+            bool Na = ((OpCode >> 48) & 1) != 0;
+
+            ShaderIrInst Inst = 0;
+
+            switch (SubOp)
+            {
+                case 0: Inst = ShaderIrInst.Fcos; break;
+                case 1: Inst = ShaderIrInst.Fsin; break;
+                case 2: Inst = ShaderIrInst.Fex2; break;
+                case 3: Inst = ShaderIrInst.Flg2; break;
+                case 4: Inst = ShaderIrInst.Frcp; break;
+                case 5: Inst = ShaderIrInst.Frsq; break;
+
+                default: throw new NotImplementedException(SubOp.ToString());
+            }
+
+            ShaderIrNode OperA = GetOperGpr8(OpCode);
+
+            ShaderIrOp Op = new ShaderIrOp(Inst, GetAluAbsNeg(OperA, Aa, Na));
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+        }
+
+        public static void Shr_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluBinary(Block, OpCode, ShaderOper.CR, GetShrInst(OpCode));
+        }
+
+        public static void Shr_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluBinary(Block, OpCode, ShaderOper.Imm, GetShrInst(OpCode));
+        }
+
+        public static void Shr_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitAluBinary(Block, OpCode, ShaderOper.RR, GetShrInst(OpCode));
+        }
+
+        private static ShaderIrInst GetShrInst(long OpCode)
+        {
+            bool Signed = ((OpCode >> 48) & 1) != 0;
+
+            return Signed ? ShaderIrInst.Asr : ShaderIrInst.Lsr;
+        }
+
+        private static void EmitAluBinary(
+            ShaderIrBlock Block,
+            long          OpCode,
+            ShaderOper    Oper,
+            ShaderIrInst  Inst)
+        {
+            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+
+            switch (Oper)
+            {
+                case ShaderOper.CR:  OperB = GetOperCbuf34  (OpCode); break;
+                case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
+                case ShaderOper.RR:  OperB = GetOperGpr20   (OpCode); break;
+
+                default: throw new ArgumentException(nameof(Oper));
+            }
+
+            ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+        }
+
+        private static void EmitAluBinaryF(
+            ShaderIrBlock Block,
+            long          OpCode,
+            ShaderOper    Oper,
+            ShaderIrInst  Inst)
+        {
+            bool Nb = ((OpCode >> 45) & 1) != 0;
+            bool Aa = ((OpCode >> 46) & 1) != 0;
+            bool Na = ((OpCode >> 48) & 1) != 0;
+            bool Ab = ((OpCode >> 49) & 1) != 0;
+
+            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+
+            if (Inst == ShaderIrInst.Fadd)
+            {
+                OperA = GetAluAbsNeg(OperA, Aa, Na);
+            }
+
+            switch (Oper)
+            {
+                case ShaderOper.CR:   OperB = GetOperCbuf34   (OpCode); break;
+                case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
+                case ShaderOper.RR:   OperB = GetOperGpr20    (OpCode); break;
+
+                default: throw new ArgumentException(nameof(Oper));
+            }
+
+            OperB = GetAluAbsNeg(OperB, Ab, Nb);
+
+            ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+        }
+
+        private static void EmitAluFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+        {
+            bool Nb = ((OpCode >> 48) & 1) != 0;
+            bool Nc = ((OpCode >> 49) & 1) != 0;
+
+            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB, OperC;
+
+            switch (Oper)
+            {
+                case ShaderOper.CR:   OperB = GetOperCbuf34   (OpCode); break;
+                case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
+                case ShaderOper.RC:   OperB = GetOperGpr39    (OpCode); break;
+                case ShaderOper.RR:   OperB = GetOperGpr20    (OpCode); break;
+
+                default: throw new ArgumentException(nameof(Oper));
+            }
+
+            OperB = GetAluNeg(OperB, Nb);
+
+            if (Oper == ShaderOper.RC)
+            {
+                OperC = GetAluNeg(GetOperCbuf34(OpCode), Nc);
+            }
+            else
+            {
+                OperC = GetAluNeg(GetOperGpr39(OpCode), Nc);
+            }
+
+            ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ffma, OperA, OperB, OperC);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+        }
+
+        private static void EmitFsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+        {
+            EmitSetp(Block, OpCode, true, Oper);
+        }
+
+        private static void EmitIsetp(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+        {
+            EmitSetp(Block, OpCode, false, Oper);
+        }
+
+        private static void EmitSetp(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper)
+        {
+            bool Aa = ((OpCode >>  7) & 1) != 0;
+            bool Np = ((OpCode >> 42) & 1) != 0;
+            bool Na = ((OpCode >> 43) & 1) != 0;
+            bool Ab = ((OpCode >> 44) & 1) != 0;
+
+            ShaderIrNode OperA = GetOperGpr8(OpCode), OperB;
+
+            switch (Oper)
+            {
+                case ShaderOper.CR:   OperB = GetOperCbuf34   (OpCode); break;
+                case ShaderOper.Imm:  OperB = GetOperImm19_20 (OpCode); break;
+                case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break;
+                case ShaderOper.RR:   OperB = GetOperGpr20    (OpCode); break;
+
+                default: throw new ArgumentException(nameof(Oper));
+            }
+
+            ShaderIrInst CmpInst;
+
+            if (IsFloat)
+            {
+                OperA = GetAluAbsNeg(OperA, Aa, Na);
+                OperB = GetAluAbs   (OperB, Ab);
+
+                CmpInst = GetCmpF(OpCode);
+            }
+            else
+            {
+                CmpInst = GetCmp(OpCode);
+            }
+
+            ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB);
+
+            ShaderIrOperPred P0Node = GetOperPred3 (OpCode);
+            ShaderIrOperPred P1Node = GetOperPred0 (OpCode);
+            ShaderIrOperPred P2Node = GetOperPred39(OpCode);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
+
+            ShaderIrInst LopInst = GetBLop(OpCode);
+
+            if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst)
+            {
+                return;
+            }
+
+            ShaderIrNode P2NNode = P2Node;
+
+            if (Np)
+            {
+                P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode);
+            }
+
+            Op = new ShaderIrOp(ShaderIrInst.Bnot, P0Node);
+
+            Op = new ShaderIrOp(LopInst, Op, P2NNode);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(P1Node, Op), OpCode));
+
+            Op = new ShaderIrOp(LopInst, P0Node, P2NNode);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
new file mode 100644
index 000000000..d3feb92e5
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeFlow.cs
@@ -0,0 +1,17 @@
+using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
+
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    static partial class ShaderDecode
+    {
+        public static void Exit(ShaderIrBlock Block, long OpCode)
+        {
+            Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Exit), OpCode));
+        }
+
+        public static void Kil(ShaderIrBlock Block, long OpCode)
+        {
+            Block.AddNode(GetPredNode(new ShaderIrOp(ShaderIrInst.Kil), OpCode));
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
new file mode 100644
index 000000000..efbce20f2
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs
@@ -0,0 +1,233 @@
+using System;
+
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    static class ShaderDecodeHelper
+    {
+        public static ShaderIrOperAbuf[] GetOperAbuf20(long OpCode)
+        {
+            int Abuf = (int)(OpCode >> 20) & 0x3ff;
+            int Reg  = (int)(OpCode >> 39) & 0xff;
+            int Size = (int)(OpCode >> 47) & 3;
+
+            ShaderIrOperAbuf[] Opers = new ShaderIrOperAbuf[Size + 1];
+
+            for (int Index = 0; Index <= Size; Index++)
+            {
+                Opers[Index] = new ShaderIrOperAbuf(Abuf, Reg);
+            }
+
+            return Opers;
+        }
+
+        public static ShaderIrOperAbuf GetOperAbuf28(long OpCode)
+        {
+            int Abuf = (int)(OpCode >> 28) & 0x3ff;
+            int Reg  = (int)(OpCode >> 39) & 0xff;
+
+            return new ShaderIrOperAbuf(Abuf, Reg);
+        }
+
+        public static ShaderIrOperCbuf GetOperCbuf34(long OpCode)
+        {
+            return new ShaderIrOperCbuf(
+                (int)(OpCode >> 34) & 0x1f,
+                (int)(OpCode >> 20) & 0x3fff);
+        }
+
+        public static ShaderIrOperGpr GetOperGpr8(long OpCode)
+        {
+            return new ShaderIrOperGpr((int)(OpCode >> 8) & 0xff);
+        }
+
+        public static ShaderIrOperGpr GetOperGpr20(long OpCode)
+        {
+            return new ShaderIrOperGpr((int)(OpCode >> 20) & 0xff);
+        }
+
+        public static ShaderIrOperGpr GetOperGpr39(long OpCode)
+        {
+            return new ShaderIrOperGpr((int)(OpCode >> 39) & 0xff);
+        }
+
+        public static ShaderIrOperGpr GetOperGpr0(long OpCode)
+        {
+            return new ShaderIrOperGpr((int)(OpCode >> 0) & 0xff);
+        }
+
+        public static ShaderIrOperGpr GetOperGpr28(long OpCode)
+        {
+            return new ShaderIrOperGpr((int)(OpCode >> 28) & 0xff);
+        }
+
+        public static ShaderIrOperImm GetOperImm13_36(long OpCode)
+        {
+            return new ShaderIrOperImm((int)(OpCode >> 36) & 0x1fff);
+        }
+
+        public static ShaderIrOperImm GetOperImm32_20(long OpCode)
+        {
+            return new ShaderIrOperImm((int)(OpCode >> 20));
+        }
+
+        public static ShaderIrOperImmf GetOperImmf32_20(long OpCode)
+        {
+            return new ShaderIrOperImmf(BitConverter.Int32BitsToSingle((int)(OpCode >> 20)));
+        }
+
+        public static ShaderIrOperImm GetOperImm19_20(long OpCode)
+        {
+            int Value = (int)(OpCode >> 20) & 0x7ffff;
+
+            bool Neg = ((OpCode >> 56) & 1) != 0;
+
+            if (Neg)
+            {
+                Value = -Value;
+            }
+
+            return new ShaderIrOperImm((int)Value);
+        }
+
+        public static ShaderIrOperImmf GetOperImmf19_20(long OpCode)
+        {
+            uint Imm = (uint)(OpCode >> 20) & 0x7ffff;
+
+            bool Neg = ((OpCode >> 56) & 1) != 0;
+
+            Imm <<= 12;
+
+            if (Neg)
+            {
+                Imm |= 0x80000000;
+            }
+
+            float Value = BitConverter.Int32BitsToSingle((int)Imm);
+
+            return new ShaderIrOperImmf(Value);
+        }
+
+        public static ShaderIrOperPred GetOperPred3(long OpCode)
+        {
+            return new ShaderIrOperPred((int)(OpCode >> 3) & 7);
+        }
+
+        public static ShaderIrOperPred GetOperPred0(long OpCode)
+        {
+            return new ShaderIrOperPred((int)(OpCode >> 0) & 7);
+        }
+
+        public static ShaderIrNode GetOperPred39N(long OpCode)
+        {
+            ShaderIrNode Node = GetOperPred39(OpCode);
+
+            if (((OpCode >> 42) & 1) != 0)
+            {
+                Node = new ShaderIrOp(ShaderIrInst.Bnot, Node);
+            }
+
+            return Node;
+        }
+
+        public static ShaderIrOperPred GetOperPred39(long OpCode)
+        {
+            return new ShaderIrOperPred((int)(OpCode >> 39) & 7);
+        }
+
+        public static ShaderIrInst GetCmp(long OpCode)
+        {
+            switch ((int)(OpCode >> 49) & 7)
+            {
+                case 1: return ShaderIrInst.Clt;
+                case 2: return ShaderIrInst.Ceq;
+                case 3: return ShaderIrInst.Cle;
+                case 4: return ShaderIrInst.Cgt;
+                case 5: return ShaderIrInst.Cne;
+                case 6: return ShaderIrInst.Cge;
+            }
+
+            throw new ArgumentException(nameof(OpCode));
+        }
+
+        public static ShaderIrInst GetCmpF(long OpCode)
+        {
+            switch ((int)(OpCode >> 48) & 0xf)
+            {
+                case 0x1: return ShaderIrInst.Fclt;
+                case 0x2: return ShaderIrInst.Fceq;
+                case 0x3: return ShaderIrInst.Fcle;
+                case 0x4: return ShaderIrInst.Fcgt;
+                case 0x5: return ShaderIrInst.Fcne;
+                case 0x6: return ShaderIrInst.Fcge;
+                case 0x7: return ShaderIrInst.Fcnum;
+                case 0x8: return ShaderIrInst.Fcnan;
+                case 0x9: return ShaderIrInst.Fcltu;
+                case 0xa: return ShaderIrInst.Fcequ;
+                case 0xb: return ShaderIrInst.Fcleu;
+                case 0xc: return ShaderIrInst.Fcgtu;
+                case 0xd: return ShaderIrInst.Fcneu;
+                case 0xe: return ShaderIrInst.Fcgeu;
+            }
+
+            throw new ArgumentException(nameof(OpCode));
+        }
+
+        public static ShaderIrInst GetBLop(long OpCode)
+        {
+            switch ((int)(OpCode >> 45) & 3)
+            {
+                case 0: return ShaderIrInst.Band;
+                case 1: return ShaderIrInst.Bor;
+                case 2: return ShaderIrInst.Bxor;
+            }
+
+            throw new ArgumentException(nameof(OpCode));
+        }
+
+        public static ShaderIrNode GetPredNode(ShaderIrNode Node, long OpCode)
+        {
+            ShaderIrOperPred Pred = GetPredNode(OpCode);
+
+            if (Pred.Index != ShaderIrOperPred.UnusedIndex)
+            {
+                bool Inv = ((OpCode >> 19) & 1) != 0;
+
+                Node = new ShaderIrCond(Pred, Node, Inv);
+            }
+
+            return Node;
+        }
+
+        private static ShaderIrOperPred GetPredNode(long OpCode)
+        {
+            int Pred = (int)(OpCode >> 16) & 0xf;
+
+            if (Pred != 0xf)
+            {
+                Pred &= 7;
+            }
+
+            return new ShaderIrOperPred(Pred);
+        }
+
+        public static ShaderIrNode GetAluAbsNeg(ShaderIrNode Node, bool Abs, bool Neg)
+        {
+            return GetAluNeg(GetAluAbs(Node, Abs), Neg);
+        }
+
+        public static ShaderIrNode GetAluAbs(ShaderIrNode Node, bool Abs)
+        {
+            return Abs ? new ShaderIrOp(ShaderIrInst.Fabs, Node) : Node;
+        }
+
+        public static ShaderIrNode GetAluNeg(ShaderIrNode Node, bool Neg)
+        {
+            return Neg ? new ShaderIrOp(ShaderIrInst.Fneg, Node) : Node;
+        }
+
+        public static ShaderIrNode GetAluNot(ShaderIrNode Node, bool Not)
+        {
+            return Not ? new ShaderIrOp(ShaderIrInst.Not, Node) : Node;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
new file mode 100644
index 000000000..33f582316
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMem.cs
@@ -0,0 +1,102 @@
+using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
+
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    static partial class ShaderDecode
+    {
+        public static void Ld_A(ShaderIrBlock Block, long OpCode)
+        {
+            ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
+
+            int Index = 0;
+
+            foreach (ShaderIrNode OperA in Opers)
+            {
+                ShaderIrOperGpr OperD = GetOperGpr0(OpCode);
+
+                OperD.Index += Index++;
+
+                Block.AddNode(GetPredNode(new ShaderIrAsg(OperD, OperA), OpCode));
+            }
+        }
+
+        public static void St_A(ShaderIrBlock Block, long OpCode)
+        {
+            ShaderIrNode[] Opers = GetOperAbuf20(OpCode);
+
+            int Index = 0;
+
+            foreach (ShaderIrNode OperA in Opers)
+            {
+                ShaderIrOperGpr OperD = GetOperGpr0(OpCode);
+
+                OperD.Index += Index++;
+
+                Block.AddNode(GetPredNode(new ShaderIrAsg(OperA, OperD), OpCode));
+            }
+        }
+
+        public static void Texq(ShaderIrBlock Block, long OpCode)
+        {
+            ShaderIrNode OperD = GetOperGpr0(OpCode);
+            ShaderIrNode OperA = GetOperGpr8(OpCode);
+
+            ShaderTexqInfo Info = (ShaderTexqInfo)((OpCode >> 22) & 0x1f);
+
+            ShaderIrMetaTexq Meta0 = new ShaderIrMetaTexq(Info, 0);
+            ShaderIrMetaTexq Meta1 = new ShaderIrMetaTexq(Info, 1);
+
+            ShaderIrNode OperC = GetOperImm13_36(OpCode);
+
+            ShaderIrOp Op0 = new ShaderIrOp(ShaderIrInst.Texq, OperA, null, OperC, Meta0);
+            ShaderIrOp Op1 = new ShaderIrOp(ShaderIrInst.Texq, OperA, null, OperC, Meta1);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(OperD, Op0), OpCode));
+            Block.AddNode(GetPredNode(new ShaderIrAsg(OperA, Op1), OpCode)); //Is this right?
+        }
+
+        public static void Texs(ShaderIrBlock Block, long OpCode)
+        {
+            EmitTex(Block, OpCode, ShaderIrInst.Texs);
+        }
+
+        public static void Tlds(ShaderIrBlock Block, long OpCode)
+        {
+            EmitTex(Block, OpCode, ShaderIrInst.Txlf);
+        }
+
+        private static void EmitTex(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst)
+        {
+            //TODO: Support other formats.
+            ShaderIrNode OperA  = GetOperGpr8    (OpCode);
+            ShaderIrNode OperB  = GetOperGpr20   (OpCode);
+            ShaderIrNode OperC  = GetOperImm13_36(OpCode);
+
+            for (int Ch = 0; Ch < 4; Ch++)
+            {
+                //Assign it to a temp because the destination registers
+                //may be used as texture coord input aswell.
+                ShaderIrOperGpr Dst = new ShaderIrOperGpr(0x100 + Ch);
+
+                ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch);
+
+                ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta);
+
+                Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode));
+            }
+
+            for (int Ch = 0; Ch < 4; Ch++)
+            {
+                ShaderIrOperGpr Src = new ShaderIrOperGpr(0x100 + Ch);
+
+                ShaderIrOperGpr Dst = (Ch >> 1) != 0
+                    ? GetOperGpr28(OpCode)
+                    : GetOperGpr0 (OpCode);
+
+                Dst.Index += Ch & 1;
+
+                Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode));
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
new file mode 100644
index 000000000..6d30cfed8
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeMove.cs
@@ -0,0 +1,286 @@
+using System;
+
+using static Ryujinx.Graphics.Gal.Shader.ShaderDecodeHelper;
+
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    static partial class ShaderDecode
+    {
+        private enum IntType
+        {
+            U8  = 0,
+            U16 = 1,
+            U32 = 2,
+            U64 = 3,
+            S8  = 4,
+            S16 = 5,
+            S32 = 6,
+            S64 = 7
+        }
+
+        private enum FloatType
+        {
+            F16 = 1,
+            F32 = 2,
+            F64 = 3
+        }
+
+        public static void F2f_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitF2f(Block, OpCode, ShaderOper.CR);
+        }
+
+        public static void F2f_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitF2f(Block, OpCode, ShaderOper.Immf);
+        }
+
+        public static void F2f_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitF2f(Block, OpCode, ShaderOper.RR);
+        }
+
+        public static void F2i_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitF2i(Block, OpCode, ShaderOper.CR);
+        }
+
+        public static void F2i_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitF2i(Block, OpCode, ShaderOper.Immf);
+        }
+
+        public static void F2i_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitF2i(Block, OpCode, ShaderOper.RR);
+        }
+
+        public static void I2f_C(ShaderIrBlock Block, long OpCode)
+        {
+            EmitI2f(Block, OpCode, ShaderOper.CR);
+        }
+
+        public static void I2f_I(ShaderIrBlock Block, long OpCode)
+        {
+            EmitI2f(Block, OpCode, ShaderOper.Imm);
+        }
+
+        public static void I2f_R(ShaderIrBlock Block, long OpCode)
+        {
+            EmitI2f(Block, OpCode, ShaderOper.RR);
+        }
+
+        public static void Mov_C(ShaderIrBlock Block, long OpCode)
+        {
+            ShaderIrOperCbuf Cbuf = GetOperCbuf34(OpCode);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Cbuf), OpCode));
+        }
+
+        public static void Mov_I(ShaderIrBlock Block, long OpCode)
+        {
+            ShaderIrOperImm Imm = GetOperImm19_20(OpCode);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
+        }
+
+        public static void Mov_R(ShaderIrBlock Block, long OpCode)
+        {
+            ShaderIrOperGpr Gpr = GetOperGpr20(OpCode);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Gpr), OpCode));
+        }
+
+        public static void Mov32i(ShaderIrBlock Block, long OpCode)
+        {
+            ShaderIrOperImm Imm = GetOperImm32_20(OpCode);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Imm), OpCode));
+        }
+
+        private static void EmitF2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+        {
+            bool Na = ((OpCode >> 45) & 1) != 0;
+            bool Aa = ((OpCode >> 49) & 1) != 0;
+
+            ShaderIrNode OperA;
+
+            switch (Oper)
+            {
+                case ShaderOper.CR:   OperA = GetOperCbuf34   (OpCode); break;
+                case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break;
+                case ShaderOper.RR:   OperA = GetOperGpr20    (OpCode); break;
+
+                default: throw new ArgumentException(nameof(Oper));
+            }
+
+            OperA = GetAluAbsNeg(OperA, Aa, Na);
+
+            ShaderIrInst RoundInst = GetRoundInst(OpCode);
+
+            if (RoundInst != ShaderIrInst.Invalid)
+            {
+                OperA = new ShaderIrOp(RoundInst, OperA);
+            }
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode));
+        }
+
+        private static void EmitF2i(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+        {
+            IntType Type = GetIntType(OpCode);
+
+            if (Type == IntType.U64 ||
+                Type == IntType.S64)
+            {
+                //TODO: 64-bits support.
+                //Note: GLSL doesn't support 64-bits integers.
+                throw new NotImplementedException();
+            }
+
+            bool Na = ((OpCode >> 45) & 1) != 0;
+            bool Aa = ((OpCode >> 49) & 1) != 0;
+
+            ShaderIrNode OperA;
+
+            switch (Oper)
+            {
+                case ShaderOper.CR:   OperA = GetOperCbuf34   (OpCode); break;
+                case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break;
+                case ShaderOper.RR:   OperA = GetOperGpr20    (OpCode); break;
+
+                default: throw new ArgumentException(nameof(Oper));
+            }
+
+            OperA = GetAluAbsNeg(OperA, Aa, Na);
+
+            ShaderIrInst RoundInst = GetRoundInst(OpCode);
+
+            if (RoundInst != ShaderIrInst.Invalid)
+            {
+                OperA = new ShaderIrOp(RoundInst, OperA);
+            }
+
+            bool Signed = Type >= IntType.S8;
+
+            int Size = 8 << ((int)Type & 3);
+
+            if (Size < 32)
+            {
+                uint Mask = uint.MaxValue >> (32 - Size);
+
+                float CMin = 0;
+                float CMax = Mask;
+
+                if (Signed)
+                {
+                    uint HalfMask = Mask >> 1;
+
+                    CMin -= HalfMask + 1;
+                    CMax  = HalfMask;
+                }
+
+                ShaderIrOperImmf IMin = new ShaderIrOperImmf(CMin);
+                ShaderIrOperImmf IMax = new ShaderIrOperImmf(CMax);
+
+                OperA = new ShaderIrOp(ShaderIrInst.Clamp, OperA, IMin, IMax);
+            }
+
+            ShaderIrInst Inst = Signed
+                ? ShaderIrInst.Ftos
+                : ShaderIrInst.Ftou;
+
+            ShaderIrNode Op = new ShaderIrOp(Inst, OperA);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+        }
+
+        private static void EmitI2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
+        {
+            IntType Type = GetIntType(OpCode);
+
+            if (Type == IntType.U64 ||
+                Type == IntType.S64)
+            {
+                //TODO: 64-bits support.
+                //Note: GLSL doesn't support 64-bits integers.
+                throw new NotImplementedException();
+            }
+
+            int Sel = (int)(OpCode >> 41) & 3;
+
+            bool Na = ((OpCode >> 45) & 1) != 0;
+            bool Aa = ((OpCode >> 49) & 1) != 0;
+
+            ShaderIrNode OperA;
+
+            switch (Oper)
+            {
+                case ShaderOper.CR:  OperA = GetOperCbuf34  (OpCode); break;
+                case ShaderOper.Imm: OperA = GetOperImm19_20(OpCode); break;
+                case ShaderOper.RR:  OperA = GetOperGpr20   (OpCode); break;
+
+                default: throw new ArgumentException(nameof(Oper));
+            }
+
+            OperA = GetAluAbsNeg(OperA, Aa, Na);
+
+            bool Signed = Type >= IntType.S8;
+
+            int Shift = Sel * 8;
+
+            int Size = 8 << ((int)Type & 3);
+
+            if (Shift != 0)
+            {
+                OperA = new ShaderIrOp(ShaderIrInst.Asr, OperA, new ShaderIrOperImm(Shift));
+            }
+
+            if (Size < 32)
+            {
+                uint Mask = uint.MaxValue >> (32 - Size);
+
+                OperA = new ShaderIrOp(ShaderIrInst.And, OperA, new ShaderIrOperImm((int)Mask));
+            }
+
+            ShaderIrInst Inst = Signed
+                ? ShaderIrInst.Stof
+                : ShaderIrInst.Utof;
+
+            ShaderIrNode Op = new ShaderIrOp(Inst, OperA);
+
+            Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
+        }
+
+        private static IntType GetIntType(long OpCode)
+        {
+            bool Signed = ((OpCode >> 13) & 1) != 0;
+
+            IntType Type = (IntType)((OpCode >> 10) & 3);
+
+            if (Signed)
+            {
+                Type += (int)IntType.S8;
+            }
+
+            return Type;
+        }
+
+        private static FloatType GetFloatType(long OpCode)
+        {
+            return (FloatType)((OpCode >> 8) & 3);
+        }
+
+        private static ShaderIrInst GetRoundInst(long OpCode)
+        {
+            switch ((OpCode >> 39) & 3)
+            {
+                case 1: return ShaderIrInst.Floor;
+                case 2: return ShaderIrInst.Ceil;
+                case 3: return ShaderIrInst.Trunc;
+            }
+
+            return ShaderIrInst.Invalid;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
new file mode 100644
index 000000000..e44d5b7d7
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs
@@ -0,0 +1,48 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    static class ShaderDecoder
+    {
+        public static ShaderIrBlock DecodeBasicBlock(int[] Code, int Offset)
+        {
+            ShaderIrBlock Block = new ShaderIrBlock();
+
+            while (Offset + 2 <= Code.Length)
+            {
+                //Ignore scheduling instructions, which are
+                //written every 32 bytes.
+                if ((Offset & 7) == 0)
+                {
+                    Offset += 2;
+
+                    continue;
+                }
+
+                uint Word0 = (uint)Code[Offset++];
+                uint Word1 = (uint)Code[Offset++];
+
+                long OpCode = Word0 | (long)Word1 << 32;
+
+                ShaderDecodeFunc Decode = ShaderOpCodeTable.GetDecoder(OpCode);
+
+                if (Decode == null)
+                {
+                    continue;
+                }
+
+                Decode(Block, OpCode);
+
+                if (Block.GetLastNode() is ShaderIrOp Op && IsFlowChange(Op.Inst))
+                {
+                    break;
+                }
+            }
+
+            return Block;
+        }
+
+        private static bool IsFlowChange(ShaderIrInst Inst)
+        {
+            return Inst == ShaderIrInst.Exit;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs
new file mode 100644
index 000000000..00f8f6a5e
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrAsg.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrAsg : ShaderIrNode
+    {
+        public ShaderIrNode Dst { get; set; }
+        public ShaderIrNode Src { get; set; }
+
+        public ShaderIrAsg(ShaderIrNode Dst, ShaderIrNode Src)
+        {
+            this.Dst = Dst;
+            this.Src = Src;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
new file mode 100644
index 000000000..c920d9fa5
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrBlock.cs
@@ -0,0 +1,34 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrBlock
+    {
+        private List<ShaderIrNode> Nodes;
+
+        public ShaderIrBlock()
+        {
+            Nodes = new List<ShaderIrNode>();
+        }
+
+        public void AddNode(ShaderIrNode Node)
+        {
+            Nodes.Add(Node);
+        }
+
+        public ShaderIrNode[] GetNodes()
+        {
+            return Nodes.ToArray();
+        }
+
+        public ShaderIrNode GetLastNode()
+        {
+            if (Nodes.Count > 0)
+            {
+                return Nodes[Nodes.Count - 1];
+            }
+
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs
new file mode 100644
index 000000000..8fb01660c
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrCond.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrCond : ShaderIrNode
+    {
+        public ShaderIrNode Pred  { get; set; }
+        public ShaderIrNode Child { get; set; }
+
+        public bool Not { get; private set; }
+
+        public ShaderIrCond(ShaderIrNode Pred, ShaderIrNode Child, bool Not)
+        {
+            this.Pred  = Pred;
+            this.Child = Child;
+            this.Not   = Not;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
new file mode 100644
index 000000000..1b72f6476
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrInst.cs
@@ -0,0 +1,72 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    enum ShaderIrInst
+    {
+        Invalid,
+
+        B_Start,
+        Band,
+        Bnot,
+        Bor,
+        Bxor,
+        B_End,
+
+        F_Start,
+        Ceil,
+        Clamp,
+        Fabs,
+        Fadd,
+        Fceq,
+        Fcequ,
+        Fcge,
+        Fcgeu,
+        Fcgt,
+        Fcgtu,
+        Fcle,
+        Fcleu,
+        Fclt,
+        Fcltu,
+        Fcnan,
+        Fcne,
+        Fcneu,
+        Fcnum,
+        Fcos,
+        Fex2,
+        Ffma,
+        Flg2,
+        Floor,
+        Fmul,
+        Fneg,
+        Frcp,
+        Frsq,
+        Fsin,
+        Ftos,
+        Ftou,
+        Ipa,
+        Texs,
+        Trunc,
+        F_End,
+
+        I_Start,
+        And,
+        Asr,
+        Ceq,
+        Cge,
+        Cgt,
+        Cle,
+        Clt,
+        Cne,
+        Lsr,
+        Not,
+        Or,
+        Stof,
+        Texq,
+        Txlf,
+        Utof,
+        Xor,
+        I_End,
+
+        Exit,
+        Kil
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs
new file mode 100644
index 000000000..afb7503be
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrMeta.cs
@@ -0,0 +1,4 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrMeta { }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs
new file mode 100644
index 000000000..82f3bb774
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTex.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrMetaTex : ShaderIrMeta
+    {
+        public int Elem { get; private set; }
+
+        public ShaderIrMetaTex(int Elem)
+        {
+            this.Elem = Elem;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTexq.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTexq.cs
new file mode 100644
index 000000000..92871137f
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrMetaTexq.cs
@@ -0,0 +1,15 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrMetaTexq : ShaderIrMeta
+    {
+        public ShaderTexqInfo Info { get; private set; }
+
+        public int Elem { get; private set; }
+
+        public ShaderIrMetaTexq(ShaderTexqInfo Info, int Elem)
+        {
+            this.Info = Info;
+            this.Elem = Elem;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs
new file mode 100644
index 000000000..2648164a1
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs
@@ -0,0 +1,4 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrNode { }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs
new file mode 100644
index 000000000..12a6123c3
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOp.cs
@@ -0,0 +1,25 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrOp : ShaderIrNode
+    {
+        public ShaderIrInst Inst     { get; private set; }
+        public ShaderIrNode OperandA { get; set; }
+        public ShaderIrNode OperandB { get; set; }
+        public ShaderIrNode OperandC { get; set; }
+        public ShaderIrMeta MetaData { get; set; }
+
+        public ShaderIrOp(
+            ShaderIrInst Inst,
+            ShaderIrNode OperandA = null,
+            ShaderIrNode OperandB = null,
+            ShaderIrNode OperandC = null,
+            ShaderIrMeta MetaData = null)
+        {
+            this.Inst     = Inst;
+            this.OperandA = OperandA;
+            this.OperandB = OperandB;
+            this.OperandC = OperandC;
+            this.MetaData = MetaData;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs
new file mode 100644
index 000000000..fa612de76
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperAbuf.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrOperAbuf : ShaderIrNode
+    {
+        public int Offs     { get; private set; }
+        public int GprIndex { get; private set; }
+
+        public ShaderIrOperAbuf(int Offs, int GprIndex)
+        {
+            this.Offs     = Offs;
+            this.GprIndex = GprIndex;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs
new file mode 100644
index 000000000..f22720563
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperCbuf.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrOperCbuf : ShaderIrNode
+    {
+        public int Index { get; private set; }
+        public int Offs  { get; private set; }
+
+        public ShaderIrOperCbuf(int Index, int Offs)
+        {
+            this.Index = Index;
+            this.Offs  = Offs;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs
new file mode 100644
index 000000000..5c69d6a67
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperGpr.cs
@@ -0,0 +1,16 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrOperGpr : ShaderIrNode
+    {
+        public const int ZRIndex = 0xff;
+
+        public bool IsConst => Index == ZRIndex;
+
+        public int Index { get; set; }
+
+        public ShaderIrOperGpr(int Index)
+        {
+            this.Index = Index;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs
new file mode 100644
index 000000000..ba2c2c9b2
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImm.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrOperImm : ShaderIrNode
+    {
+        public int Value { get; private set; }
+
+        public ShaderIrOperImm(int Value)
+        {
+            this.Value = Value;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs
new file mode 100644
index 000000000..3c27e4836
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperImmf.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrOperImmf : ShaderIrNode
+    {
+        public float Value { get; private set; }
+
+        public ShaderIrOperImmf(float Value)
+        {
+            this.Value = Value;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs
new file mode 100644
index 000000000..74cca0efe
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrOperPred.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    class ShaderIrOperPred : ShaderIrNode
+    {
+        public const int UnusedIndex  = 0x7;
+        public const int NeverExecute = 0xf;
+
+        public bool IsConst => Index >= UnusedIndex;
+
+        public int Index { get; set; }
+
+        public ShaderIrOperPred(int Index)
+        {
+            this.Index = Index;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
new file mode 100644
index 000000000..762544cb9
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderOpCodeTable.cs
@@ -0,0 +1,112 @@
+using System;
+
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    static class ShaderOpCodeTable
+    {
+        private const int EncodingBits = 14;
+
+        private static ShaderDecodeFunc[] OpCodes;
+
+        static ShaderOpCodeTable()
+        {
+            OpCodes = new ShaderDecodeFunc[1 << EncodingBits];
+
+#region Instructions
+            Set("111000110000xx", ShaderDecode.Exit);
+            Set("0100110010101x", ShaderDecode.F2f_C);
+            Set("0011100x10101x", ShaderDecode.F2f_I);
+            Set("0101110010101x", ShaderDecode.F2f_R);
+            Set("0100110010110x", ShaderDecode.F2i_C);
+            Set("0011100x10110x", ShaderDecode.F2i_I);
+            Set("0101110010110x", ShaderDecode.F2i_R);
+            Set("0100110001011x", ShaderDecode.Fadd_C);
+            Set("0011100x01011x", ShaderDecode.Fadd_I);
+            Set("0101110001011x", ShaderDecode.Fadd_R);
+            Set("010010011xxxxx", ShaderDecode.Ffma_CR);
+            Set("001100101xxxxx", ShaderDecode.Ffma_I);
+            Set("010100011xxxxx", ShaderDecode.Ffma_RC);
+            Set("010110011xxxxx", ShaderDecode.Ffma_RR);
+            Set("00011110xxxxxx", ShaderDecode.Fmul32i);
+            Set("0100110001101x", ShaderDecode.Fmul_C);
+            Set("0011100x01101x", ShaderDecode.Fmul_I);
+            Set("0101110001101x", ShaderDecode.Fmul_R);
+            Set("010010111011xx", ShaderDecode.Fsetp_C);
+            Set("0011011x1011xx", ShaderDecode.Fsetp_I);
+            Set("010110111011xx", ShaderDecode.Fsetp_R);
+            Set("0100110010111x", ShaderDecode.I2f_C);
+            Set("0011100x10111x", ShaderDecode.I2f_I);
+            Set("0101110010111x", ShaderDecode.I2f_R);
+            Set("11100000xxxxxx", ShaderDecode.Ipa);
+            Set("010010110110xx", ShaderDecode.Isetp_C);
+            Set("0011011x0110xx", ShaderDecode.Isetp_I);
+            Set("010110110110xx", ShaderDecode.Isetp_R);
+            Set("111000110011xx", ShaderDecode.Kil);
+            Set("1110111111011x", ShaderDecode.Ld_A);
+            Set("000001xxxxxxxx", ShaderDecode.Lop32i);
+            Set("0100110010011x", ShaderDecode.Mov_C);
+            Set("0011100x10011x", ShaderDecode.Mov_I);
+            Set("0101110010011x", ShaderDecode.Mov_R);
+            Set("000000010000xx", ShaderDecode.Mov32i);
+            Set("0101000010000x", ShaderDecode.Mufu);
+            Set("0100110000101x", ShaderDecode.Shr_C);
+            Set("0011100x00101x", ShaderDecode.Shr_I);
+            Set("0101110000101x", ShaderDecode.Shr_R);
+            Set("1110111111110x", ShaderDecode.St_A);
+            Set("1101111101001x", ShaderDecode.Texq);
+            Set("1101100xxxxxxx", ShaderDecode.Texs);
+            Set("1101101xxxxxxx", ShaderDecode.Tlds);
+#endregion
+        }
+
+        private static void Set(string Encoding, ShaderDecodeFunc Func)
+        {
+            if (Encoding.Length != EncodingBits)
+            {
+                throw new ArgumentException(nameof(Encoding));
+            }
+
+            int Bit   = Encoding.Length - 1;
+            int Value = 0;
+            int XMask = 0;
+            int XBits = 0;
+
+            int[] XPos = new int[Encoding.Length];
+
+            for (int Index = 0; Index < Encoding.Length; Index++, Bit--)
+            {
+                char Chr = Encoding[Index];
+
+                if (Chr == '1')
+                {
+                    Value |= 1 << Bit;
+                }
+                else if (Chr == 'x')
+                {
+                    XMask |= 1 << Bit;
+
+                    XPos[XBits++] = Bit;
+                }
+            }
+
+            XMask = ~XMask;
+
+            for (int Index = 0; Index < (1 << XBits); Index++)
+            {
+                Value &= XMask;
+
+                for (int X = 0; X < XBits; X++)
+                {
+                    Value |= ((Index >> X) & 1) << XPos[X];
+                }
+
+                OpCodes[Value] = Func;
+            }
+        }
+
+        public static ShaderDecodeFunc GetDecoder(long OpCode)
+        {
+            return OpCodes[(ulong)OpCode >> (64 - EncodingBits)];
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderOper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderOper.cs
new file mode 100644
index 000000000..aa4854828
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderOper.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    enum ShaderOper
+    {
+        CR,
+        Imm,
+        Immf,
+        RC,
+        RR
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs b/Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs
new file mode 100644
index 000000000..9158662cc
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Shader/ShaderTexqInfo.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.Graphics.Gal.Shader
+{
+    enum ShaderTexqInfo
+    {
+        Dimension   = 1,
+        TextureType = 2,
+        SamplePos   = 5,
+        Filter      = 16,
+        Lod         = 18,
+        Wrap        = 20,
+        BorderColor = 22
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs
new file mode 100644
index 000000000..d400850c8
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/ShaderDeclInfo.cs
@@ -0,0 +1,27 @@
+namespace Ryujinx.Graphics.Gal
+{
+    public class ShaderDeclInfo
+    {
+        public string Name { get; private set; }
+
+        public int Index { get; private set; }
+        public int Cbuf  { get; private set; }
+        public int Size  { get; private set; }
+
+        public ShaderDeclInfo(string Name, int Index, int Cbuf = 0, int Size = 1)
+        {
+            this.Name  = Name;
+            this.Index = Index;
+            this.Cbuf  = Cbuf;
+            this.Size  = Size;
+        }
+
+        internal void Enlarge(int NewSize)
+        {
+            if (Size < NewSize)
+            {
+                Size = NewSize;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gal/ShaderException.cs b/Ryujinx.Graphics/Gal/ShaderException.cs
new file mode 100644
index 000000000..9bc87ff3d
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/ShaderException.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Ryujinx.Graphics.Gal
+{
+    class ShaderException : Exception
+    {
+        public ShaderException() : base() { }
+
+        public ShaderException(string Message) : base(Message) { }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/BCn.cs b/Ryujinx.Graphics/Gal/Texture/BCn.cs
similarity index 85%
rename from Ryujinx.Graphics/Gpu/BCn.cs
rename to Ryujinx.Graphics/Gal/Texture/BCn.cs
index b1caf4675..f23a86c2c 100644
--- a/Ryujinx.Graphics/Gpu/BCn.cs
+++ b/Ryujinx.Graphics/Gal/Texture/BCn.cs
@@ -1,14 +1,14 @@
 using System;
 using System.Drawing;
 
-namespace Ryujinx.Graphics.Gpu
+namespace Ryujinx.Graphics.Gal.Texture
 {
     static class BCn
     {
-        public static byte[] DecodeBC1(NsGpuTexture Tex, int Offset)
+        public static byte[] DecodeBC1(GalTexture Texture, int Offset)
         {
-            int W = (Tex.Width  + 3) / 4;
-            int H = (Tex.Height + 3) / 4;
+            int W = (Texture.Width  + 3) / 4;
+            int H = (Texture.Height + 3) / 4;
 
             byte[] Output = new byte[W * H * 64];
 
@@ -20,7 +20,7 @@ namespace Ryujinx.Graphics.Gpu
                 {
                     int IOffs = Offset + Swizzle.GetSwizzledAddress64(X, Y) * 8;
 
-                    byte[] Tile = BCnDecodeTile(Tex.Data, IOffs, true);
+                    byte[] Tile = BCnDecodeTile(Texture.Data, IOffs, true);
 
                     int TOffset = 0;
 
@@ -44,10 +44,10 @@ namespace Ryujinx.Graphics.Gpu
             return Output;
         }
 
-        public static byte[] DecodeBC2(NsGpuTexture Tex, int Offset)
+        public static byte[] DecodeBC2(GalTexture Texture, int Offset)
         {
-            int W = (Tex.Width  + 3) / 4;
-            int H = (Tex.Height + 3) / 4;
+            int W = (Texture.Width  + 3) / 4;
+            int H = (Texture.Height + 3) / 4;
 
             byte[] Output = new byte[W * H * 64];
 
@@ -59,10 +59,10 @@ namespace Ryujinx.Graphics.Gpu
                 {
                     int IOffs = Offset + Swizzle.GetSwizzledAddress128(X, Y) * 16;
 
-                    byte[] Tile = BCnDecodeTile(Tex.Data, IOffs + 8, false);
+                    byte[] Tile = BCnDecodeTile(Texture.Data, IOffs + 8, false);
 
-                    int AlphaLow  = Get32(Tex.Data, IOffs + 0);
-                    int AlphaHigh = Get32(Tex.Data, IOffs + 4);
+                    int AlphaLow  = Get32(Texture.Data, IOffs + 0);
+                    int AlphaHigh = Get32(Texture.Data, IOffs + 4);
 
                     ulong AlphaCh = (uint)AlphaLow | (ulong)AlphaHigh << 32;
 
@@ -90,10 +90,10 @@ namespace Ryujinx.Graphics.Gpu
             return Output;
         }
 
-        public static byte[] DecodeBC3(NsGpuTexture Tex, int Offset)
+        public static byte[] DecodeBC3(GalTexture Texture, int Offset)
         {
-            int W = (Tex.Width  + 3) / 4;
-            int H = (Tex.Height + 3) / 4;
+            int W = (Texture.Width  + 3) / 4;
+            int H = (Texture.Height + 3) / 4;
 
             byte[] Output = new byte[W * H * 64];
 
@@ -105,17 +105,17 @@ namespace Ryujinx.Graphics.Gpu
                 {
                     int IOffs = Offset + Swizzle.GetSwizzledAddress128(X, Y) * 16;
 
-                    byte[] Tile = BCnDecodeTile(Tex.Data, IOffs + 8, false);
+                    byte[] Tile = BCnDecodeTile(Texture.Data, IOffs + 8, false);
 
                     byte[] Alpha = new byte[8];
 
-                    Alpha[0] = Tex.Data[IOffs + 0];
-                    Alpha[1] = Tex.Data[IOffs + 1];
+                    Alpha[0] = Texture.Data[IOffs + 0];
+                    Alpha[1] = Texture.Data[IOffs + 1];
 
                     CalculateBC3Alpha(Alpha);
 
-                    int AlphaLow  = Get32(Tex.Data, IOffs + 2);
-                    int AlphaHigh = Get16(Tex.Data, IOffs + 6);
+                    int AlphaLow  = Get32(Texture.Data, IOffs + 2);
+                    int AlphaHigh = Get16(Texture.Data, IOffs + 6);
 
                     ulong AlphaCh = (uint)AlphaLow | (ulong)AlphaHigh << 32;
 
@@ -143,10 +143,10 @@ namespace Ryujinx.Graphics.Gpu
             return Output;
         }
 
-        public static byte[] DecodeBC4(NsGpuTexture Tex, int Offset)
+        public static byte[] DecodeBC4(GalTexture Texture, int Offset)
         {
-            int W = (Tex.Width  + 3) / 4;
-            int H = (Tex.Height + 3) / 4;
+            int W = (Texture.Width  + 3) / 4;
+            int H = (Texture.Height + 3) / 4;
 
             byte[] Output = new byte[W * H * 64];
 
@@ -160,13 +160,13 @@ namespace Ryujinx.Graphics.Gpu
 
                     byte[] Red = new byte[8];
 
-                    Red[0] = Tex.Data[IOffs + 0];
-                    Red[1] = Tex.Data[IOffs + 1];
+                    Red[0] = Texture.Data[IOffs + 0];
+                    Red[1] = Texture.Data[IOffs + 1];
 
                     CalculateBC3Alpha(Red);
 
-                    int RedLow  = Get32(Tex.Data, IOffs + 2);
-                    int RedHigh = Get16(Tex.Data, IOffs + 6);
+                    int RedLow  = Get32(Texture.Data, IOffs + 2);
+                    int RedHigh = Get16(Texture.Data, IOffs + 6);
 
                     ulong RedCh = (uint)RedLow | (ulong)RedHigh << 32;
 
@@ -194,10 +194,10 @@ namespace Ryujinx.Graphics.Gpu
             return Output;
         }
 
-        public static byte[] DecodeBC5(NsGpuTexture Tex, int Offset, bool SNorm)
+        public static byte[] DecodeBC5(GalTexture Texture, int Offset, bool SNorm)
         {
-            int W = (Tex.Width  + 3) / 4;
-            int H = (Tex.Height + 3) / 4;
+            int W = (Texture.Width  + 3) / 4;
+            int H = (Texture.Height + 3) / 4;
 
             byte[] Output = new byte[W * H * 64];
 
@@ -212,11 +212,11 @@ namespace Ryujinx.Graphics.Gpu
                     byte[] Red   = new byte[8];
                     byte[] Green = new byte[8];
 
-                    Red[0]   = Tex.Data[IOffs + 0];
-                    Red[1]   = Tex.Data[IOffs + 1];
+                    Red[0]   = Texture.Data[IOffs + 0];
+                    Red[1]   = Texture.Data[IOffs + 1];
 
-                    Green[0] = Tex.Data[IOffs + 8];
-                    Green[1] = Tex.Data[IOffs + 9];
+                    Green[0] = Texture.Data[IOffs + 8];
+                    Green[1] = Texture.Data[IOffs + 9];
 
                     if (SNorm)
                     {
@@ -229,11 +229,11 @@ namespace Ryujinx.Graphics.Gpu
                         CalculateBC3Alpha(Green);
                     }
 
-                    int RedLow    = Get32(Tex.Data, IOffs + 2);
-                    int RedHigh   = Get16(Tex.Data, IOffs + 6);
+                    int RedLow    = Get32(Texture.Data, IOffs + 2);
+                    int RedHigh   = Get16(Texture.Data, IOffs + 6);
 
-                    int GreenLow  = Get32(Tex.Data, IOffs + 10);
-                    int GreenHigh = Get16(Tex.Data, IOffs + 14);
+                    int GreenLow  = Get32(Texture.Data, IOffs + 10);
+                    int GreenHigh = Get16(Texture.Data, IOffs + 14);
 
                     ulong RedCh   = (uint)RedLow   | (ulong)RedHigh   << 32;
                     ulong GreenCh = (uint)GreenLow | (ulong)GreenHigh << 32;
diff --git a/Ryujinx.Graphics/Gpu/SwizzleAddr.cs b/Ryujinx.Graphics/Gal/Texture/SwizzleAddr.cs
similarity index 98%
rename from Ryujinx.Graphics/Gpu/SwizzleAddr.cs
rename to Ryujinx.Graphics/Gal/Texture/SwizzleAddr.cs
index 08e61eb58..b67b841bc 100644
--- a/Ryujinx.Graphics/Gpu/SwizzleAddr.cs
+++ b/Ryujinx.Graphics/Gal/Texture/SwizzleAddr.cs
@@ -1,6 +1,6 @@
 using System;
 
-namespace Ryujinx.Graphics.Gpu
+namespace Ryujinx.Graphics.Gal.Texture
 {
     class SwizzleAddr
     {
@@ -109,7 +109,7 @@ namespace Ryujinx.Graphics.Gpu
              *     y x x x x x x y y y y x y y x y 0 0 0 0 1024 x 1024 dxt5
              *   y y x x x x x x y y y y x y y x y x 0 0 0 2048 x 2048 dxt1
              * y y y x x x x x x y y y y x y y x y x x 0 0 1024 x 1024 rgba8888
-             * 
+             *
              * Read from right to left, LSB first.
              */
             int XCnt    = XBase;
diff --git a/Ryujinx.Graphics/Gal/Texture/TextureDecoder.cs b/Ryujinx.Graphics/Gal/Texture/TextureDecoder.cs
new file mode 100644
index 000000000..4e50db51d
--- /dev/null
+++ b/Ryujinx.Graphics/Gal/Texture/TextureDecoder.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Ryujinx.Graphics.Gal.Texture
+{
+    static class TextureDecoder
+    {
+        public static byte[] Decode(GalTexture Texture)
+        {
+            switch (Texture.Format)
+            {
+                case GalTextureFormat.BC1: return BCn.DecodeBC1(Texture, 0);
+                case GalTextureFormat.BC2: return BCn.DecodeBC2(Texture, 0);
+                case GalTextureFormat.BC3: return BCn.DecodeBC3(Texture, 0);
+            }
+
+            throw new NotImplementedException(Texture.Format.ToString());
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs b/Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs
new file mode 100644
index 000000000..d2cbb1443
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/BlockLinearSwizzle.cs
@@ -0,0 +1,57 @@
+namespace Ryujinx.Graphics.Gpu
+{
+    class BlockLinearSwizzle : ISwizzle
+    {
+        private int BhShift;
+        private int BppShift;
+        private int BhMask;
+
+        private int XShift;
+        private int GobStride;
+
+        public BlockLinearSwizzle(int Width, int Bpp, int BlockHeight = 16)
+        {
+            BhMask = (BlockHeight * 8) - 1;
+
+            BhShift  = CountLsbZeros(BlockHeight * 8);
+            BppShift = CountLsbZeros(Bpp);
+
+            int WidthInGobs = Width * Bpp / 64;
+
+            GobStride = 512 * BlockHeight * WidthInGobs;
+
+            XShift = CountLsbZeros(512 * BlockHeight);
+        }
+
+        private int CountLsbZeros(int Value)
+        {
+            int Count = 0;
+
+            while (((Value >> Count) & 1) == 0)
+            {
+                Count++;
+            }
+
+            return Count;
+        }
+
+        public int GetSwizzleOffset(int X, int Y)
+        {
+            X <<= BppShift;
+
+            int Position = (Y >> BhShift) * GobStride;
+
+            Position += (X >> 6) << XShift;
+
+            Position += ((Y & BhMask) >> 3) << 9;
+
+            Position += ((X & 0x3f) >> 5) << 8;
+            Position += ((Y & 0x07) >> 1) << 6;
+            Position += ((X & 0x1f) >> 4) << 5;
+            Position += ((Y & 0x01) >> 0) << 4;
+            Position += ((X & 0x0f) >> 0) << 0;
+
+            return Position;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/INvGpuEngine.cs b/Ryujinx.Graphics/Gpu/INvGpuEngine.cs
new file mode 100644
index 000000000..17e9b435c
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/INvGpuEngine.cs
@@ -0,0 +1,11 @@
+using ChocolArm64.Memory;
+
+namespace Ryujinx.Graphics.Gpu
+{
+    interface INvGpuEngine
+    {
+        int[] Registers { get; }
+
+        void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/ISwizzle.cs b/Ryujinx.Graphics/Gpu/ISwizzle.cs
new file mode 100644
index 000000000..755051d0c
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/ISwizzle.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.Graphics.Gpu
+{
+    interface ISwizzle
+    {
+        int GetSwizzleOffset(int X, int Y);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/LinearSwizzle.cs b/Ryujinx.Graphics/Gpu/LinearSwizzle.cs
new file mode 100644
index 000000000..c7a6b304e
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/LinearSwizzle.cs
@@ -0,0 +1,19 @@
+namespace Ryujinx.Graphics.Gpu
+{
+    class LinearSwizzle : ISwizzle
+    {
+        private int Pitch;
+        private int Bpp;
+
+        public LinearSwizzle(int Pitch, int Bpp)
+        {
+            this.Pitch = Pitch;
+            this.Bpp   = Bpp;
+        }
+
+        public int GetSwizzleOffset(int X, int Y)
+        {
+            return X * Bpp + Y * Pitch;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/MacroInterpreter.cs b/Ryujinx.Graphics/Gpu/MacroInterpreter.cs
new file mode 100644
index 000000000..233baac8b
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/MacroInterpreter.cs
@@ -0,0 +1,420 @@
+using ChocolArm64.Memory;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Gpu
+{
+    class MacroInterpreter
+    {
+        private enum AssignmentOperation
+        {
+            IgnoreAndFetch                  = 0,
+            Move                            = 1,
+            MoveAndSetMaddr                 = 2,
+            FetchAndSend                    = 3,
+            MoveAndSend                     = 4,
+            FetchAndSetMaddr                = 5,
+            MoveAndSetMaddrThenFetchAndSend = 6,
+            MoveAndSetMaddrThenSendHigh     = 7
+        }
+
+        private enum AluOperation
+        {
+            AluReg                = 0,
+            AddImmediate          = 1,
+            BitfieldReplace       = 2,
+            BitfieldExtractLslImm = 3,
+            BitfieldExtractLslReg = 4,
+            ReadImmediate         = 5
+        }
+
+        private enum AluRegOperation
+        {
+            Add                = 0,
+            AddWithCarry       = 1,
+            Subtract           = 2,
+            SubtractWithBorrow = 3,
+            BitwiseExclusiveOr = 8,
+            BitwiseOr          = 9,
+            BitwiseAnd         = 10,
+            BitwiseAndNot      = 11,
+            BitwiseNotAnd      = 12
+        }
+
+        private NvGpuFifo    PFifo;
+        private INvGpuEngine Engine;
+
+        public Queue<int> Fifo { get; private set; }
+
+        private int[] Gprs;
+
+        private int MethAddr;
+        private int MethIncr;
+
+        private bool Carry;
+
+        private int OpCode;
+
+        private int PipeOp;
+
+        private long Pc;
+
+        public MacroInterpreter(NvGpuFifo PFifo, INvGpuEngine Engine)
+        {
+            this.PFifo  = PFifo;
+            this.Engine = Engine;
+
+            Fifo = new Queue<int>();
+
+            Gprs = new int[8];
+        }
+
+        public void Execute(AMemory Memory, long Position, int Param)
+        {
+            Reset();
+
+            Gprs[1] = Param;
+
+            Pc = Position;
+
+            FetchOpCode(Memory);
+
+            while (Step(Memory));
+
+            //Due to the delay slot, we still need to execute
+            //one more instruction before we actually exit.
+            Step(Memory);
+        }
+
+        private void Reset()
+        {
+            for (int Index = 0; Index < Gprs.Length; Index++)
+            {
+                Gprs[Index] = 0;
+            }
+
+            MethAddr = 0;
+            MethIncr = 0;
+
+            Carry = false;
+        }
+
+        private bool Step(AMemory Memory)
+        {
+            long BaseAddr = Pc - 4;
+
+            FetchOpCode(Memory);
+
+            if ((OpCode & 7) < 7)
+            {
+                //Operation produces a value.
+                AssignmentOperation AsgOp = (AssignmentOperation)((OpCode >> 4) & 7);
+
+                int Result = GetAluResult();
+
+                switch (AsgOp)
+                {
+                    //Fetch parameter and ignore result.
+                    case AssignmentOperation.IgnoreAndFetch:
+                    {
+                        SetDstGpr(FetchParam());
+
+                        break;
+                    }
+
+                    //Move result.
+                    case AssignmentOperation.Move:
+                    {
+                        SetDstGpr(Result);
+
+                        break;
+                    }
+
+                    //Move result and use as Method Address.
+                    case AssignmentOperation.MoveAndSetMaddr:
+                    {
+                        SetDstGpr(Result);
+
+                        SetMethAddr(Result);
+
+                        break;
+                    }
+
+                    //Fetch parameter and send result.
+                    case AssignmentOperation.FetchAndSend:
+                    {
+                        SetDstGpr(FetchParam());
+
+                        Send(Memory, Result);
+
+                        break;
+                    }
+
+                    //Move and send result.
+                    case AssignmentOperation.MoveAndSend:
+                    {
+                        SetDstGpr(Result);
+
+                        Send(Memory, Result);
+
+                        break;
+                    }
+
+                    //Fetch parameter and use result as Method Address.
+                    case AssignmentOperation.FetchAndSetMaddr:
+                    {
+                        SetDstGpr(FetchParam());
+
+                        SetMethAddr(Result);
+
+                        break;
+                    }
+
+                    //Move result and use as Method Address, then fetch and send paramter.
+                    case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend:
+                    {
+                        SetDstGpr(Result);
+
+                        SetMethAddr(Result);
+
+                        Send(Memory, FetchParam());
+
+                        break;
+                    }
+
+                    //Move result and use as Method Address, then send bits 17:12 of result.
+                    case AssignmentOperation.MoveAndSetMaddrThenSendHigh:
+                    {
+                        SetDstGpr(Result);
+
+                        SetMethAddr(Result);
+
+                        Send(Memory, (Result >> 12) & 0x3f);
+
+                        break;
+                    }
+                }
+            }
+            else
+            {
+                //Branch.
+                bool OnNotZero = ((OpCode >> 4) & 1) != 0;
+
+                bool Taken = OnNotZero
+                    ? GetGprA() != 0
+                    : GetGprA() == 0;
+
+                if (Taken)
+                {
+                    Pc = BaseAddr + (GetImm() << 2);
+
+                    bool NoDelays = (OpCode & 0x20) != 0;
+
+                    if (NoDelays)
+                    {
+                        FetchOpCode(Memory);
+                    }
+
+                    return true;
+                }
+            }
+
+            bool Exit = (OpCode & 0x80) != 0;
+
+            return !Exit;
+        }
+
+        private void FetchOpCode(AMemory Memory)
+        {
+            OpCode = PipeOp;
+
+            PipeOp = Memory.ReadInt32(Pc);
+
+            Pc += 4;
+        }
+
+        private int GetAluResult()
+        {
+            AluOperation Op = (AluOperation)(OpCode & 7);
+
+            switch (Op)
+            {
+                case AluOperation.AluReg:
+                {
+                    AluRegOperation AluOp = (AluRegOperation)((OpCode >> 17) & 0x1f);
+
+                    return GetAluResult(AluOp, GetGprA(), GetGprB());
+                }
+
+                case AluOperation.AddImmediate:
+                {
+                    return GetGprA() + GetImm();
+                }
+
+                case AluOperation.BitfieldReplace:
+                case AluOperation.BitfieldExtractLslImm:
+                case AluOperation.BitfieldExtractLslReg:
+                {
+                    int BfSrcBit = (OpCode >> 17) & 0x1f;
+                    int BfSize   = (OpCode >> 22) & 0x1f;
+                    int BfDstBit = (OpCode >> 27) & 0x1f;
+
+                    int BfMask = (1 << BfSize) - 1;
+
+                    int Dst = GetGprA();
+                    int Src = GetGprB();
+
+                    switch (Op)
+                    {
+                        case AluOperation.BitfieldReplace:
+                        {
+                            Src = (int)((uint)Src >> BfSrcBit) & BfMask;
+
+                            Dst &= ~(BfMask << BfDstBit);
+
+                            Dst |= Src << BfDstBit;
+
+                            return Dst;
+                        }
+
+                        case AluOperation.BitfieldExtractLslImm:
+                        {
+                            Src = (int)((uint)Src >> Dst) & BfMask;
+
+                            return Src << BfDstBit;
+                        }
+
+                        case AluOperation.BitfieldExtractLslReg:
+                        {
+                            Src = (int)((uint)Src >> BfSrcBit) & BfMask;
+
+                            return Src << Dst;
+                        }
+                    }
+
+                    break;
+                }
+
+                case AluOperation.ReadImmediate:
+                {
+                    return Read(GetGprA() + GetImm());
+                }
+            }
+
+            throw new ArgumentException(nameof(OpCode));
+        }
+
+        private int GetAluResult(AluRegOperation AluOp, int A, int B)
+        {
+            switch (AluOp)
+            {
+                case AluRegOperation.Add:
+                {
+                    ulong Result = (ulong)A + (ulong)B;
+
+                    Carry = Result > 0xffffffff;
+
+                    return (int)Result;
+                }
+
+                case AluRegOperation.AddWithCarry:
+                {
+                    ulong Result = (ulong)A + (ulong)B + (Carry ? 1UL : 0UL);
+
+                    Carry = Result > 0xffffffff;
+
+                    return (int)Result;
+                }
+
+                case AluRegOperation.Subtract:
+                {
+                    ulong Result = (ulong)A - (ulong)B;
+
+                    Carry = Result < 0x100000000;
+
+                    return (int)Result;
+                }
+
+                case AluRegOperation.SubtractWithBorrow:
+                {
+                    ulong Result = (ulong)A - (ulong)B - (Carry ? 0UL : 1UL);
+
+                    Carry = Result < 0x100000000;
+
+                    return (int)Result;
+                }
+
+                case AluRegOperation.BitwiseExclusiveOr: return   A ^  B;
+                case AluRegOperation.BitwiseOr:          return   A |  B;
+                case AluRegOperation.BitwiseAnd:         return   A &  B;
+                case AluRegOperation.BitwiseAndNot:      return   A & ~B;
+                case AluRegOperation.BitwiseNotAnd:      return ~(A &  B);
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(AluOp));
+        }
+
+        private int GetImm()
+        {
+            //Note: The immediate is signed, the sign-extension is intended here.
+            return OpCode >> 14;
+        }
+
+        private void SetMethAddr(int Value)
+        {
+            MethAddr = (Value >>  0) & 0xfff;
+            MethIncr = (Value >> 12) & 0x3f;
+        }
+
+        private void SetDstGpr(int Value)
+        {
+            Gprs[(OpCode >> 8) & 7] = Value;
+        }
+
+        private int GetGprA()
+        {
+            return GetGprValue((OpCode >> 11) & 7);
+        }
+
+        private int GetGprB()
+        {
+            return GetGprValue((OpCode >> 14) & 7);
+        }
+
+        private int GetGprValue(int Index)
+        {
+            return Index != 0 ? Gprs[Index] : 0;
+        }
+
+        private int FetchParam()
+        {
+            int Value;
+
+            //If we don't have any parameters in the FIFO,
+            //keep running the PFIFO engine until it writes the parameters.
+            while (!Fifo.TryDequeue(out Value))
+            {
+                if (!PFifo.Step())
+                {
+                    return 0;
+                }
+            }
+
+            return Value;
+        }
+
+        private int Read(int Reg)
+        {
+            return Engine.Registers[Reg];
+        }
+
+        private void Send(AMemory Memory, int Value)
+        {
+            NsGpuPBEntry PBEntry = new NsGpuPBEntry(MethAddr, 0, Value);
+
+            Engine.CallMethod(Memory, PBEntry);
+
+            MethAddr += MethIncr;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NsGpu.cs b/Ryujinx.Graphics/Gpu/NsGpu.cs
index 133d0af25..9a2e90128 100644
--- a/Ryujinx.Graphics/Gpu/NsGpu.cs
+++ b/Ryujinx.Graphics/Gpu/NsGpu.cs
@@ -1,5 +1,5 @@
-using ChocolArm64.Memory;
 using Ryujinx.Graphics.Gal;
+using System.Threading;
 
 namespace Ryujinx.Graphics.Gpu
 {
@@ -9,7 +9,13 @@ namespace Ryujinx.Graphics.Gpu
 
         internal NsGpuMemoryMgr MemoryMgr { get; private set; }
 
-        internal NsGpuPGraph PGraph { get; private set; }
+        public NvGpuFifo Fifo { get; private set; }
+
+        public NvGpuEngine3d Engine3d { get; private set; }
+
+        private Thread FifoProcessing;
+
+        private bool KeepRunning;
 
         public NsGpu(IGalRenderer Renderer)
         {
@@ -17,7 +23,15 @@ namespace Ryujinx.Graphics.Gpu
 
             MemoryMgr = new NsGpuMemoryMgr();
 
-            PGraph = new NsGpuPGraph(this);
+            Fifo = new NvGpuFifo(this);
+
+            Engine3d = new NvGpuEngine3d(this);
+
+            KeepRunning = true;
+
+            FifoProcessing = new Thread(ProcessFifo);
+
+            FifoProcessing.Start();
         }
 
         public long GetCpuAddr(long Position)
@@ -35,11 +49,6 @@ namespace Ryujinx.Graphics.Gpu
             return MemoryMgr.Map(CpuAddr, GpuAddr, Size);
         }
 
-        public void ProcessPushBuffer(NsGpuPBEntry[] PushBuffer, AMemory Memory)
-        {
-            PGraph.ProcessPushBuffer(PushBuffer, Memory);
-        }
-
         public long ReserveMemory(long Size, long Align)
         {
             return MemoryMgr.Reserve(Size, Align);
@@ -49,5 +58,15 @@ namespace Ryujinx.Graphics.Gpu
         {
             return MemoryMgr.Reserve(GpuAddr, Size, Align);
         }
+
+        private void ProcessFifo()
+        {
+            while (KeepRunning)
+            {
+                Fifo.DispatchCalls();
+
+                Thread.Yield();
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs b/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs
index 8063651aa..d405a93c6 100644
--- a/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs
+++ b/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs
@@ -1,13 +1,11 @@
 using System;
-using System.Collections.Generic;
 using System.Collections.ObjectModel;
-using System.IO;
 
 namespace Ryujinx.Graphics.Gpu
 {
     public struct NsGpuPBEntry
     {
-        public NsGpuRegister Register { get; private set; }
+        public int Method { get; private set; }
 
         public int SubChannel { get; private set; }
 
@@ -15,65 +13,11 @@ namespace Ryujinx.Graphics.Gpu
 
         public ReadOnlyCollection<int> Arguments => Array.AsReadOnly(m_Arguments);
 
-        public NsGpuPBEntry(NsGpuRegister Register, int SubChannel, params int[] Arguments)
+        public NsGpuPBEntry(int Method, int SubChannel, params int[] Arguments)
         {
-            this.Register    = Register;
+            this.Method      = Method;
             this.SubChannel  = SubChannel;
             this.m_Arguments = Arguments;
         }
-
-        public static NsGpuPBEntry[] DecodePushBuffer(byte[] Data)
-        {
-            using (MemoryStream MS = new MemoryStream(Data))
-            {
-                BinaryReader Reader = new BinaryReader(MS);
-
-                List<NsGpuPBEntry> GpFifos = new List<NsGpuPBEntry>();
-
-                bool CanRead() => MS.Position + 4 <= MS.Length;
-
-                while (CanRead())
-                {
-                    int Packed = Reader.ReadInt32();
-
-                    int Reg  = (Packed << 2)  & 0x7ffc;
-                    int SubC = (Packed >> 13) & 7;
-                    int Args = (Packed >> 16) & 0x1fff;
-                    int Mode = (Packed >> 29) & 7;
-
-                    if (Mode == 4)
-                    {
-                        //Inline Mode.
-                        GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Args));
-                    }
-                    else
-                    {
-                        //Word mode.
-                        if (Mode == 1)
-                        {
-                            //Sequential Mode.
-                            for (int Index = 0; Index < Args && CanRead(); Index++, Reg += 4)
-                            {
-                                GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Reader.ReadInt32()));
-                            }
-                        }
-                        else
-                        {
-                            //Non-Sequential Mode.
-                            int[] Arguments = new int[Args];
-
-                            for (int Index = 0; Index < Args && CanRead(); Index++)
-                            {
-                                Arguments[Index] = Reader.ReadInt32();
-                            }
-
-                            GpFifos.Add(new NsGpuPBEntry((NsGpuRegister)Reg, SubC, Arguments));
-                        }
-                    }
-                }
-
-                return GpFifos.ToArray();
-            }
-        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs b/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs
deleted file mode 100644
index eb893f74c..000000000
--- a/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs
+++ /dev/null
@@ -1,276 +0,0 @@
-using ChocolArm64.Memory;
-using Ryujinx.Graphics.Gal;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.Gpu
-{
-    class NsGpuPGraph
-    {
-        private NsGpu Gpu;
-
-        private int[] Registers;
-
-        public NsGpuEngine[] SubChannels;
-
-        private Dictionary<long, int> CurrentVertexBuffers;
-
-        public NsGpuPGraph(NsGpu Gpu)
-        {
-            this.Gpu = Gpu;
-
-            Registers = new int[0x1000];
-
-            SubChannels = new NsGpuEngine[8];
-
-            CurrentVertexBuffers = new Dictionary<long, int>();
-        }
-
-        public void ProcessPushBuffer(NsGpuPBEntry[] PushBuffer, AMemory Memory)
-        {
-            bool HasQuery = false;
-
-            foreach (NsGpuPBEntry Entry in PushBuffer)
-            {
-                if (Entry.Arguments.Count == 1)
-                {
-                    SetRegister(Entry.Register, Entry.Arguments[0]);
-                }
-
-                switch (Entry.Register)
-                {
-                    case NsGpuRegister.BindChannel:
-                        if (Entry.Arguments.Count > 0)
-                        {
-                            SubChannels[Entry.SubChannel] = (NsGpuEngine)Entry.Arguments[0];
-                        }
-                        break;
-
-                    case NsGpuRegister._3dVertexArray0Fetch:
-                        SendVertexBuffers(Memory);
-                        break;
-                    
-                    case NsGpuRegister._3dCbData0:
-                        if (GetRegister(NsGpuRegister._3dCbPos) == 0x20)
-                        {
-                            SendTexture(Memory);
-                        }
-                        break;
-
-                    case NsGpuRegister._3dQueryAddressHigh:
-                    case NsGpuRegister._3dQueryAddressLow:
-                    case NsGpuRegister._3dQuerySequence:
-                    case NsGpuRegister._3dQueryGet:
-                        HasQuery = true;
-                        break;
-                }
-            }
-
-            if (HasQuery)
-            {
-                long Position =
-                    (long)GetRegister(NsGpuRegister._3dQueryAddressHigh) << 32 |
-                    (long)GetRegister(NsGpuRegister._3dQueryAddressLow)  << 0;
-
-                int Seq = GetRegister(NsGpuRegister._3dQuerySequence);
-                int Get = GetRegister(NsGpuRegister._3dQueryGet);
-
-                int Mode = Get & 3;
-
-                if (Mode == 0)
-                {
-                    //Write
-                    Position = Gpu.MemoryMgr.GetCpuAddr(Position);
-
-                    if (Position != -1)
-                    {
-                        Gpu.Renderer.QueueAction(delegate()
-                        {
-                            Memory.WriteInt32(Position, Seq);
-                        });
-                    }
-                }
-            }
-        }
-
-        private void SendVertexBuffers(AMemory Memory)
-        {
-            long Position =
-                (long)GetRegister(NsGpuRegister._3dVertexArray0StartHigh) << 32 |
-                (long)GetRegister(NsGpuRegister._3dVertexArray0StartLow)  << 0;
-
-            long Limit =
-                (long)GetRegister(NsGpuRegister._3dVertexArray0LimitHigh) << 32 |
-                (long)GetRegister(NsGpuRegister._3dVertexArray0LimitLow)  << 0;
-
-            int VbIndex = CurrentVertexBuffers.Count;
-
-            if (!CurrentVertexBuffers.TryAdd(Position, VbIndex))
-            {
-                VbIndex = CurrentVertexBuffers[Position];
-            }
-
-            if (Limit != 0)
-            {
-                long Size = (Limit - Position) + 1;
-
-                Position = Gpu.MemoryMgr.GetCpuAddr(Position);
-
-                if (Position != -1)
-                {
-                    byte[] Buffer = AMemoryHelper.ReadBytes(Memory, Position, (int)Size);
-
-                    int Stride = GetRegister(NsGpuRegister._3dVertexArray0Fetch) & 0xfff;
-
-                    List<GalVertexAttrib> Attribs = new List<GalVertexAttrib>();
-
-                    for (int Attr = 0; Attr < 16; Attr++)
-                    {
-                        int Packed = GetRegister(NsGpuRegister._3dVertexAttrib0Format + Attr * 4);
-
-                        GalVertexAttrib Attrib = new GalVertexAttrib(Attr,
-                                                  (Packed >>  0) & 0x1f,
-                                                 ((Packed >>  6) & 0x1) != 0,
-                                                  (Packed >>  7) & 0x3fff,
-                            (GalVertexAttribSize)((Packed >> 21) & 0x3f),
-                            (GalVertexAttribType)((Packed >> 27) & 0x7),
-                                                 ((Packed >> 31) & 0x1) != 0);
-
-                        if (Attrib.Offset < Stride)
-                        {
-                            Attribs.Add(Attrib);
-                        }
-                    }
-
-                    Gpu.Renderer.QueueAction(delegate()
-                    {
-                        Gpu.Renderer.SendVertexBuffer(VbIndex, Buffer, Stride, Attribs.ToArray());
-                    });
-                }
-            }
-        }
-
-        private void SendTexture(AMemory Memory)
-        {
-            long TicPos = (long)GetRegister(NsGpuRegister._3dTicAddressHigh) << 32 |
-                          (long)GetRegister(NsGpuRegister._3dTicAddressLow)  << 0;
-
-            int CbData = GetRegister(NsGpuRegister._3dCbData0);
-
-            int TicIndex = (CbData >>  0) & 0xfffff;
-            int TscIndex = (CbData >> 20) & 0xfff; //I guess?
-
-            TicPos = Gpu.MemoryMgr.GetCpuAddr(TicPos + TicIndex * 0x20);
-
-            if (TicPos != -1)
-            {
-                int Word0 = Memory.ReadInt32(TicPos + 0x0);
-                int Word1 = Memory.ReadInt32(TicPos + 0x4);
-                int Word2 = Memory.ReadInt32(TicPos + 0x8);
-                int Word3 = Memory.ReadInt32(TicPos + 0xc);
-                int Word4 = Memory.ReadInt32(TicPos + 0x10);
-                int Word5 = Memory.ReadInt32(TicPos + 0x14);
-                int Word6 = Memory.ReadInt32(TicPos + 0x18);
-                int Word7 = Memory.ReadInt32(TicPos + 0x1c);
-
-                long TexAddress = Word1;
-
-                TexAddress |= (long)(Word2 & 0xff) << 32;
-
-                TexAddress = Gpu.MemoryMgr.GetCpuAddr(TexAddress);
-
-                if (TexAddress != -1)
-                {
-                    NsGpuTextureFormat Format = (NsGpuTextureFormat)(Word0 & 0x7f);
-
-                    int Width  = (Word4 & 0xffff) + 1;
-                    int Height = (Word5 & 0xffff) + 1;
-
-                    byte[] Buffer = GetDecodedTexture(Memory, Format, TexAddress, Width, Height);
-
-                    if (Buffer != null)
-                    {
-                        Gpu.Renderer.QueueAction(delegate()
-                        {
-                            Gpu.Renderer.SendR8G8B8A8Texture(0, Buffer, Width, Height);
-                        });
-                    }
-                }
-            }
-        }
-
-        private static byte[] GetDecodedTexture(
-            AMemory            Memory,
-            NsGpuTextureFormat Format,
-            long               Position,
-            int                Width,
-            int                Height)
-        {
-            byte[] Data = null;
-
-            switch (Format)
-            {
-                case NsGpuTextureFormat.BC1:
-                {
-                    int Size = (Width * Height) >> 1;
-
-                    Data = AMemoryHelper.ReadBytes(Memory, Position, Size);
-
-                    Data = BCn.DecodeBC1(new NsGpuTexture()
-                    {
-                        Width  = Width,
-                        Height = Height,
-                        Data   = Data
-                    }, 0);
-
-                    break;
-                }
-
-                case NsGpuTextureFormat.BC2:
-                {
-                    int Size = Width * Height;
-
-                    Data = AMemoryHelper.ReadBytes(Memory, Position, Size);
-
-                    Data = BCn.DecodeBC2(new NsGpuTexture()
-                    {
-                        Width  = Width,
-                        Height = Height,
-                        Data   = Data
-                    }, 0);
-
-                    break;
-                }
-
-                case NsGpuTextureFormat.BC3:
-                {
-                    int Size = Width * Height;
-
-                    Data = AMemoryHelper.ReadBytes(Memory, Position, Size);
-
-                    Data = BCn.DecodeBC3(new NsGpuTexture()
-                    {
-                        Width  = Width,
-                        Height = Height,
-                        Data   = Data
-                    }, 0);
-
-                    break;
-                }
-
-                //default: throw new NotImplementedException(Format.ToString());
-            }
-
-            return Data;
-        }
-
-        public int GetRegister(NsGpuRegister Register)
-        {
-            return Registers[((int)Register >> 2) & 0xfff];
-        }
-
-        public void SetRegister(NsGpuRegister Register, int Value)
-        {
-            Registers[((int)Register >> 2) & 0xfff] = Value;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NsGpuRegister.cs b/Ryujinx.Graphics/Gpu/NsGpuRegister.cs
deleted file mode 100644
index 319e2c01f..000000000
--- a/Ryujinx.Graphics/Gpu/NsGpuRegister.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-namespace Ryujinx.Graphics.Gpu
-{
-    public enum NsGpuRegister
-    {
-        BindChannel               = 0,
-
-        _2dClipEnable             = 0x0290,
-        _2dOperation              = 0x02ac,
-
-        _3dGlobalBase             = 0x02c8,
-        _3dRt0AddressHigh         = 0x0800,
-        _3dRt0AddressLow          = 0x0804,
-        _3dRt0Horiz               = 0x0808,
-        _3dRt0Vert                = 0x080c,
-        _3dRt0Format              = 0x0810,
-        _3dRt0BlockDimensions     = 0x0814,
-        _3dRt0ArrayMode           = 0x0818,
-        _3dRt0LayerStride         = 0x081c,
-        _3dRt0BaseLayer           = 0x0820,
-        _3dViewportScaleX         = 0x0a00,
-        _3dViewportScaleY         = 0x0a04,
-        _3dViewportScaleZ         = 0x0a08,
-        _3dViewportTranslateX     = 0x0a0c,
-        _3dViewportTranslateY     = 0x0a10,
-        _3dViewportTranslateZ     = 0x0a14,
-        _3dViewportHoriz          = 0x0c00,
-        _3dViewportVert           = 0x0c04,
-        _3dDepthRangeNear         = 0x0c08,
-        _3dDepthRangeFar          = 0x0c0c,
-        _3dClearColorR            = 0x0d80,
-        _3dClearColorG            = 0x0d84,
-        _3dClearColorB            = 0x0d88,
-        _3dClearColorA            = 0x0d8c,
-        _3dScreenScissorHoriz     = 0x0ff4,
-        _3dScreenScissorVert      = 0x0ff8,
-        _3dVertexAttrib0Format    = 0x1160,
-        _3dVertexAttrib1Format    = 0x1164,
-        _3dVertexAttrib2Format    = 0x1168,
-        _3dVertexAttrib3Format    = 0x116c,
-        _3dVertexAttrib4Format    = 0x1170,
-        _3dVertexAttrib5Format    = 0x1174,
-        _3dVertexAttrib6Format    = 0x1178,
-        _3dVertexAttrib7Format    = 0x117c,
-        _3dVertexAttrib8Format    = 0x1180,
-        _3dVertexAttrib9Format    = 0x1184,
-        _3dVertexAttrib10Format   = 0x1188,
-        _3dVertexAttrib11Format   = 0x118c,
-        _3dVertexAttrib12Format   = 0x1190,
-        _3dVertexAttrib13Format   = 0x1194,
-        _3dVertexAttrib14Format   = 0x1198,
-        _3dVertexAttrib15Format   = 0x119c,
-        _3dScreenYControl         = 0x13ac,
-        _3dTscAddressHigh         = 0x155c,
-        _3dTscAddressLow          = 0x1560,
-        _3dTscLimit               = 0x1564,
-        _3dTicAddressHigh         = 0x1574,
-        _3dTicAddressLow          = 0x1578,
-        _3dTicLimit               = 0x157c,
-        _3dMultiSampleMode        = 0x15d0,
-        _3dVertexEndGl            = 0x1614,
-        _3dVertexBeginGl          = 0x1618,
-        _3dQueryAddressHigh       = 0x1b00,
-        _3dQueryAddressLow        = 0x1b04,
-        _3dQuerySequence          = 0x1b08,
-        _3dQueryGet               = 0x1b0c,
-        _3dVertexArray0Fetch      = 0x1c00,
-        _3dVertexArray0StartHigh  = 0x1c04,
-        _3dVertexArray0StartLow   = 0x1c08,
-        _3dVertexArray1Fetch      = 0x1c10, //todo: the rest
-        _3dVertexArray0LimitHigh  = 0x1f00,
-        _3dVertexArray0LimitLow   = 0x1f04,
-        _3dCbSize                 = 0x2380,
-        _3dCbAddressHigh          = 0x2384,
-        _3dCbAddressLow           = 0x2388,
-        _3dCbPos                  = 0x238c,
-        _3dCbData0                = 0x2390,
-        _3dCbData1                = 0x2394,
-        _3dCbData2                = 0x2398,
-        _3dCbData3                = 0x239c,
-        _3dCbData4                = 0x23a0,
-        _3dCbData5                = 0x23a4,
-        _3dCbData6                = 0x23a8,
-        _3dCbData7                = 0x23ac,
-        _3dCbData8                = 0x23b0,
-        _3dCbData9                = 0x23b4,
-        _3dCbData10               = 0x23b8,
-        _3dCbData11               = 0x23bc,
-        _3dCbData12               = 0x23c0,
-        _3dCbData13               = 0x23c4,
-        _3dCbData14               = 0x23c8,
-        _3dCbData15               = 0x23cc,
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NsGpuTexture.cs b/Ryujinx.Graphics/Gpu/NsGpuTexture.cs
deleted file mode 100644
index aac422005..000000000
--- a/Ryujinx.Graphics/Gpu/NsGpuTexture.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Ryujinx.Graphics.Gpu
-{
-    struct NsGpuTexture
-    {
-        public int Width;
-        public int Height;
-
-        public byte[] Data;
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NsGpuTextureFormat.cs b/Ryujinx.Graphics/Gpu/NsGpuTextureFormat.cs
deleted file mode 100644
index 2993840be..000000000
--- a/Ryujinx.Graphics/Gpu/NsGpuTextureFormat.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Ryujinx.Graphics.Gpu
-{
-    enum NsGpuTextureFormat
-    {
-        BC1 = 0x24,
-        BC2 = 0x25,
-        BC3 = 0x26
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NsGpuEngine.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine.cs
similarity index 61%
rename from Ryujinx.Graphics/Gpu/NsGpuEngine.cs
rename to Ryujinx.Graphics/Gpu/NvGpuEngine.cs
index 118e2b72a..624915d0d 100644
--- a/Ryujinx.Graphics/Gpu/NsGpuEngine.cs
+++ b/Ryujinx.Graphics/Gpu/NvGpuEngine.cs
@@ -1,13 +1,11 @@
 namespace Ryujinx.Graphics.Gpu
 {
-    enum NsGpuEngine
+    enum NvGpuEngine
     {
-        None    = 0,
         _2d     = 0x902d,
         _3d     = 0xb197,
         Compute = 0xb1c0,
         Kepler  = 0xa140,
-        Dma     = 0xb0b5,
-        GpFifo  = 0xb06f
+        Dma     = 0xb0b5
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
new file mode 100644
index 000000000..88ad76334
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3d.cs
@@ -0,0 +1,505 @@
+using ChocolArm64.Memory;
+using Ryujinx.Graphics.Gal;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Graphics.Gpu
+{
+    public class NvGpuEngine3d : INvGpuEngine
+    {
+        public int[] Registers { get; private set; }
+
+        private NsGpu Gpu;
+
+        private Dictionary<int, NvGpuMethod> Methods;
+
+        private struct ConstBuffer
+        {
+            public bool Enabled;
+            public long Position;
+            public int  Size;
+        }
+
+        private ConstBuffer[] ConstBuffers;
+
+        private HashSet<long> FrameBuffers;
+
+        public NvGpuEngine3d(NsGpu Gpu)
+        {
+            this.Gpu = Gpu;
+
+            Registers = new int[0xe00];
+
+            Methods = new Dictionary<int, NvGpuMethod>();
+
+            void AddMethod(int Meth, int Count, int Stride, NvGpuMethod Method)
+            {
+                while (Count-- > 0)
+                {
+                    Methods.Add(Meth, Method);
+
+                    Meth += Stride;
+                }
+            }
+
+            AddMethod(0x585,  1, 1, VertexEndGl);
+            AddMethod(0x674,  1, 1, ClearBuffers);
+            AddMethod(0x6c3,  1, 1, QueryControl);
+            AddMethod(0x8e4, 16, 1, CbData);
+            AddMethod(0x904,  1, 1, CbBind);
+
+            ConstBuffers = new ConstBuffer[18];
+
+            FrameBuffers = new HashSet<long>();
+        }
+
+        public void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry)
+        {
+            if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method))
+            {
+                Method(Memory, PBEntry);
+            }
+            else
+            {
+                WriteRegister(PBEntry);
+            }
+        }
+
+        private void VertexEndGl(AMemory Memory, NsGpuPBEntry PBEntry)
+        {
+            SetFrameBuffer(0);
+
+            long[] Tags = UploadShaders(Memory);
+
+            Gpu.Renderer.BindProgram();
+
+            SetAlphaBlending();
+
+            UploadTextures(Memory, Tags);
+            UploadUniforms(Memory);
+            UploadVertexArrays(Memory);
+        }
+
+        private void ClearBuffers(AMemory Memory, NsGpuPBEntry PBEntry)
+        {
+            int Arg0 = PBEntry.Arguments[0];
+
+            int FbIndex = (Arg0 >> 6) & 0xf;
+
+            int Layer = (Arg0 >> 10) & 0x3ff;
+
+            GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f);
+
+            SetFrameBuffer(0);
+
+            //TODO: Enable this once the frame buffer problems are fixed.
+            //Gpu.Renderer.ClearBuffers(Layer, Flags);
+        }
+
+        private void SetFrameBuffer(int FbIndex)
+        {
+            long Address = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + FbIndex * 0x10);
+
+            FrameBuffers.Add(Address);
+
+            int Width  = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth  + FbIndex * 0x10);
+            int Height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + FbIndex * 0x10);
+
+            //Note: Using the Width/Height results seems to give incorrect results.
+            //Maybe the size of all frame buffers is hardcoded to screen size? This seems unlikely.
+            Gpu.Renderer.CreateFrameBuffer(Address, 1280, 720);
+            Gpu.Renderer.BindFrameBuffer(Address);
+        }
+
+        private long[] UploadShaders(AMemory Memory)
+        {
+            long[] Tags = new long[5];
+
+            long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
+
+            for (int Index = 0; Index < 6; Index++)
+            {
+                int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + Index * 0x10);
+                int Offset  = ReadRegister(NvGpuEngine3dReg.ShaderNOffset  + Index * 0x10);
+
+                //Note: Vertex Program (B) is always enabled.
+                bool Enable = (Control & 1) != 0 || Index == 1;
+
+                if (!Enable)
+                {
+                    continue;
+                }
+
+                long Tag = BasePosition + (uint)Offset;
+
+                long Position = Gpu.GetCpuAddr(Tag);
+
+                //TODO: Find a better way to calculate the size.
+                int Size = 0x20000;
+
+                byte[] Code = AMemoryHelper.ReadBytes(Memory, Position, (uint)Size);
+
+                GalShaderType ShaderType = GetTypeFromProgram(Index);
+
+                Tags[(int)ShaderType] = Tag;
+
+                Gpu.Renderer.CreateShader(Tag, ShaderType, Code);
+                Gpu.Renderer.BindShader(Tag);
+            }
+
+            int RawSX = ReadRegister(NvGpuEngine3dReg.ViewportScaleX);
+            int RawSY = ReadRegister(NvGpuEngine3dReg.ViewportScaleY);
+
+            float SX = BitConverter.Int32BitsToSingle(RawSX);
+            float SY = BitConverter.Int32BitsToSingle(RawSY);
+
+            float SignX = MathF.Sign(SX);
+            float SignY = MathF.Sign(SY);
+
+            Gpu.Renderer.SetUniform2F(GalConsts.FlipUniformName, SignX, SignY);
+
+            return Tags;
+        }
+
+        private static GalShaderType GetTypeFromProgram(int Program)
+        {
+            switch (Program)
+            {
+                case 0:
+                case 1: return GalShaderType.Vertex;
+                case 2: return GalShaderType.TessControl;
+                case 3: return GalShaderType.TessEvaluation;
+                case 4: return GalShaderType.Geometry;
+                case 5: return GalShaderType.Fragment;
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(Program));
+        }
+
+        private void SetAlphaBlending()
+        {
+            //TODO: Support independent blend properly.
+            bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0;
+
+            Gpu.Renderer.SetBlendEnable(Enable);
+
+            bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0;
+
+            GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb);
+
+            GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb);
+            GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb);
+
+            if (BlendSeparateAlpha)
+            {
+                GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationAlpha);
+
+                GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha);
+                GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha);
+
+                Gpu.Renderer.SetBlendSeparate(
+                    EquationRgb,
+                    EquationAlpha,
+                    FuncSrcRgb,
+                    FuncDstRgb,
+                    FuncSrcAlpha,
+                    FuncDstAlpha);
+            }
+            else
+            {
+                Gpu.Renderer.SetBlend(EquationRgb, FuncSrcRgb, FuncDstRgb);
+            }
+        }
+
+        private void UploadTextures(AMemory Memory, long[] Tags)
+        {
+            long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
+
+            int TextureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex);
+
+            long BasePosition = ConstBuffers[TextureCbIndex].Position;
+
+            long Size = (uint)ConstBuffers[TextureCbIndex].Size;
+
+            //Note: On the emulator renderer, Texture Unit 0 is
+            //reserved for drawing the frame buffer.
+            int TexIndex = 1;
+
+            for (int Index = 0; Index < Tags.Length; Index++)
+            {
+                foreach (ShaderDeclInfo DeclInfo in Gpu.Renderer.GetTextureUsage(Tags[Index]))
+                {
+                    long Position = BasePosition + Index * Size;
+
+                    UploadTexture(Memory, Position, TexIndex, DeclInfo.Index);
+
+                    Gpu.Renderer.SetUniform1(DeclInfo.Name, TexIndex);
+
+                    TexIndex++;
+                }
+            }
+        }
+
+        private void UploadTexture(AMemory Memory, long BasePosition, int TexIndex, int HndIndex)
+        {
+            long Position = BasePosition + HndIndex * 4;
+
+            int TextureHandle = Memory.ReadInt32(Position);
+
+            int TicIndex = (TextureHandle >>  0) & 0xfffff;
+            int TscIndex = (TextureHandle >> 20) & 0xfff;
+
+            TryGetCpuAddr(NvGpuEngine3dReg.TexHeaderPoolOffset,  out long TicPosition);
+            TryGetCpuAddr(NvGpuEngine3dReg.TexSamplerPoolOffset, out long TscPosition);
+
+            TicPosition += TicIndex * 0x20;
+            TscPosition += TscIndex * 0x20;
+
+            GalTextureSampler Sampler = TextureFactory.MakeSampler(Gpu, Memory, TscPosition);
+
+            long TextureAddress = Memory.ReadInt64(TicPosition + 4) & 0xffffffffffff;
+
+            if (FrameBuffers.Contains(TextureAddress))
+            {
+                //This texture is a frame buffer texture,
+                //we shouldn't read anything from memory and bind
+                //the frame buffer texture instead, since we're not
+                //really writing anything to memory.
+                Gpu.Renderer.BindFrameBufferTexture(TextureAddress, TexIndex, Sampler);
+            }
+            else
+            {
+                GalTexture Texture = TextureFactory.MakeTexture(Gpu, Memory, TicPosition);
+
+                Gpu.Renderer.SetTextureAndSampler(TexIndex, Texture, Sampler);
+                Gpu.Renderer.BindTexture(TexIndex);
+            }
+        }
+
+        private void UploadUniforms(AMemory Memory)
+        {
+            long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress);
+
+            for (int Index = 0; Index < 5; Index++)
+            {
+                int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + (Index + 1) * 0x10);
+                int Offset  = ReadRegister(NvGpuEngine3dReg.ShaderNOffset  + (Index + 1) * 0x10);
+
+                //Note: Vertex Program (B) is always enabled.
+                bool Enable = (Control & 1) != 0 || Index == 0;
+
+                if (!Enable)
+                {
+                    continue;
+                }
+
+                for (int Cbuf = 0; Cbuf < ConstBuffers.Length; Cbuf++)
+                {
+                    ConstBuffer Cb = ConstBuffers[Cbuf];
+
+                    if (Cb.Enabled)
+                    {
+                        long CbPosition = Cb.Position + Index * Cb.Size;
+
+                        byte[] Data = AMemoryHelper.ReadBytes(Memory, CbPosition, (uint)Cb.Size);
+
+                        Gpu.Renderer.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Data);
+                    }
+                }
+            }
+        }
+
+        private void UploadVertexArrays(AMemory Memory)
+        {
+            long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress);
+
+            int IndexSize  = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat);
+            int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst);
+            int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount);
+
+            GalIndexFormat IndexFormat = (GalIndexFormat)IndexSize;
+
+            IndexSize = 1 << IndexSize;
+
+            if (IndexSize > 4)
+            {
+                throw new InvalidOperationException();
+            }
+
+            if (IndexSize != 0)
+            {
+                IndexPosition = Gpu.GetCpuAddr(IndexPosition);
+
+                int BufferSize = IndexCount * IndexSize;
+
+                byte[] Data = AMemoryHelper.ReadBytes(Memory, IndexPosition, BufferSize);
+
+                Gpu.Renderer.SetIndexArray(Data, IndexFormat);
+            }
+
+            List<GalVertexAttrib>[] Attribs = new List<GalVertexAttrib>[32];
+
+            for (int Attr = 0; Attr < 16; Attr++)
+            {
+                int Packed = ReadRegister(NvGpuEngine3dReg.VertexAttribNFormat + Attr);
+
+                int ArrayIndex = Packed & 0x1f;
+
+                if (Attribs[ArrayIndex] == null)
+                {
+                    Attribs[ArrayIndex] = new List<GalVertexAttrib>();
+                }
+
+                Attribs[ArrayIndex].Add(new GalVertexAttrib(
+                                         ((Packed >>  6) & 0x1) != 0,
+                                          (Packed >>  7) & 0x3fff,
+                    (GalVertexAttribSize)((Packed >> 21) & 0x3f),
+                    (GalVertexAttribType)((Packed >> 27) & 0x7),
+                                         ((Packed >> 31) & 0x1) != 0));
+            }
+
+            for (int Index = 0; Index < 32; Index++)
+            {
+                int Control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + Index * 4);
+
+                bool Enable = (Control & 0x1000) != 0;
+
+                if (!Enable)
+                {
+                    continue;
+                }
+
+                long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4);
+                long VertexEndPos   = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 4);
+
+                long Size = (VertexEndPos - VertexPosition) + 1;
+
+                int Stride = Control & 0xfff;
+
+                VertexPosition = Gpu.GetCpuAddr(VertexPosition);
+
+                byte[] Data = AMemoryHelper.ReadBytes(Memory, VertexPosition, Size);
+
+                GalVertexAttrib[] AttribArray = Attribs[Index]?.ToArray() ?? new GalVertexAttrib[0];
+
+                Gpu.Renderer.SetVertexArray(Index, Stride, Data, AttribArray);
+
+                int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl);
+
+                GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff);
+
+                if (IndexCount != 0)
+                {
+                    Gpu.Renderer.DrawElements(Index, IndexFirst, PrimType);
+                }
+                else
+                {
+                    Gpu.Renderer.DrawArrays(Index, PrimType);
+                }
+            }
+        }
+
+        private void QueryControl(AMemory Memory, NsGpuPBEntry PBEntry)
+        {
+            if (TryGetCpuAddr(NvGpuEngine3dReg.QueryAddress, out long Position))
+            {
+                int Seq  = Registers[(int)NvGpuEngine3dReg.QuerySequence];
+                int Ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl];
+
+                int Mode = Ctrl & 3;
+
+                if (Mode == 0)
+                {
+                    //Write mode.
+                    Memory.WriteInt32(Position, Seq);
+                }
+            }
+
+            WriteRegister(PBEntry);
+        }
+
+        private void CbData(AMemory Memory, NsGpuPBEntry PBEntry)
+        {
+            if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position))
+            {
+                int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferNOffset);
+
+                foreach (int Arg in PBEntry.Arguments)
+                {
+                    Memory.WriteInt32(Position + Offset, Arg);
+
+                    Offset += 4;
+                }
+
+                WriteRegister(NvGpuEngine3dReg.ConstBufferNOffset, Offset);
+            }
+        }
+
+        private void CbBind(AMemory Memory, NsGpuPBEntry PBEntry)
+        {
+            int Index = PBEntry.Arguments[0];
+
+            bool Enabled = (Index & 1) != 0;
+
+            Index = (Index >> 4) & 0x1f;
+
+            if (TryGetCpuAddr(NvGpuEngine3dReg.ConstBufferNAddress, out long Position))
+            {
+                ConstBuffers[Index].Position = Position;
+                ConstBuffers[Index].Enabled  = Enabled;
+
+                ConstBuffers[Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferNSize);
+            }
+        }
+
+        private int ReadCb(AMemory Memory, int Cbuf, int Offset)
+        {
+            long Position = ConstBuffers[Cbuf].Position;
+
+            int Value = Memory.ReadInt32(Position + Offset);
+
+            return Value;
+        }
+
+        private bool TryGetCpuAddr(NvGpuEngine3dReg Reg, out long Position)
+        {
+            Position = MakeInt64From2xInt32(Reg);
+
+            Position = Gpu.GetCpuAddr(Position);
+
+            return Position != -1;
+        }
+
+        private long MakeInt64From2xInt32(NvGpuEngine3dReg Reg)
+        {
+            return
+                (long)Registers[(int)Reg + 0] << 32 |
+                (uint)Registers[(int)Reg + 1];
+        }
+
+        private void WriteRegister(NsGpuPBEntry PBEntry)
+        {
+            int ArgsCount = PBEntry.Arguments.Count;
+
+            if (ArgsCount > 0)
+            {
+                Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1];
+            }
+        }
+
+        private int ReadRegister(NvGpuEngine3dReg Reg)
+        {
+            return Registers[(int)Reg];
+        }
+
+        private void WriteRegister(NvGpuEngine3dReg Reg, int Value)
+        {
+            Registers[(int)Reg] = Value;
+        }
+
+        public bool IsFrameBufferPosition(long Position)
+        {
+            return FrameBuffers.Contains(Position);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs
new file mode 100644
index 000000000..605ca9dab
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/NvGpuEngine3dReg.cs
@@ -0,0 +1,59 @@
+namespace Ryujinx.Graphics.Gpu
+{
+    enum NvGpuEngine3dReg
+    {
+        FrameBufferNAddress  = 0x200,
+        FrameBufferNWidth    = 0x202,
+        FrameBufferNHeight   = 0x203,
+        FrameBufferNFormat   = 0x204,
+        ViewportScaleX       = 0x280,
+        ViewportScaleY       = 0x281,
+        ViewportScaleZ       = 0x282,
+        ViewportTranslateX   = 0x283,
+        ViewportTranslateY   = 0x284,
+        ViewportTranslateZ   = 0x285,
+        VertexAttribNFormat  = 0x458,
+        IBlendEnable         = 0x4b9,
+        BlendSeparateAlpha   = 0x4cf,
+        BlendEquationRgb     = 0x4d0,
+        BlendFuncSrcRgb      = 0x4d1,
+        BlendFuncDstRgb      = 0x4d2,
+        BlendEquationAlpha   = 0x4d3,
+        BlendFuncSrcAlpha    = 0x4d4,
+        BlendFuncDstAlpha    = 0x4d6,
+        BlendEnableMaster    = 0x4d7,
+        IBlendNEnable        = 0x4d8,
+        VertexArrayElemBase  = 0x50d,
+        TexHeaderPoolOffset  = 0x55d,
+        TexSamplerPoolOffset = 0x557,
+        ShaderAddress        = 0x582,
+        VertexBeginGl        = 0x586,
+        IndexArrayAddress    = 0x5f2,
+        IndexArrayEndAddr    = 0x5f4,
+        IndexArrayFormat     = 0x5f6,
+        IndexBatchFirst      = 0x5f7,
+        IndexBatchCount      = 0x5f8,
+        QueryAddress         = 0x6c0,
+        QuerySequence        = 0x6c2,
+        QueryControl         = 0x6c3,
+        VertexArrayNControl  = 0x700,
+        VertexArrayNAddress  = 0x701,
+        VertexArrayNDivisor  = 0x703,
+        IBlendNSeparateAlpha = 0x780,
+        IBlendNEquationRgb   = 0x781,
+        IBlendNFuncSrcRgb    = 0x782,
+        IBlendNFuncDstRgb    = 0x783,
+        IBlendNEquationAlpha = 0x784,
+        IBlendNFuncSrcAlpha  = 0x785,
+        IBlendNFuncDstAlpha  = 0x786,
+        VertexArrayNEndAddr  = 0x7c0,
+        ShaderNControl       = 0x800,
+        ShaderNOffset        = 0x801,
+        ShaderNMaxGprs       = 0x803,
+        ShaderNType          = 0x804,
+        ConstBufferNSize     = 0x8e0,
+        ConstBufferNAddress  = 0x8e1,
+        ConstBufferNOffset   = 0x8e3,
+        TextureCbIndex       = 0x982
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NvGpuFifo.cs b/Ryujinx.Graphics/Gpu/NvGpuFifo.cs
new file mode 100644
index 000000000..df765895c
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/NvGpuFifo.cs
@@ -0,0 +1,171 @@
+using ChocolArm64.Memory;
+using System.Collections.Concurrent;
+
+namespace Ryujinx.Graphics.Gpu
+{
+    public class NvGpuFifo
+    {
+        private const int MacrosCount    = 0x80;
+        private const int MacroIndexMask = MacrosCount - 1;
+
+        private NsGpu Gpu;
+
+        private ConcurrentQueue<(AMemory, NsGpuPBEntry)> BufferQueue;
+
+        private NvGpuEngine[] SubChannels;
+
+        private struct CachedMacro
+        {
+            public long Position { get; private set; }
+
+            private MacroInterpreter Interpreter;
+
+            public CachedMacro(NvGpuFifo PFifo, INvGpuEngine Engine, long Position)
+            {
+                this.Position = Position;
+
+                Interpreter = new MacroInterpreter(PFifo, Engine);
+            }
+
+            public void PushParam(int Param)
+            {
+                Interpreter?.Fifo.Enqueue(Param);
+            }
+
+            public void Execute(AMemory Memory, int Param)
+            {
+                Interpreter?.Execute(Memory, Position, Param);
+            }
+        }
+
+        private long CurrMacroPosition;
+        private int  CurrMacroBindIndex;
+
+        private CachedMacro[] Macros;
+
+        public NvGpuFifo(NsGpu Gpu)
+        {
+            this.Gpu = Gpu;
+
+            BufferQueue = new ConcurrentQueue<(AMemory, NsGpuPBEntry)>();
+
+            SubChannels = new NvGpuEngine[8];
+
+            Macros = new CachedMacro[MacrosCount];
+        }
+
+        public void PushBuffer(AMemory Memory, NsGpuPBEntry[] Buffer)
+        {
+            foreach (NsGpuPBEntry PBEntry in Buffer)
+            {
+                BufferQueue.Enqueue((Memory, PBEntry));
+            }
+        }
+
+        public void DispatchCalls()
+        {
+            while (Step());
+        }
+
+        public bool Step()
+        {
+            if (BufferQueue.TryDequeue(out (AMemory Memory, NsGpuPBEntry PBEntry) Tuple))
+            {
+                CallMethod(Tuple.Memory, Tuple.PBEntry);
+
+                return true;
+            }
+
+            return false;
+        }
+
+        private void CallMethod(AMemory Memory, NsGpuPBEntry PBEntry)
+        {
+            if (PBEntry.Method < 0x80)
+            {
+                switch ((NvGpuFifoMeth)PBEntry.Method)
+                {
+                    case NvGpuFifoMeth.BindChannel:
+                    {
+                        NvGpuEngine Engine = (NvGpuEngine)PBEntry.Arguments[0];
+
+                        SubChannels[PBEntry.SubChannel] = Engine;
+
+                        break;
+                    }
+
+                    case NvGpuFifoMeth.SetMacroUploadAddress:
+                    {
+                        CurrMacroPosition = (long)((ulong)PBEntry.Arguments[0] << 2);
+
+                        break;
+                    }
+
+                    case NvGpuFifoMeth.SendMacroCodeData:
+                    {
+                        long Position = Gpu.GetCpuAddr(CurrMacroPosition);
+
+                        foreach (int Arg in PBEntry.Arguments)
+                        {
+                            Memory.WriteInt32(Position, Arg);
+
+                            CurrMacroPosition += 4;
+
+                            Position += 4;
+                        }
+                        break;
+                    }
+
+                    case NvGpuFifoMeth.SetMacroBindingIndex:
+                    {
+                        CurrMacroBindIndex = PBEntry.Arguments[0];
+
+                        break;
+                    }
+
+                    case NvGpuFifoMeth.BindMacro:
+                    {
+                        long Position = (long)((ulong)PBEntry.Arguments[0] << 2);
+
+                        Position = Gpu.GetCpuAddr(Position);
+
+                        Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position);
+
+                        break;
+                    }
+                }
+            }
+            else
+            {
+                switch (SubChannels[PBEntry.SubChannel])
+                {
+                    case NvGpuEngine._3d: Call3dMethod(Memory, PBEntry); break;
+                }
+            }
+        }
+
+        private void Call3dMethod(AMemory Memory, NsGpuPBEntry PBEntry)
+        {
+            if (PBEntry.Method < 0xe00)
+            {
+                Gpu.Engine3d.CallMethod(Memory, PBEntry);
+            }
+            else
+            {
+                int MacroIndex = (PBEntry.Method >> 1) & MacroIndexMask;
+
+                if ((PBEntry.Method & 1) != 0)
+                {
+                    foreach (int Arg in PBEntry.Arguments)
+                    {
+                        Macros[MacroIndex].PushParam(Arg);
+                    }
+                }
+                else
+                {
+                    Macros[MacroIndex].Execute(Memory, PBEntry.Arguments[0]);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs b/Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs
new file mode 100644
index 000000000..4287e2500
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/NvGpuFifoMeth.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.Graphics.Gpu
+{
+    enum NvGpuFifoMeth
+    {
+        BindChannel           = 0,
+        SetMacroUploadAddress = 0x45,
+        SendMacroCodeData     = 0x46,
+        SetMacroBindingIndex  = 0x47,
+        BindMacro             = 0x48
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NvGpuMethod.cs b/Ryujinx.Graphics/Gpu/NvGpuMethod.cs
new file mode 100644
index 000000000..2923ddff0
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/NvGpuMethod.cs
@@ -0,0 +1,6 @@
+using ChocolArm64.Memory;
+
+namespace Ryujinx.Graphics.Gpu
+{
+    delegate void NvGpuMethod(AMemory Memory, NsGpuPBEntry PBEntry);
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs b/Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs
new file mode 100644
index 000000000..8cbb3288e
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/NvGpuPushBuffer.cs
@@ -0,0 +1,101 @@
+using System.Collections.Generic;
+using System.IO;
+
+namespace Ryujinx.Graphics.Gpu
+{
+    public static class NvGpuPushBuffer
+    {
+        private enum SubmissionMode
+        {
+            Incrementing    = 1,
+            NonIncrementing = 3,
+            Immediate       = 4,
+            IncrementOnce   = 5
+        }
+
+        public static NsGpuPBEntry[] Decode(byte[] Data)
+        {
+            using (MemoryStream MS = new MemoryStream(Data))
+            {
+                BinaryReader Reader = new BinaryReader(MS);
+
+                List<NsGpuPBEntry> PushBuffer = new List<NsGpuPBEntry>();
+
+                bool CanRead() => MS.Position + 4 <= MS.Length;
+
+                while (CanRead())
+                {
+                    int Packed = Reader.ReadInt32();
+
+                    int Meth = (Packed >> 0)  & 0x1fff;
+                    int SubC = (Packed >> 13) & 7;
+                    int Args = (Packed >> 16) & 0x1fff;
+                    int Mode = (Packed >> 29) & 7;
+
+                    switch ((SubmissionMode)Mode)
+                    {
+                        case SubmissionMode.Incrementing:
+                        {
+                            for (int Index = 0; Index < Args && CanRead(); Index++, Meth++)
+                            {
+                                PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Reader.ReadInt32()));
+                            }
+
+                            break;
+                        }
+
+                        case SubmissionMode.NonIncrementing:
+                        {
+                            int[] Arguments = new int[Args];
+
+                            for (int Index = 0; Index < Arguments.Length; Index++)
+                            {
+                                if (!CanRead())
+                                {
+                                    break;
+                                }
+
+                                Arguments[Index] = Reader.ReadInt32();
+                            }
+
+                            PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Arguments));
+
+                            break;
+                        }
+
+                        case SubmissionMode.Immediate:
+                        {
+                            PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Args));
+
+                            break;
+                        }
+
+                        case SubmissionMode.IncrementOnce:
+                        {
+                            if (CanRead())
+                            {
+                                PushBuffer.Add(new NsGpuPBEntry(Meth, SubC, Reader.ReadInt32()));
+                            }
+    
+                            if (CanRead() && Args > 1)
+                            {
+                                int[] Arguments = new int[Args - 1];
+
+                                for (int Index = 0; Index < Arguments.Length && CanRead(); Index++)
+                                {
+                                    Arguments[Index] = Reader.ReadInt32();
+                                }
+
+                                PushBuffer.Add(new NsGpuPBEntry(Meth + 1, SubC, Arguments));
+                            }
+
+                            break;
+                        }
+                    }
+                }
+
+                return PushBuffer.ToArray();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/Texture.cs b/Ryujinx.Graphics/Gpu/Texture.cs
new file mode 100644
index 000000000..cbfa683dc
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/Texture.cs
@@ -0,0 +1,55 @@
+using Ryujinx.Graphics.Gal;
+
+namespace Ryujinx.Graphics.Gpu
+{
+    public struct Texture
+    {
+        public long Position { get; private set; }
+
+        public int Width  { get; private set; }
+        public int Height { get; private set; }
+        public int Pitch  { get; private set; }
+
+        public int BlockHeight { get; private set; }
+
+        public TextureSwizzle Swizzle { get; private set; }
+
+        public GalTextureFormat Format { get; private set; }
+
+        public Texture(
+            long Position,
+            int  Width,
+            int  Height)
+        {
+            this.Position = Position;
+            this.Width    = Width;
+            this.Height   = Height;
+
+            Pitch = 0;
+
+            BlockHeight = 16;
+
+            Swizzle = TextureSwizzle.BlockLinear;
+
+            Format = GalTextureFormat.A8B8G8R8;
+        }
+
+        public Texture(
+            long             Position,
+            int              Width,
+            int              Height,
+            int              Pitch,
+            int              BlockHeight,
+            TextureSwizzle   Swizzle,
+            GalTextureFormat Format)
+        {
+            this.Position    = Position;
+            this.Width       = Width;
+            this.Height      = Height;
+            this.Pitch       = Pitch;
+            this.BlockHeight = BlockHeight;
+            this.Swizzle     = Swizzle;
+            this.Format      = Format;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/TextureFactory.cs b/Ryujinx.Graphics/Gpu/TextureFactory.cs
new file mode 100644
index 000000000..7f8580d98
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/TextureFactory.cs
@@ -0,0 +1,86 @@
+using ChocolArm64.Memory;
+using Ryujinx.Graphics.Gal;
+using System;
+
+namespace Ryujinx.Graphics.Gpu
+{
+    static class TextureFactory
+    {
+        public static GalTexture MakeTexture(NsGpu Gpu, AMemory Memory, long TicPosition)
+        {
+            int[] Tic = ReadWords(Memory, TicPosition, 8);
+
+            GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f);
+
+            long TextureAddress = (uint)Tic[1];
+
+            TextureAddress |= (long)((ushort)Tic[2]) << 32;
+
+            TextureAddress = Gpu.GetCpuAddr(TextureAddress);
+
+            TextureSwizzle Swizzle = (TextureSwizzle)((Tic[2] >> 21) & 7);
+
+            int Pitch = (Tic[3] & 0xffff) << 5;
+
+            int BlockHeightLog2 = (Tic[3] >> 3) & 7;
+
+            int BlockHeight = 1 << BlockHeightLog2;
+
+            int Width  = (Tic[4] & 0xffff) + 1;
+            int Height = (Tic[5] & 0xffff) + 1;
+
+            Texture Texture = new Texture(
+                TextureAddress,
+                Width,
+                Height,
+                Pitch,
+                BlockHeight,
+                Swizzle,
+                Format);
+
+            byte[] Data = TextureReader.Read(Memory, Texture);
+
+            return new GalTexture(Data, Width, Height, Format);
+        }
+
+        public static GalTextureSampler MakeSampler(NsGpu Gpu, AMemory Memory, long TscPosition)
+        {
+            int[] Tsc = ReadWords(Memory, TscPosition, 8);
+
+            GalTextureWrap AddressU = (GalTextureWrap)((Tsc[0] >> 0) & 7);
+            GalTextureWrap AddressV = (GalTextureWrap)((Tsc[0] >> 3) & 7);
+            GalTextureWrap AddressP = (GalTextureWrap)((Tsc[0] >> 6) & 7);
+
+            GalTextureFilter    MagFilter = (GalTextureFilter)   ((Tsc[1] >> 0) & 3);
+            GalTextureFilter    MinFilter = (GalTextureFilter)   ((Tsc[1] >> 4) & 3);
+            GalTextureMipFilter MipFilter = (GalTextureMipFilter)((Tsc[1] >> 6) & 3);
+
+            GalColorF BorderColor = new GalColorF(
+                BitConverter.Int32BitsToSingle(Tsc[4]),
+                BitConverter.Int32BitsToSingle(Tsc[5]),
+                BitConverter.Int32BitsToSingle(Tsc[6]),
+                BitConverter.Int32BitsToSingle(Tsc[7]));
+
+            return new GalTextureSampler(
+                AddressU,
+                AddressV,
+                AddressP,
+                MinFilter,
+                MagFilter,
+                MipFilter,
+                BorderColor);
+        }
+
+        private static int[] ReadWords(AMemory Memory, long Position, int Count)
+        {
+            int[] Words = new int[Count];
+
+            for (int Index = 0; Index < Count; Index++, Position += 4)
+            {
+                Words[Index] = Memory.ReadInt32(Position);
+            }
+
+            return Words;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics/Gpu/TextureReader.cs b/Ryujinx.Graphics/Gpu/TextureReader.cs
new file mode 100644
index 000000000..4c3b4fb17
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/TextureReader.cs
@@ -0,0 +1,160 @@
+using ChocolArm64.Memory;
+using Ryujinx.Graphics.Gal;
+using System;
+
+namespace Ryujinx.Graphics.Gpu
+{
+    public static class TextureReader
+    {
+        public static byte[] Read(AMemory Memory, Texture Texture)
+        {
+            switch (Texture.Format)
+            {
+                case GalTextureFormat.A8B8G8R8: return Read4Bpp    (Memory, Texture);
+                case GalTextureFormat.A1B5G5R5: return Read2Bpp    (Memory, Texture);
+                case GalTextureFormat.B5G6R5:   return Read2Bpp    (Memory, Texture);
+                case GalTextureFormat.BC1:      return Read8Bpt4x4 (Memory, Texture);
+                case GalTextureFormat.BC2:      return Read16Bpt4x4(Memory, Texture);
+                case GalTextureFormat.BC3:      return Read16Bpt4x4(Memory, Texture);
+                case GalTextureFormat.BC4:      return Read8Bpt4x4 (Memory, Texture);
+                case GalTextureFormat.BC5:      return Read16Bpt4x4(Memory, Texture);
+            }
+
+            throw new NotImplementedException(Texture.Format.ToString());
+        }
+
+        private unsafe static byte[] Read2Bpp(AMemory Memory, Texture Texture)
+        {
+            int Width  = Texture.Width;
+            int Height = Texture.Height;
+
+            byte[] Output = new byte[Width * Height * 2];
+
+            ISwizzle Swizzle = GetSwizzle(Texture, Width, 2);
+
+            fixed (byte* BuffPtr = Output)
+            {
+                long OutOffs = 0;
+
+                for (int Y = 0; Y < Height; Y++)
+                for (int X = 0; X < Width;  X++)
+                {
+                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
+
+                    short Pixel = Memory.ReadInt16Unchecked(Texture.Position + Offset);
+
+                    *(short*)(BuffPtr + OutOffs) = Pixel;
+
+                    OutOffs += 2;
+                }
+            }
+
+            return Output;
+        }
+
+        private unsafe static byte[] Read4Bpp(AMemory Memory, Texture Texture)
+        {
+            int Width  = Texture.Width;
+            int Height = Texture.Height;
+
+            byte[] Output = new byte[Width * Height * 4];
+
+            ISwizzle Swizzle = GetSwizzle(Texture, Width, 4);
+
+            fixed (byte* BuffPtr = Output)
+            {
+                long OutOffs = 0;
+
+                for (int Y = 0; Y < Height; Y++)
+                for (int X = 0; X < Width;  X++)
+                {
+                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
+
+                    int Pixel = Memory.ReadInt32Unchecked(Texture.Position + Offset);
+
+                    *(int*)(BuffPtr + OutOffs) = Pixel;
+
+                    OutOffs += 4;
+                }
+            }
+
+            return Output;
+        }
+
+        private unsafe static byte[] Read8Bpt4x4(AMemory Memory, Texture Texture)
+        {
+            int Width  = (Texture.Width  + 3) / 4;
+            int Height = (Texture.Height + 3) / 4;
+
+            byte[] Output = new byte[Width * Height * 8];
+
+            ISwizzle Swizzle = GetSwizzle(Texture, Width, 8);
+
+            fixed (byte* BuffPtr = Output)
+            {
+                long OutOffs = 0;
+
+                for (int Y = 0; Y < Height; Y++)
+                for (int X = 0; X < Width;  X++)
+                {
+                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
+
+                    long Tile = Memory.ReadInt64Unchecked(Texture.Position + Offset);
+
+                    *(long*)(BuffPtr + OutOffs) = Tile;
+
+                    OutOffs += 8;
+                }
+            }
+
+            return Output;
+        }
+
+        private unsafe static byte[] Read16Bpt4x4(AMemory Memory, Texture Texture)
+        {
+            int Width  = (Texture.Width  + 3) / 4;
+            int Height = (Texture.Height + 3) / 4;
+
+            byte[] Output = new byte[Width * Height * 16];
+
+            ISwizzle Swizzle = GetSwizzle(Texture, Width, 16);
+
+            fixed (byte* BuffPtr = Output)
+            {
+                long OutOffs = 0;
+
+                for (int Y = 0; Y < Height; Y++)
+                for (int X = 0; X < Width;  X++)
+                {
+                    long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
+
+                    long Tile0 = Memory.ReadInt64Unchecked(Texture.Position + Offset + 0);
+                    long Tile1 = Memory.ReadInt64Unchecked(Texture.Position + Offset + 8);
+
+                    *(long*)(BuffPtr + OutOffs + 0) = Tile0;
+                    *(long*)(BuffPtr + OutOffs + 8) = Tile1;
+
+                    OutOffs += 16;
+                }
+            }
+
+            return Output;
+        }
+
+        private static ISwizzle GetSwizzle(Texture Texture, int Width, int Bpp)
+        {
+            switch (Texture.Swizzle)
+            {
+                case TextureSwizzle.Pitch:
+                case TextureSwizzle.PitchColorKey:
+                     return new LinearSwizzle(Texture.Pitch, Bpp);
+
+                case TextureSwizzle.BlockLinear:
+                case TextureSwizzle.BlockLinearColorKey:
+                    return new BlockLinearSwizzle(Width, Bpp, Texture.BlockHeight);
+            }
+
+            throw new NotImplementedException(Texture.Swizzle.ToString());
+        }
+    }
+}
diff --git a/Ryujinx.Graphics/Gpu/TextureSwizzle.cs b/Ryujinx.Graphics/Gpu/TextureSwizzle.cs
new file mode 100644
index 000000000..7d99279cd
--- /dev/null
+++ b/Ryujinx.Graphics/Gpu/TextureSwizzle.cs
@@ -0,0 +1,11 @@
+namespace Ryujinx.Graphics.Gpu
+{
+    public enum TextureSwizzle
+    {
+        _1dBuffer           = 0,
+        PitchColorKey       = 1,
+        Pitch               = 2,
+        BlockLinear         = 3,
+        BlockLinearColorKey = 4
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs
index b5bbdbc17..14ef0a15d 100644
--- a/Ryujinx.Tests/Cpu/CpuTest.cs
+++ b/Ryujinx.Tests/Cpu/CpuTest.cs
@@ -1,7 +1,9 @@
 using ChocolArm64;
 using ChocolArm64.Memory;
 using ChocolArm64.State;
+
 using NUnit.Framework;
+
 using System.Threading;
 
 namespace Ryujinx.Tests.Cpu
@@ -51,13 +53,14 @@ namespace Ryujinx.Tests.Cpu
             Position += 4;
         }
 
-        protected void SetThreadState(ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X31 = 0,
+        protected void SetThreadState(ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X3 = 0, ulong X31 = 0,
                                       AVec V0 = default(AVec), AVec V1 = default(AVec), AVec V2 = default(AVec),
                                       bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, int Fpcr = 0x0)
         {
             Thread.ThreadState.X0 = X0;
             Thread.ThreadState.X1 = X1;
             Thread.ThreadState.X2 = X2;
+            Thread.ThreadState.X3 = X3;
             Thread.ThreadState.X31 = X31;
             Thread.ThreadState.V0 = V0;
             Thread.ThreadState.V1 = V1;
@@ -87,14 +90,14 @@ namespace Ryujinx.Tests.Cpu
         }
 
         protected AThreadState SingleOpcode(uint Opcode,
-                                            ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X31 = 0,
+                                            ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X3 = 0, ulong X31 = 0,
                                             AVec V0 = default(AVec), AVec V1 = default(AVec), AVec V2 = default(AVec),
                                             bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, int Fpcr = 0x0)
         {
             this.Opcode(Opcode);
             this.Opcode(0xD4200000); // BRK #0
             this.Opcode(0xD65F03C0); // RET
-            SetThreadState(X0, X1, X2, X31, V0, V1, V2, Overflow, Carry, Zero, Negative, Fpcr);
+            SetThreadState(X0, X1, X2, X3, X31, V0, V1, V2, Overflow, Carry, Zero, Negative, Fpcr);
             ExecuteOpcodes();
 
             return GetThreadState();
diff --git a/Ryujinx.Tests/Cpu/CpuTestAlu.cs b/Ryujinx.Tests/Cpu/CpuTestAlu.cs
index 8116fc7c7..564fadec2 100644
--- a/Ryujinx.Tests/Cpu/CpuTestAlu.cs
+++ b/Ryujinx.Tests/Cpu/CpuTestAlu.cs
@@ -1,162 +1,331 @@
+//#define Alu
+
 using ChocolArm64.State;
+
 using NUnit.Framework;
 
 namespace Ryujinx.Tests.Cpu
 {
-    public class CpuTestAlu : CpuTest
+    using Tester;
+    using Tester.Types;
+
+    [Category("Alu"), Ignore("Tested: first half of 2018.")]
+    public sealed class CpuTestAlu : CpuTest
     {
-        [TestCase(0x9A020020u, 2u,          3u,   true,  6u)]
-        [TestCase(0x9A020020u, 2u,          3u,   false, 5u)]
-        [TestCase(0x1A020020u, 2u,          3u,   true,  6u)]
-        [TestCase(0x1A020020u, 2u,          3u,   false, 5u)]
-        [TestCase(0x1A020020u, 0xFFFFFFFFu, 0x2u, false, 0x1u)]
-        public void Adc(uint Opcode, uint A, uint B, bool CarryState, uint Result)
+#if Alu
+        [SetUp]
+        public void SetupTester()
         {
-            // ADC (X0/W0), (X1/W1), (X2/W2)
-            AThreadState ThreadState = SingleOpcode(Opcode, X1: A, X2: B, Carry: CarryState);
-            Assert.AreEqual(Result, ThreadState.X0);
+            AArch64.TakeReset(false);
         }
 
-        [TestCase(0x3A020020u, 2u,          3u,          false, false, false, false, 5u)]
-        [TestCase(0x3A020020u, 2u,          3u,          true,  false, false, false, 6u)]
-        [TestCase(0xBA020020u, 2u,          3u,          false, false, false, false, 5u)]
-        [TestCase(0xBA020020u, 2u,          3u,          true,  false, false, false, 6u)]
-        [TestCase(0x3A020020u, 0xFFFFFFFEu, 0x1u,        true,  false, true,  true,  0x0u)]
-        [TestCase(0x3A020020u, 0xFFFFFFFFu, 0xFFFFFFFFu, true,  true,  false, true,  0xFFFFFFFFu)]
-        public void Adcs(uint Opcode, uint A, uint B, bool CarryState, bool Negative, bool Zero, bool Carry, uint Result)
+        [Test, Description("CLS <Xd>, <Xn>")]
+        public void Cls_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(256)] ulong Xn)
         {
-            //ADCS (X0/W0), (X1, W1), (X2/W2)
-            AThreadState ThreadState = SingleOpcode(Opcode, X1: A, X2: B, Carry: CarryState);
-            Assert.Multiple(() =>
+            uint Opcode = 0xDAC01400; // CLS X0, X0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            if (Rd != 31)
             {
-                Assert.IsFalse(ThreadState.Overflow);
-                Assert.AreEqual(Negative, ThreadState.Negative);
-                Assert.AreEqual(Zero,     ThreadState.Zero);
-                Assert.AreEqual(Carry,    ThreadState.Carry);
-                Assert.AreEqual(Result,   ThreadState.X0);
-            });
-        }
-    
-        [Test]
-        public void Add()
-        {
-            // ADD X0, X1, X2
-            AThreadState ThreadState = SingleOpcode(0x8B020020, X1: 1, X2: 2);
-            Assert.AreEqual(3, ThreadState.X0);
-        }
+                Bits Op = new Bits(Opcode);
 
-        [TestCase(2u,          false, false)]
-        [TestCase(5u,          false, false)]
-        [TestCase(7u,          false, false)]
-        [TestCase(0xFFFFFFFFu, false, true )]
-        [TestCase(0xFFFFFFFBu, true,  true )]
-        public void Adds(uint A, bool Zero, bool Carry)
-        {
-            //ADDS WZR, WSP, #5
-            AThreadState ThreadState = SingleOpcode(0x310017FF, X31: A);
-            Assert.Multiple(() =>
+                AArch64.X((int)Rn, new Bits(Xn));
+                Base.Cls(Op[31], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
             {
-                Assert.IsFalse(ThreadState.Negative);
-                Assert.IsFalse(ThreadState.Overflow);
-                Assert.AreEqual(Zero,  ThreadState.Zero);
-                Assert.AreEqual(Carry, ThreadState.Carry);
-                Assert.AreEqual(A,     ThreadState.X31);
-            });
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
         }
 
-        [TestCase(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFul, true,  false)]
-        [TestCase(0xFFFFFFFFu, 0x00000000u, 0x00000000ul, false, true)]
-        [TestCase(0x12345678u, 0x7324A993u, 0x12240010ul, false, false)]
-        public void Ands(uint A, uint B, ulong Result, bool Negative, bool Zero)
+        [Test, Description("CLS <Wd>, <Wn>")]
+        public void Cls_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(256)] uint Wn)
         {
-            // ANDS W0, W1, W2
-            uint Opcode = 0x6A020020;
-            AThreadState ThreadState = SingleOpcode(Opcode, X1: A, X2: B);
-            Assert.Multiple(() =>
+            uint Opcode = 0x5AC01400; // CLS W0, W0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            if (Rd != 31)
             {
-                Assert.AreEqual(Result,   ThreadState.X0);
-                Assert.AreEqual(Negative, ThreadState.Negative);
-                Assert.AreEqual(Zero,     ThreadState.Zero);
-            });
-        }
+                Bits Op = new Bits(Opcode);
 
-        [TestCase(0x0000FF44u, 0x00000004u, 0x00000FF4u)]
-        [TestCase(0x00000000u, 0x00000004u, 0x00000000u)]
-        [TestCase(0x0000FF44u, 0x00000008u, 0x000000FFu)]
-        [TestCase(0xFFFFFFFFu, 0x00000004u, 0xFFFFFFFFu)]
-        [TestCase(0xFFFFFFFFu, 0x00000008u, 0xFFFFFFFFu)]
-        [TestCase(0xFFFFFFFFu, 0x00000020u, 0xFFFFFFFFu)]
-        [TestCase(0x0FFFFFFFu, 0x0000001Cu, 0x00000000u)]
-        [TestCase(0x80000000u, 0x0000001Fu, 0xFFFFFFFFu)]
-        [TestCase(0xCAFE0000u, 0x00000020u, 0xCAFE0000u)]
-        public void Asrv32(uint A, uint ShiftValue, uint Result)
-        {
-            // ASRV W0, W1, W2
-            AThreadState ThreadState = SingleOpcode(0x1AC22820, X1: A, X2: ShiftValue);
-            Assert.AreEqual(Result, ThreadState.X0);
-        }
+                AArch64.X((int)Rn, new Bits(Wn));
+                Base.Cls(Op[31], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
 
-        [TestCase(0x000000000000FF44ul, 0x00000004u, 0x0000000000000FF4ul)]
-        [TestCase(0x0000000000000000ul, 0x00000004u, 0x0000000000000000ul)]
-        [TestCase(0x000000000000FF44ul, 0x00000008u, 0x00000000000000FFul)]
-        [TestCase(0x00000000FFFFFFFFul, 0x00000004u, 0x000000000FFFFFFFul)]
-        [TestCase(0x00000000FFFFFFFFul, 0x00000008u, 0x0000000000FFFFFFul)]
-        [TestCase(0x00000000FFFFFFFFul, 0x00000020u, 0x0000000000000000ul)]
-        [TestCase(0x000000000FFFFFFFul, 0x0000001Cu, 0x0000000000000000ul)]
-        [TestCase(0x000CC4488FFFFFFFul, 0x0000001Cu, 0x0000000000CC4488ul)]
-        [TestCase(0xFFFFFFFFFFFFFFFFul, 0x0000001Cu, 0xFFFFFFFFFFFFFFFFul)]
-        [TestCase(0x8000000000000000ul, 0x0000003Fu, 0xFFFFFFFFFFFFFFFFul)]
-        [TestCase(0xCAFE000000000000ul, 0x00000040u, 0xCAFE000000000000ul)]
-        public void Asrv64(ulong A, uint ShiftValue, ulong Result)
-        {
-            // ASRV X0, X1, X2
-            AThreadState ThreadState = SingleOpcode(0x9AC22820, X1: A, X2: ShiftValue);
-            Assert.AreEqual(Result, ThreadState.X0);
-        }
-
-        [TestCase(0x01010101u, 0x3200C3E2u)]
-        [TestCase(0x00F000F0u, 0x320C8FE2u)]
-        [TestCase(0x00000001u, 0x320003E2u)]
-        public void OrrBitmasks(uint Bitmask, uint Opcode)
-        {
-            // ORR W2, WZR, #Bitmask
-            Assert.AreEqual(Bitmask, SingleOpcode(Opcode).X2);
-        }
-
-        [Test]
-        public void RevX0X0()
-        {
-            // REV X0, X0
-            AThreadState ThreadState = SingleOpcode(0xDAC00C00, X0: 0xAABBCCDDEEFF1100);
-            Assert.AreEqual(0x0011FFEEDDCCBBAA, ThreadState.X0);
-        }
-
-        [Test]
-        public void RevW1W1()
-        {
-            // REV W1, W1
-            AThreadState ThreadState = SingleOpcode(0x5AC00821, X1: 0x12345678);
-            Assert.AreEqual(0x78563412, ThreadState.X1);
-        }
-
-        [TestCase(0x7A020020u, 4u, 2u, false, false, false, true,  1u)]
-        [TestCase(0x7A020020u, 4u, 2u, true,  false, false, true,  2u)]
-        [TestCase(0xFA020020u, 4u, 2u, false, false, false, true,  1u)]
-        [TestCase(0xFA020020u, 4u, 2u, true,  false, false, true,  2u)]
-        [TestCase(0x7A020020u, 4u, 4u, false, true,  false, false, 0xFFFFFFFFu)]
-        [TestCase(0x7A020020u, 4u, 4u, true,  false, true,  true,  0x0u)]
-        public void Sbcs(uint Opcode, uint A, uint B, bool CarryState, bool Negative, bool Zero, bool Carry, uint Result)
-        {
-            //SBCS (X0/W0), (X1, W1), (X2/W2)
-            AThreadState ThreadState = SingleOpcode(Opcode, X1: A, X2: B, Carry: CarryState);
-            Assert.Multiple(() =>
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
             {
-                Assert.IsFalse(ThreadState.Overflow);
-                Assert.AreEqual(Negative, ThreadState.Negative);
-                Assert.AreEqual(Zero,     ThreadState.Zero);
-                Assert.AreEqual(Carry,    ThreadState.Carry);
-                Assert.AreEqual(Result,   ThreadState.X0);
-            });
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
         }
+
+        [Test, Description("CLZ <Xd>, <Xn>")]
+        public void Clz_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(256)] ulong Xn)
+        {
+            uint Opcode = 0xDAC01000; // CLZ X0, X0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                Base.Clz(Op[31], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("CLZ <Wd>, <Wn>")]
+        public void Clz_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(256)] uint Wn)
+        {
+            uint Opcode = 0x5AC01000; // CLZ W0, W0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                Base.Clz(Op[31], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("RBIT <Xd>, <Xn>")]
+        public void Rbit_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(256)] ulong Xn)
+        {
+            uint Opcode = 0xDAC00000; // RBIT X0, X0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                Base.Rbit(Op[31], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("RBIT <Wd>, <Wn>")]
+        public void Rbit_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(256)] uint Wn)
+        {
+            uint Opcode = 0x5AC00000; // RBIT W0, W0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                Base.Rbit(Op[31], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("REV16 <Xd>, <Xn>")]
+        public void Rev16_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(256)] ulong Xn)
+        {
+            uint Opcode = 0xDAC00400; // REV16 X0, X0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                Base.Rev16(Op[31], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("REV16 <Wd>, <Wn>")]
+        public void Rev16_32bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(256)] uint Wn)
+        {
+            uint Opcode = 0x5AC00400; // REV16 W0, W0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                Base.Rev16(Op[31], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("REV32 <Xd>, <Xn>")]
+        public void Rev32_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(256)] ulong Xn)
+        {
+            uint Opcode = 0xDAC00800; // REV32 X0, X0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                Base.Rev32(Op[31], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("REV <Wd>, <Wn>")]
+        public void Rev32_32bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(256)] uint Wn)
+        {
+            uint Opcode = 0x5AC00800; // REV W0, W0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                Base.Rev32(Op[31], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("REV64 <Xd>, <Xn>")]
+        public void Rev64_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(256)] ulong Xn)
+        {
+            uint Opcode = 0xDAC00C00; // REV64 X0, X0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                Base.Rev64(Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+#endif
     }
 }
diff --git a/Ryujinx.Tests/Cpu/CpuTestAluImm.cs b/Ryujinx.Tests/Cpu/CpuTestAluImm.cs
new file mode 100644
index 000000000..5d1f0b6ba
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestAluImm.cs
@@ -0,0 +1,811 @@
+//#define AluImm
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+    using Tester;
+    using Tester.Types;
+
+    [Category("AluImm"), Ignore("Tested: first half of 2018.")]
+    public sealed class CpuTestAluImm : CpuTest
+    {
+#if AluImm
+        [SetUp]
+        public void SetupTester()
+        {
+            AArch64.TakeReset(false);
+        }
+
+        [Test, Description("ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}")]
+        public void Add_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(8)] ulong Xn_SP,
+                              [Values(0u, 4095u)] [Random(0u, 4095u, 10)] uint imm,
+                              [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12>
+        {
+            uint Opcode = 0x91000000; // ADD X0, X0, #0, LSL #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ThreadState = SingleOpcode(Opcode, X1: Xn_SP);
+
+                AArch64.X((int)Rn, new Bits(Xn_SP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Xn_SP);
+
+                AArch64.SP(new Bits(Xn_SP));
+            }
+
+            Base.Add_Imm(Op[31], Op[23, 22], Op[21, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("ADD <Wd|WSP>, <Wn|WSP>, #<imm>{, <shift>}")]
+        public void Add_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(8)] uint Wn_WSP,
+                              [Values(0u, 4095u)] [Random(0u, 4095u, 10)] uint imm,
+                              [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12>
+        {
+            uint Opcode = 0x11000000; // ADD W0, W0, #0, LSL #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ThreadState = SingleOpcode(Opcode, X1: Wn_WSP);
+
+                AArch64.X((int)Rn, new Bits(Wn_WSP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Wn_WSP);
+
+                AArch64.SP(new Bits(Wn_WSP));
+            }
+
+            Base.Add_Imm(Op[31], Op[23, 22], Op[21, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint WSP = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
+            }
+        }
+
+        [Test, Description("ADDS <Xd>, <Xn|SP>, #<imm>{, <shift>}")]
+        public void Adds_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(8)] ulong Xn_SP,
+                               [Values(0u, 4095u)] [Random(0u, 4095u, 10)] uint imm,
+                               [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12>
+        {
+            uint Opcode = 0xB1000000; // ADDS X0, X0, #0, LSL #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ThreadState = SingleOpcode(Opcode, X1: Xn_SP);
+
+                AArch64.X((int)Rn, new Bits(Xn_SP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Xn_SP);
+
+                AArch64.SP(new Bits(Xn_SP));
+            }
+
+            Base.Adds_Imm(Op[31], Op[23, 22], Op[21, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong _X31 = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ADDS <Wd>, <Wn|WSP>, #<imm>{, <shift>}")]
+        public void Adds_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(8)] uint Wn_WSP,
+                               [Values(0u, 4095u)] [Random(0u, 4095u, 10)] uint imm,
+                               [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12>
+        {
+            uint Opcode = 0x31000000; // ADDS W0, W0, #0, LSL #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ThreadState = SingleOpcode(Opcode, X1: Wn_WSP);
+
+                AArch64.X((int)Rn, new Bits(Wn_WSP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Wn_WSP);
+
+                AArch64.SP(new Bits(Wn_WSP));
+            }
+
+            Base.Adds_Imm(Op[31], Op[23, 22], Op[21, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint _W31 = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("AND <Xd|SP>, <Xn>, #<imm>")]
+        public void And_N1_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                                 [Values(0u, 31u, 32u, 62u)] [Random(0u, 62u, 2)] uint imms, // <imm>
+                                 [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 2)] uint immr) // <imm>
+        {
+            uint Opcode = 0x92400000; // AND X0, X0, #0x1
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            Base.And_Imm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("AND <Xd|SP>, <Xn>, #<imm>")]
+        public void And_N0_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                                 [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, 2)] uint imms, // <imm>
+                                 [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint immr) // <imm>
+        {
+            uint Opcode = 0x92000000; // AND X0, X0, #0x100000001
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            Base.And_Imm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("AND <Wd|WSP>, <Wn>, #<imm>")]
+        public void And_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                              [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, 2)] uint imms, // <imm>
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint immr) // <imm>
+        {
+            uint Opcode = 0x12000000; // AND W0, W0, #0x1
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            Base.And_Imm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint WSP = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
+            }
+        }
+
+        [Test, Description("ANDS <Xd>, <Xn>, #<imm>")]
+        public void Ands_N1_64bit([Values(0u, 31u)] uint Rd,
+                                  [Values(1u, 31u)] uint Rn,
+                                  [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                          0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                                  [Values(0u, 31u, 32u, 62u)] [Random(0u, 62u, 2)] uint imms, // <imm>
+                                  [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 2)] uint immr) // <imm>
+        {
+            uint Opcode = 0xF2400000; // ANDS X0, X0, #0x1
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            Base.Ands_Imm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+            ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+            if (Rd != 31)
+            {
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ANDS <Xd>, <Xn>, #<imm>")]
+        public void Ands_N0_64bit([Values(0u, 31u)] uint Rd,
+                                  [Values(1u, 31u)] uint Rn,
+                                  [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                          0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                                  [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, 2)] uint imms, // <imm>
+                                  [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint immr) // <imm>
+        {
+            uint Opcode = 0xF2000000; // ANDS X0, X0, #0x100000001
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            Base.Ands_Imm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+            ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+            if (Rd != 31)
+            {
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ANDS <Wd>, <Wn>, #<imm>")]
+        public void Ands_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                               [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, 2)] uint imms, // <imm>
+                               [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint immr) // <imm>
+        {
+            uint Opcode = 0x72000000; // ANDS W0, W0, #0x1
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            Base.Ands_Imm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+            uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+            if (Rd != 31)
+            {
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("EOR <Xd|SP>, <Xn>, #<imm>")]
+        public void Eor_N1_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                                 [Values(0u, 31u, 32u, 62u)] [Random(0u, 62u, 2)] uint imms, // <imm>
+                                 [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 2)] uint immr) // <imm>
+        {
+            uint Opcode = 0xD2400000; // EOR X0, X0, #0x1
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            Base.Eor_Imm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("EOR <Xd|SP>, <Xn>, #<imm>")]
+        public void Eor_N0_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                                 [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, 2)] uint imms, // <imm>
+                                 [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint immr) // <imm>
+        {
+            uint Opcode = 0xD2000000; // EOR X0, X0, #0x100000001
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            Base.Eor_Imm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("EOR <Wd>, <Wn>, #<imm>")]
+        public void Eor_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                              [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, 2)] uint imms, // <imm>
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint immr) // <imm>
+        {
+            uint Opcode = 0x52000000; // EOR W0, W0, #0x1
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            Base.Eor_Imm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint WSP = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
+            }
+        }
+
+        [Test, Description("ORR <Xd|SP>, <Xn>, #<imm>")]
+        public void Orr_N1_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                                 [Values(0u, 31u, 32u, 62u)] [Random(0u, 62u, 2)] uint imms, // <imm>
+                                 [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 2)] uint immr) // <imm>
+        {
+            uint Opcode = 0xB2400000; // ORR X0, X0, #0x1
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            Base.Orr_Imm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("ORR <Xd|SP>, <Xn>, #<imm>")]
+        public void Orr_N0_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                                 [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, 2)] uint imms, // <imm>
+                                 [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint immr) // <imm>
+        {
+            uint Opcode = 0xB2000000; // ORR X0, X0, #0x100000001
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            Base.Orr_Imm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("ORR <Wd|WSP>, <Wn>, #<imm>")]
+        public void Orr_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                              [Values(0u, 15u, 16u, 30u)] [Random(0u, 30u, 2)] uint imms, // <imm>
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint immr) // <imm>
+        {
+            uint Opcode = 0x32000000; // ORR W0, W0, #0x1
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            Base.Orr_Imm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint WSP = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
+            }
+        }
+
+        [Test, Description("SUB <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}")]
+        public void Sub_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(8)] ulong Xn_SP,
+                              [Values(0u, 4095u)] [Random(0u, 4095u, 10)] uint imm,
+                              [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12>
+        {
+            uint Opcode = 0xD1000000; // SUB X0, X0, #0, LSL #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ThreadState = SingleOpcode(Opcode, X1: Xn_SP);
+
+                AArch64.X((int)Rn, new Bits(Xn_SP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Xn_SP);
+
+                AArch64.SP(new Bits(Xn_SP));
+            }
+
+            Base.Sub_Imm(Op[31], Op[23, 22], Op[21, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("SUB <Wd|WSP>, <Wn|WSP>, #<imm>{, <shift>}")]
+        public void Sub_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(8)] uint Wn_WSP,
+                              [Values(0u, 4095u)] [Random(0u, 4095u, 10)] uint imm,
+                              [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12>
+        {
+            uint Opcode = 0x51000000; // SUB W0, W0, #0, LSL #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ThreadState = SingleOpcode(Opcode, X1: Wn_WSP);
+
+                AArch64.X((int)Rn, new Bits(Wn_WSP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Wn_WSP);
+
+                AArch64.SP(new Bits(Wn_WSP));
+            }
+
+            Base.Sub_Imm(Op[31], Op[23, 22], Op[21, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint WSP = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
+            }
+        }
+
+        [Test, Description("SUBS <Xd>, <Xn|SP>, #<imm>{, <shift>}")]
+        public void Subs_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(8)] ulong Xn_SP,
+                               [Values(0u, 4095u)] [Random(0u, 4095u, 10)] uint imm,
+                               [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12>
+        {
+            uint Opcode = 0xF1000000; // SUBS X0, X0, #0, LSL #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ThreadState = SingleOpcode(Opcode, X1: Xn_SP);
+
+                AArch64.X((int)Rn, new Bits(Xn_SP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Xn_SP);
+
+                AArch64.SP(new Bits(Xn_SP));
+            }
+
+            Base.Subs_Imm(Op[31], Op[23, 22], Op[21, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong _X31 = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("SUBS <Wd>, <Wn|WSP>, #<imm>{, <shift>}")]
+        public void Subs_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(8)] uint Wn_WSP,
+                               [Values(0u, 4095u)] [Random(0u, 4095u, 10)] uint imm,
+                               [Values(0b00u, 0b01u)] uint shift) // <LSL #0, LSL #12>
+        {
+            uint Opcode = 0x71000000; // SUBS W0, W0, #0, LSL #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((imm & 4095) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ThreadState = SingleOpcode(Opcode, X1: Wn_WSP);
+
+                AArch64.X((int)Rn, new Bits(Wn_WSP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Wn_WSP);
+
+                AArch64.SP(new Bits(Wn_WSP));
+            }
+
+            Base.Subs_Imm(Op[31], Op[23, 22], Op[21, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint _W31 = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+#endif
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/CpuTestAluRs.cs b/Ryujinx.Tests/Cpu/CpuTestAluRs.cs
new file mode 100644
index 000000000..b81f7100c
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestAluRs.cs
@@ -0,0 +1,1911 @@
+//#define AluRs
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+    using Tester;
+    using Tester.Types;
+
+    [Category("AluRs"), Ignore("Tested: first half of 2018.")]
+    public sealed class CpuTestAluRs : CpuTest
+    {
+#if AluRs
+        [SetUp]
+        public void SetupTester()
+        {
+            AArch64.TakeReset(false);
+        }
+
+        [Test, Description("ADC <Xd>, <Xn>, <Xm>")]
+        public void Adc_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(4)] ulong Xn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(4)] ulong Xm,
+                              [Values] bool CarryIn)
+        {
+            uint Opcode = 0x9A000000; // ADC X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31, Carry: CarryIn);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Shared.PSTATE.C = CarryIn;
+                Base.Adc(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("ADC <Wd>, <Wn>, <Wm>")]
+        public void Adc_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(4)] uint Wn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(4)] uint Wm,
+                              [Values] bool CarryIn)
+        {
+            uint Opcode = 0x1A000000; // ADC W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31, Carry: CarryIn);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Shared.PSTATE.C = CarryIn;
+                Base.Adc(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("ADCS <Xd>, <Xn>, <Xm>")]
+        public void Adcs_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(4)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(4)] ulong Xm,
+                               [Values] bool CarryIn)
+        {
+            uint Opcode = 0xBA000000; // ADCS X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31, Carry: CarryIn);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            AArch64.X((int)Rm, new Bits(Xm));
+            Shared.PSTATE.C = CarryIn;
+            Base.Adcs(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+            ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+            if (Rd != 31)
+            {
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ADCS <Wd>, <Wn>, <Wm>")]
+        public void Adcs_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(4)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(4)] uint Wm,
+                               [Values] bool CarryIn)
+        {
+            uint Opcode = 0x3A000000; // ADCS W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Bits Op = new Bits(Opcode);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31, Carry: CarryIn);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            AArch64.X((int)Rm, new Bits(Wm));
+            Shared.PSTATE.C = CarryIn;
+            Base.Adcs(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+            uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+            if (Rd != 31)
+            {
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ADD <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
+        public void Add_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                              [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR>
+                              [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 1)] uint amount)
+        {
+            uint Opcode = 0x8B000000; // ADD X0, X0, X0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Add_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("ADD <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
+        public void Add_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                              [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR>
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 1)] uint amount)
+        {
+            uint Opcode = 0x0B000000; // ADD W0, W0, W0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Add_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("ADDS <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
+        public void Adds_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                               [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR>
+                               [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 1)] uint amount)
+        {
+            uint Opcode = 0xAB000000; // ADDS X0, X0, X0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            AArch64.X((int)Rm, new Bits(Xm));
+            Base.Adds_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+            ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+            if (Rd != 31)
+            {
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ADDS <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
+        public void Adds_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                               [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR>
+                               [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 1)] uint amount)
+        {
+            uint Opcode = 0x2B000000; // ADDS W0, W0, W0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Adds_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+            uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+            if (Rd != 31)
+            {
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("AND <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
+        public void And_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                              [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                              [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 1)] uint amount)
+        {
+            uint Opcode = 0x8A000000; // AND X0, X0, X0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.And_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("AND <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
+        public void And_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                              [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 1)] uint amount)
+        {
+            uint Opcode = 0x0A000000; // AND W0, W0, W0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.And_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("ANDS <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
+        public void Ands_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                               [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                               [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 1)] uint amount)
+        {
+            uint Opcode = 0xEA000000; // ANDS X0, X0, X0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            AArch64.X((int)Rm, new Bits(Xm));
+            Base.Ands_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+            ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+            if (Rd != 31)
+            {
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ANDS <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
+        public void Ands_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                               [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                               [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 1)] uint amount)
+        {
+            uint Opcode = 0x6A000000; // ANDS W0, W0, W0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Ands_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+            uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+            if (Rd != 31)
+            {
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ASRV <Xd>, <Xn>, <Xm>")]
+        public void Asrv_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(8)] ulong Xn,
+                               [Values(0ul, 31ul, 32ul, 63ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(5)] ulong Xm)
+        {
+            uint Opcode = 0x9AC02800; // ASRV X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Asrv(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("ASRV <Wd>, <Wn>, <Wm>")]
+        public void Asrv_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(8)] uint Wn,
+                               [Values(0u, 15u, 16u, 31u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(5)] uint Wm)
+        {
+            uint Opcode = 0x1AC02800; // ASRV W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Asrv(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("BIC <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
+        public void Bic_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                              [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                              [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 1)] uint amount)
+        {
+            uint Opcode = 0x8A200000; // BIC X0, X0, X0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Bic(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("BIC <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
+        public void Bic_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                              [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 1)] uint amount)
+        {
+            uint Opcode = 0x0A200000; // BIC W0, W0, W0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Bic(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("BICS <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
+        public void Bics_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                               [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                               [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 1)] uint amount)
+        {
+            uint Opcode = 0xEA200000; // BICS X0, X0, X0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            AArch64.X((int)Rm, new Bits(Xm));
+            Base.Bics(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+            ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+            if (Rd != 31)
+            {
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("BICS <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
+        public void Bics_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                               [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                               [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 1)] uint amount)
+        {
+            uint Opcode = 0x6A200000; // BICS W0, W0, W0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Bics(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+            uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+            if (Rd != 31)
+            {
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("CRC32X <Wd>, <Wn>, <Xm>")]
+        public void Crc32x([Values(0u, 31u)] uint Rd,
+                           [Values(1u, 31u)] uint Rn,
+                           [Values(2u, 31u)] uint Rm,
+                           [Values(0x00000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                           [Values((ulong)0x00_00_00_00_00_00_00_00,
+                                   (ulong)0x7F_FF_FF_FF_FF_FF_FF_FF,
+                                   (ulong)0x80_00_00_00_00_00_00_00,
+                                   (ulong)0xFF_FF_FF_FF_FF_FF_FF_FF)] [Random(64)] ulong Xm)
+        {
+            uint Opcode = 0x9AC04C00; // CRC32X W0, W0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Xm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Crc32(Op[31], Op[20, 16], Op[11, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("CRC32W <Wd>, <Wn>, <Wm>")]
+        public void Crc32w([Values(0u, 31u)] uint Rd,
+                           [Values(1u, 31u)] uint Rn,
+                           [Values(2u, 31u)] uint Rm,
+                           [Values(0x00000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                           [Values((uint)0x00_00_00_00, (uint)0x7F_FF_FF_FF,
+                                   (uint)0x80_00_00_00, (uint)0xFF_FF_FF_FF)] [Random(64)] uint Wm)
+        {
+            uint Opcode = 0x1AC04800; // CRC32W W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Crc32(Op[31], Op[20, 16], Op[11, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("CRC32H <Wd>, <Wn>, <Wm>")]
+        public void Crc32h([Values(0u, 31u)] uint Rd,
+                           [Values(1u, 31u)] uint Rn,
+                           [Values(2u, 31u)] uint Rm,
+                           [Values(0x00000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                           [Values((ushort)0x00_00, (ushort)0x7F_FF,
+                                   (ushort)0x80_00, (ushort)0xFF_FF)] [Random(64)] ushort Wm)
+        {
+            uint Opcode = 0x1AC04400; // CRC32H W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Crc32(Op[31], Op[20, 16], Op[11, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("CRC32B <Wd>, <Wn>, <Wm>")]
+        public void Crc32b([Values(0u, 31u)] uint Rd,
+                           [Values(1u, 31u)] uint Rn,
+                           [Values(2u, 31u)] uint Rm,
+                           [Values(0x00000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                           [Values((byte)0x00, (byte)0x7F,
+                                   (byte)0x80, (byte)0xFF)] [Random(64)] byte Wm)
+        {
+            uint Opcode = 0x1AC04000; // CRC32B W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Crc32(Op[31], Op[20, 16], Op[11, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("CRC32CX <Wd>, <Wn>, <Xm>")]
+        public void Crc32cx([Values(0u, 31u)] uint Rd,
+                            [Values(1u, 31u)] uint Rn,
+                            [Values(2u, 31u)] uint Rm,
+                            [Values(0x00000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                            [Values((ulong)0x00_00_00_00_00_00_00_00,
+                                    (ulong)0x7F_FF_FF_FF_FF_FF_FF_FF,
+                                    (ulong)0x80_00_00_00_00_00_00_00,
+                                    (ulong)0xFF_FF_FF_FF_FF_FF_FF_FF)] [Random(64)] ulong Xm)
+        {
+            uint Opcode = 0x9AC05C00; // CRC32CX W0, W0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Xm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Crc32c(Op[31], Op[20, 16], Op[11, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("CRC32CW <Wd>, <Wn>, <Wm>")]
+        public void Crc32cw([Values(0u, 31u)] uint Rd,
+                            [Values(1u, 31u)] uint Rn,
+                            [Values(2u, 31u)] uint Rm,
+                            [Values(0x00000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                            [Values((uint)0x00_00_00_00, (uint)0x7F_FF_FF_FF,
+                                    (uint)0x80_00_00_00, (uint)0xFF_FF_FF_FF)] [Random(64)] uint Wm)
+        {
+            uint Opcode = 0x1AC05800; // CRC32CW W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Crc32c(Op[31], Op[20, 16], Op[11, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("CRC32CH <Wd>, <Wn>, <Wm>")]
+        public void Crc32ch([Values(0u, 31u)] uint Rd,
+                            [Values(1u, 31u)] uint Rn,
+                            [Values(2u, 31u)] uint Rm,
+                            [Values(0x00000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                            [Values((ushort)0x00_00, (ushort)0x7F_FF,
+                                    (ushort)0x80_00, (ushort)0xFF_FF)] [Random(64)] ushort Wm)
+        {
+            uint Opcode = 0x1AC05400; // CRC32CH W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Crc32c(Op[31], Op[20, 16], Op[11, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("CRC32CB <Wd>, <Wn>, <Wm>")]
+        public void Crc32cb([Values(0u, 31u)] uint Rd,
+                            [Values(1u, 31u)] uint Rn,
+                            [Values(2u, 31u)] uint Rm,
+                            [Values(0x00000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                            [Values((byte)0x00, (byte)0x7F,
+                                    (byte)0x80, (byte)0xFF)] [Random(64)] byte Wm)
+        {
+            uint Opcode = 0x1AC05000; // CRC32CB W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Crc32c(Op[31], Op[20, 16], Op[11, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("EON <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
+        public void Eon_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                              [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                              [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 1)] uint amount)
+        {
+            uint Opcode = 0xCA200000; // EON X0, X0, X0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Eon(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("EON <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
+        public void Eon_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                              [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 1)] uint amount)
+        {
+            uint Opcode = 0x4A200000; // EON W0, W0, W0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Eon(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("EOR <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
+        public void Eor_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                              [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                              [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 1)] uint amount)
+        {
+            uint Opcode = 0xCA000000; // EOR X0, X0, X0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Eor_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("EOR <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
+        public void Eor_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                              [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 1)] uint amount)
+        {
+            uint Opcode = 0x4A000000; // EOR W0, W0, W0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Eor_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("EXTR <Xd>, <Xn>, <Xm>, #<lsb>")]
+        public void Extr_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xm,
+                               [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 2)] uint lsb)
+        {
+            uint Opcode = 0x93C00000; // EXTR X0, X0, X0, #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((lsb & 63) << 10);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Extr(Op[31], Op[22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("EXTR <Wd>, <Wn>, <Wm>, #<lsb>")]
+        public void Extr_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wm,
+                               [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint lsb)
+        {
+            uint Opcode = 0x13800000; // EXTR W0, W0, W0, #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((lsb & 63) << 10);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Extr(Op[31], Op[22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("LSLV <Xd>, <Xn>, <Xm>")]
+        public void Lslv_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(8)] ulong Xn,
+                               [Values(0ul, 31ul, 32ul, 63ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(5)] ulong Xm)
+        {
+            uint Opcode = 0x9AC02000; // LSLV X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Lslv(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("LSLV <Wd>, <Wn>, <Wm>")]
+        public void Lslv_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(8)] uint Wn,
+                               [Values(0u, 15u, 16u, 31u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(5)] uint Wm)
+        {
+            uint Opcode = 0x1AC02000; // LSLV W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Lslv(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("LSRV <Xd>, <Xn>, <Xm>")]
+        public void Lsrv_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(8)] ulong Xn,
+                               [Values(0ul, 31ul, 32ul, 63ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(5)] ulong Xm)
+        {
+            uint Opcode = 0x9AC02400; // LSRV X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Lsrv(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("LSRV <Wd>, <Wn>, <Wm>")]
+        public void Lsrv_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(8)] uint Wn,
+                               [Values(0u, 15u, 16u, 31u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(5)] uint Wm)
+        {
+            uint Opcode = 0x1AC02400; // LSRV W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Lsrv(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("ORN <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
+        public void Orn_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                              [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                              [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 1)] uint amount)
+        {
+            uint Opcode = 0xAA200000; // ORN X0, X0, X0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Orn(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("ORN <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
+        public void Orn_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                              [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 1)] uint amount)
+        {
+            uint Opcode = 0x2A200000; // ORN W0, W0, W0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Orn(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("ORR <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
+        public void Orr_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                              [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                              [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 1)] uint amount)
+        {
+            uint Opcode = 0xAA000000; // ORR X0, X0, X0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Orr_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("ORR <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
+        public void Orr_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                              [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint shift, // <LSL, LSR, ASR, ROR>
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 1)] uint amount)
+        {
+            uint Opcode = 0x2A000000; // ORR W0, W0, W0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Orr_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("RORV <Xd>, <Xn>, <Xm>")]
+        public void Rorv_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(8)] ulong Xn,
+                               [Values(0ul, 31ul, 32ul, 63ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(5)] ulong Xm)
+        {
+            uint Opcode = 0x9AC02C00; // RORV X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Rorv(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("RORV <Wd>, <Wn>, <Wm>")]
+        public void Rorv_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(8)] uint Wn,
+                               [Values(0u, 15u, 16u, 31u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(5)] uint Wm)
+        {
+            uint Opcode = 0x1AC02C00; // RORV W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Rorv(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("SBC <Xd>, <Xn>, <Xm>")]
+        public void Sbc_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(4)] ulong Xn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(4)] ulong Xm,
+                              [Values] bool CarryIn)
+        {
+            uint Opcode = 0xDA000000; // SBC X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31, Carry: CarryIn);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Shared.PSTATE.C = CarryIn;
+                Base.Sbc(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("SBC <Wd>, <Wn>, <Wm>")]
+        public void Sbc_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(4)] uint Wn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(4)] uint Wm,
+                              [Values] bool CarryIn)
+        {
+            uint Opcode = 0x5A000000; // SBC W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31, Carry: CarryIn);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Shared.PSTATE.C = CarryIn;
+                Base.Sbc(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("SBCS <Xd>, <Xn>, <Xm>")]
+        public void Sbcs_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(4)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(4)] ulong Xm,
+                               [Values] bool CarryIn)
+        {
+            uint Opcode = 0xFA000000; // SBCS X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31, Carry: CarryIn);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            AArch64.X((int)Rm, new Bits(Xm));
+            Shared.PSTATE.C = CarryIn;
+            Base.Sbcs(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+            ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+            if (Rd != 31)
+            {
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("SBCS <Wd>, <Wn>, <Wm>")]
+        public void Sbcs_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(4)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(4)] uint Wm,
+                               [Values] bool CarryIn)
+        {
+            uint Opcode = 0x7A000000; // SBCS W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Bits Op = new Bits(Opcode);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31, Carry: CarryIn);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            AArch64.X((int)Rm, new Bits(Wm));
+            Shared.PSTATE.C = CarryIn;
+            Base.Sbcs(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+            uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+            if (Rd != 31)
+            {
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("SDIV <Xd>, <Xn>, <Xm>")]
+        public void Sdiv_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(8)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(8)] ulong Xm)
+        {
+            uint Opcode = 0x9AC00C00; // SDIV X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Sdiv(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("SDIV <Wd>, <Wn>, <Wm>")]
+        public void Sdiv_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(8)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(8)] uint Wm)
+        {
+            uint Opcode = 0x1AC00C00; // SDIV W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Sdiv(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("SUB <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
+        public void Sub_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                              [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR>
+                              [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 1)] uint amount)
+        {
+            uint Opcode = 0xCB000000; // SUB X0, X0, X0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Sub_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("SUB <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
+        public void Sub_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Values(2u, 31u)] uint Rm,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                              [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR>
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 1)] uint amount)
+        {
+            uint Opcode = 0x4B000000; // SUB W0, W0, W0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Sub_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("SUBS <Xd>, <Xn>, <Xm>{, <shift> #<amount>}")]
+        public void Subs_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                               [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR>
+                               [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 1)] uint amount)
+        {
+            uint Opcode = 0xEB000000; // SUBS X0, X0, X0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            AArch64.X((int)Rm, new Bits(Xm));
+            Base.Subs_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+            ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+            if (Rd != 31)
+            {
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("SUBS <Wd>, <Wn>, <Wm>{, <shift> #<amount>}")]
+        public void Subs_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                               [Values(0b00u, 0b01u, 0b10u)] uint shift, // <LSL, LSR, ASR>
+                               [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 1)] uint amount)
+        {
+            uint Opcode = 0x6B000000; // SUBS W0, W0, W0, LSL #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((shift & 3) << 22) | ((amount & 63) << 10);
+            Bits Op = new Bits(Opcode);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Subs_Rs(Op[31], Op[23, 22], Op[20, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+            uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+            if (Rd != 31)
+            {
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("UDIV <Xd>, <Xn>, <Xm>")]
+        public void Udiv_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(8)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(8)] ulong Xm)
+        {
+            uint Opcode = 0x9AC00800; // UDIV X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Udiv(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("UDIV <Wd>, <Wn>, <Wm>")]
+        public void Udiv_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(8)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(8)] uint Wm)
+        {
+            uint Opcode = 0x1AC00800; // UDIV W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Udiv(Op[31], Op[20, 16], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+#endif
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/CpuTestAluRx.cs b/Ryujinx.Tests/Cpu/CpuTestAluRx.cs
new file mode 100644
index 000000000..26169bca6
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestAluRx.cs
@@ -0,0 +1,1349 @@
+//#define AluRx
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+    using Tester;
+    using Tester.Types;
+
+    [Category("AluRx"), Ignore("Tested: first half of 2018.")]
+    public sealed class CpuTestAluRx : CpuTest
+    {
+#if AluRx
+        [SetUp]
+        public void SetupTester()
+        {
+            AArch64.TakeReset(false);
+        }
+
+        [Test, Description("ADD <Xd|SP>, <Xn|SP>, <X><m>{, <extend> {#<amount>}}")]
+        public void Add_X_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                [Values((ulong)0x0000000000000000, (ulong)0x7FFFFFFFFFFFFFFF,
+                                        (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(2)] ulong Xm,
+                                [Values(0b011u, 0b111u)] uint extend, // <LSL|UXTX, SXTX>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x8B206000; // ADD X0, X0, X0, UXTX #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+                ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Xm, X31: _X31);
+
+                AArch64.X((int)Rn, new Bits(Xn_SP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Xm);
+
+                AArch64.SP(new Bits(Xn_SP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Xm));
+            Base.Add_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("ADD <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
+        public void Add_W_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                [Values((uint)0x00000000, (uint)0x7FFFFFFF,
+                                        (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(2)] uint Wm,
+                                [Values(0b000u, 0b001u, 0b010u,               // <UXTB, UXTH, UXTW,
+                                        0b100u, 0b101u, 0b110u)] uint extend, //  SXTB, SXTH, SXTW>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x8B200000; // ADD X0, X0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+                ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: _X31);
+
+                AArch64.X((int)Rn, new Bits(Xn_SP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Wm);
+
+                AArch64.SP(new Bits(Xn_SP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Add_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("ADD <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
+        public void Add_H_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                [Values((ushort)0x0000, (ushort)0x7FFF,
+                                        (ushort)0x8000, (ushort)0xFFFF)] [Random(2)] ushort Wm,
+                                [Values(0b000u, 0b001u, 0b010u,               // <UXTB, UXTH, UXTW,
+                                        0b100u, 0b101u, 0b110u)] uint extend, //  SXTB, SXTH, SXTW>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x8B200000; // ADD X0, X0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+                ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: _X31);
+
+                AArch64.X((int)Rn, new Bits(Xn_SP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Wm);
+
+                AArch64.SP(new Bits(Xn_SP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Add_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("ADD <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
+        public void Add_B_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                [Values((byte)0x00, (byte)0x7F,
+                                        (byte)0x80, (byte)0xFF)] [Random(2)] byte Wm,
+                                [Values(0b000u, 0b001u, 0b010u,               // <UXTB, UXTH, UXTW,
+                                        0b100u, 0b101u, 0b110u)] uint extend, //  SXTB, SXTH, SXTW>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x8B200000; // ADD X0, X0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+                ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: _X31);
+
+                AArch64.X((int)Rn, new Bits(Xn_SP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Wm);
+
+                AArch64.SP(new Bits(Xn_SP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Add_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("ADD <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
+        public void Add_W_32bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn_WSP,
+                                [Values((uint)0x00000000, (uint)0x7FFFFFFF,
+                                        (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(2)] uint Wm,
+                                [Values(0b000u, 0b001u, 0b010u, 0b011u,               // <UXTB, UXTH, LSL|UXTW, UXTX,
+                                        0b100u, 0b101u, 0b110u, 0b111u)] uint extend, //  SXTB, SXTH, SXTW, SXTX>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x0B200000; // ADD W0, W0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+                ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: _W31);
+
+                AArch64.X((int)Rn, new Bits(Wn_WSP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Wn_WSP, X2: Wm);
+
+                AArch64.SP(new Bits(Wn_WSP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Add_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint WSP = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
+            }
+        }
+
+        [Test, Description("ADD <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
+        public void Add_H_32bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn_WSP,
+                                [Values((ushort)0x0000, (ushort)0x7FFF,
+                                        (ushort)0x8000, (ushort)0xFFFF)] [Random(2)] ushort Wm,
+                                [Values(0b000u, 0b001u, 0b010u, 0b011u,               // <UXTB, UXTH, LSL|UXTW, UXTX,
+                                        0b100u, 0b101u, 0b110u, 0b111u)] uint extend, //  SXTB, SXTH, SXTW, SXTX>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x0B200000; // ADD W0, W0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+                ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: _W31);
+
+                AArch64.X((int)Rn, new Bits(Wn_WSP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Wn_WSP, X2: Wm);
+
+                AArch64.SP(new Bits(Wn_WSP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Add_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint WSP = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
+            }
+        }
+
+        [Test, Description("ADD <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
+        public void Add_B_32bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn_WSP,
+                                [Values((byte)0x00, (byte)0x7F,
+                                        (byte)0x80, (byte)0xFF)] [Random(2)] byte Wm,
+                                [Values(0b000u, 0b001u, 0b010u, 0b011u,               // <UXTB, UXTH, LSL|UXTW, UXTX,
+                                        0b100u, 0b101u, 0b110u, 0b111u)] uint extend, //  SXTB, SXTH, SXTW, SXTX>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x0B200000; // ADD W0, W0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+                ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: _W31);
+
+                AArch64.X((int)Rn, new Bits(Wn_WSP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Wn_WSP, X2: Wm);
+
+                AArch64.SP(new Bits(Wn_WSP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Add_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint WSP = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
+            }
+        }
+
+        [Test, Description("ADDS <Xd>, <Xn|SP>, <X><m>{, <extend> {#<amount>}}")]
+        public void Adds_X_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                 [Values((ulong)0x0000000000000000, (ulong)0x7FFFFFFFFFFFFFFF,
+                                         (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(2)] ulong Xm,
+                                 [Values(0b011u, 0b111u)] uint extend, // <LSL|UXTX, SXTX>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0xAB206000; // ADDS X0, X0, X0, UXTX #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Xm, X31: Xn_SP);
+
+            AArch64.X((int)Rn, new Bits(Xn_SP));
+            AArch64.X((int)Rm, new Bits(Xm));
+            AArch64.SP(new Bits(Xn_SP));
+            Base.Adds_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong _X31 = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ADDS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
+        public void Adds_W_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                 [Values((uint)0x00000000, (uint)0x7FFFFFFF,
+                                         (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(2)] uint Wm,
+                                 [Values(0b000u, 0b001u, 0b010u,               // <UXTB, UXTH, UXTW,
+                                         0b100u, 0b101u, 0b110u)] uint extend, //  SXTB, SXTH, SXTW>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0xAB200000; // ADDS X0, X0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: Xn_SP);
+
+            AArch64.X((int)Rn, new Bits(Xn_SP));
+            AArch64.X((int)Rm, new Bits(Wm));
+            AArch64.SP(new Bits(Xn_SP));
+            Base.Adds_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong _X31 = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ADDS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
+        public void Adds_H_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                 [Values((ushort)0x0000, (ushort)0x7FFF,
+                                         (ushort)0x8000, (ushort)0xFFFF)] [Random(2)] ushort Wm,
+                                 [Values(0b000u, 0b001u, 0b010u,               // <UXTB, UXTH, UXTW,
+                                         0b100u, 0b101u, 0b110u)] uint extend, //  SXTB, SXTH, SXTW>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0xAB200000; // ADDS X0, X0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: Xn_SP);
+
+            AArch64.X((int)Rn, new Bits(Xn_SP));
+            AArch64.X((int)Rm, new Bits(Wm));
+            AArch64.SP(new Bits(Xn_SP));
+            Base.Adds_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong _X31 = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ADDS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
+        public void Adds_B_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                 [Values((byte)0x00, (byte)0x7F,
+                                         (byte)0x80, (byte)0xFF)] [Random(2)] byte Wm,
+                                 [Values(0b000u, 0b001u, 0b010u,               // <UXTB, UXTH, UXTW,
+                                         0b100u, 0b101u, 0b110u)] uint extend, //  SXTB, SXTH, SXTW>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0xAB200000; // ADDS X0, X0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: Xn_SP);
+
+            AArch64.X((int)Rn, new Bits(Xn_SP));
+            AArch64.X((int)Rm, new Bits(Wm));
+            AArch64.SP(new Bits(Xn_SP));
+            Base.Adds_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong _X31 = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ADDS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
+        public void Adds_W_32bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn_WSP,
+                                 [Values((uint)0x00000000, (uint)0x7FFFFFFF,
+                                         (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(2)] uint Wm,
+                                 [Values(0b000u, 0b001u, 0b010u, 0b011u,               // <UXTB, UXTH, LSL|UXTW, UXTX,
+                                         0b100u, 0b101u, 0b110u, 0b111u)] uint extend, //  SXTB, SXTH, SXTW, SXTX>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x2B200000; // ADDS W0, W0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: Wn_WSP);
+
+            AArch64.X((int)Rn, new Bits(Wn_WSP));
+            AArch64.X((int)Rm, new Bits(Wm));
+            AArch64.SP(new Bits(Wn_WSP));
+            Base.Adds_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint _W31 = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ADDS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
+        public void Adds_H_32bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn_WSP,
+                                 [Values((ushort)0x0000, (ushort)0x7FFF,
+                                         (ushort)0x8000, (ushort)0xFFFF)] [Random(2)] ushort Wm,
+                                 [Values(0b000u, 0b001u, 0b010u, 0b011u,               // <UXTB, UXTH, LSL|UXTW, UXTX,
+                                         0b100u, 0b101u, 0b110u, 0b111u)] uint extend, //  SXTB, SXTH, SXTW, SXTX>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x2B200000; // ADDS W0, W0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: Wn_WSP);
+
+            AArch64.X((int)Rn, new Bits(Wn_WSP));
+            AArch64.X((int)Rm, new Bits(Wm));
+            AArch64.SP(new Bits(Wn_WSP));
+            Base.Adds_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint _W31 = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("ADDS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
+        public void Adds_B_32bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn_WSP,
+                                 [Values((byte)0x00, (byte)0x7F,
+                                         (byte)0x80, (byte)0xFF)] [Random(2)] byte Wm,
+                                 [Values(0b000u, 0b001u, 0b010u, 0b011u,               // <UXTB, UXTH, LSL|UXTW, UXTX,
+                                         0b100u, 0b101u, 0b110u, 0b111u)] uint extend, //  SXTB, SXTH, SXTW, SXTX>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x2B200000; // ADDS W0, W0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: Wn_WSP);
+
+            AArch64.X((int)Rn, new Bits(Wn_WSP));
+            AArch64.X((int)Rm, new Bits(Wm));
+            AArch64.SP(new Bits(Wn_WSP));
+            Base.Adds_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint _W31 = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("SUB <Xd|SP>, <Xn|SP>, <X><m>{, <extend> {#<amount>}}")]
+        public void Sub_X_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                [Values((ulong)0x0000000000000000, (ulong)0x7FFFFFFFFFFFFFFF,
+                                        (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(2)] ulong Xm,
+                                [Values(0b011u, 0b111u)] uint extend, // <LSL|UXTX, SXTX>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0xCB206000; // SUB X0, X0, X0, UXTX #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+                ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Xm, X31: _X31);
+
+                AArch64.X((int)Rn, new Bits(Xn_SP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Xm);
+
+                AArch64.SP(new Bits(Xn_SP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Xm));
+            Base.Sub_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("SUB <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
+        public void Sub_W_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                [Values((uint)0x00000000, (uint)0x7FFFFFFF,
+                                        (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(2)] uint Wm,
+                                [Values(0b000u, 0b001u, 0b010u,               // <UXTB, UXTH, UXTW,
+                                        0b100u, 0b101u, 0b110u)] uint extend, //  SXTB, SXTH, SXTW>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0xCB200000; // SUB X0, X0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+                ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: _X31);
+
+                AArch64.X((int)Rn, new Bits(Xn_SP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Wm);
+
+                AArch64.SP(new Bits(Xn_SP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Sub_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("SUB <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
+        public void Sub_H_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                [Values((ushort)0x0000, (ushort)0x7FFF,
+                                        (ushort)0x8000, (ushort)0xFFFF)] [Random(2)] ushort Wm,
+                                [Values(0b000u, 0b001u, 0b010u,               // <UXTB, UXTH, UXTW,
+                                        0b100u, 0b101u, 0b110u)] uint extend, //  SXTB, SXTH, SXTW>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0xCB200000; // SUB X0, X0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+                ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: _X31);
+
+                AArch64.X((int)Rn, new Bits(Xn_SP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Wm);
+
+                AArch64.SP(new Bits(Xn_SP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Sub_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("SUB <Xd|SP>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
+        public void Sub_B_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                [Values((byte)0x00, (byte)0x7F,
+                                        (byte)0x80, (byte)0xFF)] [Random(2)] byte Wm,
+                                [Values(0b000u, 0b001u, 0b010u,               // <UXTB, UXTH, UXTW,
+                                        0b100u, 0b101u, 0b110u)] uint extend, //  SXTB, SXTH, SXTW>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0xCB200000; // SUB X0, X0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+                ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: _X31);
+
+                AArch64.X((int)Rn, new Bits(Xn_SP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Xn_SP, X2: Wm);
+
+                AArch64.SP(new Bits(Xn_SP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Sub_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong SP = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(SP));
+            }
+        }
+
+        [Test, Description("SUB <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
+        public void Sub_W_32bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn_WSP,
+                                [Values((uint)0x00000000, (uint)0x7FFFFFFF,
+                                        (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(2)] uint Wm,
+                                [Values(0b000u, 0b001u, 0b010u, 0b011u,               // <UXTB, UXTH, LSL|UXTW, UXTX,
+                                        0b100u, 0b101u, 0b110u, 0b111u)] uint extend, //  SXTB, SXTH, SXTW, SXTX>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x4B200000; // SUB W0, W0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+                ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: _W31);
+
+                AArch64.X((int)Rn, new Bits(Wn_WSP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Wn_WSP, X2: Wm);
+
+                AArch64.SP(new Bits(Wn_WSP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Sub_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint WSP = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
+            }
+        }
+
+        [Test, Description("SUB <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
+        public void Sub_H_32bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn_WSP,
+                                [Values((ushort)0x0000, (ushort)0x7FFF,
+                                        (ushort)0x8000, (ushort)0xFFFF)] [Random(2)] ushort Wm,
+                                [Values(0b000u, 0b001u, 0b010u, 0b011u,               // <UXTB, UXTH, LSL|UXTW, UXTX,
+                                        0b100u, 0b101u, 0b110u, 0b111u)] uint extend, //  SXTB, SXTH, SXTW, SXTX>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x4B200000; // SUB W0, W0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+                ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: _W31);
+
+                AArch64.X((int)Rn, new Bits(Wn_WSP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Wn_WSP, X2: Wm);
+
+                AArch64.SP(new Bits(Wn_WSP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Sub_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint WSP = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
+            }
+        }
+
+        [Test, Description("SUB <Wd|WSP>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
+        public void Sub_B_32bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn_WSP,
+                                [Values((byte)0x00, (byte)0x7F,
+                                        (byte)0x80, (byte)0xFF)] [Random(2)] byte Wm,
+                                [Values(0b000u, 0b001u, 0b010u, 0b011u,               // <UXTB, UXTH, LSL|UXTW, UXTX,
+                                        0b100u, 0b101u, 0b110u, 0b111u)] uint extend, //  SXTB, SXTH, SXTW, SXTX>
+                                [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x4B200000; // SUB W0, W0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState;
+
+            if (Rn != 31)
+            {
+                uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+                ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: _W31);
+
+                AArch64.X((int)Rn, new Bits(Wn_WSP));
+            }
+            else
+            {
+                ThreadState = SingleOpcode(Opcode, X31: Wn_WSP, X2: Wm);
+
+                AArch64.SP(new Bits(Wn_WSP));
+            }
+
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Sub_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint WSP = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(WSP));
+            }
+        }
+
+        [Test, Description("SUBS <Xd>, <Xn|SP>, <X><m>{, <extend> {#<amount>}}")]
+        public void Subs_X_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                 [Values((ulong)0x0000000000000000, (ulong)0x7FFFFFFFFFFFFFFF,
+                                         (ulong)0x8000000000000000, (ulong)0xFFFFFFFFFFFFFFFF)] [Random(2)] ulong Xm,
+                                 [Values(0b011u, 0b111u)] uint extend, // <LSL|UXTX, SXTX>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0xEB206000; // SUBS X0, X0, X0, UXTX #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Xm, X31: Xn_SP);
+
+            AArch64.X((int)Rn, new Bits(Xn_SP));
+            AArch64.X((int)Rm, new Bits(Xm));
+            AArch64.SP(new Bits(Xn_SP));
+            Base.Subs_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong _X31 = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("SUBS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
+        public void Subs_W_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                 [Values((uint)0x00000000, (uint)0x7FFFFFFF,
+                                         (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(2)] uint Wm,
+                                 [Values(0b000u, 0b001u, 0b010u,               // <UXTB, UXTH, UXTW,
+                                         0b100u, 0b101u, 0b110u)] uint extend, //  SXTB, SXTH, SXTW>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0xEB200000; // SUBS X0, X0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: Xn_SP);
+
+            AArch64.X((int)Rn, new Bits(Xn_SP));
+            AArch64.X((int)Rm, new Bits(Wm));
+            AArch64.SP(new Bits(Xn_SP));
+            Base.Subs_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong _X31 = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("SUBS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
+        public void Subs_H_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                 [Values((ushort)0x0000, (ushort)0x7FFF,
+                                         (ushort)0x8000, (ushort)0xFFFF)] [Random(2)] ushort Wm,
+                                 [Values(0b000u, 0b001u, 0b010u,               // <UXTB, UXTH, UXTW,
+                                         0b100u, 0b101u, 0b110u)] uint extend, //  SXTB, SXTH, SXTW>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0xEB200000; // SUBS X0, X0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: Xn_SP);
+
+            AArch64.X((int)Rn, new Bits(Xn_SP));
+            AArch64.X((int)Rm, new Bits(Wm));
+            AArch64.SP(new Bits(Xn_SP));
+            Base.Subs_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong _X31 = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("SUBS <Xd>, <Xn|SP>, <W><m>{, <extend> {#<amount>}}")]
+        public void Subs_B_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn_SP,
+                                 [Values((byte)0x00, (byte)0x7F,
+                                         (byte)0x80, (byte)0xFF)] [Random(2)] byte Wm,
+                                 [Values(0b000u, 0b001u, 0b010u,               // <UXTB, UXTH, UXTW,
+                                         0b100u, 0b101u, 0b110u)] uint extend, //  SXTB, SXTH, SXTW>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0xEB200000; // SUBS X0, X0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn_SP, X2: Wm, X31: Xn_SP);
+
+            AArch64.X((int)Rn, new Bits(Xn_SP));
+            AArch64.X((int)Rm, new Bits(Wm));
+            AArch64.SP(new Bits(Xn_SP));
+            Base.Subs_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                ulong _X31 = AArch64.SP(64).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("SUBS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
+        public void Subs_W_32bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn_WSP,
+                                 [Values((uint)0x00000000, (uint)0x7FFFFFFF,
+                                         (uint)0x80000000, (uint)0xFFFFFFFF)] [Random(2)] uint Wm,
+                                 [Values(0b000u, 0b001u, 0b010u, 0b011u,               // <UXTB, UXTH, LSL|UXTW, UXTX,
+                                         0b100u, 0b101u, 0b110u, 0b111u)] uint extend, //  SXTB, SXTH, SXTW, SXTX>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x6B200000; // SUBS W0, W0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: Wn_WSP);
+
+            AArch64.X((int)Rn, new Bits(Wn_WSP));
+            AArch64.X((int)Rm, new Bits(Wm));
+            AArch64.SP(new Bits(Wn_WSP));
+            Base.Subs_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint _W31 = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("SUBS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
+        public void Subs_H_32bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn_WSP,
+                                 [Values((ushort)0x0000, (ushort)0x7FFF,
+                                         (ushort)0x8000, (ushort)0xFFFF)] [Random(2)] ushort Wm,
+                                 [Values(0b000u, 0b001u, 0b010u, 0b011u,               // <UXTB, UXTH, LSL|UXTW, UXTX,
+                                         0b100u, 0b101u, 0b110u, 0b111u)] uint extend, //  SXTB, SXTH, SXTW, SXTX>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x6B200000; // SUBS W0, W0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: Wn_WSP);
+
+            AArch64.X((int)Rn, new Bits(Wn_WSP));
+            AArch64.X((int)Rm, new Bits(Wm));
+            AArch64.SP(new Bits(Wn_WSP));
+            Base.Subs_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint _W31 = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("SUBS <Wd>, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}")]
+        public void Subs_B_32bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn_WSP,
+                                 [Values((byte)0x00, (byte)0x7F,
+                                         (byte)0x80, (byte)0xFF)] [Random(2)] byte Wm,
+                                 [Values(0b000u, 0b001u, 0b010u, 0b011u,               // <UXTB, UXTH, LSL|UXTW, UXTX,
+                                         0b100u, 0b101u, 0b110u, 0b111u)] uint extend, //  SXTB, SXTH, SXTW, SXTX>
+                                 [Values(0u, 1u, 2u, 3u, 4u)] uint amount)
+        {
+            uint Opcode = 0x6B200000; // SUBS W0, W0, W0, UXTB #0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((extend & 7) << 13) | ((amount & 7) << 10);
+            Bits Op = new Bits(Opcode);
+
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn_WSP, X2: Wm, X31: Wn_WSP);
+
+            AArch64.X((int)Rn, new Bits(Wn_WSP));
+            AArch64.X((int)Rm, new Bits(Wm));
+            AArch64.SP(new Bits(Wn_WSP));
+            Base.Subs_Rx(Op[31], Op[20, 16], Op[15, 13], Op[12, 10], Op[9, 5], Op[4, 0]);
+
+            if (Rd != 31)
+            {
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                uint _W31 = AArch64.SP(32).ToUInt32();
+
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+#endif
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/CpuTestBfm.cs b/Ryujinx.Tests/Cpu/CpuTestBfm.cs
new file mode 100644
index 000000000..2952bca4c
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestBfm.cs
@@ -0,0 +1,213 @@
+//#define Bfm
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+    using Tester;
+    using Tester.Types;
+
+    [Category("Bfm"), Ignore("Tested: first half of 2018.")]
+    public sealed class CpuTestBfm : CpuTest
+    {
+#if Bfm
+        [SetUp]
+        public void SetupTester()
+        {
+            AArch64.TakeReset(false);
+        }
+
+        [Test, Description("BFM <Xd>, <Xn>, #<immr>, #<imms>")]
+        public void Bfm_64bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Random(2)] ulong _Xd,
+                              [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                      0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                              [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 2)] uint immr,
+                              [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 2)] uint imms)
+        {
+            uint Opcode = 0xB3400000; // BFM X0, X0, #0, #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X0: _Xd, X1: Xn, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rd, new Bits(_Xd));
+                AArch64.X((int)Rn, new Bits(Xn));
+                Base.Bfm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("BFM <Wd>, <Wn>, #<immr>, #<imms>")]
+        public void Bfm_32bit([Values(0u, 31u)] uint Rd,
+                              [Values(1u, 31u)] uint Rn,
+                              [Random(2)] uint _Wd,
+                              [Values(0x00000000u, 0x7FFFFFFFu,
+                                      0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint immr,
+                              [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint imms)
+        {
+            uint Opcode = 0x33000000; // BFM W0, W0, #0, #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X0: _Wd, X1: Wn, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rd, new Bits(_Wd));
+                AArch64.X((int)Rn, new Bits(Wn));
+                Base.Bfm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("SBFM <Xd>, <Xn>, #<immr>, #<imms>")]
+        public void Sbfm_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                               [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 2)] uint immr,
+                               [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 2)] uint imms)
+        {
+            uint Opcode = 0x93400000; // SBFM X0, X0, #0, #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                Base.Sbfm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("SBFM <Wd>, <Wn>, #<immr>, #<imms>")]
+        public void Sbfm_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                               [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint immr,
+                               [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint imms)
+        {
+            uint Opcode = 0x13000000; // SBFM W0, W0, #0, #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                Base.Sbfm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("UBFM <Xd>, <Xn>, #<immr>, #<imms>")]
+        public void Ubfm_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                               [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 2)] uint immr,
+                               [Values(0u, 31u, 32u, 63u)] [Random(0u, 63u, 2)] uint imms)
+        {
+            uint Opcode = 0xD3400000; // UBFM X0, X0, #0, #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                Base.Ubfm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("UBFM <Wd>, <Wn>, #<immr>, #<imms>")]
+        public void Ubfm_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                               [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint immr,
+                               [Values(0u, 15u, 16u, 31u)] [Random(0u, 31u, 2)] uint imms)
+        {
+            uint Opcode = 0x53000000; // UBFM W0, W0, #0, #0
+            Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((immr & 63) << 16) | ((imms & 63) << 10);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                Base.Ubfm(Op[31], Op[22], Op[21, 16], Op[15, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+#endif
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs b/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs
new file mode 100644
index 000000000..38d73878a
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestCcmpImm.cs
@@ -0,0 +1,151 @@
+//#define CcmpImm
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+    using Tester;
+    using Tester.Types;
+
+    [Category("CcmpImm"), Ignore("Tested: first half of 2018.")]
+    public sealed class CpuTestCcmpImm : CpuTest
+    {
+#if CcmpImm
+        [SetUp]
+        public void SetupTester()
+        {
+            AArch64.TakeReset(false);
+        }
+
+        [Test, Description("CCMN <Xn>, #<imm>, #<nzcv>, <cond>")]
+        public void Ccmn_64bit([Values(1u, 31u)] uint Rn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                               [Values(0u, 31u)] [Random(0u, 31u, 3)] uint imm,
+                               [Random(0u, 15u, 1)] uint nzcv,
+                               [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                       0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                       0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                       0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0xBA400800; // CCMN X0, #0, #0, EQ
+            Opcode |= ((Rn & 31) << 5);
+            Opcode |= ((imm & 31) << 16) | ((cond & 15) << 12) | ((nzcv & 15) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            Bits Op = new Bits(Opcode);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            Base.Ccmn_Imm(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[3, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("CCMN <Wn>, #<imm>, #<nzcv>, <cond>")]
+        public void Ccmn_32bit([Values(1u, 31u)] uint Rn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                               [Values(0u, 31u)] [Random(0u, 31u, 3)] uint imm,
+                               [Random(0u, 15u, 1)] uint nzcv,
+                               [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                       0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                       0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                       0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0x3A400800; // CCMN W0, #0, #0, EQ
+            Opcode |= ((Rn & 31) << 5);
+            Opcode |= ((imm & 31) << 16) | ((cond & 15) << 12) | ((nzcv & 15) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            Bits Op = new Bits(Opcode);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            Base.Ccmn_Imm(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[3, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("CCMP <Xn>, #<imm>, #<nzcv>, <cond>")]
+        public void Ccmp_64bit([Values(1u, 31u)] uint Rn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                               [Values(0u, 31u)] [Random(0u, 31u, 3)] uint imm,
+                               [Random(0u, 15u, 1)] uint nzcv,
+                               [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                       0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                       0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                       0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0xFA400800; // CCMP X0, #0, #0, EQ
+            Opcode |= ((Rn & 31) << 5);
+            Opcode |= ((imm & 31) << 16) | ((cond & 15) << 12) | ((nzcv & 15) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X31: _X31);
+
+            Bits Op = new Bits(Opcode);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            Base.Ccmp_Imm(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[3, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("CCMP <Wn>, #<imm>, #<nzcv>, <cond>")]
+        public void Ccmp_32bit([Values(1u, 31u)] uint Rn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                               [Values(0u, 31u)] [Random(0u, 31u, 3)] uint imm,
+                               [Random(0u, 15u, 1)] uint nzcv,
+                               [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                       0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                       0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                       0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0x7A400800; // CCMP W0, #0, #0, EQ
+            Opcode |= ((Rn & 31) << 5);
+            Opcode |= ((imm & 31) << 16) | ((cond & 15) << 12) | ((nzcv & 15) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X31: _W31);
+
+            Bits Op = new Bits(Opcode);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            Base.Ccmp_Imm(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[3, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+#endif
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs b/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs
new file mode 100644
index 000000000..eb1c3abf2
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestCcmpReg.cs
@@ -0,0 +1,163 @@
+//#define CcmpReg
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+    using Tester;
+    using Tester.Types;
+
+    [Category("CcmpReg"), Ignore("Tested: first half of 2018.")]
+    public sealed class CpuTestCcmpReg : CpuTest
+    {
+#if CcmpReg
+        [SetUp]
+        public void SetupTester()
+        {
+            AArch64.TakeReset(false);
+        }
+
+        [Test, Description("CCMN <Xn>, <Xm>, #<nzcv>, <cond>")]
+        public void Ccmn_64bit([Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                               [Random(0u, 15u, 1)] uint nzcv,
+                               [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                       0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                       0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                       0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0xBA400000; // CCMN X0, X0, #0, EQ
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5);
+            Opcode |= ((cond & 15) << 12) | ((nzcv & 15) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            Bits Op = new Bits(Opcode);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            AArch64.X((int)Rm, new Bits(Xm));
+            Base.Ccmn_Reg(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[3, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("CCMN <Wn>, <Wm>, #<nzcv>, <cond>")]
+        public void Ccmn_32bit([Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                               [Random(0u, 15u, 1)] uint nzcv,
+                               [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                       0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                       0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                       0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0x3A400000; // CCMN W0, W0, #0, EQ
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5);
+            Opcode |= ((cond & 15) << 12) | ((nzcv & 15) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            Bits Op = new Bits(Opcode);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Ccmn_Reg(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[3, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("CCMP <Xn>, <Xm>, #<nzcv>, <cond>")]
+        public void Ccmp_64bit([Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                               [Random(0u, 15u, 1)] uint nzcv,
+                               [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                       0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                       0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                       0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0xFA400000; // CCMP X0, X0, #0, EQ
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5);
+            Opcode |= ((cond & 15) << 12) | ((nzcv & 15) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            Bits Op = new Bits(Opcode);
+
+            AArch64.X((int)Rn, new Bits(Xn));
+            AArch64.X((int)Rm, new Bits(Xm));
+            Base.Ccmp_Reg(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[3, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+
+        [Test, Description("CCMP <Wn>, <Wm>, #<nzcv>, <cond>")]
+        public void Ccmp_32bit([Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                               [Random(0u, 15u, 1)] uint nzcv,
+                               [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                       0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                       0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                       0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0x7A400000; // CCMP W0, W0, #0, EQ
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5);
+            Opcode |= ((cond & 15) << 12) | ((nzcv & 15) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            Bits Op = new Bits(Opcode);
+
+            AArch64.X((int)Rn, new Bits(Wn));
+            AArch64.X((int)Rm, new Bits(Wm));
+            Base.Ccmp_Reg(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[3, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.Negative, Is.EqualTo(Shared.PSTATE.N));
+                Assert.That(ThreadState.Zero,     Is.EqualTo(Shared.PSTATE.Z));
+                Assert.That(ThreadState.Carry,    Is.EqualTo(Shared.PSTATE.C));
+                Assert.That(ThreadState.Overflow, Is.EqualTo(Shared.PSTATE.V));
+            });
+        }
+#endif
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/CpuTestCsel.cs b/Ryujinx.Tests/Cpu/CpuTestCsel.cs
new file mode 100644
index 000000000..9dd61957f
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestCsel.cs
@@ -0,0 +1,319 @@
+//#define Csel
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+    using Tester;
+    using Tester.Types;
+
+    [Category("Csel"), Ignore("Tested: first half of 2018.")]
+    public sealed class CpuTestCsel : CpuTest
+    {
+#if Csel
+        [SetUp]
+        public void SetupTester()
+        {
+            AArch64.TakeReset(false);
+        }
+
+        [Test, Description("CSEL <Xd>, <Xn>, <Xm>, <cond>")]
+        public void Csel_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                               [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                       0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                       0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                       0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0x9A800000; // CSEL X0, X0, X0, EQ
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((cond & 15) << 12);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Csel(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("CSEL <Wd>, <Wn>, <Wm>, <cond>")]
+        public void Csel_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                               [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                       0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                       0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                       0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0x1A800000; // CSEL W0, W0, W0, EQ
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((cond & 15) << 12);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Csel(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("CSINC <Xd>, <Xn>, <Xm>, <cond>")]
+        public void Csinc_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                                [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                        0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                        0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                        0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0x9A800400; // CSINC X0, X0, X0, EQ
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((cond & 15) << 12);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Csinc(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("CSINC <Wd>, <Wn>, <Wm>, <cond>")]
+        public void Csinc_32bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                                [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                        0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                        0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                        0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0x1A800400; // CSINC W0, W0, W0, EQ
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((cond & 15) << 12);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Csinc(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("CSINV <Xd>, <Xn>, <Xm>, <cond>")]
+        public void Csinv_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                                [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                        0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                        0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                        0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0xDA800000; // CSINV X0, X0, X0, EQ
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((cond & 15) << 12);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Csinv(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("CSINV <Wd>, <Wn>, <Wm>, <cond>")]
+        public void Csinv_32bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                                [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                        0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                        0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                        0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0x5A800000; // CSINV W0, W0, W0, EQ
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((cond & 15) << 12);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Csinv(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("CSNEG <Xd>, <Xn>, <Xm>, <cond>")]
+        public void Csneg_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xn,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(1)] ulong Xm,
+                                [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                        0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                        0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                        0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0xDA800400; // CSNEG X0, X0, X0, EQ
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((cond & 15) << 12);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Csneg(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("CSNEG <Wd>, <Wn>, <Wm>, <cond>")]
+        public void Csneg_32bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wn,
+                                [Values(0x00000000u, 0x7FFFFFFFu,
+                                        0x80000000u, 0xFFFFFFFFu)] [Random(1)] uint Wm,
+                                [Values(0b0000u, 0b0001u, 0b0010u, 0b0011u,             // <EQ, NE, CS/HS, CC/LO,
+                                        0b0100u, 0b0101u, 0b0110u, 0b0111u,             //  MI, PL, VS, VC,
+                                        0b1000u, 0b1001u, 0b1010u, 0b1011u,             //  HI, LS, GE, LT,
+                                        0b1100u, 0b1101u, 0b1110u, 0b1111u)] uint cond) //  GT, LE, AL, NV>
+        {
+            uint Opcode = 0x5A800400; // CSNEG W0, W0, W0, EQ
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+            Opcode |= ((cond & 15) << 12);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                Base.Csneg(Op[31], Op[20, 16], Op[15, 12], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+#endif
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/CpuTestMisc.cs b/Ryujinx.Tests/Cpu/CpuTestMisc.cs
index 3a995fe26..647d46c00 100644
--- a/Ryujinx.Tests/Cpu/CpuTestMisc.cs
+++ b/Ryujinx.Tests/Cpu/CpuTestMisc.cs
@@ -1,22 +1,12 @@
 using ChocolArm64.State;
+
 using NUnit.Framework;
 
 namespace Ryujinx.Tests.Cpu
 {
-    public class CpuTestMisc : CpuTest
+    [Category("Misc"), Explicit]
+    public sealed class CpuTestMisc : CpuTest
     {
-        [TestCase(0ul)]
-        [TestCase(1ul)]
-        [TestCase(2ul)]
-        [TestCase(42ul)]
-        public void SanityCheck(ulong A)
-        {
-            // NOP
-            uint Opcode = 0xD503201F;
-            AThreadState ThreadState = SingleOpcode(Opcode, X0: A);
-            Assert.AreEqual(A, ThreadState.X0);
-        }
-
         [TestCase(0xFFFFFFFDu)] // Roots.
         [TestCase(0x00000005u)]
         public void Misc1(uint A)
@@ -46,27 +36,28 @@ namespace Ryujinx.Tests.Cpu
             Opcode(0xD4200000);
             Opcode(0xD65F03C0);
             ExecuteOpcodes();
-            Assert.AreEqual(0, GetThreadState().X0);
+
+            Assert.That(GetThreadState().X0, Is.Zero);
         }
 
-        [TestCase(-20f, -5f)] // 18 integer solutions.
-        [TestCase(-12f, -6f)]
-        [TestCase(-12f, 3f)]
-        [TestCase(-8f, -8f)]
-        [TestCase(-6f, -12f)]
-        [TestCase(-5f, -20f)]
-        [TestCase(-4f, 2f)]
-        [TestCase(-3f, 12f)]
-        [TestCase(-2f, 4f)]
-        [TestCase(2f, -4f)]
-        [TestCase(3f, -12f)]
-        [TestCase(4f, -2f)]
-        [TestCase(5f, 20f)]
-        [TestCase(6f, 12f)]
-        [TestCase(8f, 8f)]
-        [TestCase(12f, -3f)]
-        [TestCase(12f, 6f)]
-        [TestCase(20f, 5f)]
+        [TestCase(-20f,  -5f)] // 18 integer solutions.
+        [TestCase(-12f,  -6f)]
+        [TestCase(-12f,   3f)]
+        [TestCase( -8f,  -8f)]
+        [TestCase( -6f, -12f)]
+        [TestCase( -5f, -20f)]
+        [TestCase( -4f,   2f)]
+        [TestCase( -3f,  12f)]
+        [TestCase( -2f,   4f)]
+        [TestCase(  2f,  -4f)]
+        [TestCase(  3f, -12f)]
+        [TestCase(  4f,  -2f)]
+        [TestCase(  5f,  20f)]
+        [TestCase(  6f,  12f)]
+        [TestCase(  8f,   8f)]
+        [TestCase( 12f,  -3f)]
+        [TestCase( 12f,   6f)]
+        [TestCase( 20f,   5f)]
         public void Misc2(float A, float B)
         {
             // 1 / ((1 / A + 1 / B) ^ 2) = 16
@@ -92,27 +83,28 @@ namespace Ryujinx.Tests.Cpu
             Opcode(0xD4200000);
             Opcode(0xD65F03C0);
             ExecuteOpcodes();
-            Assert.AreEqual(16f, GetThreadState().V0.S0);
+
+            Assert.That(GetThreadState().V0.S0, Is.EqualTo(16f));
         }
 
-        [TestCase(-20d, -5d)] // 18 integer solutions.
-        [TestCase(-12d, -6d)]
-        [TestCase(-12d, 3d)]
-        [TestCase(-8d, -8d)]
-        [TestCase(-6d, -12d)]
-        [TestCase(-5d, -20d)]
-        [TestCase(-4d, 2d)]
-        [TestCase(-3d, 12d)]
-        [TestCase(-2d, 4d)]
-        [TestCase(2d, -4d)]
-        [TestCase(3d, -12d)]
-        [TestCase(4d, -2d)]
-        [TestCase(5d, 20d)]
-        [TestCase(6d, 12d)]
-        [TestCase(8d, 8d)]
-        [TestCase(12d, -3d)]
-        [TestCase(12d, 6d)]
-        [TestCase(20d, 5d)]
+        [TestCase(-20d,  -5d)] // 18 integer solutions.
+        [TestCase(-12d,  -6d)]
+        [TestCase(-12d,   3d)]
+        [TestCase( -8d,  -8d)]
+        [TestCase( -6d, -12d)]
+        [TestCase( -5d, -20d)]
+        [TestCase( -4d,   2d)]
+        [TestCase( -3d,  12d)]
+        [TestCase( -2d,   4d)]
+        [TestCase(  2d,  -4d)]
+        [TestCase(  3d, -12d)]
+        [TestCase(  4d,  -2d)]
+        [TestCase(  5d,  20d)]
+        [TestCase(  6d,  12d)]
+        [TestCase(  8d,   8d)]
+        [TestCase( 12d,  -3d)]
+        [TestCase( 12d,   6d)]
+        [TestCase( 20d,   5d)]
         public void Misc3(double A, double B)
         {
             // 1 / ((1 / A + 1 / B) ^ 2) = 16
@@ -138,74 +130,12 @@ namespace Ryujinx.Tests.Cpu
             Opcode(0xD4200000);
             Opcode(0xD65F03C0);
             ExecuteOpcodes();
-            Assert.AreEqual(16d, GetThreadState().V0.D0);
+
+            Assert.That(GetThreadState().V0.D0, Is.EqualTo(16d));
         }
 
         [Test]
-        public void MiscR()
-        {
-            ulong Result = 5;
-
-            /*
-            0x0000000000000000: MOV X0, #2
-            0x0000000000000004: MOV X1, #3
-            0x0000000000000008: ADD X0, X0, X1
-            0x000000000000000C: BRK #0
-            0x0000000000000010: RET
-            */
-
-            Opcode(0xD2800040);
-            Opcode(0xD2800061);
-            Opcode(0x8B010000);
-            Opcode(0xD4200000);
-            Opcode(0xD65F03C0);
-            ExecuteOpcodes();
-            Assert.AreEqual(Result, GetThreadState().X0);
-
-            Reset();
-
-            /*
-            0x0000000000000000: MOV X0, #3
-            0x0000000000000004: MOV X1, #2
-            0x0000000000000008: ADD X0, X0, X1
-            0x000000000000000C: BRK #0
-            0x0000000000000010: RET
-            */
-
-            Opcode(0xD2800060);
-            Opcode(0xD2800041);
-            Opcode(0x8B010000);
-            Opcode(0xD4200000);
-            Opcode(0xD65F03C0);
-            ExecuteOpcodes();
-            Assert.AreEqual(Result, GetThreadState().X0);
-        }
-
-        [Test, Explicit]
-        public void Misc5()
-        {
-            /*
-            0x0000000000000000: SUBS X0, X0, #1
-            0x0000000000000004: B.NE #0
-            0x0000000000000008: BRK #0
-            0x000000000000000C: RET
-            */
-
-            SetThreadState(X0: 0x100000000);
-            Opcode(0xF1000400);
-            Opcode(0x54FFFFE1);
-            Opcode(0xD4200000);
-            Opcode(0xD65F03C0);
-            ExecuteOpcodes();
-            Assert.Multiple(() =>
-            {
-                Assert.AreEqual(0, GetThreadState().X0);
-                Assert.IsTrue(GetThreadState().Zero);
-            });
-        }
-
-        [Test]
-        public void MiscF([Range(0, 92, 1)] int A)
+        public void MiscF([Range(0u, 92u, 1u)] uint A)
         {
             ulong F_n(uint n)
             {
@@ -250,7 +180,7 @@ namespace Ryujinx.Tests.Cpu
             0x0000000000000050: RET
             */
 
-            SetThreadState(X0: (uint)A);
+            SetThreadState(X0: A);
             Opcode(0x2A0003E4);
             Opcode(0x340001C0);
             Opcode(0x7100041F);
@@ -273,7 +203,62 @@ namespace Ryujinx.Tests.Cpu
             Opcode(0xD4200000);
             Opcode(0xD65F03C0);
             ExecuteOpcodes();
-            Assert.AreEqual(F_n((uint)A), GetThreadState().X0);
+
+            Assert.That(GetThreadState().X0, Is.EqualTo(F_n(A)));
+        }
+
+        [Test]
+        public void MiscR()
+        {
+            const ulong Result = 5;
+
+            /*
+            0x0000000000000000: MOV X0, #2
+            0x0000000000000004: MOV X1, #3
+            0x0000000000000008: ADD X0, X0, X1
+            0x000000000000000C: BRK #0
+            0x0000000000000010: RET
+            */
+
+            Opcode(0xD2800040);
+            Opcode(0xD2800061);
+            Opcode(0x8B010000);
+            Opcode(0xD4200000);
+            Opcode(0xD65F03C0);
+            ExecuteOpcodes();
+
+            Assert.That(GetThreadState().X0, Is.EqualTo(Result));
+
+            Reset();
+
+            /*
+            0x0000000000000000: MOV X0, #3
+            0x0000000000000004: MOV X1, #2
+            0x0000000000000008: ADD X0, X0, X1
+            0x000000000000000C: BRK #0
+            0x0000000000000010: RET
+            */
+
+            Opcode(0xD2800060);
+            Opcode(0xD2800041);
+            Opcode(0x8B010000);
+            Opcode(0xD4200000);
+            Opcode(0xD65F03C0);
+            ExecuteOpcodes();
+
+            Assert.That(GetThreadState().X0, Is.EqualTo(Result));
+        }
+
+        [TestCase( 0ul)]
+        [TestCase( 1ul)]
+        [TestCase( 2ul)]
+        [TestCase(42ul)]
+        public void SanityCheck(ulong A)
+        {
+            uint Opcode = 0xD503201F; // NOP
+            AThreadState ThreadState = SingleOpcode(Opcode, X0: A);
+
+            Assert.That(ThreadState.X0, Is.EqualTo(A));
         }
     }
 }
diff --git a/Ryujinx.Tests/Cpu/CpuTestMov.cs b/Ryujinx.Tests/Cpu/CpuTestMov.cs
new file mode 100644
index 000000000..9c7e3255a
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestMov.cs
@@ -0,0 +1,189 @@
+//#define Mov
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+    using Tester;
+    using Tester.Types;
+
+    [Category("Mov"), Ignore("Tested: first half of 2018.")]
+    public sealed class CpuTestMov : CpuTest
+    {
+#if Mov
+        [SetUp]
+        public void SetupTester()
+        {
+            AArch64.TakeReset(false);
+        }
+
+        [Test, Description("MOVK <Xd>, #<imm>{, LSL #<shift>}")]
+        public void Movk_64bit([Values(0u, 31u)] uint Rd,
+                               [Random(12)] ulong _Xd,
+                               [Values(0u, 65535u)] [Random(0u, 65535u, 10)] uint imm,
+                               [Values(0u, 16u, 32u, 48u)] uint shift)
+        {
+            uint Opcode = 0xF2800000; // MOVK X0, #0, LSL #0
+            Opcode |= ((Rd & 31) << 0);
+            Opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X0: _Xd, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rd, new Bits(_Xd));
+                Base.Movk(Op[31], Op[22, 21], Op[20, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("MOVK <Wd>, #<imm>{, LSL #<shift>}")]
+        public void Movk_32bit([Values(0u, 31u)] uint Rd,
+                               [Random(12)] uint _Wd,
+                               [Values(0u, 65535u)] [Random(0u, 65535u, 10)] uint imm,
+                               [Values(0u, 16u)] uint shift)
+        {
+            uint Opcode = 0x72800000; // MOVK W0, #0, LSL #0
+            Opcode |= ((Rd & 31) << 0);
+            Opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X0: _Wd, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rd, new Bits(_Wd));
+                Base.Movk(Op[31], Op[22, 21], Op[20, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("MOVN <Xd>, #<imm>{, LSL #<shift>}")]
+        public void Movn_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(0u, 65535u)] [Random(0u, 65535u, 128)] uint imm,
+                               [Values(0u, 16u, 32u, 48u)] uint shift)
+        {
+            uint Opcode = 0x92800000; // MOVN X0, #0, LSL #0
+            Opcode |= ((Rd & 31) << 0);
+            Opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                Base.Movn(Op[31], Op[22, 21], Op[20, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("MOVN <Wd>, #<imm>{, LSL #<shift>}")]
+        public void Movn_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(0u, 65535u)] [Random(0u, 65535u, 128)] uint imm,
+                               [Values(0u, 16u)] uint shift)
+        {
+            uint Opcode = 0x12800000; // MOVN W0, #0, LSL #0
+            Opcode |= ((Rd & 31) << 0);
+            Opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                Base.Movn(Op[31], Op[22, 21], Op[20, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("MOVZ <Xd>, #<imm>{, LSL #<shift>}")]
+        public void Movz_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(0u, 65535u)] [Random(0u, 65535u, 128)] uint imm,
+                               [Values(0u, 16u, 32u, 48u)] uint shift)
+        {
+            uint Opcode = 0xD2800000; // MOVZ X0, #0, LSL #0
+            Opcode |= ((Rd & 31) << 0);
+            Opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                Base.Movz(Op[31], Op[22, 21], Op[20, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("MOVZ <Wd>, #<imm>{, LSL #<shift>}")]
+        public void Movz_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(0u, 65535u)] [Random(0u, 65535u, 128)] uint imm,
+                               [Values(0u, 16u)] uint shift)
+        {
+            uint Opcode = 0x52800000; // MOVZ W0, #0, LSL #0
+            Opcode |= ((Rd & 31) << 0);
+            Opcode |= (((shift / 16) & 3) << 21) | ((imm & 65535) << 5);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                Base.Movz(Op[31], Op[22, 21], Op[20, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+#endif
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/CpuTestMul.cs b/Ryujinx.Tests/Cpu/CpuTestMul.cs
new file mode 100644
index 000000000..9bdc1fa65
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestMul.cs
@@ -0,0 +1,375 @@
+//#define Mul
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+    using Tester;
+    using Tester.Types;
+
+    [Category("Mul"), Ignore("Tested: first half of 2018.")]
+    public sealed class CpuTestMul : CpuTest
+    {
+#if Mul
+        [SetUp]
+        public void SetupTester()
+        {
+            AArch64.TakeReset(false);
+        }
+
+        [Test, Description("MADD <Xd>, <Xn>, <Xm>, <Xa>")]
+        public void Madd_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(3u, 31u)] uint Ra,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xa)
+        {
+            uint Opcode = 0x9B000000; // MADD X0, X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X3: Xa, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                AArch64.X((int)Ra, new Bits(Xa));
+                Base.Madd(Op[31], Op[20, 16], Op[14, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("MADD <Wd>, <Wn>, <Wm>, <Wa>")]
+        public void Madd_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(3u, 31u)] uint Ra,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wa)
+        {
+            uint Opcode = 0x1B000000; // MADD W0, W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X3: Wa, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                AArch64.X((int)Ra, new Bits(Wa));
+                Base.Madd(Op[31], Op[20, 16], Op[14, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("MSUB <Xd>, <Xn>, <Xm>, <Xa>")]
+        public void Msub_64bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(3u, 31u)] uint Ra,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xn,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xm,
+                               [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                       0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xa)
+        {
+            uint Opcode = 0x9B008000; // MSUB X0, X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X3: Xa, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                AArch64.X((int)Ra, new Bits(Xa));
+                Base.Msub(Op[31], Op[20, 16], Op[14, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("MSUB <Wd>, <Wn>, <Wm>, <Wa>")]
+        public void Msub_32bit([Values(0u, 31u)] uint Rd,
+                               [Values(1u, 31u)] uint Rn,
+                               [Values(2u, 31u)] uint Rm,
+                               [Values(3u, 31u)] uint Ra,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wm,
+                               [Values(0x00000000u, 0x7FFFFFFFu,
+                                       0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wa)
+        {
+            uint Opcode = 0x1B008000; // MSUB W0, W0, W0, W0
+            Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            uint _W31 = TestContext.CurrentContext.Random.NextUInt();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X3: Wa, X31: _W31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                AArch64.X((int)Ra, new Bits(Wa));
+                Base.Msub(Op[31], Op[20, 16], Op[14, 10], Op[9, 5], Op[4, 0]);
+                uint Wd = AArch64.X(32, (int)Rd).ToUInt32();
+
+                Assert.That((uint)ThreadState.X0, Is.EqualTo(Wd));
+            }
+            else
+            {
+                Assert.That((uint)ThreadState.X31, Is.EqualTo(_W31));
+            }
+        }
+
+        [Test, Description("SMADDL <Xd>, <Wn>, <Wm>, <Xa>")]
+        public void Smaddl_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(3u, 31u)] uint Ra,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wm,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xa)
+        {
+            uint Opcode = 0x9B200000; // SMADDL X0, W0, W0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X3: Xa, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                AArch64.X((int)Ra, new Bits(Xa));
+                Base.Smaddl(Op[20, 16], Op[14, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("UMADDL <Xd>, <Wn>, <Wm>, <Xa>")]
+        public void Umaddl_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(3u, 31u)] uint Ra,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wm,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xa)
+        {
+            uint Opcode = 0x9BA00000; // UMADDL X0, W0, W0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X3: Xa, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                AArch64.X((int)Ra, new Bits(Xa));
+                Base.Umaddl(Op[20, 16], Op[14, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("SMSUBL <Xd>, <Wn>, <Wm>, <Xa>")]
+        public void Smsubl_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(3u, 31u)] uint Ra,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wm,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xa)
+        {
+            uint Opcode = 0x9B208000; // SMSUBL X0, W0, W0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X3: Xa, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                AArch64.X((int)Ra, new Bits(Xa));
+                Base.Smsubl(Op[20, 16], Op[14, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("UMSUBL <Xd>, <Wn>, <Wm>, <Xa>")]
+        public void Umsubl_64bit([Values(0u, 31u)] uint Rd,
+                                 [Values(1u, 31u)] uint Rn,
+                                 [Values(2u, 31u)] uint Rm,
+                                 [Values(3u, 31u)] uint Ra,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wn,
+                                 [Values(0x00000000u, 0x7FFFFFFFu,
+                                         0x80000000u, 0xFFFFFFFFu)] [Random(2)] uint Wm,
+                                 [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                         0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(2)] ulong Xa)
+        {
+            uint Opcode = 0x9BA08000; // UMSUBL X0, W0, W0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Ra & 31) << 10) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Wn, X2: Wm, X3: Xa, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Wn));
+                AArch64.X((int)Rm, new Bits(Wm));
+                AArch64.X((int)Ra, new Bits(Xa));
+                Base.Umsubl(Op[20, 16], Op[14, 10], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("SMULH <Xd>, <Xn>, <Xm>")]
+        public void Smulh_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(16)] ulong Xn,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(16)] ulong Xm)
+        {
+            uint Opcode = 0x9B407C00; // SMULH X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Smulh(Op[20, 16], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+
+        [Test, Description("UMULH <Xd>, <Xn>, <Xm>")]
+        public void Umulh_64bit([Values(0u, 31u)] uint Rd,
+                                [Values(1u, 31u)] uint Rn,
+                                [Values(2u, 31u)] uint Rm,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(16)] ulong Xn,
+                                [Values(0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                        0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul)] [Random(16)] ulong Xm)
+        {
+            uint Opcode = 0x9BC07C00; // UMULH X0, X0, X0
+            Opcode |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0);
+
+            ulong _X31 = TestContext.CurrentContext.Random.NextULong();
+            AThreadState ThreadState = SingleOpcode(Opcode, X1: Xn, X2: Xm, X31: _X31);
+
+            if (Rd != 31)
+            {
+                Bits Op = new Bits(Opcode);
+
+                AArch64.X((int)Rn, new Bits(Xn));
+                AArch64.X((int)Rm, new Bits(Xm));
+                Base.Umulh(Op[20, 16], Op[9, 5], Op[4, 0]);
+                ulong Xd = AArch64.X(64, (int)Rd).ToUInt64();
+
+                Assert.That((ulong)ThreadState.X0, Is.EqualTo(Xd));
+            }
+            else
+            {
+                Assert.That((ulong)ThreadState.X31, Is.EqualTo(_X31));
+            }
+        }
+#endif
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/CpuTestScalar.cs b/Ryujinx.Tests/Cpu/CpuTestScalar.cs
index a178be272..c7c3aa051 100644
--- a/Ryujinx.Tests/Cpu/CpuTestScalar.cs
+++ b/Ryujinx.Tests/Cpu/CpuTestScalar.cs
@@ -5,24 +5,52 @@ namespace Ryujinx.Tests.Cpu
 {
     public class CpuTestScalar : CpuTest
     {
-        [TestCase(0x00000000u, 0x80000000u, 0x00000000u)]
-        [TestCase(0x80000000u, 0x00000000u, 0x00000000u)]
-        [TestCase(0x80000000u, 0x80000000u, 0x80000000u)]
-        [TestCase(0x3DCCCCCDu, 0x3C9623B1u, 0x3DCCCCCDu)]
-        [TestCase(0x8BA98D27u, 0x00000076u, 0x00000076u)]
-        [TestCase(0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu)]
-        [TestCase(0x7F7FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu)]
-        [TestCase(0x7FC00000u, 0x3F800000u, 0x7FC00000u)]
-        [TestCase(0x3F800000u, 0x7FC00000u, 0x7FC00000u)]
-        [TestCase(0x7F800001u, 0x7FC00042u, 0x7FC00001u)]
-        [TestCase(0x7FC00042u, 0x7F800001u, 0x7FC00001u)]
-        [TestCase(0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Au)]
-        public void Fmax_S(uint A, uint B, uint Result)
+        [TestCase(0x1E224820u, 0x0000000000000000ul, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0x1E224820u, 0x0000000080000000ul, 0x0000000000000000ul, 0x0000000000000000ul)]
+        [TestCase(0x1E224820u, 0x0000000080000000ul, 0x0000000080000000ul, 0x0000000080000000ul)]
+        [TestCase(0x1E224820u, 0x0000000080000000ul, 0x000000003DCCCCCDul, 0x000000003DCCCCCDul)]
+        [TestCase(0x1E224820u, 0x000000003DCCCCCDul, 0x000000003C9623B1ul, 0x000000003DCCCCCDul)]
+        [TestCase(0x1E224820u, 0x000000008BA98D27ul, 0x0000000000000076ul, 0x0000000000000076ul)]
+        [TestCase(0x1E224820u, 0x00000000807FFFFFul, 0x000000007F7FFFFFul, 0x000000007F7FFFFFul)]
+        [TestCase(0x1E224820u, 0x000000007F7FFFFFul, 0x00000000807FFFFFul, 0x000000007F7FFFFFul)]
+        [TestCase(0x1E224820u, 0x000000007FC00000ul, 0x000000003F800000ul, 0x000000007FC00000ul)]
+        [TestCase(0x1E224820u, 0x000000003F800000ul, 0x000000007FC00000ul, 0x000000007FC00000ul)]
+        [TestCase(0x1E224820u, 0x000000007F800001ul, 0x000000007FC00042ul, 0x000000007FC00001ul, Ignore = "NaN test.")]
+        [TestCase(0x1E224820u, 0x000000007FC00042ul, 0x000000007F800001ul, 0x000000007FC00001ul, Ignore = "NaN test.")]
+        [TestCase(0x1E224820u, 0x000000007FC0000Aul, 0x000000007FC0000Bul, 0x000000007FC0000Aul, Ignore = "NaN test.")]
+        [TestCase(0x1E624820u, 0x0000000000000000ul, 0x8000000000000000ul, 0x0000000000000000ul)]
+        [TestCase(0x1E624820u, 0x8000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)]
+        [TestCase(0x1E624820u, 0x8000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)]
+        [TestCase(0x1E624820u, 0x8000000000000000ul, 0x3FF3333333333333ul, 0x3FF3333333333333ul)]
+        public void Fmax_S(uint Opcode, ulong A, ulong B, ulong Result)
         {
             // FMAX S0, S1, S2
-            uint Opcode = 0x1E224820;
-            AThreadState ThreadState = SingleOpcode(Opcode, V1: new AVec { W0 = A }, V2: new AVec { W0 = B });
-            Assert.AreEqual(Result, ThreadState.V0.W0);
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: new AVec { X0 = A }, V2: new AVec { X0 = B });
+            Assert.AreEqual(Result, ThreadState.V0.X0);
+        }
+
+        [TestCase(0x1E225820u, 0x0000000000000000ul, 0x0000000080000000ul, 0x0000000080000000ul)]
+        [TestCase(0x1E225820u, 0x0000000080000000ul, 0x0000000000000000ul, 0x0000000080000000ul)]
+        [TestCase(0x1E225820u, 0x0000000080000000ul, 0x0000000080000000ul, 0x0000000080000000ul)]
+        [TestCase(0x1E225820u, 0x0000000080000000ul, 0x000000003DCCCCCDul, 0x0000000080000000ul)]
+        [TestCase(0x1E225820u, 0x000000003DCCCCCDul, 0x000000003C9623B1ul, 0x000000003C9623B1ul)]
+        [TestCase(0x1E225820u, 0x000000008BA98D27ul, 0x0000000000000076ul, 0x000000008BA98D27ul)]
+        [TestCase(0x1E225820u, 0x00000000807FFFFFul, 0x000000007F7FFFFFul, 0x00000000807FFFFFul)]
+        [TestCase(0x1E225820u, 0x000000007F7FFFFFul, 0x00000000807FFFFFul, 0x00000000807FFFFFul)]
+        [TestCase(0x1E225820u, 0x000000007FC00000ul, 0x000000003F800000ul, 0x000000007FC00000ul)]
+        [TestCase(0x1E225820u, 0x000000003F800000ul, 0x000000007FC00000ul, 0x000000007FC00000ul)]
+        [TestCase(0x1E225820u, 0x000000007F800001ul, 0x000000007FC00042ul, 0x000000007FC00001ul, Ignore = "NaN test.")]
+        [TestCase(0x1E225820u, 0x000000007FC00042ul, 0x000000007F800001ul, 0x000000007FC00001ul, Ignore = "NaN test.")]
+        [TestCase(0x1E225820u, 0x000000007FC0000Aul, 0x000000007FC0000Bul, 0x000000007FC0000Aul, Ignore = "NaN test.")]
+        [TestCase(0x1E625820u, 0x0000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)]
+        [TestCase(0x1E625820u, 0x8000000000000000ul, 0x0000000000000000ul, 0x8000000000000000ul)]
+        [TestCase(0x1E625820u, 0x8000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)]
+        [TestCase(0x1E625820u, 0x8000000000000000ul, 0x3FF3333333333333ul, 0x8000000000000000ul)]
+        public void Fmin_S(uint Opcode, ulong A, ulong B, ulong Result)
+        {
+            // FMIN S0, S1, S2
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: new AVec { X0 = A }, V2: new AVec { X0 = B });
+            Assert.AreEqual(Result, ThreadState.V0.X0);
         }
     }
 }
diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs
new file mode 100644
index 000000000..5fa3a72df
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs
@@ -0,0 +1,242 @@
+#define Simd
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+    using Tester;
+    using Tester.Types;
+
+    [Category("Simd")]
+    public sealed class CpuTestSimd : CpuTest
+    {
+#if Simd
+        [SetUp]
+        public void SetupTester()
+        {
+            AArch64.TakeReset(false);
+        }
+
+#region "ValueSource"
+        private static ulong[] _1D_()
+        {
+            return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
+        }
+
+        private static ulong[] _8B4H_()
+        {
+            return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful,
+                                 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul,
+                                 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul };
+        }
+
+        private static ulong[] _8B4H2S_()
+        {
+            return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful,
+                                 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul,
+                                 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul,
+                                 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul };
+        }
+
+        private static ulong[] _8B4H2S1D_()
+        {
+            return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful,
+                                 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul,
+                                 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul,
+                                 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
+        }
+#endregion
+
+        [Test, Description("ABS <V><d>, <V><n>")]
+        public void Abs_S_D([ValueSource("_1D_")] [Random(1)] ulong A)
+        {
+            uint Opcode = 0x5EE0B820; // ABS D0, D1
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
+
+            AArch64.V(1, new Bits(A));
+            SimdFp.Abs_S(Op[23, 22], Op[9, 5], Op[4, 0]);
+
+            Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+            Assert.That(ThreadState.V0.X1, Is.Zero);
+        }
+
+        [Test, Description("ABS <Vd>.<T>, <Vn>.<T>")]
+        public void Abs_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A,
+                                   [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S>
+        {
+            uint Opcode = 0x0E20B820; // ABS V0.8B, V1.8B
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
+
+            AArch64.V(1, new Bits(A));
+            SimdFp.Abs_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
+
+            Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+            Assert.That(ThreadState.V0.X1, Is.Zero);
+        }
+
+        [Test, Pairwise, Description("ABS <Vd>.<T>, <Vn>.<T>")]
+        public void Abs_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0,
+                                       [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1,
+                                       [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D>
+        {
+            uint Opcode = 0x4E20B820; // ABS V0.16B, V1.16B
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            SimdFp.Abs_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
+                Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+            });
+        }
+
+        [Test, Pairwise, Description("ADDP <V><d>, <Vn>.<T>")]
+        public void Addp_S_2DD([ValueSource("_1D_")] [Random(1)] ulong A0,
+                               [ValueSource("_1D_")] [Random(1)] ulong A1)
+        {
+            uint Opcode = 0x5EF1B820; // ADDP D0, V1.2D
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            SimdFp.Addp_S(Op[23, 22], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+                Assert.That(ThreadState.V0.X1, Is.Zero);
+            });
+        }
+
+        [Test, Description("ADDV <V><d>, <Vn>.<T>")]
+        public void Addv_V_8BB_4HH([ValueSource("_8B4H_")] [Random(1)] ulong A,
+                                   [Values(0b00u, 0b01u)] uint size) // <8B, 4H>
+        {
+            uint Opcode = 0x0E31B820; // ADDV B0, V1.8B
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X0 = TestContext.CurrentContext.Random.NextULong(),
+                                 X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
+
+            AArch64.Vpart(0, 0, new Bits(TestContext.CurrentContext.Random.NextULong()));
+            AArch64.V(1, new Bits(A));
+            SimdFp.Addv_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
+
+            Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+            Assert.That(ThreadState.V0.X1, Is.Zero);
+        }
+
+        [Test, Pairwise, Description("ADDV <V><d>, <Vn>.<T>")]
+        public void Addv_V_16BB_8HH_4SS([ValueSource("_8B4H2S_")] [Random(1)] ulong A0,
+                                        [ValueSource("_8B4H2S_")] [Random(1)] ulong A1,
+                                        [Values(0b00u, 0b01u, 0b10u)] uint size) // <16B, 8H, 4S>
+        {
+            uint Opcode = 0x4E31B820; // ADDV B0, V1.16B
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X0 = TestContext.CurrentContext.Random.NextULong(),
+                                 X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
+
+            AArch64.Vpart(0, 0, new Bits(TestContext.CurrentContext.Random.NextULong()));
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            SimdFp.Addv_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+                Assert.That(ThreadState.V0.X1, Is.Zero);
+            });
+        }
+
+        [Test, Description("NEG <V><d>, <V><n>")]
+        public void Neg_S_D([ValueSource("_1D_")] [Random(1)] ulong A)
+        {
+            uint Opcode = 0x7EE0B820; // NEG D0, D1
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
+
+            AArch64.V(1, new Bits(A));
+            SimdFp.Neg_S(Op[23, 22], Op[9, 5], Op[4, 0]);
+
+            Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+            Assert.That(ThreadState.V0.X1, Is.Zero);
+        }
+
+        [Test, Description("NEG <Vd>.<T>, <Vn>.<T>")]
+        public void Neg_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A,
+                                   [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S>
+        {
+            uint Opcode = 0x2E20B820; // NEG V0.8B, V1.8B
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1);
+
+            AArch64.V(1, new Bits(A));
+            SimdFp.Neg_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
+
+            Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+            Assert.That(ThreadState.V0.X1, Is.Zero);
+        }
+
+        [Test, Pairwise, Description("NEG <Vd>.<T>, <Vn>.<T>")]
+        public void Neg_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0,
+                                       [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1,
+                                       [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D>
+        {
+            uint Opcode = 0x6E20B820; // NEG V0.16B, V1.16B
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            SimdFp.Neg_V(Op[30], Op[23, 22], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
+                Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+            });
+        }
+#endif
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs
index 56aaef488..f32fe398d 100644
--- a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs
+++ b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs
@@ -5,6 +5,583 @@ namespace Ryujinx.Tests.Cpu
 {
     public class CpuTestSimdArithmetic : CpuTest
     {
+        [TestCase(0xE228420u,   0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)]
+        [TestCase(0xE228420u,   0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x00000000FFFFFF00ul, 0x0000000000000000ul)]
+        [TestCase(0xE228420u,   0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFEFEFEFEFEFEFEFEul, 0x0000000000000000ul)]
+        [TestCase(0xE228420u,   0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0x0000000000000000ul)]
+        [TestCase(0x4E228420u,  0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)]
+        [TestCase(0x4E228420u,  0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x00000000FFFFFF00ul, 0x00000000FFFFFF00ul)]
+        [TestCase(0x4E228420u,  0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFEFEFEFEFEFEFEFEul, 0xFEFEFEFEFEFEFEFEul)]
+        [TestCase(0x4E228420u,  0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0xCCCCCCCCCCCCCCCCul)]
+        [TestCase(0xE628420u,   0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)]
+        [TestCase(0xE628420u,   0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x00000000FFFF0000ul, 0x0000000000000000ul)]
+        [TestCase(0xE628420u,   0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFEFFFEFFFEFFFEul, 0x0000000000000000ul)]
+        [TestCase(0xE628420u,   0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0x0000000000000000ul)]
+        [TestCase(0x4E628420u,  0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)]
+        [TestCase(0x4E628420u,  0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x00000000FFFF0000ul, 0x00000000FFFF0000ul)]
+        [TestCase(0x4E628420u,  0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFEFFFEFFFEFFFEul, 0xFFFEFFFEFFFEFFFEul)]
+        [TestCase(0x4E628420u,  0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0xCCCCCCCCCCCCCCCCul)]
+        [TestCase(0xEA28420u,   0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)]
+        [TestCase(0xEA28420u,   0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x0000000000000000ul, 0x0000000000000000ul)]
+        [TestCase(0xEA28420u,   0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFEFFFFFFFEul, 0x0000000000000000ul)]
+        [TestCase(0xEA28420u,   0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0x0000000000000000ul)]
+        [TestCase(0x4EA28420u,  0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)]
+        [TestCase(0x4EA28420u,  0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x0000000000000000ul, 0x0000000000000000ul)]
+        [TestCase(0x4EA28420u,  0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFEFFFFFFFEul, 0xFFFFFFFEFFFFFFFEul)]
+        [TestCase(0x4EA28420u,  0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0xCCCCCCCCCCCCCCCCul)]
+        [TestCase(0x4EE28420u,  0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)]
+        [TestCase(0x4EE28420u,  0x00000000FFFFFFFFul, 0x00000000FFFFFFFFul, 0x0000000000000001ul, 0x0000000000000001ul, 0x0000000100000000ul, 0x0000000100000000ul)]
+        [TestCase(0x4EE28420u,  0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFEul, 0xFFFFFFFFFFFFFFFEul)]
+        [TestCase(0x4EE28420u,  0x0102030405060708ul, 0xAAAAAAAAAAAAAAAAul, 0x0807060504030201ul, 0x2222222222222222ul, 0x0909090909090909ul, 0xCCCCCCCCCCCCCCCCul)]
+        public void Add_V(uint Opcode, ulong A0, ulong A1, ulong B0, ulong B1, ulong Result0, ulong Result1)
+        {
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AVec V2 = new AVec { X0 = B0, X1 = B1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
+            Assert.Multiple(() =>
+            {
+                Assert.AreEqual(Result0, ThreadState.V0.X0);
+                Assert.AreEqual(Result1, ThreadState.V0.X1);
+            });
+        }
+
+        [TestCase(0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u)]
+        [TestCase(0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u)]
+        [TestCase(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u)]
+        [TestCase(0x80000000u, 0x80000000u, 0x3DCCCCCDu, 0x3DCCCCCDu, 0x3DCCCCCDu, 0x3DCCCCCDu)]
+        [TestCase(0x3DCCCCCDu, 0x3DCCCCCDu, 0x3C9623B1u, 0x3C9623B1u, 0x3DCCCCCDu, 0x3DCCCCCDu)]
+        [TestCase(0x8BA98D27u, 0x8BA98D27u, 0x00000076u, 0x00000076u, 0x00000076u, 0x00000076u)]
+        [TestCase(0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu)]
+        [TestCase(0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu)]
+        [TestCase(0x7FC00000u, 0x7FC00000u, 0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u)]
+        [TestCase(0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u)]
+        [TestCase(0x7F800001u, 0x7F800001u, 0x7FC00042u, 0x7FC00042u, 0x7FC00001u, 0x7FC00001u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00042u, 0x7FC00042u, 0x7F800001u, 0x7F800001u, 0x7FC00001u, 0x7FC00001u, Ignore = "NaN test.")]
+        [TestCase(0x7FC0000Au, 0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Bu, 0x7FC0000Au, 0x7FC0000Au, Ignore = "NaN test.")]
+        public void Fmax_V(uint A, uint B, uint C, uint D, uint Result0, uint Result1)
+        {
+            uint Opcode = 0x4E22F420;
+            AVec V1 = new AVec { X0 = A, X1 = B };
+            AVec V2 = new AVec { X0 = C, X1 = D };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
+            Assert.Multiple(() =>
+            {
+                Assert.AreEqual(Result0, ThreadState.V0.X0);
+                Assert.AreEqual(Result1, ThreadState.V0.X1);
+            });
+        }
+
+        [TestCase(0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u)]
+        [TestCase(0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u)]
+        [TestCase(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u)]
+        [TestCase(0x80000000u, 0x80000000u, 0x3DCCCCCDu, 0x3DCCCCCDu, 0x80000000u, 0x80000000u)]
+        [TestCase(0x3DCCCCCDu, 0x3DCCCCCDu, 0x3C9623B1u, 0x3C9623B1u, 0x3C9623B1u, 0x3C9623B1u)]
+        [TestCase(0x8BA98D27u, 0x8BA98D27u, 0x00000076u, 0x00000076u, 0x8BA98D27u, 0x8BA98D27u)]
+        [TestCase(0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu)]
+        [TestCase(0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x807FFFFFu)]
+        [TestCase(0x7FC00000u, 0x7FC00000u, 0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u)]
+        [TestCase(0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u)]
+        [TestCase(0x7F800001u, 0x7F800001u, 0x7FC00042u, 0x7FC00042u, 0x7FC00001u, 0x7FC00001u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00042u, 0x7FC00042u, 0x7F800001u, 0x7F800001u, 0x7FC00001u, 0x7FC00001u, Ignore = "NaN test.")]
+        [TestCase(0x7FC0000Au, 0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Bu, 0x7FC0000Au, 0x7FC0000Au, Ignore = "NaN test.")]
+        public void Fmin_V(uint A, uint B, uint C, uint D, uint Result0, uint Result1)
+        {
+            uint Opcode = 0x4EA2F420;
+            AVec V1 = new AVec { X0 = A, X1 = B };
+            AVec V2 = new AVec { X0 = C, X1 = D };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
+            Assert.Multiple(() =>
+            {
+                Assert.AreEqual(Result0, ThreadState.V0.X0);
+                Assert.AreEqual(Result1, ThreadState.V0.X1);
+            });
+        }
+
+        [Test, Description("fmul s6, s1, v0.s[2]")]
+        public void Fmul_Se([Random(10)] float A, [Random(10)] float B)
+        {
+            AThreadState ThreadState = SingleOpcode(0x5F809826, V1: new AVec { S0 = A }, V0: new AVec { S2 = B });
+
+            Assert.That(ThreadState.V6.S0, Is.EqualTo(A * B));
+        }
+
+        [Test, Description("frecpe v2.4s, v0.4s")]
+        public void Frecpe_V([Random(100)] float A)
+        {
+            AThreadState ThreadState = SingleOpcode(0x4EA1D802, V0: new AVec { S0 = A, S1 = A, S2 = A, S3 = A });
+
+            Assert.That(ThreadState.V2.S0, Is.EqualTo(1 / A));
+            Assert.That(ThreadState.V2.S1, Is.EqualTo(1 / A));
+            Assert.That(ThreadState.V2.S2, Is.EqualTo(1 / A));
+            Assert.That(ThreadState.V2.S3, Is.EqualTo(1 / A));
+        }
+
+        [Test, Description("frecpe d0, d1")]
+        public void Frecpe_S([Random(100)] double A)
+        {
+            AThreadState ThreadState = SingleOpcode(0x5EE1D820, V1: new AVec { D0 = A });
+
+            Assert.That(ThreadState.V0.D0, Is.EqualTo(1 / A));
+        }
+
+        [Test, Description("frecps v4.4s, v2.4s, v0.4s")]
+        public void Frecps_V([Random(10)] float A, [Random(10)] float B)
+        {
+            AThreadState ThreadState = SingleOpcode(0x4E20FC44, V2: new AVec { S0 = A, S1 = A, S2 = A, S3 = A },
+                                                                V0: new AVec { S0 = B, S1 = B, S2 = B, S3 = B });
+
+            Assert.That(ThreadState.V4.S0, Is.EqualTo(2 - (A * B)));
+            Assert.That(ThreadState.V4.S1, Is.EqualTo(2 - (A * B)));
+            Assert.That(ThreadState.V4.S2, Is.EqualTo(2 - (A * B)));
+            Assert.That(ThreadState.V4.S3, Is.EqualTo(2 - (A * B)));
+        }
+
+        [Test, Description("frecps d0, d1, d2")]
+        public void Frecps_S([Random(10)] double A, [Random(10)] double B)
+        {
+            AThreadState ThreadState = SingleOpcode(0x5E62FC20, V1: new AVec { D0 = A }, V2: new AVec { D0 = B });
+
+            Assert.That(ThreadState.V0.D0, Is.EqualTo(2 - (A * B)));
+        }
+    
+        [TestCase(0x3FE66666u, false, 0x40000000u)]
+        [TestCase(0x3F99999Au, false, 0x3F800000u)]
+        [TestCase(0x404CCCCDu, false, 0x40400000u)]
+        [TestCase(0x40733333u, false, 0x40800000u)]
+        [TestCase(0x3fc00000u, false, 0x40000000u)]
+        [TestCase(0x40200000u, false, 0x40400000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        public void Frinta_S(uint A, bool DefaultNaN, uint Result)
+        {
+            int FpcrTemp = 0x0;
+            if(DefaultNaN)
+            {
+                FpcrTemp = 0x2000000;
+            }
+            AVec V1 = new AVec { X0 = A };
+            AThreadState ThreadState = SingleOpcode(0x1E264020, V1: V1, Fpcr: FpcrTemp);
+            Assert.AreEqual(Result, ThreadState.V0.X0);
+        }
+
+        [TestCase(0x6E618820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x6E618820u, 0x3FFCCCCCCCCCCCCDul, 0x3FFCCCCCCCCCCCCDul, false, 0x4000000000000000ul, 0x4000000000000000ul)]
+        [TestCase(0x6E618820u, 0x3FF8000000000000ul, 0x3FF8000000000000ul, false, 0x4000000000000000ul, 0x4000000000000000ul)]
+        [TestCase(0x6E219820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, false, 0x3f80000040000000ul, 0x3f80000040000000ul)]
+        [TestCase(0x6E219820u, 0x3fc000003fc00000ul, 0x3fc000003fc00000ul, false, 0x4000000040000000ul, 0x4000000040000000ul)]    
+        [TestCase(0x2E219820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, false, 0x3f80000040000000ul, 0x0000000000000000ul)]    
+        [TestCase(0x2E219820u, 0x3fc000003fc00000ul, 0x3fc000003fc00000ul, false, 0x4000000040000000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E218820u, 0x0000000080000000ul, 0x0000000000000000ul, false, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E218820u, 0x7F800000FF800000ul, 0x0000000000000000ul, false, 0x7F800000FF800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E218820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2E218820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, true,  0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        public void Frinta_V(uint Opcode, ulong A, ulong B, bool DefaultNaN, ulong Result0, ulong Result1)
+        {
+            int FpcrTemp = 0x0;
+            if(DefaultNaN)
+            {
+                FpcrTemp = 0x2000000;
+            }
+            AVec V1 = new AVec { X0 = A, X1 = B };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp);
+            Assert.Multiple(() =>
+            {
+                Assert.AreEqual(Result0, ThreadState.V0.X0);
+                Assert.AreEqual(Result1, ThreadState.V0.X1);
+            });
+        }
+
+        [TestCase(0x3FE66666u, 'N', false, 0x40000000u)]
+        [TestCase(0x3F99999Au, 'N', false, 0x3F800000u)]
+        [TestCase(0x404CCCCDu, 'P', false, 0x40800000u)]
+        [TestCase(0x40733333u, 'P', false, 0x40800000u)]
+        [TestCase(0x404CCCCDu, 'M', false, 0x40400000u)]
+        [TestCase(0x40733333u, 'M', false, 0x40400000u)]
+        [TestCase(0x3F99999Au, 'Z', false, 0x3F800000u)]
+        [TestCase(0x3FE66666u, 'Z', false, 0x3F800000u)]
+        [TestCase(0x00000000u, 'N', false, 0x00000000u)]
+        [TestCase(0x00000000u, 'P', false, 0x00000000u)]
+        [TestCase(0x00000000u, 'M', false, 0x00000000u)]
+        [TestCase(0x00000000u, 'Z', false, 0x00000000u)]
+        [TestCase(0x80000000u, 'N', false, 0x80000000u)]
+        [TestCase(0x80000000u, 'P', false, 0x80000000u)]
+        [TestCase(0x80000000u, 'M', false, 0x80000000u)]
+        [TestCase(0x80000000u, 'Z', false, 0x80000000u)]
+        [TestCase(0x7F800000u, 'N', false, 0x7F800000u)]
+        [TestCase(0x7F800000u, 'P', false, 0x7F800000u)]
+        [TestCase(0x7F800000u, 'M', false, 0x7F800000u)]
+        [TestCase(0x7F800000u, 'Z', false, 0x7F800000u)]
+        [TestCase(0xFF800000u, 'N', false, 0xFF800000u)]
+        [TestCase(0xFF800000u, 'P', false, 0xFF800000u)]
+        [TestCase(0xFF800000u, 'M', false, 0xFF800000u)]
+        [TestCase(0xFF800000u, 'Z', false, 0xFF800000u)]
+        [TestCase(0xFF800001u, 'N', false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, 'P', false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, 'M', false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, 'Z', false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, 'N', true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, 'P', true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, 'M', true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, 'Z', true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, 'N', false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, 'P', false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, 'M', false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, 'Z', false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, 'N', true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, 'P', true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, 'M', true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, 'Z', true,  0x7FC00000u, Ignore = "NaN test.")]
+        public void Frinti_S(uint A, char RoundType, bool DefaultNaN, uint Result)
+        {
+            int FpcrTemp = 0x0;
+            switch(RoundType)
+            {
+                case 'N':
+                FpcrTemp = 0x0;
+                break;
+
+                case 'P':
+                FpcrTemp = 0x400000;
+                break;
+
+                case 'M':
+                FpcrTemp = 0x800000;
+                break;
+
+                case 'Z':
+                FpcrTemp = 0xC00000;
+                break;
+            }
+            if(DefaultNaN)
+            {
+                FpcrTemp |= 1 << 25;
+            }
+            AVec V1 = new AVec { X0 = A };
+            AThreadState ThreadState = SingleOpcode(0x1E27C020, V1: V1, Fpcr: FpcrTemp);
+            Assert.AreEqual(Result, ThreadState.V0.X0);
+        }
+
+        [TestCase(0x6EE19820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, 'N', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x6EE19820u, 0x3FFCCCCCCCCCCCCDul, 0x3FFCCCCCCCCCCCCDul, 'N', false, 0x4000000000000000ul, 0x4000000000000000ul)]
+        [TestCase(0x6EE19820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, 'P', false, 0x4000000000000000ul, 0x4000000000000000ul)]
+        [TestCase(0x6EE19820u, 0x3FFCCCCCCCCCCCCDul, 0x3FFCCCCCCCCCCCCDul, 'P', false, 0x4000000000000000ul, 0x4000000000000000ul)]
+        [TestCase(0x6EE19820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, 'M', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x6EE19820u, 0x3FFCCCCCCCCCCCCDul, 0x3FFCCCCCCCCCCCCDul, 'M', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x6EE19820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, 'Z', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x6EE19820u, 0x3FFCCCCCCCCCCCCDul, 0x3FFCCCCCCCCCCCCDul, 'Z', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x6EA19820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'N', false, 0x3f80000040000000ul, 0x3f80000040000000ul)]
+        [TestCase(0x6EA19820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'P', false, 0x4000000040000000ul, 0x4000000040000000ul)]    
+        [TestCase(0x6EA19820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'M', false, 0x3f8000003f800000ul, 0x3f8000003f800000ul)]
+        [TestCase(0x6EA19820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'Z', false, 0x3f8000003f800000ul, 0x3f8000003f800000ul)]
+        [TestCase(0x2EA19820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'N', false, 0x3f80000040000000ul, 0x0000000000000000ul)]
+        [TestCase(0x2EA19820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'P', false, 0x4000000040000000ul, 0x0000000000000000ul)]    
+        [TestCase(0x2EA19820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'M', false, 0x3f8000003f800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2EA19820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'Z', false, 0x3f8000003f800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2EA19820u, 0x0000000080000000ul, 0x0000000000000000ul, 'N', false, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0x2EA19820u, 0x0000000080000000ul, 0x0000000000000000ul, 'P', false, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0x2EA19820u, 0x0000000080000000ul, 0x0000000000000000ul, 'M', false, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0x2EA19820u, 0x0000000080000000ul, 0x0000000000000000ul, 'Z', false, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0x2EA19820u, 0x7F800000FF800000ul, 0x0000000000000000ul, 'N', false, 0x7F800000FF800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2EA19820u, 0x7F800000FF800000ul, 0x0000000000000000ul, 'P', false, 0x7F800000FF800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2EA19820u, 0x7F800000FF800000ul, 0x0000000000000000ul, 'M', false, 0x7F800000FF800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2EA19820u, 0x7F800000FF800000ul, 0x0000000000000000ul, 'Z', false, 0x7F800000FF800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2EA19820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'N', false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2EA19820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'P', false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2EA19820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'M', false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2EA19820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'Z', false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2EA19820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'N', true,  0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2EA19820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'P', true,  0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2EA19820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'M', true,  0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2EA19820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'Z', true,  0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        public void Frinti_V(uint Opcode, ulong A, ulong B, char RoundType, bool DefaultNaN, ulong Result0, ulong Result1)
+        {
+            int FpcrTemp = 0x0;
+            switch(RoundType)
+            {
+                case 'N':
+                FpcrTemp = 0x0;
+                break;
+
+                case 'P':
+                FpcrTemp = 0x400000;
+                break;
+
+                case 'M':
+                FpcrTemp = 0x800000;
+                break;
+
+                case 'Z':
+                FpcrTemp = 0xC00000;
+                break;
+            }
+            if(DefaultNaN)
+            {
+                FpcrTemp |= 1 << 25;
+            }
+            AVec V1 = new AVec { X0 = A, X1 = B };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp);
+            Assert.Multiple(() =>
+            {
+                Assert.AreEqual(Result0, ThreadState.V0.X0);
+                Assert.AreEqual(Result1, ThreadState.V0.X1);
+            });
+        }
+
+        [TestCase(0x3FE66666u, false, 0x3F800000u)]
+        [TestCase(0x3F99999Au, false, 0x3F800000u)]
+        [TestCase(0x404CCCCDu, false, 0x40400000u)]
+        [TestCase(0x40733333u, false, 0x40400000u)]
+        [TestCase(0x3fc00000u, false, 0x3F800000u)]
+        [TestCase(0x40200000u, false, 0x40000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        public void Frintm_S(uint A, bool DefaultNaN, uint Result)
+        {
+            int FpcrTemp = 0x0;
+            if(DefaultNaN)
+            {
+                FpcrTemp = 0x2000000;
+            }
+            AVec V1 = new AVec { X0 = A };
+            AThreadState ThreadState = SingleOpcode(0x1E254020, V1: V1, Fpcr: FpcrTemp);
+            Assert.AreEqual(Result, ThreadState.V0.X0);
+        }
+
+        [TestCase(0x4E619820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x4E619820u, 0x3FFCCCCCCCCCCCCDul, 0x3FFCCCCCCCCCCCCDul, false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x4E219820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, false, 0x3f8000003f800000ul, 0x3f8000003f800000ul)]    
+        [TestCase(0xE219820u,  0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, false, 0x3f8000003f800000ul, 0x0000000000000000ul)]    
+        [TestCase(0xE219820u,  0x0000000080000000ul, 0x0000000000000000ul, false, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0xE219820u,  0x7F800000FF800000ul, 0x0000000000000000ul, false, 0x7F800000FF800000ul, 0x0000000000000000ul)]
+        [TestCase(0xE219820u,  0xFF8000017FC00002ul, 0x0000000000000000ul, false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0xE219820u,  0xFF8000017FC00002ul, 0x0000000000000000ul, true,  0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        public void Frintm_V(uint Opcode, ulong A, ulong B, bool DefaultNaN, ulong Result0, ulong Result1)
+        {
+            int FpcrTemp = 0x0;
+            if(DefaultNaN)
+            {
+                FpcrTemp = 0x2000000;
+            }
+            AVec V1 = new AVec { X0 = A, X1 = B };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp);
+            Assert.Multiple(() =>
+            {
+                Assert.AreEqual(Result0, ThreadState.V0.X0);
+                Assert.AreEqual(Result1, ThreadState.V0.X1);
+            });
+        }
+
+        [TestCase(0x3FE66666u, false, 0x40000000u)]
+        [TestCase(0x3F99999Au, false, 0x3F800000u)]
+        [TestCase(0x404CCCCDu, false, 0x40400000u)]
+        [TestCase(0x40733333u, false, 0x40800000u)]
+        [TestCase(0x3fc00000u, false, 0x40000000u)]
+        [TestCase(0x40200000u, false, 0x40400000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        public void Frintn_S(uint A, bool DefaultNaN, uint Result)
+        {
+            int FpcrTemp = 0x0;
+            if(DefaultNaN)
+            {
+                FpcrTemp = 0x2000000;
+            }
+            AVec V1 = new AVec { X0 = A };
+            AThreadState ThreadState = SingleOpcode(0x1E264020, V1: V1, Fpcr: FpcrTemp);
+            Assert.AreEqual(Result, ThreadState.V0.X0);
+        }
+
+        [TestCase(0x4E618820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x4E618820u, 0x3FFCCCCCCCCCCCCDul, 0x3FFCCCCCCCCCCCCDul, false, 0x4000000000000000ul, 0x4000000000000000ul)]
+        [TestCase(0x4E618820u, 0x3FF8000000000000ul, 0x3FF8000000000000ul, false, 0x4000000000000000ul, 0x4000000000000000ul)]
+        [TestCase(0x4E218820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, false, 0x3f80000040000000ul, 0x3f80000040000000ul)]
+        [TestCase(0x4E218820u, 0x3fc000003fc00000ul, 0x3fc000003fc00000ul, false, 0x4000000040000000ul, 0x4000000040000000ul)]    
+        [TestCase(0xE218820u,  0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, false, 0x3f80000040000000ul, 0x0000000000000000ul)]    
+        [TestCase(0xE218820u,  0x3fc000003fc00000ul, 0x3fc000003fc00000ul, false, 0x4000000040000000ul, 0x0000000000000000ul)]
+        [TestCase(0xE218820u,  0x0000000080000000ul, 0x0000000000000000ul, false, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0xE218820u,  0x7F800000FF800000ul, 0x0000000000000000ul, false, 0x7F800000FF800000ul, 0x0000000000000000ul)]
+        [TestCase(0xE218820u,  0xFF8000017FC00002ul, 0x0000000000000000ul, false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0xE218820u,  0xFF8000017FC00002ul, 0x0000000000000000ul, true,  0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        public void Frintn_V(uint Opcode, ulong A, ulong B, bool DefaultNaN, ulong Result0, ulong Result1)
+        {
+            int FpcrTemp = 0x0;
+            if(DefaultNaN)
+            {
+                FpcrTemp = 0x2000000;
+            }
+            AVec V1 = new AVec { X0 = A, X1 = B };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp);
+            Assert.Multiple(() =>
+            {
+                Assert.AreEqual(Result0, ThreadState.V0.X0);
+                Assert.AreEqual(Result1, ThreadState.V0.X1);
+            });
+        }
+
+        [TestCase(0x3FE66666u, false, 0x40000000u)]
+        [TestCase(0x3F99999Au, false, 0x40000000u)]
+        [TestCase(0x404CCCCDu, false, 0x40800000u)]
+        [TestCase(0x40733333u, false, 0x40800000u)]
+        [TestCase(0x3fc00000u, false, 0x40000000u)]
+        [TestCase(0x40200000u, false, 0x40400000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x00000000u, false, 0x00000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x80000000u, false, 0x80000000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0x7F800000u, false, 0x7F800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800000u, false, 0xFF800000u)]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, false, 0xFFC00001u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0xFF800001u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, false, 0x7FC00002u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        [TestCase(0x7FC00002u, true,  0x7FC00000u, Ignore = "NaN test.")]
+        public void Frintp_S(uint A, bool DefaultNaN, uint Result)
+        {
+            int FpcrTemp = 0x0;
+            if(DefaultNaN)
+            {
+                FpcrTemp = 0x2000000;
+            }
+            AVec V1 = new AVec { X0 = A };
+            AThreadState ThreadState = SingleOpcode(0x1E24C020, V1: V1, Fpcr: FpcrTemp);
+            Assert.AreEqual(Result, ThreadState.V0.X0);
+        }
+
+        [TestCase(0x4EE18820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, false, 0x4000000000000000ul, 0x4000000000000000ul)]
+        [TestCase(0x4EE18820u, 0x3FFCCCCCCCCCCCCDul, 0x3FFCCCCCCCCCCCCDul, false, 0x4000000000000000ul, 0x4000000000000000ul)]
+        [TestCase(0x4EA18820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, false, 0x4000000040000000ul, 0x4000000040000000ul)]    
+        [TestCase(0xEA18820u,  0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, false, 0x4000000040000000ul, 0x0000000000000000ul)]    
+        [TestCase(0xEA18820u,  0x0000000080000000ul, 0x0000000000000000ul, false, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0xEA18820u,  0x7F800000FF800000ul, 0x0000000000000000ul, false, 0x7F800000FF800000ul, 0x0000000000000000ul)]
+        [TestCase(0xEA18820u,  0xFF8000017FC00002ul, 0x0000000000000000ul, false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0xEA18820u,  0xFF8000017FC00002ul, 0x0000000000000000ul, true,  0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        public void Frintp_V(uint Opcode, ulong A, ulong B, bool DefaultNaN, ulong Result0, ulong Result1)
+        {
+            int FpcrTemp = 0x0;
+            if(DefaultNaN)
+            {
+                FpcrTemp = 0x2000000;
+            }
+            AVec V1 = new AVec { X0 = A, X1 = B };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp);
+            Assert.Multiple(() =>
+            {
+                Assert.AreEqual(Result0, ThreadState.V0.X0);
+                Assert.AreEqual(Result1, ThreadState.V0.X1);
+            });
+        }
+
     	[TestCase(0x3FE66666u, 'N', false, 0x40000000u)]
     	[TestCase(0x3F99999Au, 'N', false, 0x3F800000u)]
     	[TestCase(0x404CCCCDu, 'P', false, 0x40800000u)]
@@ -29,22 +606,22 @@ namespace Ryujinx.Tests.Cpu
     	[TestCase(0xFF800000u, 'P', false, 0xFF800000u)]
     	[TestCase(0xFF800000u, 'M', false, 0xFF800000u)]
     	[TestCase(0xFF800000u, 'Z', false, 0xFF800000u)]
-    	[TestCase(0xFF800001u, 'N', false, 0xFFC00001u)]
-    	[TestCase(0xFF800001u, 'P', false, 0xFFC00001u)]
-    	[TestCase(0xFF800001u, 'M', false, 0xFFC00001u)]
-    	[TestCase(0xFF800001u, 'Z', false, 0xFFC00001u)]
-    	[TestCase(0xFF800001u, 'N', true,  0x7FC00000u)]
-    	[TestCase(0xFF800001u, 'P', true,  0x7FC00000u)]
-    	[TestCase(0xFF800001u, 'M', true,  0x7FC00000u)]
-    	[TestCase(0xFF800001u, 'Z', true,  0x7FC00000u)]
-    	[TestCase(0x7FC00002u, 'N', false, 0x7FC00002u)]
-    	[TestCase(0x7FC00002u, 'P', false, 0x7FC00002u)]
-    	[TestCase(0x7FC00002u, 'M', false, 0x7FC00002u)]
-    	[TestCase(0x7FC00002u, 'Z', false, 0x7FC00002u)]
-    	[TestCase(0x7FC00002u, 'N', true,  0x7FC00000u)]
-    	[TestCase(0x7FC00002u, 'P', true,  0x7FC00000u)]
-    	[TestCase(0x7FC00002u, 'M', true,  0x7FC00000u)]
-    	[TestCase(0x7FC00002u, 'Z', true,  0x7FC00000u)]
+    	[TestCase(0xFF800001u, 'N', false, 0xFFC00001u, Ignore = "NaN test.")]
+    	[TestCase(0xFF800001u, 'P', false, 0xFFC00001u, Ignore = "NaN test.")]
+    	[TestCase(0xFF800001u, 'M', false, 0xFFC00001u, Ignore = "NaN test.")]
+    	[TestCase(0xFF800001u, 'Z', false, 0xFFC00001u, Ignore = "NaN test.")]
+    	[TestCase(0xFF800001u, 'N', true,  0x7FC00000u, Ignore = "NaN test.")]
+    	[TestCase(0xFF800001u, 'P', true,  0x7FC00000u, Ignore = "NaN test.")]
+    	[TestCase(0xFF800001u, 'M', true,  0x7FC00000u, Ignore = "NaN test.")]
+    	[TestCase(0xFF800001u, 'Z', true,  0x7FC00000u, Ignore = "NaN test.")]
+    	[TestCase(0x7FC00002u, 'N', false, 0x7FC00002u, Ignore = "NaN test.")]
+    	[TestCase(0x7FC00002u, 'P', false, 0x7FC00002u, Ignore = "NaN test.")]
+    	[TestCase(0x7FC00002u, 'M', false, 0x7FC00002u, Ignore = "NaN test.")]
+    	[TestCase(0x7FC00002u, 'Z', false, 0x7FC00002u, Ignore = "NaN test.")]
+    	[TestCase(0x7FC00002u, 'N', true,  0x7FC00000u, Ignore = "NaN test.")]
+    	[TestCase(0x7FC00002u, 'P', true,  0x7FC00000u, Ignore = "NaN test.")]
+    	[TestCase(0x7FC00002u, 'M', true,  0x7FC00000u, Ignore = "NaN test.")]
+    	[TestCase(0x7FC00002u, 'Z', true,  0x7FC00000u, Ignore = "NaN test.")]
     	public void Frintx_S(uint A, char RoundType, bool DefaultNaN, uint Result)
     	{
         	int FpcrTemp = 0x0;
@@ -74,5 +651,79 @@ namespace Ryujinx.Tests.Cpu
         	AThreadState ThreadState = SingleOpcode(0x1E274020, V1: V1, Fpcr: FpcrTemp);
         	Assert.AreEqual(Result, ThreadState.V0.X0);
         }
+
+        [TestCase(0x6E619820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, 'N', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x6E619820u, 0x3FFCCCCCCCCCCCCDul, 0x3FFCCCCCCCCCCCCDul, 'N', false, 0x4000000000000000ul, 0x4000000000000000ul)]
+        [TestCase(0x6E619820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, 'P', false, 0x4000000000000000ul, 0x4000000000000000ul)]
+        [TestCase(0x6E619820u, 0x3FFCCCCCCCCCCCCDul, 0x3FFCCCCCCCCCCCCDul, 'P', false, 0x4000000000000000ul, 0x4000000000000000ul)]
+        [TestCase(0x6E619820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, 'M', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x6E619820u, 0x3FFCCCCCCCCCCCCDul, 0x3FFCCCCCCCCCCCCDul, 'M', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x6E619820u, 0x3FF3333333333333ul, 0x3FF3333333333333ul, 'Z', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x6E619820u, 0x3FFCCCCCCCCCCCCDul, 0x3FFCCCCCCCCCCCCDul, 'Z', false, 0x3FF0000000000000ul, 0x3FF0000000000000ul)]
+        [TestCase(0x6E219820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'N', false, 0x3f80000040000000ul, 0x3f80000040000000ul)]
+        [TestCase(0x6E219820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'P', false, 0x4000000040000000ul, 0x4000000040000000ul)]    
+        [TestCase(0x6E219820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'M', false, 0x3f8000003f800000ul, 0x3f8000003f800000ul)]
+        [TestCase(0x6E219820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'Z', false, 0x3f8000003f800000ul, 0x3f8000003f800000ul)]
+        [TestCase(0x2E219820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'N', false, 0x3f80000040000000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E219820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'P', false, 0x4000000040000000ul, 0x0000000000000000ul)]    
+        [TestCase(0x2E219820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'M', false, 0x3f8000003f800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E219820u, 0x3f99999a3fe66666ul, 0x3f99999a3fe66666ul, 'Z', false, 0x3f8000003f800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E219820u, 0x0000000080000000ul, 0x0000000000000000ul, 'N', false, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E219820u, 0x0000000080000000ul, 0x0000000000000000ul, 'P', false, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E219820u, 0x0000000080000000ul, 0x0000000000000000ul, 'M', false, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E219820u, 0x0000000080000000ul, 0x0000000000000000ul, 'Z', false, 0x0000000080000000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E219820u, 0x7F800000FF800000ul, 0x0000000000000000ul, 'N', false, 0x7F800000FF800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E219820u, 0x7F800000FF800000ul, 0x0000000000000000ul, 'P', false, 0x7F800000FF800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E219820u, 0x7F800000FF800000ul, 0x0000000000000000ul, 'M', false, 0x7F800000FF800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E219820u, 0x7F800000FF800000ul, 0x0000000000000000ul, 'Z', false, 0x7F800000FF800000ul, 0x0000000000000000ul)]
+        [TestCase(0x2E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'N', false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'P', false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'M', false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'Z', false, 0xFFC000017FC00002ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'N', true,  0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'P', true,  0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'M', true,  0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        [TestCase(0x2E219820u, 0xFF8000017FC00002ul, 0x0000000000000000ul, 'Z', true,  0x7FC000007FC00000ul, 0x0000000000000000ul, Ignore = "NaN test.")]
+        public void Frintx_V(uint Opcode, ulong A, ulong B, char RoundType, bool DefaultNaN, ulong Result0, ulong Result1)
+        {
+            int FpcrTemp = 0x0;
+            switch(RoundType)
+            {
+                case 'N':
+                FpcrTemp = 0x0;
+                break;
+
+                case 'P':
+                FpcrTemp = 0x400000;
+                break;
+
+                case 'M':
+                FpcrTemp = 0x800000;
+                break;
+
+                case 'Z':
+                FpcrTemp = 0xC00000;
+                break;
+            }
+            if(DefaultNaN)
+            {
+                FpcrTemp |= 1 << 25;
+            }
+            AVec V1 = new AVec { X0 = A, X1 = B };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, Fpcr: FpcrTemp);
+            Assert.Multiple(() =>
+            {
+                Assert.AreEqual(Result0, ThreadState.V0.X0);
+                Assert.AreEqual(Result1, ThreadState.V0.X1);
+            });
+        }
+
+        [TestCase(0x41200000u, 0x3EA18000u)]
+        public void Frsqrte_S(uint A, uint Result)
+        {
+            AVec V1 = new AVec { X0 = A };
+            AThreadState ThreadState = SingleOpcode(0x7EA1D820, V1: V1);
+            Assert.AreEqual(Result, ThreadState.V0.X0);
+        }
     }
 }
diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs b/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs
index 372689d08..0681b6139 100644
--- a/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs
+++ b/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs
@@ -5,6 +5,82 @@ namespace Ryujinx.Tests.Cpu
 {
     public class CpuTestSimdMove : CpuTest
     {
+        [Test, Description("trn1 v0.4s, v1.4s, v2.4s")]
+        public void Trn1_V_4S([Random(2)] uint A0, [Random(2)] uint A1, [Random(2)] uint A2, [Random(2)] uint A3,
+                              [Random(2)] uint B0, [Random(2)] uint B1, [Random(2)] uint B2, [Random(2)] uint B3)
+        {
+            uint Opcode = 0x4E822820;
+            AVec V1 = new AVec { W0 = A0, W1 = A1, W2 = A2, W3 = A3 };
+            AVec V2 = new AVec { W0 = B0, W1 = B1, W2 = B2, W3 = B3 };
+
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
+
+            Assert.That(ThreadState.V0.W0, Is.EqualTo(A0));
+            Assert.That(ThreadState.V0.W1, Is.EqualTo(B0));
+            Assert.That(ThreadState.V0.W2, Is.EqualTo(A2));
+            Assert.That(ThreadState.V0.W3, Is.EqualTo(B2));
+        }
+
+        [Test, Description("trn1 v0.8b, v1.8b, v2.8b")]
+        public void Trn1_V_8B([Random(2)] byte A0, [Random(1)] byte A1, [Random(2)] byte A2, [Random(1)] byte A3,
+                              [Random(2)] byte A4, [Random(1)] byte A5, [Random(2)] byte A6, [Random(1)] byte A7,
+                              [Random(2)] byte B0, [Random(1)] byte B1, [Random(2)] byte B2, [Random(1)] byte B3,
+                              [Random(2)] byte B4, [Random(1)] byte B5, [Random(2)] byte B6, [Random(1)] byte B7)
+        {
+            uint Opcode = 0x0E022820;
+            AVec V1 = new AVec { B0 = A0, B1 = A1, B2 = A2, B3 = A3, B4 = A4, B5 = A5, B6 = A6, B7 = A7 };
+            AVec V2 = new AVec { B0 = B0, B1 = B1, B2 = B2, B3 = B3, B4 = B4, B5 = B5, B6 = B6, B7 = B7 };
+
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
+
+            Assert.That(ThreadState.V0.B0, Is.EqualTo(A0));
+            Assert.That(ThreadState.V0.B1, Is.EqualTo(B0));
+            Assert.That(ThreadState.V0.B2, Is.EqualTo(A2));
+            Assert.That(ThreadState.V0.B3, Is.EqualTo(B2));
+            Assert.That(ThreadState.V0.B4, Is.EqualTo(A4));
+            Assert.That(ThreadState.V0.B5, Is.EqualTo(B4));
+            Assert.That(ThreadState.V0.B6, Is.EqualTo(A6));
+            Assert.That(ThreadState.V0.B7, Is.EqualTo(B6));
+        }
+
+        [Test, Description("trn2 v0.4s, v1.4s, v2.4s")]
+        public void Trn2_V_4S([Random(2)] uint A0, [Random(2)] uint A1, [Random(2)] uint A2, [Random(2)] uint A3,
+                              [Random(2)] uint B0, [Random(2)] uint B1, [Random(2)] uint B2, [Random(2)] uint B3)
+        {
+            uint Opcode = 0x4E826820;
+            AVec V1 = new AVec { W0 = A0, W1 = A1, W2 = A2, W3 = A3 };
+            AVec V2 = new AVec { W0 = B0, W1 = B1, W2 = B2, W3 = B3 };
+
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
+
+            Assert.That(ThreadState.V0.W0, Is.EqualTo(A1));
+            Assert.That(ThreadState.V0.W1, Is.EqualTo(B1));
+            Assert.That(ThreadState.V0.W2, Is.EqualTo(A3));
+            Assert.That(ThreadState.V0.W3, Is.EqualTo(B3));
+        }
+
+        [Test, Description("trn2 v0.8b, v1.8b, v2.8b")]
+        public void Trn2_V_8B([Random(1)] byte A0, [Random(2)] byte A1, [Random(1)] byte A2, [Random(2)] byte A3,
+                              [Random(1)] byte A4, [Random(2)] byte A5, [Random(1)] byte A6, [Random(2)] byte A7,
+                              [Random(1)] byte B0, [Random(2)] byte B1, [Random(1)] byte B2, [Random(2)] byte B3,
+                              [Random(1)] byte B4, [Random(2)] byte B5, [Random(1)] byte B6, [Random(2)] byte B7)
+        {
+            uint Opcode = 0x0E026820;
+            AVec V1 = new AVec { B0 = A0, B1 = A1, B2 = A2, B3 = A3, B4 = A4, B5 = A5, B6 = A6, B7 = A7 };
+            AVec V2 = new AVec { B0 = B0, B1 = B1, B2 = B2, B3 = B3, B4 = B4, B5 = B5, B6 = B6, B7 = B7 };
+
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
+
+            Assert.That(ThreadState.V0.B0, Is.EqualTo(A1));
+            Assert.That(ThreadState.V0.B1, Is.EqualTo(B1));
+            Assert.That(ThreadState.V0.B2, Is.EqualTo(A3));
+            Assert.That(ThreadState.V0.B3, Is.EqualTo(B3));
+            Assert.That(ThreadState.V0.B4, Is.EqualTo(A5));
+            Assert.That(ThreadState.V0.B5, Is.EqualTo(B5));
+            Assert.That(ThreadState.V0.B6, Is.EqualTo(A7));
+            Assert.That(ThreadState.V0.B7, Is.EqualTo(B7));
+        }
+
         [TestCase(0u, 0u, 0x2313221221112010ul, 0x0000000000000000ul)]
         [TestCase(1u, 0u, 0x2313221221112010ul, 0x2717261625152414ul)]
         [TestCase(0u, 1u, 0x2322131221201110ul, 0x0000000000000000ul)]
diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs
new file mode 100644
index 000000000..8c13c33a2
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs
@@ -0,0 +1,482 @@
+#define SimdReg
+
+using ChocolArm64.State;
+
+using NUnit.Framework;
+
+namespace Ryujinx.Tests.Cpu
+{
+    using Tester;
+    using Tester.Types;
+
+    [Category("SimdReg")]
+    public sealed class CpuTestSimdReg : CpuTest
+    {
+#if SimdReg
+        [SetUp]
+        public void SetupTester()
+        {
+            AArch64.TakeReset(false);
+        }
+
+#region "ValueSource"
+        private static ulong[] _1D_()
+        {
+            return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
+        }
+
+        private static ulong[] _8B4H2S_()
+        {
+            return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful,
+                                 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul,
+                                 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul,
+                                 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul };
+        }
+
+        private static ulong[] _8B4H2S1D_()
+        {
+            return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful,
+                                 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul,
+                                 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul,
+                                 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
+        }
+
+        private static ulong[] _4H2S1D_()
+        {
+            return new ulong[] { 0x0000000000000000ul, 0x7FFF7FFF7FFF7FFFul,
+                                 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul,
+                                 0x8000000080000000ul, 0x7FFFFFFFFFFFFFFFul,
+                                 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul };
+        }
+#endregion
+
+        [Test, Description("ADD <V><d>, <V><n>, <V><m>")]
+        public void Add_S_D([ValueSource("_1D_")] [Random(1)] ulong A,
+                            [ValueSource("_1D_")] [Random(1)] ulong B)
+        {
+            uint Opcode = 0x5EE28420; // ADD D0, D1, D2
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A };
+            AVec V2 = new AVec { X0 = B };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.V(1, new Bits(A));
+            AArch64.V(2, new Bits(B));
+            SimdFp.Add_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+            Assert.That(ThreadState.V0.X1, Is.Zero);
+        }
+
+        [Test, Description("ADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
+        public void Add_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A,
+                                   [ValueSource("_8B4H2S_")] [Random(1)] ulong B,
+                                   [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S>
+        {
+            uint Opcode = 0x0E228420; // ADD V0.8B, V1.8B, V2.8B
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A };
+            AVec V2 = new AVec { X0 = B };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.V(1, new Bits(A));
+            AArch64.V(2, new Bits(B));
+            SimdFp.Add_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+            Assert.That(ThreadState.V0.X1, Is.Zero);
+        }
+
+        [Test, Pairwise, Description("ADD <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
+        public void Add_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0,
+                                       [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1,
+                                       [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B0,
+                                       [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B1,
+                                       [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D>
+        {
+            uint Opcode = 0x4E228420; // ADD V0.16B, V1.16B, V2.16B
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AVec V2 = new AVec { X0 = B0, X1 = B1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            AArch64.Vpart(2, 0, new Bits(B0));
+            AArch64.Vpart(2, 1, new Bits(B1));
+            SimdFp.Add_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
+                Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+            });
+        }
+
+        [Test, Pairwise, Description("ADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+        public void Addhn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0,
+                                           [ValueSource("_4H2S1D_")] [Random(1)] ulong A1,
+                                           [ValueSource("_4H2S1D_")] [Random(1)] ulong B0,
+                                           [ValueSource("_4H2S1D_")] [Random(1)] ulong B1,
+                                           [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S>
+        {
+            uint Opcode = 0x0E224020; // ADDHN V0.8B, V1.8H, V2.8H
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AVec V2 = new AVec { X0 = B0, X1 = B1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            AArch64.Vpart(2, 0, new Bits(B0));
+            AArch64.Vpart(2, 1, new Bits(B1));
+            SimdFp.Addhn_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+                Assert.That(ThreadState.V0.X1, Is.Zero);
+            });
+        }
+
+        [Test, Pairwise, Description("ADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+        public void Addhn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0,
+                                            [ValueSource("_4H2S1D_")] [Random(1)] ulong A1,
+                                            [ValueSource("_4H2S1D_")] [Random(1)] ulong B0,
+                                            [ValueSource("_4H2S1D_")] [Random(1)] ulong B1,
+                                            [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S>
+        {
+            uint Opcode = 0x4E224020; // ADDHN2 V0.16B, V1.8H, V2.8H
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X0 = TestContext.CurrentContext.Random.NextULong();
+            AVec V0 = new AVec { X0 = _X0 };
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AVec V2 = new AVec { X0 = B0, X1 = B1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            AArch64.Vpart(2, 0, new Bits(B0));
+            AArch64.Vpart(2, 1, new Bits(B1));
+            SimdFp.Addhn_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(_X0));
+                Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+            });
+        }
+
+        [Test, Description("ADDP <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
+        public void Addp_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A,
+                                    [ValueSource("_8B4H2S_")] [Random(1)] ulong B,
+                                    [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S>
+        {
+            uint Opcode = 0x0E22BC20; // ADDP V0.8B, V1.8B, V2.8B
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A };
+            AVec V2 = new AVec { X0 = B };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.V(1, new Bits(A));
+            AArch64.V(2, new Bits(B));
+            SimdFp.Addp_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+            Assert.That(ThreadState.V0.X1, Is.Zero);
+        }
+
+        [Test, Pairwise, Description("ADDP <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
+        public void Addp_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0,
+                                        [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1,
+                                        [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B0,
+                                        [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B1,
+                                        [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D>
+        {
+            uint Opcode = 0x4E22BC20; // ADDP V0.16B, V1.16B, V2.16B
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AVec V2 = new AVec { X0 = B0, X1 = B1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            AArch64.Vpart(2, 0, new Bits(B0));
+            AArch64.Vpart(2, 1, new Bits(B1));
+            SimdFp.Addp_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
+                Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+            });
+        }
+
+        [Test, Pairwise, Description("RADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+        public void Raddhn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0,
+                                            [ValueSource("_4H2S1D_")] [Random(1)] ulong A1,
+                                            [ValueSource("_4H2S1D_")] [Random(1)] ulong B0,
+                                            [ValueSource("_4H2S1D_")] [Random(1)] ulong B1,
+                                            [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S>
+        {
+            uint Opcode = 0x2E224020; // RADDHN V0.8B, V1.8H, V2.8H
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AVec V2 = new AVec { X0 = B0, X1 = B1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            AArch64.Vpart(2, 0, new Bits(B0));
+            AArch64.Vpart(2, 1, new Bits(B1));
+            SimdFp.Raddhn_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+                Assert.That(ThreadState.V0.X1, Is.Zero);
+            });
+        }
+
+        [Test, Pairwise, Description("RADDHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+        public void Raddhn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0,
+                                             [ValueSource("_4H2S1D_")] [Random(1)] ulong A1,
+                                             [ValueSource("_4H2S1D_")] [Random(1)] ulong B0,
+                                             [ValueSource("_4H2S1D_")] [Random(1)] ulong B1,
+                                             [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S>
+        {
+            uint Opcode = 0x6E224020; // RADDHN2 V0.16B, V1.8H, V2.8H
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X0 = TestContext.CurrentContext.Random.NextULong();
+            AVec V0 = new AVec { X0 = _X0 };
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AVec V2 = new AVec { X0 = B0, X1 = B1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            AArch64.Vpart(2, 0, new Bits(B0));
+            AArch64.Vpart(2, 1, new Bits(B1));
+            SimdFp.Raddhn_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(_X0));
+                Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+            });
+        }
+
+        [Test, Pairwise, Description("RSUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+        public void Rsubhn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0,
+                                            [ValueSource("_4H2S1D_")] [Random(1)] ulong A1,
+                                            [ValueSource("_4H2S1D_")] [Random(1)] ulong B0,
+                                            [ValueSource("_4H2S1D_")] [Random(1)] ulong B1,
+                                            [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S>
+        {
+            uint Opcode = 0x2E226020; // RSUBHN V0.8B, V1.8H, V2.8H
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AVec V2 = new AVec { X0 = B0, X1 = B1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            AArch64.Vpart(2, 0, new Bits(B0));
+            AArch64.Vpart(2, 1, new Bits(B1));
+            SimdFp.Rsubhn_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+                Assert.That(ThreadState.V0.X1, Is.Zero);
+            });
+        }
+
+        [Test, Pairwise, Description("RSUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+        public void Rsubhn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0,
+                                             [ValueSource("_4H2S1D_")] [Random(1)] ulong A1,
+                                             [ValueSource("_4H2S1D_")] [Random(1)] ulong B0,
+                                             [ValueSource("_4H2S1D_")] [Random(1)] ulong B1,
+                                             [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S>
+        {
+            uint Opcode = 0x6E226020; // RSUBHN2 V0.16B, V1.8H, V2.8H
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X0 = TestContext.CurrentContext.Random.NextULong();
+            AVec V0 = new AVec { X0 = _X0 };
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AVec V2 = new AVec { X0 = B0, X1 = B1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            AArch64.Vpart(2, 0, new Bits(B0));
+            AArch64.Vpart(2, 1, new Bits(B1));
+            SimdFp.Rsubhn_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(_X0));
+                Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+            });
+        }
+
+        [Test, Description("SUB <V><d>, <V><n>, <V><m>")]
+        public void Sub_S_D([ValueSource("_1D_")] [Random(1)] ulong A,
+                            [ValueSource("_1D_")] [Random(1)] ulong B)
+        {
+            uint Opcode = 0x7EE28420; // SUB D0, D1, D2
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A };
+            AVec V2 = new AVec { X0 = B };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.V(1, new Bits(A));
+            AArch64.V(2, new Bits(B));
+            SimdFp.Sub_S(Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+            Assert.That(ThreadState.V0.X1, Is.Zero);
+        }
+
+        [Test, Description("SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
+        public void Sub_V_8B_4H_2S([ValueSource("_8B4H2S_")] [Random(1)] ulong A,
+                                   [ValueSource("_8B4H2S_")] [Random(1)] ulong B,
+                                   [Values(0b00u, 0b01u, 0b10u)] uint size) // <8B, 4H, 2S>
+        {
+            uint Opcode = 0x2E228420; // SUB V0.8B, V1.8B, V2.8B
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A };
+            AVec V2 = new AVec { X0 = B };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.V(1, new Bits(A));
+            AArch64.V(2, new Bits(B));
+            SimdFp.Sub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+            Assert.That(ThreadState.V0.X1, Is.Zero);
+        }
+
+        [Test, Pairwise, Description("SUB <Vd>.<T>, <Vn>.<T>, <Vm>.<T>")]
+        public void Sub_V_16B_8H_4S_2D([ValueSource("_8B4H2S1D_")] [Random(1)] ulong A0,
+                                       [ValueSource("_8B4H2S1D_")] [Random(1)] ulong A1,
+                                       [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B0,
+                                       [ValueSource("_8B4H2S1D_")] [Random(1)] ulong B1,
+                                       [Values(0b00u, 0b01u, 0b10u, 0b11u)] uint size) // <16B, 8H, 4S, 2D>
+        {
+            uint Opcode = 0x6E228420; // SUB V0.16B, V1.16B, V2.16B
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AVec V2 = new AVec { X0 = B0, X1 = B1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            AArch64.Vpart(2, 0, new Bits(B0));
+            AArch64.Vpart(2, 1, new Bits(B1));
+            SimdFp.Sub_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.Vpart(64, 0, 0).ToUInt64()));
+                Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+            });
+        }
+
+        [Test, Pairwise, Description("SUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+        public void Subhn_V_8H8B_4S4H_2D2S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0,
+                                           [ValueSource("_4H2S1D_")] [Random(1)] ulong A1,
+                                           [ValueSource("_4H2S1D_")] [Random(1)] ulong B0,
+                                           [ValueSource("_4H2S1D_")] [Random(1)] ulong B1,
+                                           [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H8B, 4S4H, 2D2S>
+        {
+            uint Opcode = 0x0E226020; // SUBHN V0.8B, V1.8H, V2.8H
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            AVec V0 = new AVec { X1 = TestContext.CurrentContext.Random.NextULong() };
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AVec V2 = new AVec { X0 = B0, X1 = B1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            AArch64.Vpart(2, 0, new Bits(B0));
+            AArch64.Vpart(2, 1, new Bits(B1));
+            SimdFp.Subhn_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(AArch64.V(64, 0).ToUInt64()));
+                Assert.That(ThreadState.V0.X1, Is.Zero);
+            });
+        }
+
+        [Test, Pairwise, Description("SUBHN{2} <Vd>.<Tb>, <Vn>.<Ta>, <Vm>.<Ta>")]
+        public void Subhn_V_8H16B_4S8H_2D4S([ValueSource("_4H2S1D_")] [Random(1)] ulong A0,
+                                            [ValueSource("_4H2S1D_")] [Random(1)] ulong A1,
+                                            [ValueSource("_4H2S1D_")] [Random(1)] ulong B0,
+                                            [ValueSource("_4H2S1D_")] [Random(1)] ulong B1,
+                                            [Values(0b00u, 0b01u, 0b10u)] uint size) // <8H16B, 4S8H, 2D4S>
+        {
+            uint Opcode = 0x4E226020; // SUBHN2 V0.16B, V1.8H, V2.8H
+            Opcode |= ((size & 3) << 22);
+            Bits Op = new Bits(Opcode);
+
+            ulong _X0 = TestContext.CurrentContext.Random.NextULong();
+            AVec V0 = new AVec { X0 = _X0 };
+            AVec V1 = new AVec { X0 = A0, X1 = A1 };
+            AVec V2 = new AVec { X0 = B0, X1 = B1 };
+            AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2);
+
+            AArch64.Vpart(1, 0, new Bits(A0));
+            AArch64.Vpart(1, 1, new Bits(A1));
+            AArch64.Vpart(2, 0, new Bits(B0));
+            AArch64.Vpart(2, 1, new Bits(B1));
+            SimdFp.Subhn_V(Op[30], Op[23, 22], Op[20, 16], Op[9, 5], Op[4, 0]);
+
+            Assert.Multiple(() =>
+            {
+                Assert.That(ThreadState.V0.X0, Is.EqualTo(_X0));
+                Assert.That(ThreadState.V0.X1, Is.EqualTo(AArch64.Vpart(64, 0, 1).ToUInt64()));
+            });
+        }
+#endif
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/Tester/Instructions.cs b/Ryujinx.Tests/Cpu/Tester/Instructions.cs
new file mode 100644
index 000000000..e866a9a0d
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/Tester/Instructions.cs
@@ -0,0 +1,2333 @@
+// https://github.com/LDj3SNuD/ARM_v8-A_AArch64_Instructions_Tester/blob/master/Tester/Instructions.cs
+
+// https://meriac.github.io/archex/A64_v83A_ISA/index.xml
+// https://meriac.github.io/archex/A64_v83A_ISA/fpsimdindex.xml
+
+using System.Numerics;
+
+namespace Ryujinx.Tests.Cpu.Tester
+{
+    using Types;
+
+    using static AArch64;
+    using static Shared;
+
+    internal static class Base
+    {
+#region "Alu"
+        // https://meriac.github.io/archex/A64_v83A_ISA/cls_int.xml
+        public static void Cls(bool sf, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+
+            BigInteger result = (BigInteger)CountLeadingSignBits(operand1);
+
+            X(d, result.SubBigInteger(datasize - 1, 0));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/clz_int.xml
+        public static void Clz(bool sf, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+
+            BigInteger result = (BigInteger)CountLeadingZeroBits(operand1);
+
+            X(d, result.SubBigInteger(datasize - 1, 0));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/rbit_int.xml
+        public static void Rbit(bool sf, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits result = new Bits(datasize);
+            Bits operand = X(datasize, n);
+
+            for (int i = 0; i <= datasize - 1; i++)
+            {
+                result[datasize - 1 - i] = operand[i];
+            }
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/rev16_int.xml
+        public static void Rev16(bool sf, Bits Rn, Bits Rd)
+        {
+            /* Bits opc = "01"; */
+
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            int container_size = 16;
+
+            /* Operation */
+            Bits result = new Bits(datasize);
+            Bits operand = X(datasize, n);
+
+            int containers = datasize / container_size;
+            int elements_per_container = container_size / 8;
+            int index = 0;
+            int rev_index;
+
+            for (int c = 0; c <= containers - 1; c++)
+            {
+                rev_index = index + ((elements_per_container - 1) * 8);
+
+                for (int e = 0; e <= elements_per_container - 1; e++)
+                {
+                    result[rev_index + 7, rev_index] = operand[index + 7, index];
+
+                    index = index + 8;
+                    rev_index = rev_index - 8;
+                }
+            }
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/rev32_int.xml
+        // (https://meriac.github.io/archex/A64_v83A_ISA/rev.xml)
+        public static void Rev32(bool sf, Bits Rn, Bits Rd)
+        {
+            /* Bits opc = "10"; */
+
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            int container_size = 32;
+
+            /* Operation */
+            Bits result = new Bits(datasize);
+            Bits operand = X(datasize, n);
+
+            int containers = datasize / container_size;
+            int elements_per_container = container_size / 8;
+            int index = 0;
+            int rev_index;
+
+            for (int c = 0; c <= containers - 1; c++)
+            {
+                rev_index = index + ((elements_per_container - 1) * 8);
+
+                for (int e = 0; e <= elements_per_container - 1; e++)
+                {
+                    result[rev_index + 7, rev_index] = operand[index + 7, index];
+
+                    index = index + 8;
+                    rev_index = rev_index - 8;
+                }
+            }
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/rev64_rev.xml
+        // (https://meriac.github.io/archex/A64_v83A_ISA/rev.xml)
+        public static void Rev64(Bits Rn, Bits Rd)
+        {
+            /* Bits opc = "11"; */
+
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+
+            int container_size = 64;
+
+            /* Operation */
+            Bits result = new Bits(64);
+            Bits operand = X(64, n);
+
+            int containers = 64 / container_size;
+            int elements_per_container = container_size / 8;
+            int index = 0;
+            int rev_index;
+
+            for (int c = 0; c <= containers - 1; c++)
+            {
+                rev_index = index + ((elements_per_container - 1) * 8);
+
+                for (int e = 0; e <= elements_per_container - 1; e++)
+                {
+                    result[rev_index + 7, rev_index] = operand[index + 7, index];
+
+                    index = index + 8;
+                    rev_index = rev_index - 8;
+                }
+            }
+
+            X(d, result);
+        }
+#endregion
+
+#region "AluImm"
+        // https://meriac.github.io/archex/A64_v83A_ISA/add_addsub_imm.xml
+        public static void Add_Imm(bool sf, Bits shift, Bits imm12, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            Bits imm;
+
+            switch (shift)
+            {
+                default:
+                case Bits bits when bits == "00":
+                    imm = ZeroExtend(imm12, datasize);
+                    break;
+                case Bits bits when bits == "01":
+                    imm = ZeroExtend(Bits.Concat(imm12, Zeros(12)), datasize);
+                    break;
+                /* when '1x' ReservedValue(); */
+            }
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n));
+
+            (result, _) = AddWithCarry(datasize, operand1, imm, false);
+
+            if (d == 31)
+            {
+                SP(result);
+            }
+            else
+            {
+                X(d, result);
+            }
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/adds_addsub_imm.xml
+        public static void Adds_Imm(bool sf, Bits shift, Bits imm12, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            Bits imm;
+
+            switch (shift)
+            {
+                default:
+                case Bits bits when bits == "00":
+                    imm = ZeroExtend(imm12, datasize);
+                    break;
+                case Bits bits when bits == "01":
+                    imm = ZeroExtend(Bits.Concat(imm12, Zeros(12)), datasize);
+                    break;
+                /* when '1x' ReservedValue(); */
+            }
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n));
+            Bits nzcv;
+
+            (result, nzcv) = AddWithCarry(datasize, operand1, imm, false);
+
+            PSTATE.NZCV(nzcv);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/and_log_imm.xml
+        public static void And_Imm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            Bits imm;
+
+            /* if sf == '0' && N != '0' then ReservedValue(); */
+
+            (imm, _) = DecodeBitMasks(datasize, N, imms, immr, true);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+
+            Bits result = AND(operand1, imm);
+
+            if (d == 31)
+            {
+                SP(result);
+            }
+            else
+            {
+                X(d, result);
+            }
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/ands_log_imm.xml
+        public static void Ands_Imm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            Bits imm;
+
+            /* if sf == '0' && N != '0' then ReservedValue(); */
+
+            (imm, _) = DecodeBitMasks(datasize, N, imms, immr, true);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+
+            Bits result = AND(operand1, imm);
+
+            PSTATE.NZCV(result[datasize - 1], IsZeroBit(result), false, false);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/eor_log_imm.xml
+        public static void Eor_Imm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            Bits imm;
+
+            /* if sf == '0' && N != '0' then ReservedValue(); */
+
+            (imm, _) = DecodeBitMasks(datasize, N, imms, immr, true);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+
+            Bits result = EOR(operand1, imm);
+
+            if (d == 31)
+            {
+                SP(result);
+            }
+            else
+            {
+                X(d, result);
+            }
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/orr_log_imm.xml
+        public static void Orr_Imm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            Bits imm;
+
+            /* if sf == '0' && N != '0' then ReservedValue(); */
+
+            (imm, _) = DecodeBitMasks(datasize, N, imms, immr, true);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+
+            Bits result = OR(operand1, imm);
+
+            if (d == 31)
+            {
+                SP(result);
+            }
+            else
+            {
+                X(d, result);
+            }
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/sub_addsub_imm.xml
+        public static void Sub_Imm(bool sf, Bits shift, Bits imm12, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            Bits imm;
+
+            switch (shift)
+            {
+                default:
+                case Bits bits when bits == "00":
+                    imm = ZeroExtend(imm12, datasize);
+                    break;
+                case Bits bits when bits == "01":
+                    imm = ZeroExtend(Bits.Concat(imm12, Zeros(12)), datasize);
+                    break;
+                /* when '1x' ReservedValue(); */
+            }
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n));
+            Bits operand2 = NOT(imm);
+
+            (result, _) = AddWithCarry(datasize, operand1, operand2, true);
+
+            if (d == 31)
+            {
+                SP(result);
+            }
+            else
+            {
+                X(d, result);
+            }
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/subs_addsub_imm.xml
+        public static void Subs_Imm(bool sf, Bits shift, Bits imm12, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            Bits imm;
+
+            switch (shift)
+            {
+                default:
+                case Bits bits when bits == "00":
+                    imm = ZeroExtend(imm12, datasize);
+                    break;
+                case Bits bits when bits == "01":
+                    imm = ZeroExtend(Bits.Concat(imm12, Zeros(12)), datasize);
+                    break;
+                /* when '1x' ReservedValue(); */
+            }
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n));
+            Bits operand2 = NOT(imm);
+            Bits nzcv;
+
+            (result, nzcv) = AddWithCarry(datasize, operand1, operand2, true);
+
+            PSTATE.NZCV(nzcv);
+
+            X(d, result);
+        }
+#endregion
+
+#region "AluRs"
+        // https://meriac.github.io/archex/A64_v83A_ISA/adc.xml
+        public static void Adc(bool sf, Bits Rm, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+
+            (result, _) = AddWithCarry(datasize, operand1, operand2, PSTATE.C);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/adcs.xml
+        public static void Adcs(bool sf, Bits Rm, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+            Bits nzcv;
+
+            (result, nzcv) = AddWithCarry(datasize, operand1, operand2, PSTATE.C);
+
+            PSTATE.NZCV(nzcv);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/add_addsub_shift.xml
+        public static void Add_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if shift == '11' then ReservedValue(); */
+            /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
+
+            ShiftType shift_type = DecodeShift(shift);
+            int shift_amount = (int)UInt(imm6);
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount);
+
+            (result, _) = AddWithCarry(datasize, operand1, operand2, false);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/adds_addsub_shift.xml
+        public static void Adds_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if shift == '11' then ReservedValue(); */
+            /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
+
+            ShiftType shift_type = DecodeShift(shift);
+            int shift_amount = (int)UInt(imm6);
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount);
+            Bits nzcv;
+
+            (result, nzcv) = AddWithCarry(datasize, operand1, operand2, false);
+
+            PSTATE.NZCV(nzcv);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/and_log_shift.xml
+        public static void And_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
+
+            ShiftType shift_type = DecodeShift(shift);
+            int shift_amount = (int)UInt(imm6);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount);
+
+            Bits result = AND(operand1, operand2);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/ands_log_shift.xml
+        public static void Ands_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
+
+            ShiftType shift_type = DecodeShift(shift);
+            int shift_amount = (int)UInt(imm6);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount);
+
+            Bits result = AND(operand1, operand2);
+
+            PSTATE.NZCV(result[datasize - 1], IsZeroBit(result), false, false);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/asrv.xml
+        public static void Asrv(bool sf, Bits Rm, Bits Rn, Bits Rd)
+        {
+            Bits op2 = "10";
+
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            ShiftType shift_type = DecodeShift(op2);
+
+            /* Operation */
+            Bits operand2 = X(datasize, m);
+
+            Bits result = ShiftReg(datasize, n, shift_type, (int)(UInt(operand2) % datasize)); // BigInteger.Modulus Operator (BigInteger, BigInteger)
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/bic_log_shift.xml
+        public static void Bic(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
+
+            ShiftType shift_type = DecodeShift(shift);
+            int shift_amount = (int)UInt(imm6);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount);
+
+            operand2 = NOT(operand2);
+
+            Bits result = AND(operand1, operand2);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/bics.xml
+        public static void Bics(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
+
+            ShiftType shift_type = DecodeShift(shift);
+            int shift_amount = (int)UInt(imm6);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount);
+
+            operand2 = NOT(operand2);
+
+            Bits result = AND(operand1, operand2);
+
+            PSTATE.NZCV(result[datasize - 1], IsZeroBit(result), false, false);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/crc32.xml
+        public static void Crc32(bool sf, Bits Rm, Bits sz, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* if sf == '1' && sz != '11' then UnallocatedEncoding(); */
+            /* if sf == '0' && sz == '11' then UnallocatedEncoding(); */
+
+            int size = 8 << (int)UInt(sz);
+
+            /* Operation */
+            /* if !HaveCRCExt() then UnallocatedEncoding(); */
+
+            Bits acc = X(32, n); // accumulator
+            Bits val = X(size, m); // input value
+            Bits poly = new Bits(0x04C11DB7u);
+
+            Bits tempacc = Bits.Concat(BitReverse(acc), Zeros(size));
+            Bits tempval = Bits.Concat(BitReverse(val), Zeros(32));
+
+            // Poly32Mod2 on a bitstring does a polynomial Modulus over {0,1} operation
+            X(d, BitReverse(Poly32Mod2(EOR(tempacc, tempval), poly)));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/crc32c.xml
+        public static void Crc32c(bool sf, Bits Rm, Bits sz, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* if sf == '1' && sz != '11' then UnallocatedEncoding(); */
+            /* if sf == '0' && sz == '11' then UnallocatedEncoding(); */
+
+            int size = 8 << (int)UInt(sz);
+
+            /* Operation */
+            /* if !HaveCRCExt() then UnallocatedEncoding(); */
+
+            Bits acc = X(32, n); // accumulator
+            Bits val = X(size, m); // input value
+            Bits poly = new Bits(0x1EDC6F41u);
+
+            Bits tempacc = Bits.Concat(BitReverse(acc), Zeros(size));
+            Bits tempval = Bits.Concat(BitReverse(val), Zeros(32));
+
+            // Poly32Mod2 on a bitstring does a polynomial Modulus over {0,1} operation
+            X(d, BitReverse(Poly32Mod2(EOR(tempacc, tempval), poly)));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/eon.xml
+        public static void Eon(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
+
+            ShiftType shift_type = DecodeShift(shift);
+            int shift_amount = (int)UInt(imm6);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount);
+
+            operand2 = NOT(operand2);
+
+            Bits result = EOR(operand1, operand2);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/eor_log_shift.xml
+        public static void Eor_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
+
+            ShiftType shift_type = DecodeShift(shift);
+            int shift_amount = (int)UInt(imm6);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount);
+
+            Bits result = EOR(operand1, operand2);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/extr.xml
+        public static void Extr(bool sf, bool N, Bits Rm, Bits imms, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if N != sf then UnallocatedEncoding(); */
+            /* if sf == '0' && imms<5> == '1' then ReservedValue(); */
+
+            int lsb = (int)UInt(imms);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+            Bits concat = Bits.Concat(operand1, operand2);
+
+            Bits result = concat[lsb + datasize - 1, lsb];
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/lslv.xml
+        public static void Lslv(bool sf, Bits Rm, Bits Rn, Bits Rd)
+        {
+            Bits op2 = "00";
+
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            ShiftType shift_type = DecodeShift(op2);
+
+            /* Operation */
+            Bits operand2 = X(datasize, m);
+
+            Bits result = ShiftReg(datasize, n, shift_type, (int)(UInt(operand2) % datasize)); // BigInteger.Modulus Operator (BigInteger, BigInteger)
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/lsrv.xml
+        public static void Lsrv(bool sf, Bits Rm, Bits Rn, Bits Rd)
+        {
+            Bits op2 = "01";
+
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            ShiftType shift_type = DecodeShift(op2);
+
+            /* Operation */
+            Bits operand2 = X(datasize, m);
+
+            Bits result = ShiftReg(datasize, n, shift_type, (int)(UInt(operand2) % datasize)); // BigInteger.Modulus Operator (BigInteger, BigInteger)
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/orn_log_shift.xml
+        public static void Orn(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
+
+            ShiftType shift_type = DecodeShift(shift);
+            int shift_amount = (int)UInt(imm6);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount);
+
+            operand2 = NOT(operand2);
+
+            Bits result = OR(operand1, operand2);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/orr_log_shift.xml
+        public static void Orr_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
+
+            ShiftType shift_type = DecodeShift(shift);
+            int shift_amount = (int)UInt(imm6);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount);
+
+            Bits result = OR(operand1, operand2);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/rorv.xml
+        public static void Rorv(bool sf, Bits Rm, Bits Rn, Bits Rd)
+        {
+            Bits op2 = "11";
+
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            ShiftType shift_type = DecodeShift(op2);
+
+            /* Operation */
+            Bits operand2 = X(datasize, m);
+
+            Bits result = ShiftReg(datasize, n, shift_type, (int)(UInt(operand2) % datasize)); // BigInteger.Modulus Operator (BigInteger, BigInteger)
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/sbc.xml
+        public static void Sbc(bool sf, Bits Rm, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+
+            operand2 = NOT(operand2);
+
+            (result, _) = AddWithCarry(datasize, operand1, operand2, PSTATE.C);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/sbcs.xml
+        public static void Sbcs(bool sf, Bits Rm, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+            Bits nzcv;
+
+            operand2 = NOT(operand2);
+
+            (result, nzcv) = AddWithCarry(datasize, operand1, operand2, PSTATE.C);
+
+            PSTATE.NZCV(nzcv);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/sdiv.xml
+        public static void Sdiv(bool sf, Bits Rm, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            BigInteger result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+
+            if (IsZero(operand2))
+            {
+                result = (BigInteger)0m;
+            }
+            else
+            {
+                result = RoundTowardsZero(Real(Int(operand1, false)) / Real(Int(operand2, false)));
+            }
+
+            X(d, result.SubBigInteger(datasize - 1, 0));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/sub_addsub_shift.xml
+        public static void Sub_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if shift == '11' then ReservedValue(); */
+            /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
+
+            ShiftType shift_type = DecodeShift(shift);
+            int shift_amount = (int)UInt(imm6);
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount);
+
+            operand2 = NOT(operand2);
+
+            (result, _) = AddWithCarry(datasize, operand1, operand2, true);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/subs_addsub_shift.xml
+        public static void Subs_Rs(bool sf, Bits shift, Bits Rm, Bits imm6, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* if shift == '11' then ReservedValue(); */
+            /* if sf == '0' && imm6<5> == '1' then ReservedValue(); */
+
+            ShiftType shift_type = DecodeShift(shift);
+            int shift_amount = (int)UInt(imm6);
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = ShiftReg(datasize, m, shift_type, shift_amount);
+            Bits nzcv;
+
+            operand2 = NOT(operand2);
+
+            (result, nzcv) = AddWithCarry(datasize, operand1, operand2, true);
+
+            PSTATE.NZCV(nzcv);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/udiv.xml
+        public static void Udiv(bool sf, Bits Rm, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            BigInteger result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+
+            if (IsZero(operand2))
+            {
+                result = (BigInteger)0m;
+            }
+            else
+            {
+                result = RoundTowardsZero(Real(Int(operand1, true)) / Real(Int(operand2, true)));
+            }
+
+            X(d, result.SubBigInteger(datasize - 1, 0));
+        }
+#endregion
+
+#region "AluRx"
+        // https://meriac.github.io/archex/A64_v83A_ISA/add_addsub_ext.xml
+        public static void Add_Rx(bool sf, Bits Rm, Bits option, Bits imm3, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            ExtendType extend_type = DecodeRegExtend(option);
+            int shift = (int)UInt(imm3);
+
+            /* if shift > 4 then ReservedValue(); */
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n));
+            Bits operand2 = ExtendReg(datasize, m, extend_type, shift);
+
+            (result, _) = AddWithCarry(datasize, operand1, operand2, false);
+
+            if (d == 31)
+            {
+                SP(result);
+            }
+            else
+            {
+                X(d, result);
+            }
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/adds_addsub_ext.xml
+        public static void Adds_Rx(bool sf, Bits Rm, Bits option, Bits imm3, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            ExtendType extend_type = DecodeRegExtend(option);
+            int shift = (int)UInt(imm3);
+
+            /* if shift > 4 then ReservedValue(); */
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n));
+            Bits operand2 = ExtendReg(datasize, m, extend_type, shift);
+            Bits nzcv;
+
+            (result, nzcv) = AddWithCarry(datasize, operand1, operand2, false);
+
+            PSTATE.NZCV(nzcv);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/sub_addsub_ext.xml
+        public static void Sub_Rx(bool sf, Bits Rm, Bits option, Bits imm3, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            ExtendType extend_type = DecodeRegExtend(option);
+            int shift = (int)UInt(imm3);
+
+            /* if shift > 4 then ReservedValue(); */
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n));
+            Bits operand2 = ExtendReg(datasize, m, extend_type, shift);
+
+            operand2 = NOT(operand2);
+
+            (result, _) = AddWithCarry(datasize, operand1, operand2, true);
+
+            if (d == 31)
+            {
+                SP(result);
+            }
+            else
+            {
+                X(d, result);
+            }
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/subs_addsub_ext.xml
+        public static void Subs_Rx(bool sf, Bits Rm, Bits option, Bits imm3, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            ExtendType extend_type = DecodeRegExtend(option);
+            int shift = (int)UInt(imm3);
+
+            /* if shift > 4 then ReservedValue(); */
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = (n == 31 ? SP(datasize) : X(datasize, n));
+            Bits operand2 = ExtendReg(datasize, m, extend_type, shift);
+            Bits nzcv;
+
+            operand2 = NOT(operand2);
+
+            (result, nzcv) = AddWithCarry(datasize, operand1, operand2, true);
+
+            PSTATE.NZCV(nzcv);
+
+            X(d, result);
+        }
+#endregion
+
+#region "Bfm"
+        // https://meriac.github.io/archex/A64_v83A_ISA/bfm.xml
+        public static void Bfm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            int R;
+            Bits wmask;
+            Bits tmask;
+
+            /* if sf == '1' && N != '1' then ReservedValue(); */
+            /* if sf == '0' && (N != '0' || immr<5> != '0' || imms<5> != '0') then ReservedValue(); */
+
+            R = (int)UInt(immr);
+            (wmask, tmask) = DecodeBitMasks(datasize, N, imms, immr, false);
+
+            /* Operation */
+            Bits dst = X(datasize, d);
+            Bits src = X(datasize, n);
+
+            // perform bitfield move on low bits
+            Bits bot = OR(AND(dst, NOT(wmask)), AND(ROR(src, R), wmask));
+
+            // combine extension bits and result bits
+            X(d, OR(AND(dst, NOT(tmask)), AND(bot, tmask)));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/sbfm.xml
+        public static void Sbfm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            int R;
+            int S;
+            Bits wmask;
+            Bits tmask;
+
+            /* if sf == '1' && N != '1' then ReservedValue(); */
+            /* if sf == '0' && (N != '0' || immr<5> != '0' || imms<5> != '0') then ReservedValue(); */
+
+            R = (int)UInt(immr);
+            S = (int)UInt(imms);
+            (wmask, tmask) = DecodeBitMasks(datasize, N, imms, immr, false);
+
+            /* Operation */
+            Bits src = X(datasize, n);
+
+            // perform bitfield move on low bits
+            Bits bot = AND(ROR(src, R), wmask);
+
+            // determine extension bits (sign, zero or dest register)
+            Bits top = Replicate(datasize, src[S]);
+
+            // combine extension bits and result bits
+            X(d, OR(AND(top, NOT(tmask)), AND(bot, tmask)));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/ubfm.xml
+        public static void Ubfm(bool sf, bool N, Bits immr, Bits imms, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            int R;
+            Bits wmask;
+            Bits tmask;
+
+            /* if sf == '1' && N != '1' then ReservedValue(); */
+            /* if sf == '0' && (N != '0' || immr<5> != '0' || imms<5> != '0') then ReservedValue(); */
+
+            R = (int)UInt(immr);
+            (wmask, tmask) = DecodeBitMasks(datasize, N, imms, immr, false);
+
+            /* Operation */
+            Bits src = X(datasize, n);
+
+            // perform bitfield move on low bits
+            Bits bot = AND(ROR(src, R), wmask);
+
+            // combine extension bits and result bits
+            X(d, AND(bot, tmask));
+        }
+#endregion
+
+#region "CcmpImm"
+        // https://meriac.github.io/archex/A64_v83A_ISA/ccmn_imm.xml
+        public static void Ccmn_Imm(bool sf, Bits imm5, Bits cond, Bits Rn, Bits nzcv)
+        {
+            /* Decode */
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            Bits flags = nzcv;
+            Bits imm = ZeroExtend(imm5, datasize);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+
+            if (ConditionHolds(cond))
+            {
+                (_, flags) = AddWithCarry(datasize, operand1, imm, false);
+            }
+
+            PSTATE.NZCV(flags);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/ccmp_imm.xml
+        public static void Ccmp_Imm(bool sf, Bits imm5, Bits cond, Bits Rn, Bits nzcv)
+        {
+            /* Decode */
+            int n = (int)UInt(Rn);
+            int datasize = (sf ? 64 : 32);
+
+            Bits flags = nzcv;
+            Bits imm = ZeroExtend(imm5, datasize);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2;
+
+            if (ConditionHolds(cond))
+            {
+                operand2 = NOT(imm);
+                (_, flags) = AddWithCarry(datasize, operand1, operand2, true);
+            }
+
+            PSTATE.NZCV(flags);
+        }
+#endregion
+
+#region "CcmpReg"
+        // https://meriac.github.io/archex/A64_v83A_ISA/ccmn_reg.xml
+        public static void Ccmn_Reg(bool sf, Bits Rm, Bits cond, Bits Rn, Bits nzcv)
+        {
+            /* Decode */
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            Bits flags = nzcv;
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+
+            if (ConditionHolds(cond))
+            {
+                (_, flags) = AddWithCarry(datasize, operand1, operand2, false);
+            }
+
+            PSTATE.NZCV(flags);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/ccmp_reg.xml
+        public static void Ccmp_Reg(bool sf, Bits Rm, Bits cond, Bits Rn, Bits nzcv)
+        {
+            /* Decode */
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            Bits flags = nzcv;
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+
+            if (ConditionHolds(cond))
+            {
+                operand2 = NOT(operand2);
+                (_, flags) = AddWithCarry(datasize, operand1, operand2, true);
+            }
+
+            PSTATE.NZCV(flags);
+        }
+#endregion
+
+#region "Csel"
+        // https://meriac.github.io/archex/A64_v83A_ISA/csel.xml
+        public static void Csel(bool sf, Bits Rm, Bits cond, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+
+            if (ConditionHolds(cond))
+            {
+                result = operand1;
+            }
+            else
+            {
+                result = operand2;
+            }
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/csinc.xml
+        public static void Csinc(bool sf, Bits Rm, Bits cond, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+
+            if (ConditionHolds(cond))
+            {
+                result = operand1;
+            }
+            else
+            {
+                result = operand2 + 1;
+            }
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/csinv.xml
+        public static void Csinv(bool sf, Bits Rm, Bits cond, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+
+            if (ConditionHolds(cond))
+            {
+                result = operand1;
+            }
+            else
+            {
+                result = NOT(operand2);
+            }
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/csneg.xml
+        public static void Csneg(bool sf, Bits Rm, Bits cond, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits result;
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+
+            if (ConditionHolds(cond))
+            {
+                result = operand1;
+            }
+            else
+            {
+                result = NOT(operand2);
+                result = result + 1;
+            }
+
+            X(d, result);
+        }
+#endregion
+
+#region "Mov"
+        // https://meriac.github.io/archex/A64_v83A_ISA/movk.xml
+        public static void Movk(bool sf, Bits hw, Bits imm16, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int datasize = (sf ? 64 : 32);
+
+            /* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */
+
+            int pos = (int)UInt(Bits.Concat(hw, "0000"));
+
+            /* Operation */
+            Bits result = X(datasize, d);
+
+            result[pos + 15, pos] = imm16;
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/movn.xml
+        public static void Movn(bool sf, Bits hw, Bits imm16, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int datasize = (sf ? 64 : 32);
+
+            /* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */
+
+            int pos = (int)UInt(Bits.Concat(hw, "0000"));
+
+            /* Operation */
+            Bits result = Zeros(datasize);
+
+            result[pos + 15, pos] = imm16;
+            result = NOT(result);
+
+            X(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/movz.xml
+        public static void Movz(bool sf, Bits hw, Bits imm16, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int datasize = (sf ? 64 : 32);
+
+            /* if sf == '0' && hw<1> == '1' then UnallocatedEncoding(); */
+
+            int pos = (int)UInt(Bits.Concat(hw, "0000"));
+
+            /* Operation */
+            Bits result = Zeros(datasize);
+
+            result[pos + 15, pos] = imm16;
+
+            X(d, result);
+        }
+#endregion
+
+#region "Mul"
+        // https://meriac.github.io/archex/A64_v83A_ISA/madd.xml
+        public static void Madd(bool sf, Bits Rm, Bits Ra, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int a = (int)UInt(Ra);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+            Bits operand3 = X(datasize, a);
+
+            BigInteger result = UInt(operand3) + (UInt(operand1) * UInt(operand2));
+
+            X(d, result.SubBigInteger(datasize - 1, 0));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/msub.xml
+        public static void Msub(bool sf, Bits Rm, Bits Ra, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int a = (int)UInt(Ra);
+            int datasize = (sf ? 64 : 32);
+
+            /* Operation */
+            Bits operand1 = X(datasize, n);
+            Bits operand2 = X(datasize, m);
+            Bits operand3 = X(datasize, a);
+
+            BigInteger result = UInt(operand3) - (UInt(operand1) * UInt(operand2));
+
+            X(d, result.SubBigInteger(datasize - 1, 0));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/smaddl.xml
+        public static void Smaddl(Bits Rm, Bits Ra, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int a = (int)UInt(Ra);
+
+            /* Operation */
+            Bits operand1 = X(32, n);
+            Bits operand2 = X(32, m);
+            Bits operand3 = X(64, a);
+
+            BigInteger result = Int(operand3, false) + (Int(operand1, false) * Int(operand2, false));
+
+            X(d, result.SubBigInteger(63, 0));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/umaddl.xml
+        public static void Umaddl(Bits Rm, Bits Ra, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int a = (int)UInt(Ra);
+
+            /* Operation */
+            Bits operand1 = X(32, n);
+            Bits operand2 = X(32, m);
+            Bits operand3 = X(64, a);
+
+            BigInteger result = Int(operand3, true) + (Int(operand1, true) * Int(operand2, true));
+
+            X(d, result.SubBigInteger(63, 0));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/smsubl.xml
+        public static void Smsubl(Bits Rm, Bits Ra, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int a = (int)UInt(Ra);
+
+            /* Operation */
+            Bits operand1 = X(32, n);
+            Bits operand2 = X(32, m);
+            Bits operand3 = X(64, a);
+
+            BigInteger result = Int(operand3, false) - (Int(operand1, false) * Int(operand2, false));
+
+            X(d, result.SubBigInteger(63, 0));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/umsubl.xml
+        public static void Umsubl(Bits Rm, Bits Ra, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+            int a = (int)UInt(Ra);
+
+            /* Operation */
+            Bits operand1 = X(32, n);
+            Bits operand2 = X(32, m);
+            Bits operand3 = X(64, a);
+
+            BigInteger result = Int(operand3, true) - (Int(operand1, true) * Int(operand2, true));
+
+            X(d, result.SubBigInteger(63, 0));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/smulh.xml
+        public static void Smulh(Bits Rm, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* Operation */
+            Bits operand1 = X(64, n);
+            Bits operand2 = X(64, m);
+
+            BigInteger result = Int(operand1, false) * Int(operand2, false);
+
+            X(d, result.SubBigInteger(127, 64));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/umulh.xml
+        public static void Umulh(Bits Rm, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* Operation */
+            Bits operand1 = X(64, n);
+            Bits operand2 = X(64, m);
+
+            BigInteger result = Int(operand1, true) * Int(operand2, true);
+
+            X(d, result.SubBigInteger(127, 64));
+        }
+#endregion
+    }
+
+    internal static class SimdFp
+    {
+#region "Simd"
+        // https://meriac.github.io/archex/A64_v83A_ISA/abs_advsimd.xml#ABS_asisdmisc_R
+        public static void Abs_S(Bits size, Bits Rn, Bits Rd)
+        {
+            bool U = false;
+
+            /* Decode Scalar */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+
+            /* if size != '11' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = esize;
+            int elements = 1;
+
+            bool neg = (U == true);
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand = V(datasize, n);
+            BigInteger element;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element = SInt(Elem(operand, e, esize));
+
+                if (neg)
+                {
+                    element = -element;
+                }
+                else
+                {
+                    element = Abs(element);
+                }
+
+                Elem(result, e, esize, element.SubBigInteger(esize - 1, 0));
+            }
+
+            V(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/abs_advsimd.xml#ABS_asimdmisc_R
+        public static void Abs_V(bool Q, Bits size, Bits Rn, Bits Rd)
+        {
+            bool U = false;
+
+            /* Decode Vector */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+
+            /* if size:Q == '110' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = (Q ? 128 : 64);
+            int elements = datasize / esize;
+
+            bool neg = (U == true);
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand = V(datasize, n);
+            BigInteger element;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element = SInt(Elem(operand, e, esize));
+
+                if (neg)
+                {
+                    element = -element;
+                }
+                else
+                {
+                    element = Abs(element);
+                }
+
+                Elem(result, e, esize, element.SubBigInteger(esize - 1, 0));
+            }
+
+            V(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/addp_advsimd_pair.xml
+        public static void Addp_S(Bits size, Bits Rn, Bits Rd)
+        {
+            /* Decode Scalar */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+
+            /* if size != '11' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = esize * 2;
+            // int elements = 2;
+
+            ReduceOp op = ReduceOp.ReduceOp_ADD;
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits operand = V(datasize, n);
+
+            V(d, Reduce(op, operand, esize));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/addv_advsimd.xml
+        public static void Addv_V(bool Q, Bits size, Bits Rn, Bits Rd)
+        {
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+
+            /* if size:Q == '100' then ReservedValue(); */
+            /* if size == '11' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = (Q ? 128 : 64);
+            // int elements = datasize / esize;
+
+            ReduceOp op = ReduceOp.ReduceOp_ADD;
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits operand = V(datasize, n);
+
+            V(d, Reduce(op, operand, esize));
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/neg_advsimd.xml#NEG_asisdmisc_R
+        public static void Neg_S(Bits size, Bits Rn, Bits Rd)
+        {
+            bool U = true;
+
+            /* Decode Scalar */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+
+            /* if size != '11' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = esize;
+            int elements = 1;
+
+            bool neg = (U == true);
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand = V(datasize, n);
+            BigInteger element;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element = SInt(Elem(operand, e, esize));
+
+                if (neg)
+                {
+                    element = -element;
+                }
+                else
+                {
+                    element = Abs(element);
+                }
+
+                Elem(result, e, esize, element.SubBigInteger(esize - 1, 0));
+            }
+
+            V(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/neg_advsimd.xml#NEG_asimdmisc_R
+        public static void Neg_V(bool Q, Bits size, Bits Rn, Bits Rd)
+        {
+            bool U = true;
+
+            /* Decode Vector */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+
+            /* if size:Q == '110' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = (Q ? 128 : 64);
+            int elements = datasize / esize;
+
+            bool neg = (U == true);
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand = V(datasize, n);
+            BigInteger element;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element = SInt(Elem(operand, e, esize));
+
+                if (neg)
+                {
+                    element = -element;
+                }
+                else
+                {
+                    element = Abs(element);
+                }
+
+                Elem(result, e, esize, element.SubBigInteger(esize - 1, 0));
+            }
+
+            V(d, result);
+        }
+#endregion
+
+#region "SimdReg"
+        // https://meriac.github.io/archex/A64_v83A_ISA/add_advsimd.xml#ADD_asisdsame_only
+        public static void Add_S(Bits size, Bits Rm, Bits Rn, Bits Rd)
+        {
+            bool U = false;
+
+            /* Decode Scalar */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* if size != '11' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = esize;
+            int elements = 1;
+
+            bool sub_op = (U == true);
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand1 = V(datasize, n);
+            Bits operand2 = V(datasize, m);
+            Bits element1;
+            Bits element2;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element1 = Elem(operand1, e, esize);
+                element2 = Elem(operand2, e, esize);
+
+                if (sub_op)
+                {
+                    Elem(result, e, esize, element1 - element2);
+                }
+                else
+                {
+                    Elem(result, e, esize, element1 + element2);
+                }
+            }
+
+            V(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/add_advsimd.xml#ADD_asimdsame_only
+        public static void Add_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+        {
+            bool U = false;
+
+            /* Decode Vector */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* if size:Q == '110' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = (Q ? 128 : 64);
+            int elements = datasize / esize;
+
+            bool sub_op = (U == true);
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand1 = V(datasize, n);
+            Bits operand2 = V(datasize, m);
+            Bits element1;
+            Bits element2;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element1 = Elem(operand1, e, esize);
+                element2 = Elem(operand2, e, esize);
+
+                if (sub_op)
+                {
+                    Elem(result, e, esize, element1 - element2);
+                }
+                else
+                {
+                    Elem(result, e, esize, element1 + element2);
+                }
+            }
+
+            V(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/addhn_advsimd.xml
+        public static void Addhn_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+        {
+            bool U = false;
+            bool o1 = false;
+
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* if size == '11' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = 64;
+            int part = (int)UInt(Q);
+            int elements = datasize / esize;
+
+            bool sub_op = (o1 == true);
+            bool round = (U == true);
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand1 = V(2 * datasize, n);
+            Bits operand2 = V(2 * datasize, m);
+            BigInteger round_const = (round ? (BigInteger)1 << (esize - 1) : 0);
+            Bits sum;
+            Bits element1;
+            Bits element2;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element1 = Elem(operand1, e, 2 * esize);
+                element2 = Elem(operand2, e, 2 * esize);
+
+                if (sub_op)
+                {
+                    sum = element1 - element2;
+                }
+                else
+                {
+                    sum = element1 + element2;
+                }
+
+                sum = sum + round_const;
+
+                Elem(result, e, esize, sum[2 * esize - 1, esize]);
+            }
+
+            Vpart(d, part, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/addp_advsimd_vec.xml
+        public static void Addp_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+        {
+            /* Decode Vector */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* if size:Q == '110' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = (Q ? 128 : 64);
+            int elements = datasize / esize;
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand1 = V(datasize, n);
+            Bits operand2 = V(datasize, m);
+            Bits concat =  Bits.Concat(operand2, operand1);
+            Bits element1;
+            Bits element2;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element1 = Elem(concat, 2 * e, esize);
+                element2 = Elem(concat, (2 * e) + 1, esize);
+
+                Elem(result, e, esize, element1 + element2);
+            }
+
+            V(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/raddhn_advsimd.xml
+        public static void Raddhn_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+        {
+            bool U = true;
+            bool o1 = false;
+
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* if size == '11' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = 64;
+            int part = (int)UInt(Q);
+            int elements = datasize / esize;
+
+            bool sub_op = (o1 == true);
+            bool round = (U == true);
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand1 = V(2 * datasize, n);
+            Bits operand2 = V(2 * datasize, m);
+            BigInteger round_const = (round ? (BigInteger)1 << (esize - 1) : 0);
+            Bits sum;
+            Bits element1;
+            Bits element2;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element1 = Elem(operand1, e, 2 * esize);
+                element2 = Elem(operand2, e, 2 * esize);
+
+                if (sub_op)
+                {
+                    sum = element1 - element2;
+                }
+                else
+                {
+                    sum = element1 + element2;
+                }
+
+                sum = sum + round_const;
+
+                Elem(result, e, esize, sum[2 * esize - 1, esize]);
+            }
+
+            Vpart(d, part, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/rsubhn_advsimd.xml
+        public static void Rsubhn_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+        {
+            bool U = true;
+            bool o1 = true;
+
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* if size == '11' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = 64;
+            int part = (int)UInt(Q);
+            int elements = datasize / esize;
+
+            bool sub_op = (o1 == true);
+            bool round = (U == true);
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand1 = V(2 * datasize, n);
+            Bits operand2 = V(2 * datasize, m);
+            BigInteger round_const = (round ? (BigInteger)1 << (esize - 1) : 0);
+            Bits sum;
+            Bits element1;
+            Bits element2;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element1 = Elem(operand1, e, 2 * esize);
+                element2 = Elem(operand2, e, 2 * esize);
+
+                if (sub_op)
+                {
+                    sum = element1 - element2;
+                }
+                else
+                {
+                    sum = element1 + element2;
+                }
+
+                sum = sum + round_const;
+
+                Elem(result, e, esize, sum[2 * esize - 1, esize]);
+            }
+
+            Vpart(d, part, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/sub_advsimd.xml#SUB_asisdsame_only
+        public static void Sub_S(Bits size, Bits Rm, Bits Rn, Bits Rd)
+        {
+            bool U = true;
+
+            /* Decode Scalar */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* if size != '11' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = esize;
+            int elements = 1;
+
+            bool sub_op = (U == true);
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand1 = V(datasize, n);
+            Bits operand2 = V(datasize, m);
+            Bits element1;
+            Bits element2;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element1 = Elem(operand1, e, esize);
+                element2 = Elem(operand2, e, esize);
+
+                if (sub_op)
+                {
+                    Elem(result, e, esize, element1 - element2);
+                }
+                else
+                {
+                    Elem(result, e, esize, element1 + element2);
+                }
+            }
+
+            V(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/sub_advsimd.xml#SUB_asimdsame_only
+        public static void Sub_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+        {
+            bool U = true;
+
+            /* Decode Vector */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* if size:Q == '110' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = (Q ? 128 : 64);
+            int elements = datasize / esize;
+
+            bool sub_op = (U == true);
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand1 = V(datasize, n);
+            Bits operand2 = V(datasize, m);
+            Bits element1;
+            Bits element2;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element1 = Elem(operand1, e, esize);
+                element2 = Elem(operand2, e, esize);
+
+                if (sub_op)
+                {
+                    Elem(result, e, esize, element1 - element2);
+                }
+                else
+                {
+                    Elem(result, e, esize, element1 + element2);
+                }
+            }
+
+            V(d, result);
+        }
+
+        // https://meriac.github.io/archex/A64_v83A_ISA/subhn_advsimd.xml
+        public static void Subhn_V(bool Q, Bits size, Bits Rm, Bits Rn, Bits Rd)
+        {
+            bool U = false;
+            bool o1 = true;
+
+            /* Decode */
+            int d = (int)UInt(Rd);
+            int n = (int)UInt(Rn);
+            int m = (int)UInt(Rm);
+
+            /* if size == '11' then ReservedValue(); */
+
+            int esize = 8 << (int)UInt(size);
+            int datasize = 64;
+            int part = (int)UInt(Q);
+            int elements = datasize / esize;
+
+            bool sub_op = (o1 == true);
+            bool round = (U == true);
+
+            /* Operation */
+            /* CheckFPAdvSIMDEnabled64(); */
+
+            Bits result = new Bits(datasize);
+            Bits operand1 = V(2 * datasize, n);
+            Bits operand2 = V(2 * datasize, m);
+            BigInteger round_const = (round ? (BigInteger)1 << (esize - 1) : 0);
+            Bits sum;
+            Bits element1;
+            Bits element2;
+
+            for (int e = 0; e <= elements - 1; e++)
+            {
+                element1 = Elem(operand1, e, 2 * esize);
+                element2 = Elem(operand2, e, 2 * esize);
+
+                if (sub_op)
+                {
+                    sum = element1 - element2;
+                }
+                else
+                {
+                    sum = element1 + element2;
+                }
+
+                sum = sum + round_const;
+
+                Elem(result, e, esize, sum[2 * esize - 1, esize]);
+            }
+
+            Vpart(d, part, result);
+        }
+#endregion
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs b/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs
new file mode 100644
index 000000000..cfe8aa3d6
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/Tester/Pseudocode.cs
@@ -0,0 +1,1112 @@
+// https://github.com/LDj3SNuD/ARM_v8-A_AArch64_Instructions_Tester/blob/master/Tester/Pseudocode.cs
+
+// https://meriac.github.io/archex/A64_v83A_ISA/shared_pseudocode.xml
+// https://alastairreid.github.io/asl-lexical-syntax/
+
+// | ------------------------|----------------------------------- |
+// | ASL                     | C#                                 |
+// | ------------------------|----------------------------------- |
+// | bit, bits(1); boolean   | bool                               |
+// | bits                    | Bits                               |
+// | integer                 | BigInteger, int                    |
+// | real                    | decimal                            |
+// | ------------------------|----------------------------------- |
+// | '0'; FALSE              | false                              |
+// | '1'; TRUE               | true                               |
+// | '010'                   | "010"                              |
+// | bitsX IN {bitsY, bitsZ} | (bitsX == bitsY || bitsX == bitsZ) |
+// | DIV                     | /                                  |
+// | MOD                     | %                                  |
+// | ------------------------|----------------------------------- |
+
+using System;
+using System.Numerics;
+
+namespace Ryujinx.Tests.Cpu.Tester
+{
+    using Types;
+
+    using static Shared;
+
+    internal static class AArch64
+    {
+#region "exceptions/exceptions/"
+        /* #AArch64.ResetControlRegisters.1 */
+        public static void ResetControlRegisters(bool cold_reset)
+        {
+            PSTATE.N = cold_reset;
+            PSTATE.Z = cold_reset;
+            PSTATE.C = cold_reset;
+            PSTATE.V = cold_reset;
+        }
+
+        /* */
+        public static void TakeReset(bool cold_reset)
+        {
+            /* assert !HighestELUsingAArch32(); */
+
+            // Enter the highest implemented Exception level in AArch64 state
+            if (HaveEL(EL3))
+            {
+                PSTATE.EL = EL3;
+            }
+            else if (HaveEL(EL2))
+            {
+                PSTATE.EL = EL2;
+            }
+            else
+            {
+                PSTATE.EL = EL1;
+            }
+
+            // Reset the system registers and other system components
+            AArch64.ResetControlRegisters(cold_reset);
+
+            // Reset all other PSTATE fields
+            PSTATE.SP = true; // Select stack pointer
+
+            // All registers, bits and fields not reset by the above pseudocode or by the BranchTo() call
+            // below are UNKNOWN bitstrings after reset. In particular, the return information registers
+            // ELR_ELx and SPSR_ELx have UNKNOWN values, so that it
+            // is impossible to return from a reset in an architecturally defined way.
+            AArch64.ResetGeneralRegisters();
+            AArch64.ResetSIMDFPRegisters();
+            AArch64.ResetSpecialRegisters();
+        }
+#endregion
+
+#region "functions/registers/"
+        /* #AArch64.ResetGeneralRegisters.0 */
+        public static void ResetGeneralRegisters()
+        {
+            for (int i = 0; i <= 30; i++)
+            {
+                /* X[i] = bits(64) UNKNOWN; */
+                _R[i].SetAll(false);
+            }
+        }
+
+        /* #AArch64.ResetSIMDFPRegisters.0 */
+        public static void ResetSIMDFPRegisters()
+        {
+            for (int i = 0; i <= 31; i++)
+            {
+                /* V[i] = bits(128) UNKNOWN; */
+                _V[i].SetAll(false);
+            }
+        }
+
+        /* #AArch64.ResetSpecialRegisters.0 */
+        public static void ResetSpecialRegisters()
+        {
+            // AArch64 special registers
+            /* SP_EL0 = bits(64) UNKNOWN; */
+            SP_EL0.SetAll(false);
+            /* SP_EL1 = bits(64) UNKNOWN; */
+            SP_EL1.SetAll(false);
+        }
+
+        // #impl-aarch64.SP.write.0
+        public static void SP(Bits value)
+        {
+            /* int width = value.Count; */
+
+            /* assert width IN {32,64}; */
+
+            if (!PSTATE.SP)
+            {
+                SP_EL0 = ZeroExtend(64, value);
+            }
+            else
+            {
+                switch (PSTATE.EL)
+                {
+                    case Bits bits when bits == EL0:
+                        SP_EL0 = ZeroExtend(64, value);
+                        break;
+                    default:
+                    case Bits bits when bits == EL1:
+                        SP_EL1 = ZeroExtend(64, value);
+                        break;/*
+                    case Bits bits when bits == EL2:
+                        SP_EL2 = ZeroExtend(64, value);
+                        break;
+                    case Bits bits when bits == EL3:
+                        SP_EL3 = ZeroExtend(64, value);
+                        break;*/
+                }
+            }
+        }
+
+        // #impl-aarch64.SP.read.0
+        public static Bits SP(int width)
+        {
+            /* assert width IN {8,16,32,64}; */
+
+            if (!PSTATE.SP)
+            {
+                return SP_EL0[width - 1, 0];
+            }
+            else
+            {
+                switch (PSTATE.EL)
+                {
+                    case Bits bits when bits == EL0:
+                        return SP_EL0[width - 1, 0];
+                    default:
+                    case Bits bits when bits == EL1:
+                        return SP_EL1[width - 1, 0];/*
+                    case Bits bits when bits == EL2:
+                        return SP_EL2[width - 1, 0];
+                    case Bits bits when bits == EL3:
+                        return SP_EL3[width - 1, 0];*/
+                }
+            }
+        }
+
+        // #impl-aarch64.V.write.1
+        public static void V(int n, Bits value)
+        {
+            /* int width = value.Count; */
+
+            /* assert n >= 0 && n <= 31; */
+            /* assert width IN {8,16,32,64,128}; */
+
+            _V[n] = ZeroExtend(128, value);
+        }
+
+        /* #impl-aarch64.V.read.1 */
+        public static Bits V(int width, int n)
+        {
+            /* assert n >= 0 && n <= 31; */
+            /* assert width IN {8,16,32,64,128}; */
+
+            return _V[n][width - 1, 0];
+        }
+
+        /* #impl-aarch64.Vpart.read.2 */
+        public static Bits Vpart(int width, int n, int part)
+        {
+            /* assert n >= 0 && n <= 31; */
+            /* assert part IN {0, 1}; */
+
+            if (part == 0)
+            {
+                /* assert width IN {8,16,32,64}; */
+                return _V[n][width - 1, 0];
+            }
+            else
+            {
+                /* assert width == 64; */
+                return _V[n][(width * 2) - 1, width];
+            }
+        }
+
+        // #impl-aarch64.Vpart.write.2
+        public static void Vpart(int n, int part, Bits value)
+        {
+            int width = value.Count;
+
+            /* assert n >= 0 && n <= 31; */
+            /* assert part IN {0, 1}; */
+
+            if (part == 0)
+            {
+                /* assert width IN {8,16,32,64}; */
+                _V[n] = ZeroExtend(128, value);
+            }
+            else
+            {
+                /* assert width == 64; */
+                _V[n][(width * 2) - 1, width] = value[width - 1, 0];
+            }
+        }
+
+        // #impl-aarch64.X.write.1
+        public static void X(int n, Bits value)
+        {
+            /* int width = value.Count; */
+
+            /* assert n >= 0 && n <= 31; */
+            /* assert width IN {32,64}; */
+
+            if (n != 31)
+            {
+                _R[n] = ZeroExtend(64, value);
+            }
+        }
+
+        /* #impl-aarch64.X.read.1 */
+        public static Bits X(int width, int n)
+        {
+            /* assert n >= 0 && n <= 31; */
+            /* assert width IN {8,16,32,64}; */
+
+            if (n != 31)
+            {
+                return _R[n][width - 1, 0];
+            }
+            else
+            {
+                return Zeros(width);
+            }
+        }
+#endregion
+
+#region "instrs/extendreg/"
+        /* #impl-aarch64.DecodeRegExtend.1 */
+        public static ExtendType DecodeRegExtend(Bits op)
+        {
+            switch (op)
+            {
+                default:
+                case Bits bits when bits == "000":
+                    return ExtendType.ExtendType_UXTB;
+                case Bits bits when bits == "001":
+                    return ExtendType.ExtendType_UXTH;
+                case Bits bits when bits == "010":
+                    return ExtendType.ExtendType_UXTW;
+                case Bits bits when bits == "011":
+                    return ExtendType.ExtendType_UXTX;
+                case Bits bits when bits == "100":
+                    return ExtendType.ExtendType_SXTB;
+                case Bits bits when bits == "101":
+                    return ExtendType.ExtendType_SXTH;
+                case Bits bits when bits == "110":
+                    return ExtendType.ExtendType_SXTW;
+                case Bits bits when bits == "111":
+                    return ExtendType.ExtendType_SXTX;
+            }
+        }
+
+        /* #impl-aarch64.ExtendReg.3 */
+        public static Bits ExtendReg(int N, int reg, ExtendType type, int shift)
+        {
+            /* assert shift >= 0 && shift <= 4; */
+
+            Bits val = X(N, reg);
+            bool unsigned;
+            int len;
+
+            switch (type)
+            {
+                default:
+                case ExtendType.ExtendType_SXTB:
+                    unsigned = false; len = 8;
+                    break;
+                case ExtendType.ExtendType_SXTH:
+                    unsigned = false; len = 16;
+                    break;
+                case ExtendType.ExtendType_SXTW:
+                    unsigned = false; len = 32;
+                    break;
+                case ExtendType.ExtendType_SXTX:
+                    unsigned = false; len = 64;
+                    break;
+                case ExtendType.ExtendType_UXTB:
+                    unsigned = true;  len = 8;
+                    break;
+                case ExtendType.ExtendType_UXTH:
+                    unsigned = true;  len = 16;
+                    break;
+                case ExtendType.ExtendType_UXTW:
+                    unsigned = true;  len = 32;
+                    break;
+                case ExtendType.ExtendType_UXTX:
+                    unsigned = true;  len = 64;
+                    break;
+            }
+
+            // Note the extended width of the intermediate value and
+            // that sign extension occurs from bit <len+shift-1>, not
+            // from bit <len-1>. This is equivalent to the instruction
+            //   [SU]BFIZ Rtmp, Rreg, #shift, #len
+            // It may also be seen as a sign/zero extend followed by a shift:
+            //   LSL(Extend(val<len-1:0>, N, unsigned), shift);
+
+            len = Min(len, N - shift);
+            return Extend(Bits.Concat(val[len - 1, 0], Zeros(shift)), N, unsigned);
+        }
+
+        // #ExtendType
+        public enum ExtendType {ExtendType_SXTB, ExtendType_SXTH, ExtendType_SXTW, ExtendType_SXTX,
+                                ExtendType_UXTB, ExtendType_UXTH, ExtendType_UXTW, ExtendType_UXTX};
+#endregion
+
+#region "instrs/integer/bitmasks/"
+        /* #impl-aarch64.DecodeBitMasks.4 */
+        public static (Bits, Bits) DecodeBitMasks(int M, bool immN, Bits imms, Bits immr, bool immediate)
+        {
+            Bits tmask, wmask;
+            Bits tmask_and, wmask_and;
+            Bits tmask_or, wmask_or;
+            Bits levels;
+
+            // Compute log2 of element size
+            // 2^len must be in range [2, M]
+            int len = HighestSetBit(Bits.Concat(immN, NOT(imms)));
+            /* if len < 1 then ReservedValue(); */
+            /* assert M >= (1 << len); */
+
+            // Determine S, R and S - R parameters
+            levels = ZeroExtend(Ones(len), 6);
+
+            // For logical immediates an all-ones value of S is reserved
+            // since it would generate a useless all-ones result (many times)
+            /* if immediate && (imms AND levels) == levels then ReservedValue(); */
+
+            BigInteger S = UInt(AND(imms, levels));
+            BigInteger R = UInt(AND(immr, levels));
+            BigInteger diff = S - R; // 6-bit subtract with borrow
+
+            // Compute "top mask"
+            tmask_and = OR(diff.SubBigInteger(5, 0), NOT(levels));
+            tmask_or = AND(diff.SubBigInteger(5, 0), levels);
+
+            tmask = Ones(64);
+            tmask = OR(AND(tmask, Replicate(Bits.Concat(Replicate(tmask_and[0],  1), Ones( 1)), 32)), Replicate(Bits.Concat(Zeros( 1), Replicate(tmask_or[0],  1)), 32));
+            tmask = OR(AND(tmask, Replicate(Bits.Concat(Replicate(tmask_and[1],  2), Ones( 2)), 16)), Replicate(Bits.Concat(Zeros( 2), Replicate(tmask_or[1],  2)), 16));
+            tmask = OR(AND(tmask, Replicate(Bits.Concat(Replicate(tmask_and[2],  4), Ones( 4)),  8)), Replicate(Bits.Concat(Zeros( 4), Replicate(tmask_or[2],  4)),  8));
+            tmask = OR(AND(tmask, Replicate(Bits.Concat(Replicate(tmask_and[3],  8), Ones( 8)),  4)), Replicate(Bits.Concat(Zeros( 8), Replicate(tmask_or[3],  8)),  4));
+            tmask = OR(AND(tmask, Replicate(Bits.Concat(Replicate(tmask_and[4], 16), Ones(16)),  2)), Replicate(Bits.Concat(Zeros(16), Replicate(tmask_or[4], 16)),  2));
+            tmask = OR(AND(tmask, Replicate(Bits.Concat(Replicate(tmask_and[5], 32), Ones(32)),  1)), Replicate(Bits.Concat(Zeros(32), Replicate(tmask_or[5], 32)),  1));
+
+            // Compute "wraparound mask"
+            wmask_and = OR(immr, NOT(levels));
+            wmask_or = AND(immr, levels);
+
+            wmask = Zeros(64);
+            wmask = OR(AND(wmask, Replicate(Bits.Concat(Ones( 1), Replicate(wmask_and[0],  1)), 32)), Replicate(Bits.Concat(Replicate(wmask_or[0],  1), Zeros( 1)), 32));
+            wmask = OR(AND(wmask, Replicate(Bits.Concat(Ones( 2), Replicate(wmask_and[1],  2)), 16)), Replicate(Bits.Concat(Replicate(wmask_or[1],  2), Zeros( 2)), 16));
+            wmask = OR(AND(wmask, Replicate(Bits.Concat(Ones( 4), Replicate(wmask_and[2],  4)),  8)), Replicate(Bits.Concat(Replicate(wmask_or[2],  4), Zeros( 4)),  8));
+            wmask = OR(AND(wmask, Replicate(Bits.Concat(Ones( 8), Replicate(wmask_and[3],  8)),  4)), Replicate(Bits.Concat(Replicate(wmask_or[3],  8), Zeros( 8)),  4));
+            wmask = OR(AND(wmask, Replicate(Bits.Concat(Ones(16), Replicate(wmask_and[4], 16)),  2)), Replicate(Bits.Concat(Replicate(wmask_or[4], 16), Zeros(16)),  2));
+            wmask = OR(AND(wmask, Replicate(Bits.Concat(Ones(32), Replicate(wmask_and[5], 32)),  1)), Replicate(Bits.Concat(Replicate(wmask_or[5], 32), Zeros(32)),  1));
+
+            if (diff.SubBigInteger(6)) // borrow from S - R
+            {
+                wmask = AND(wmask, tmask);
+            }
+            else
+            {
+                wmask = OR(wmask, tmask);
+            }
+
+            return (wmask[M - 1, 0], tmask[M - 1, 0]);
+        }
+#endregion
+
+#region "instrs/integer/shiftreg/"
+        /* #impl-aarch64.DecodeShift.1 */
+        public static ShiftType DecodeShift(Bits op)
+        {
+            switch (op)
+            {
+                default:
+                case Bits bits when bits == "00":
+                    return ShiftType.ShiftType_LSL;
+                case Bits bits when bits == "01":
+                    return ShiftType.ShiftType_LSR;
+                case Bits bits when bits == "10":
+                    return ShiftType.ShiftType_ASR;
+                case Bits bits when bits == "11":
+                    return ShiftType.ShiftType_ROR;
+            }
+        }
+
+        /* #impl-aarch64.ShiftReg.3 */
+        public static Bits ShiftReg(int N, int reg, ShiftType type, int amount)
+        {
+            Bits result = X(N, reg);
+
+            switch (type)
+            {
+                default:
+                case ShiftType.ShiftType_LSL:
+                    result = LSL(result, amount);
+                    break;
+                case ShiftType.ShiftType_LSR:
+                    result = LSR(result, amount);
+                    break;
+                case ShiftType.ShiftType_ASR:
+                    result = ASR(result, amount);
+                    break;
+                case ShiftType.ShiftType_ROR:
+                    result = ROR(result, amount);
+                    break;
+            }
+
+            return result;
+        }
+
+        // #ShiftType
+        public enum ShiftType {ShiftType_LSL, ShiftType_LSR, ShiftType_ASR, ShiftType_ROR};
+#endregion
+
+#region "instrs/vector/reduce/reduceop/"
+        public static Bits Reduce(ReduceOp op, Bits input, int esize)
+        {
+            int N = input.Count;
+
+            int half;
+            Bits hi;
+            Bits lo;
+            Bits result = new Bits(esize);
+
+            if (N == esize)
+            {
+                return new Bits(input);
+            }
+
+            half = N / 2;
+            hi = Reduce(op, input[N - 1, half], esize);
+            lo = Reduce(op, input[half - 1, 0], esize);
+
+            switch (op)
+            {
+                case ReduceOp.ReduceOp_FMINNUM:
+                    /* result = FPMinNum(lo, hi, FPCR); */
+                    break;
+                case ReduceOp.ReduceOp_FMAXNUM:
+                    /* result = FPMaxNum(lo, hi, FPCR); */
+                    break;
+                case ReduceOp.ReduceOp_FMIN:
+                    /* result = FPMin(lo, hi, FPCR); */
+                    break;
+                case ReduceOp.ReduceOp_FMAX:
+                    /* result = FPMax(lo, hi, FPCR); */
+                    break;
+                case ReduceOp.ReduceOp_FADD:
+                    /* result = FPAdd(lo, hi, FPCR); */
+                    break;
+                default:
+                case ReduceOp.ReduceOp_ADD:
+                    result = lo + hi;
+                    break;
+            }
+
+            return result;
+        }
+
+        public enum ReduceOp {ReduceOp_FMINNUM, ReduceOp_FMAXNUM,
+                              ReduceOp_FMIN, ReduceOp_FMAX,
+                              ReduceOp_FADD, ReduceOp_ADD};
+#endregion
+    }
+
+    internal static class Shared
+    {
+        static Shared()
+        {
+            _R = new Bits[31];
+            for (int i = 0; i <= 30; i++)
+            {
+                _R[i] = new Bits(64, false);
+            }
+
+            _V = new Bits[32];
+            for (int i = 0; i <= 31; i++)
+            {
+                _V[i] = new Bits(128, false);
+            }
+
+            SP_EL0 = new Bits(64, false);
+            SP_EL1 = new Bits(64, false);
+
+            PSTATE.N = false;
+            PSTATE.Z = false;
+            PSTATE.C = false;
+            PSTATE.V = false;
+            PSTATE.EL = EL1;
+            PSTATE.SP = true;
+        }
+
+#region "functions/common/"
+        /* */
+        public static Bits AND(Bits x, Bits y)
+        {
+            return x.And(y);
+        }
+
+        // #impl-shared.ASR.2
+        public static Bits ASR(Bits x, int shift)
+        {
+            int N = x.Count;
+
+            /* assert shift >= 0; */
+
+            Bits result;
+
+            if (shift == 0)
+            {
+                result = new Bits(x);
+            }
+            else
+            {
+                (result, _) = ASR_C(x, shift);
+            }
+
+            return result;
+        }
+
+        // #impl-shared.ASR_C.2
+        public static (Bits, bool) ASR_C(Bits x, int shift)
+        {
+            int N = x.Count;
+
+            /* assert shift > 0; */
+
+            Bits extended_x = SignExtend(x, shift + N);
+            Bits result = extended_x[shift + N - 1, shift];
+            bool carry_out = extended_x[shift - 1];
+
+            return (result, carry_out);
+        }
+
+        // #impl-shared.Abs.1
+        public static BigInteger Abs(BigInteger x)
+        {
+            return (x >= 0 ? x : -x);
+        }
+
+        // #impl-shared.CountLeadingSignBits.1
+        public static int CountLeadingSignBits(Bits x)
+        {
+            int N = x.Count;
+
+            return CountLeadingZeroBits(EOR(x[N - 1, 1], x[N - 2, 0]));
+        }
+
+        // #impl-shared.CountLeadingZeroBits.1
+        public static int CountLeadingZeroBits(Bits x)
+        {
+            int N = x.Count;
+
+            return (N - 1 - HighestSetBit(x));
+        }
+
+        // #impl-shared.Elem.read.3
+        public static Bits Elem(/*in */Bits vector, int e, int size)
+        {
+            /* int N = vector.Count; */
+
+            /* assert e >= 0 && (e+1)*size <= N; */
+
+            return vector[e * size + size - 1, e * size];
+        }
+
+        // #impl-shared.Elem.write.3
+        public static void Elem(/*out */Bits vector, int e, int size, Bits value)
+        {
+            /* int N = vector.Count; */
+
+            /* assert e >= 0 && (e+1)*size <= N; */
+
+            vector[(e + 1) * size - 1, e * size] = value;
+        }
+
+        /* */
+        public static Bits EOR(Bits x, Bits y)
+        {
+            return x.Xor(y);
+        }
+
+        // #impl-shared.Extend.3
+        public static Bits Extend(Bits x, int N, bool unsigned)
+        {
+            if (unsigned)
+            {
+                return ZeroExtend(x, N);
+            }
+            else
+            {
+                return SignExtend(x, N);
+            }
+        }
+
+        /* #impl-shared.Extend.2 */
+        public static Bits Extend(int N, Bits x, bool unsigned)
+        {
+            return Extend(x, N, unsigned);
+        }
+
+        // #impl-shared.HighestSetBit.1
+        public static int HighestSetBit(Bits x)
+        {
+            int N = x.Count;
+
+            for (int i = N - 1; i >= 0; i--)
+            {
+                if (x[i])
+                {
+                    return i;
+                }
+            }
+
+            return -1;
+        }
+
+        // #impl-shared.Int.2
+        public static BigInteger Int(Bits x, bool unsigned)
+        {
+            return (unsigned ? UInt(x) : SInt(x));
+        }
+
+        // #impl-shared.IsOnes.1
+        public static bool IsOnes(Bits x)
+        {
+            int N = x.Count;
+
+            return (x == Ones(N));
+        }
+
+        // #impl-shared.IsZero.1
+        public static bool IsZero(Bits x)
+        {
+            int N = x.Count;
+
+            return (x == Zeros(N));
+        }
+
+        // #impl-shared.IsZeroBit.1
+        public static bool IsZeroBit(Bits x)
+        {
+            return IsZero(x);
+        }
+
+        // #impl-shared.LSL.2
+        public static Bits LSL(Bits x, int shift)
+        {
+            int N = x.Count;
+
+            /* assert shift >= 0; */
+
+            Bits result;
+
+            if (shift == 0)
+            {
+                result = new Bits(x);
+            }
+            else
+            {
+                (result, _) = LSL_C(x, shift);
+            }
+
+            return result;
+        }
+
+        // #impl-shared.LSL_C.2
+        public static (Bits, bool) LSL_C(Bits x, int shift)
+        {
+            int N = x.Count;
+
+            /* assert shift > 0; */
+
+            Bits extended_x = Bits.Concat(x, Zeros(shift));
+            Bits result = extended_x[N - 1, 0];
+            bool carry_out = extended_x[N];
+
+            return (result, carry_out);
+        }
+
+        // #impl-shared.LSR.2
+        public static Bits LSR(Bits x, int shift)
+        {
+            int N = x.Count;
+
+            /* assert shift >= 0; */
+
+            Bits result;
+
+            if (shift == 0)
+            {
+                result = new Bits(x);
+            }
+            else
+            {
+                (result, _) = LSR_C(x, shift);
+            }
+
+            return result;
+        }
+
+        // #impl-shared.LSR_C.2
+        public static (Bits, bool) LSR_C(Bits x, int shift)
+        {
+            int N = x.Count;
+
+            /* assert shift > 0; */
+
+            Bits extended_x = ZeroExtend(x, shift + N);
+            Bits result = extended_x[shift + N - 1, shift];
+            bool carry_out = extended_x[shift - 1];
+
+            return (result, carry_out);
+        }
+
+        // #impl-shared.Min.2
+        public static int Min(int a, int b)
+        {
+            if (a <= b)
+            {
+                return a;
+            }
+            else
+            {
+                return b;
+            }
+        }
+
+        /* #impl-shared.NOT.1 */
+        public static Bits NOT(Bits x)
+        {
+            return x.Not();
+        }
+
+        // #impl-shared.Ones.1
+        public static Bits Ones(int N)
+        {
+            return Replicate(true, N);
+        }
+
+        /* */
+        public static Bits OR(Bits x, Bits y)
+        {
+            return x.Or(y);
+        }
+
+        /* */
+        public static decimal Real(BigInteger value)
+        {
+            return (decimal)value;
+        }
+
+        // #impl-shared.ROR.2
+        public static Bits ROR(Bits x, int shift)
+        {
+            /* assert shift >= 0; */
+
+            Bits result;
+
+            if (shift == 0)
+            {
+                result = new Bits(x);
+            }
+            else
+            {
+                (result, _) = ROR_C(x, shift);
+            }
+
+            return result;
+        }
+
+        // #impl-shared.ROR_C.2
+        public static (Bits, bool) ROR_C(Bits x, int shift)
+        {
+            int N = x.Count;
+
+            /* assert shift != 0; */
+
+            int m = shift % N;
+            Bits result = OR(LSR(x, m), LSL(x, N - m));
+            bool carry_out = result[N - 1];
+
+            return (result, carry_out);
+        }
+
+        /* #impl-shared.Replicate.1 */
+        public static Bits Replicate(int N, Bits x)
+        {
+            int M = x.Count;
+
+            /* assert N MOD M == 0; */
+
+            return Replicate(x, N / M);
+        }
+
+        /* #impl-shared.Replicate.2 */
+        public static Bits Replicate(Bits x, int N)
+        {
+            int M = x.Count;
+
+            bool[] dst = new bool[M * N];
+
+            for (int i = 0; i < N; i++)
+            {
+                x.CopyTo(dst, i * M);
+            }
+
+            return new Bits(dst);
+        }
+
+        /* #impl-shared.RoundDown.1 */
+        public static BigInteger RoundDown(decimal x)
+        {
+            return (BigInteger)Decimal.Floor(x);
+        }
+
+        // #impl-shared.RoundTowardsZero.1
+        public static BigInteger RoundTowardsZero(decimal x)
+        {
+            if (x == 0.0m)
+            {
+                return (BigInteger)0m;
+            }
+            else if (x >= 0.0m)
+            {
+                return RoundDown(x);
+            }
+            else
+            {
+                return RoundUp(x);
+            }
+        }
+
+        /* #impl-shared.RoundUp.1 */
+        public static BigInteger RoundUp(decimal x)
+        {
+            return (BigInteger)Decimal.Ceiling(x);
+        }
+
+        // #impl-shared.SInt.1
+        public static BigInteger SInt(Bits x)
+        {
+            int N = x.Count;
+
+            BigInteger result = 0;
+
+            for (int i = 0; i <= N - 1; i++)
+            {
+                if (x[i])
+                {
+                    result = result + BigInteger.Pow(2, i);
+                }
+            }
+
+            if (x[N - 1])
+            {
+                result = result - BigInteger.Pow(2, N);
+            }
+
+            return result;
+        }
+
+        // #impl-shared.SignExtend.2
+        public static Bits SignExtend(Bits x, int N)
+        {
+            int M = x.Count;
+
+            /* assert N >= M; */
+
+            return Bits.Concat(Replicate(x[M - 1], N - M), x);
+        }
+
+        /* #impl-shared.SignExtend.1 */
+        public static Bits SignExtend(int N, Bits x)
+        {
+            return SignExtend(x, N);
+        }
+
+        // #impl-shared.UInt.1
+        public static BigInteger UInt(Bits x)
+        {
+            int N = x.Count;
+
+            BigInteger result = 0;
+
+            for (int i = 0; i <= N - 1; i++)
+            {
+                if (x[i])
+                {
+                    result = result + BigInteger.Pow(2, i);
+                }
+            }
+
+            return result;
+        }
+
+        // #impl-shared.ZeroExtend.2
+        public static Bits ZeroExtend(Bits x, int N)
+        {
+            int M = x.Count;
+
+            /* assert N >= M; */
+
+            return Bits.Concat(Zeros(N - M), x);
+        }
+
+        /* #impl-shared.ZeroExtend.1 */
+        public static Bits ZeroExtend(int N, Bits x)
+        {
+            return ZeroExtend(x, N);
+        }
+
+        // #impl-shared.Zeros.1
+        /* #impl-shared.Zeros.0 */
+        public static Bits Zeros(int N)
+        {
+            return Replicate(false, N);
+        }
+#endregion
+
+#region "functions/crc/"
+        // #impl-shared.BitReverse.1
+        public static Bits BitReverse(Bits data)
+        {
+            int N = data.Count;
+
+            Bits result = new Bits(N);
+
+            for (int i = 0; i <= N - 1; i++)
+            {
+                result[N - i - 1] = data[i];
+            }
+
+            return result;
+        }
+
+        // #impl-shared.Poly32Mod2.2
+        public static Bits Poly32Mod2(Bits _data, Bits poly)
+        {
+            int N = _data.Count;
+
+            /* assert N > 32; */
+
+            Bits data = new Bits(_data);
+
+            for (int i = N - 1; i >= 32; i--)
+            {
+                if (data[i])
+                {
+                    data[i - 1, 0] = EOR(data[i - 1, 0], Bits.Concat(poly, Zeros(i - 32)));
+                }
+            }
+
+            return data[31, 0];
+        }
+#endregion
+
+#region "functions/integer/"
+        /* #impl-shared.AddWithCarry.3 */
+        public static (Bits, Bits) AddWithCarry(int N, Bits x, Bits y, bool carry_in)
+        {
+            BigInteger unsigned_sum = UInt(x) + UInt(y) + UInt(carry_in);
+            BigInteger signed_sum = SInt(x) + SInt(y) + UInt(carry_in);
+
+            Bits result = unsigned_sum.SubBigInteger(N - 1, 0); // same value as signed_sum<N-1:0>
+
+            bool n = result[N - 1];
+            bool z = IsZero(result);
+            bool c = !(UInt(result) == unsigned_sum);
+            bool v = !(SInt(result) == signed_sum);
+
+            return (result, Bits.Concat(n, z, c, v));
+        }
+#endregion
+
+#region "functions/registers/"
+        public static readonly Bits[] _R;
+
+        public static readonly Bits[] _V;
+
+        public static Bits SP_EL0;
+        public static Bits SP_EL1;
+#endregion
+
+#region "functions/system/"
+        // #impl-shared.ConditionHolds.1
+        public static bool ConditionHolds(Bits cond)
+        {
+            bool result;
+
+            // Evaluate base condition.
+            switch (cond[3, 1])
+            {
+                case Bits bits when bits == "000":
+                    result = (PSTATE.Z == true);                          // EQ or NE
+                    break;
+                case Bits bits when bits == "001":
+                    result = (PSTATE.C == true);                          // CS or CC
+                    break;
+                case Bits bits when bits == "010":
+                    result = (PSTATE.N == true);                          // MI or PL
+                    break;
+                case Bits bits when bits == "011":
+                    result = (PSTATE.V == true);                          // VS or VC
+                    break;
+                case Bits bits when bits == "100":
+                    result = (PSTATE.C == true && PSTATE.Z == false);     // HI or LS
+                    break;
+                case Bits bits when bits == "101":
+                    result = (PSTATE.N == PSTATE.V);                      // GE or LT
+                    break;
+                case Bits bits when bits == "110":
+                    result = (PSTATE.N == PSTATE.V && PSTATE.Z == false); // GT or LE
+                    break;
+                default:
+                case Bits bits when bits == "111":
+                    result = true;                                        // AL
+                    break;
+            }
+
+            // Condition flag values in the set '111x' indicate always true
+            // Otherwise, invert condition if necessary.
+            if (cond[0] == true && cond != "1111")
+            {
+                result = !result;
+            }
+
+            return result;
+        }
+
+        // #EL3
+        public static readonly Bits EL3 = "11";
+        // #EL2
+        public static readonly Bits EL2 = "10";
+        // #EL1
+        public static readonly Bits EL1 = "01";
+        // #EL0
+        public static readonly Bits EL0 = "00";
+
+        /* #impl-shared.HaveEL.1 */
+        public static bool HaveEL(Bits el)
+        {
+            if (el == EL1 || el == EL0)
+            {
+                return true; // EL1 and EL0 must exist
+            }
+
+            return false;
+        }
+
+        public static ProcState PSTATE;
+
+        /* #ProcState */
+        internal struct ProcState
+        {
+            public void NZCV(Bits nzcv) // ASL: ".<,,,>".
+            {
+                N = nzcv[3];
+                Z = nzcv[2];
+                C = nzcv[1];
+                V = nzcv[0];
+            }
+
+            public void NZCV(bool n, bool z, bool c, bool v) // ASL: ".<,,,>".
+            {
+                N = n;
+                Z = z;
+                C = c;
+                V = v;
+            }
+
+            public bool N;  // Negative condition flag
+            public bool Z;  // Zero condition flag
+            public bool C;  // Carry condition flag
+            public bool V;  // oVerflow condition flag
+            public Bits EL; // Exception Level
+            public bool SP; // Stack pointer select: 0=SP0, 1=SPx [AArch64 only]
+        }
+#endregion
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/Tester/Types/Bits.cs b/Ryujinx.Tests/Cpu/Tester/Types/Bits.cs
new file mode 100644
index 000000000..30d632640
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/Tester/Types/Bits.cs
@@ -0,0 +1,313 @@
+// https://github.com/LDj3SNuD/ARM_v8-A_AArch64_Instructions_Tester/blob/master/Tester/Types/Bits.cs
+
+// https://github.com/dotnet/corefx/blob/master/src/System.Collections/src/System/Collections/BitArray.cs
+
+using System;
+using System.Collections;
+using System.Numerics;
+
+namespace Ryujinx.Tests.Cpu.Tester.Types
+{
+    internal sealed class Bits : ICollection, IEnumerable, IEquatable<Bits>
+    {
+        private BitArray bits;
+
+        public Bits(bool[] values) => bits = new BitArray(values);
+        public Bits(byte[] bytes) => bits = new BitArray(bytes);
+        public Bits(Bits bits) => this.bits = new BitArray(bits.bits);
+        public Bits(int length) => bits = new BitArray(length);
+        public Bits(int length, bool defaultValue) => bits = new BitArray(length, defaultValue);
+        private Bits(BitArray bitArray) => bits = new BitArray(bitArray);
+        public Bits(ulong value) => bits = new BitArray(BitConverter.GetBytes(value));
+        public Bits(uint value) => bits = new BitArray(BitConverter.GetBytes(value));
+        public Bits(ushort value) => bits = new BitArray(BitConverter.GetBytes(value));
+        public Bits(byte value) => bits = new BitArray(new byte[1] {value});
+
+        private BitArray ToBitArray() => new BitArray(bits);
+        public ulong ToUInt64()
+        {
+            byte[] dst = new byte[8];
+
+            bits.CopyTo(dst, 0);
+
+            return BitConverter.ToUInt64(dst, 0);
+        }
+        public uint ToUInt32()
+        {
+            byte[] dst = new byte[4];
+
+            bits.CopyTo(dst, 0);
+
+            return BitConverter.ToUInt32(dst, 0);
+        }
+        public ushort ToUInt16()
+        {
+            byte[] dst = new byte[2];
+
+            bits.CopyTo(dst, 0);
+
+            return BitConverter.ToUInt16(dst, 0);
+        }
+        public byte ToByte()
+        {
+            byte[] dst = new byte[1];
+
+            bits.CopyTo(dst, 0);
+
+            return dst[0];
+        }
+
+        public bool this[int index] // ASL: "<>".
+        {
+            get
+            {
+                return bits.Get(index);
+            }
+            set
+            {
+                bits.Set(index, value);
+            }
+        }
+        public Bits this[int highIndex, int lowIndex] // ASL: "<:>".
+        {
+            get
+            {
+                if (highIndex < lowIndex)
+                {
+                    throw new IndexOutOfRangeException();
+                }
+
+                bool[] dst = new bool[highIndex - lowIndex + 1];
+
+                for (int i = lowIndex, n = 0; i <= highIndex; i++, n++)
+                {
+                    dst[n] = bits.Get(i);
+                }
+
+                return new Bits(dst);
+            }
+            set
+            {
+                if (highIndex < lowIndex)
+                {
+                    throw new IndexOutOfRangeException();
+                }
+
+                for (int i = lowIndex, n = 0; i <= highIndex; i++, n++)
+                {
+                    bits.Set(i, value.Get(n));
+                }
+            }
+        }
+
+        public bool IsReadOnly { get => false; } // Mutable.
+        public int Count { get => bits.Count; }
+        public bool IsSynchronized { get => bits.IsSynchronized; }
+        public object SyncRoot { get => bits.SyncRoot; }
+        public Bits And(Bits value) => new Bits(new BitArray(this.bits).And(value.bits)); // Immutable.
+        public void CopyTo(Array array, int index) => bits.CopyTo(array, index);
+        public bool Get(int index) => bits.Get(index);
+        public IEnumerator GetEnumerator() => bits.GetEnumerator();
+        //public Bits LeftShift(int count) => new Bits(new BitArray(bits).LeftShift(count)); // Immutable.
+        public Bits Not() => new Bits(new BitArray(bits).Not()); // Immutable.
+        public Bits Or(Bits value) => new Bits(new BitArray(this.bits).Or(value.bits)); // Immutable.
+        //public Bits RightShift(int count) => new Bits(new BitArray(bits).RightShift(count)); // Immutable.
+        public void Set(int index, bool value) => bits.Set(index, value);
+        public void SetAll(bool value) => bits.SetAll(value);
+        public Bits Xor(Bits value) => new Bits(new BitArray(this.bits).Xor(value.bits)); // Immutable.
+
+        public static Bits Concat(Bits highBits, Bits lowBits) // ASL: ":".
+        {
+            if (((object)lowBits == null) || ((object)highBits == null))
+            {
+                throw new ArgumentNullException();
+            }
+
+            bool[] dst = new bool[lowBits.Count + highBits.Count];
+
+            lowBits.CopyTo(dst, 0);
+            highBits.CopyTo(dst, lowBits.Count);
+
+            return new Bits(dst);
+        }
+        public static Bits Concat(bool bit3, bool bit2, bool bit1, bool bit0) // ASL: ":::".
+        {
+            return new Bits(new bool[] {bit0, bit1, bit2, bit3});
+        }
+
+        public static implicit operator Bits(bool value) => new Bits(1, value);
+        public static implicit operator Bits(string value)
+        {
+            if (String.IsNullOrEmpty(value))
+            {
+                throw new InvalidCastException();
+            }
+
+            bool[] dst = new bool[value.Length];
+
+            for (int i = value.Length - 1, n = 0; i >= 0; i--, n++)
+            {
+                if (value[i] == '1')
+                {
+                    dst[n] = true;
+                }
+                else if (value[i] == '0')
+                {
+                    dst[n] = false;
+                }
+                else
+                {
+                    throw new InvalidCastException();
+                }
+            }
+
+            return new Bits(dst);
+        }
+        public static explicit operator bool(Bits bit)
+        {
+            if (((object)bit == null) || (bit.Count != 1))
+            {
+                throw new InvalidCastException();
+            }
+
+            return bit.Get(0);
+        }
+
+        public static Bits operator +(Bits left, BigInteger right) // ASL: "+".
+        {
+            if (((object)left == null) || ((object)right == null))
+            {
+                throw new ArgumentNullException();
+            }
+
+            BigInteger dst;
+
+            switch (left.Count)
+            {
+                case  8: dst = left.ToByte()   + right; break;
+                case 16: dst = left.ToUInt16() + right; break;
+                case 32: dst = left.ToUInt32() + right; break;
+                case 64: dst = left.ToUInt64() + right; break;
+
+                default: throw new ArgumentOutOfRangeException();
+            }
+
+            return dst.SubBigInteger(left.Count - 1, 0);
+        }
+        public static Bits operator +(Bits left, Bits right) // ASL: "+".
+        {
+            if (((object)left == null) || ((object)right == null))
+            {
+                throw new ArgumentNullException();
+            }
+
+            if (left.Count != right.Count)
+            {
+                throw new ArgumentException();
+            }
+
+            BigInteger dst;
+
+            switch (left.Count)
+            {
+                case  8: dst = left.ToByte()   + (BigInteger)right.ToByte();   break;
+                case 16: dst = left.ToUInt16() + (BigInteger)right.ToUInt16(); break;
+                case 32: dst = left.ToUInt32() + (BigInteger)right.ToUInt32(); break;
+                case 64: dst = left.ToUInt64() + (BigInteger)right.ToUInt64(); break;
+
+                default: throw new ArgumentOutOfRangeException();
+            }
+
+            return dst.SubBigInteger(left.Count - 1, 0);
+        }
+        public static Bits operator -(Bits left, Bits right) // ASL: "-".
+        {
+            if (((object)left == null) || ((object)right == null))
+            {
+                throw new ArgumentNullException();
+            }
+
+            if (left.Count != right.Count)
+            {
+                throw new ArgumentException();
+            }
+
+            BigInteger dst;
+
+            switch (left.Count)
+            {
+                case  8: dst = left.ToByte()   - (BigInteger)right.ToByte();   break;
+                case 16: dst = left.ToUInt16() - (BigInteger)right.ToUInt16(); break;
+                case 32: dst = left.ToUInt32() - (BigInteger)right.ToUInt32(); break;
+                case 64: dst = left.ToUInt64() - (BigInteger)right.ToUInt64(); break;
+
+                default: throw new ArgumentOutOfRangeException();
+            }
+
+            return dst.SubBigInteger(left.Count - 1, 0);
+        }
+        public static bool operator ==(Bits left, Bits right) // ASL: "==".
+        {
+            if (((object)left == null) || ((object)right == null))
+            {
+                throw new ArgumentNullException();
+            }
+
+            if (left.Count != right.Count)
+            {
+                return false;
+            }
+
+            for (int i = 0; i <= left.Count - 1; i++)
+            {
+                if (left.Get(i) != right.Get(i))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+        public static bool operator !=(Bits left, Bits right) // ASL: "!=".
+        {
+            return !(left == right);
+        }
+
+        public bool Equals(Bits right) // ASL: "==".
+        {
+            if ((object)right == null)
+            {
+                throw new ArgumentNullException();
+            }
+
+            Bits left = this;
+
+            if (left.Count != right.Count)
+            {
+                return false;
+            }
+
+            for (int i = 0; i <= left.Count - 1; i++)
+            {
+                if (left.Get(i) != right.Get(i))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+        public override bool Equals(object obj)
+        {
+            if (obj == null)
+            {
+                throw new ArgumentNullException();
+            }
+
+            Bits right = obj as Bits;
+
+            return Equals(right);
+        }
+        public override int GetHashCode() => bits.GetHashCode();
+    }
+}
diff --git a/Ryujinx.Tests/Cpu/Tester/Types/Integer.cs b/Ryujinx.Tests/Cpu/Tester/Types/Integer.cs
new file mode 100644
index 000000000..c72f3e252
--- /dev/null
+++ b/Ryujinx.Tests/Cpu/Tester/Types/Integer.cs
@@ -0,0 +1,42 @@
+// https://github.com/LDj3SNuD/ARM_v8-A_AArch64_Instructions_Tester/blob/master/Tester/Types/Integer.cs
+
+using System;
+using System.Numerics;
+
+namespace Ryujinx.Tests.Cpu.Tester.Types
+{
+    internal static class Integer
+    {
+        public static Bits SubBigInteger(this BigInteger x, int highIndex, int lowIndex) // ASL: "<:>".
+        {
+            if (highIndex < lowIndex)
+            {
+                throw new IndexOutOfRangeException();
+            }
+
+            Bits src = new Bits(x.ToByteArray());
+            bool[] dst = new bool[highIndex - lowIndex + 1];
+
+            for (int i = lowIndex, n = 0; i <= highIndex; i++, n++)
+            {
+                if (i <= src.Count - 1)
+                {
+                    dst[n] = src[i];
+                }
+                else
+                {
+                    dst[n] = (x.Sign != -1 ? false : true); // Zero / Sign Extension.
+                }
+            }
+
+            return new Bits(dst);
+        }
+
+        public static bool SubBigInteger(this BigInteger x, int index) // ASL: "<>".
+        {
+            Bits dst = x.SubBigInteger(index, index);
+
+            return (bool)dst;
+        }
+    }
+}
diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj
index ae4ea6c33..77d86ec4f 100644
--- a/Ryujinx.Tests/Ryujinx.Tests.csproj
+++ b/Ryujinx.Tests/Ryujinx.Tests.csproj
@@ -1,15 +1,17 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <TargetFramework>netcoreapp2.0</TargetFramework>
+    <RuntimeIdentifier>win10-x64</RuntimeIdentifier>
+    <OutputType>Exe</OutputType>
     <IsPackable>false</IsPackable>
   </PropertyGroup>
   <PropertyGroup>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170628-02" />
-    <PackageReference Include="NUnit" Version="3.9.0" />
-    <PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
+    <PackageReference Include="NUnit" Version="3.10.1" />
+    <PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
diff --git a/Ryujinx.sln b/Ryujinx.sln
index 34a5f4887..036421f90 100644
--- a/Ryujinx.sln
+++ b/Ryujinx.sln
@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
-VisualStudioVersion = 15.0.27130.2027
+VisualStudioVersion = 15.0.26730.8
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}"
 EndProject
@@ -9,9 +9,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "Ryujinx.Te
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Core", "Ryujinx.Core\Ryujinx.Core.csproj", "{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChocolArm64", "ChocolArm64\ChocolArm64.csproj", "{2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChocolArm64", "ChocolArm64\ChocolArm64.csproj", "{2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics", "Ryujinx.Graphics\Ryujinx.Graphics.csproj", "{EAAE36AF-7781-4578-A7E0-F0EFD2025569}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics", "Ryujinx.Graphics\Ryujinx.Graphics.csproj", "{EAAE36AF-7781-4578-A7E0-F0EFD2025569}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "Ryujinx.Audio\Ryujinx.Audio.csproj", "{5C1D818E-682A-46A5-9D54-30006E26C270}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -39,6 +41,10 @@ Global
 		{EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5C1D818E-682A-46A5-9D54-30006E26C270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5C1D818E-682A-46A5-9D54-30006E26C270}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5C1D818E-682A-46A5-9D54-30006E26C270}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5C1D818E-682A-46A5-9D54-30006E26C270}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf
index e8effac1d..0c88b34b4 100644
--- a/Ryujinx/Ryujinx.conf
+++ b/Ryujinx/Ryujinx.conf
@@ -1,25 +1,37 @@
-#Enabled print informations logs
+#Enable cpu memory checks (slow)
+Enable_Memory_Checks = false
+
+#Enable print informations logs
 Logging_Enable_Info = true
 
-#Enabled print trace logs
+#Enable print trace logs
 Logging_Enable_Trace = false
 
-#Enabled print debug logs
+#Enable print debug logs
 Logging_Enable_Debug = false
 
-#Enabled print warning logs
+#Enable print warning logs
 Logging_Enable_Warn = true
 
-#Enabled print error logs
+#Enable print error logs
 Logging_Enable_Error = true
 
-#Enabled print fatal logs
+#Enable print fatal logs
 Logging_Enable_Fatal = true
 
-#Enabled print Ipc logs
+#Enable print stubbed calls logs
+Logging_Enable_Stub = false
+
+#Enable print Ipc logs
 Logging_Enable_Ipc = false
 
-#Saved logs into Ryujinx.log
+#Enable log filter
+Logging_Enable_Filter = false
+
+#Filtered log classes, seperated by ',', eg. `Logging_Filtered_Classes = Loader,ServiceFS`
+Logging_Filtered_Classes =
+
+#Save logs into Ryujinx.log
 Logging_Enable_LogFile = false
 
 #https://github.com/opentk/opentk/blob/develop/src/OpenTK/Input/Key.cs
diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj
index bc5dbe042..f2d9cafe9 100644
--- a/Ryujinx/Ryujinx.csproj
+++ b/Ryujinx/Ryujinx.csproj
@@ -11,6 +11,7 @@
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\ChocolArm64\ChocolArm64.csproj" />
+    <ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
     <ProjectReference Include="..\Ryujinx.Core\Ryujinx.Core.csproj" />
     <ProjectReference Include="..\Ryujinx.Graphics\Ryujinx.Graphics.csproj" />
   </ItemGroup>
diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs
index b0dca81b7..6b6ae6a01 100644
--- a/Ryujinx/Ui/GLScreen.cs
+++ b/Ryujinx/Ui/GLScreen.cs
@@ -1,6 +1,5 @@
 using OpenTK;
 using OpenTK.Graphics;
-using OpenTK.Graphics.OpenGL;
 using OpenTK.Input;
 using Ryujinx.Core;
 using Ryujinx.Core.Input;
@@ -39,7 +38,7 @@ namespace Ryujinx
         {
             VSync = VSyncMode.On;
 
-            Renderer.InitializeFrameBuffer();
+            Renderer.SetWindowSize(Width, Height);
         }
 
         protected override void OnUpdateFrame(FrameEventArgs e)
@@ -156,6 +155,13 @@ namespace Ryujinx
                 Ns.Hid.SetTouchPoints();
             }
 
+            Ns.Hid.SetJoyconButton(
+                HidControllerId.CONTROLLER_HANDHELD,
+                HidControllerLayouts.Handheld_Joined,
+                CurrentButton,
+                LeftJoystick,
+                RightJoystick);
+
             Ns.Hid.SetJoyconButton(
                 HidControllerId.CONTROLLER_HANDHELD,
                 HidControllerLayouts.Main,
@@ -168,19 +174,17 @@ namespace Ryujinx
         {
             Ns.Statistics.StartSystemFrame();
 
-            GL.Viewport(0, 0, Width, Height);
-
             Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " +
                 $"{Ns.Statistics.GameFrameRate:0})";
 
-            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
-
             Renderer.RunActions();
             Renderer.Render();
 
             SwapBuffers();
 
             Ns.Statistics.EndSystemFrame();
+
+            Ns.Os.SignalVsync();
         }
 
         protected override void OnResize(EventArgs e)
diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs
index b67e52bdc..f9d40eb56 100644
--- a/Ryujinx/Ui/Program.cs
+++ b/Ryujinx/Ui/Program.cs
@@ -1,4 +1,6 @@
-using Ryujinx.Core;
+using Ryujinx.Audio;
+using Ryujinx.Audio.OpenAL;
+using Ryujinx.Core;
 using Ryujinx.Graphics.Gal;
 using Ryujinx.Graphics.Gal.OpenGL;
 using System;
@@ -10,15 +12,17 @@ namespace Ryujinx
     {
         static void Main(string[] args)
         {
-            AOptimizations.DisableMemoryChecks = true;
-
             Config.Read();
 
+            AOptimizations.DisableMemoryChecks = !Config.EnableMemoryChecks;
+
             Console.Title = "Ryujinx Console";
 
             IGalRenderer Renderer = new OpenGLRenderer();
 
-            Switch Ns = new Switch(Renderer);
+            IAalOutput AudioOut = new OpenALAudioOut();
+
+            Switch Ns = new Switch(Renderer, AudioOut);
 
             if (args.Length == 1)
             {
@@ -26,29 +30,34 @@ namespace Ryujinx
                 {
                     string[] RomFsFiles = Directory.GetFiles(args[0], "*.istorage");
 
+                    if (RomFsFiles.Length == 0)
+                    {
+                        RomFsFiles = Directory.GetFiles(args[0], "*.romfs");
+                    }
+
                     if (RomFsFiles.Length > 0)
                     {
-                        Logging.Info("Loading as cart with RomFS.");
+                        Logging.Info(LogClass.Loader, "Loading as cart with RomFS.");
 
                         Ns.LoadCart(args[0], RomFsFiles[0]);
                     }
                     else
                     {
-                        Logging.Info("Loading as cart WITHOUT RomFS.");
+                        Logging.Info(LogClass.Loader, "Loading as cart WITHOUT RomFS.");
 
                         Ns.LoadCart(args[0]);
                     }
                 }
                 else if (File.Exists(args[0]))
                 {
-                    Logging.Info("Loading as homebrew.");
+                    Logging.Info(LogClass.Loader, "Loading as homebrew.");
 
                     Ns.LoadProgram(args[0]);
                 }
             }
             else
             {
-                Logging.Error("Please specify the folder with the NSOs/IStorage or a NSO/NRO.");
+                Logging.Error(LogClass.Loader, "Please specify the folder with the NSOs/IStorage or a NSO/NRO.");
             }
 
             using (GLScreen Screen = new GLScreen(Ns, Renderer))