commit
fe94cd2037
283 changed files with 23071 additions and 3710 deletions
|
@ -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));
|
||||
|
@ -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));
|
||||
|
@ -187,11 +212,15 @@ namespace ChocolArm64
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
public static class AOptimizations
|
||||
{
|
||||
public static bool DisableMemoryChecks = false;
|
||||
|
||||
public static bool GenerateCallStack = true;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 (CallCount < MinCallCountForReJit)
|
||||
if (NeedsReJit && CallCount < MinCallCountForReJit)
|
||||
{
|
||||
CallCount++;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return CallCount == MinCallCountForReJit;
|
||||
}
|
||||
|
||||
return Type == ATranslatedSubType.SubTier1 && NeedsReJit;
|
||||
return NeedsReJit;
|
||||
}
|
||||
|
||||
public long Execute(AThreadState ThreadState, AMemory Memory)
|
||||
|
@ -125,9 +119,31 @@ 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 SetType(ATranslatedSubType Type)
|
||||
{
|
||||
this.Type = Type;
|
||||
|
||||
if (Type == ATranslatedSubType.SubTier0)
|
||||
{
|
||||
NeedsReJit = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void MarkForReJit() => NeedsReJit = true;
|
||||
}
|
||||
|
|
|
@ -107,6 +107,8 @@ namespace ChocolArm64
|
|||
|
||||
ATranslatedSub Subroutine = Context.GetSubroutine();
|
||||
|
||||
lock (SubBlocks)
|
||||
{
|
||||
if (SubBlocks.Contains(Position))
|
||||
{
|
||||
SubBlocks.Remove(Position);
|
||||
|
@ -117,16 +119,20 @@ namespace ChocolArm64
|
|||
{
|
||||
Subroutine.SetType(ATranslatedSubType.SubTier0);
|
||||
}
|
||||
}
|
||||
|
||||
CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine);
|
||||
|
||||
AOpCode LastOp = Block.GetLastOp();
|
||||
|
||||
lock (SubBlocks)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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, () =>
|
||||
EmitVectorUnaryOpF(Context, () => Context.Emit(OpCodes.Neg));
|
||||
}
|
||||
|
||||
public static void Fnmadd_S(AILEmitterCtx Context)
|
||||
{
|
||||
Context.Emit(OpCodes.Mul);
|
||||
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)
|
||||
|
@ -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,6 +996,18 @@ 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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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, () =>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -42,14 +50,14 @@ namespace ChocolArm64.Instruction
|
|||
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 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 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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
|
103
ChocolArm64/Instruction/ASoftFloat.cs
Normal file
103
ChocolArm64/Instruction/ASoftFloat.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -12,8 +12,6 @@ namespace ChocolArm64.Translation
|
|||
{
|
||||
private ATranslator Translator;
|
||||
|
||||
private HashSet<long> Callees;
|
||||
|
||||
private Dictionary<long, AILLabel> Labels;
|
||||
|
||||
private int BlkIndex;
|
||||
|
@ -66,8 +64,6 @@ namespace ChocolArm64.Translation
|
|||
this.Graph = Graph;
|
||||
this.Root = Root;
|
||||
|
||||
Callees = new HashSet<long>();
|
||||
|
||||
Labels = new Dictionary<long, AILLabel>();
|
||||
|
||||
Emitter = new AILEmitter(Graph, Root, SubName);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
|
|
@ -49,6 +49,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**
|
||||
|
||||
If you have some homebrew that currently don't work on the emulator, you can contact us through Discord with the compiled NRO/NSO (and source code if possible) and then we'll make changes to make the requested app / game work.
|
||||
|
|
13
Ryujinx.Audio/AudioFormat.cs
Normal file
13
Ryujinx.Audio/AudioFormat.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace Ryujinx.Audio
|
||||
{
|
||||
public enum AudioFormat
|
||||
{
|
||||
Invalid = 0,
|
||||
PcmInt8 = 1,
|
||||
PcmInt16 = 2,
|
||||
PcmImt24 = 3,
|
||||
PcmImt32 = 4,
|
||||
PcmFloat = 5,
|
||||
Adpcm = 6
|
||||
}
|
||||
}
|
24
Ryujinx.Audio/IAalOutput.cs
Normal file
24
Ryujinx.Audio/IAalOutput.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
365
Ryujinx.Audio/OpenAL/OpenALAudioOut.cs
Normal file
365
Ryujinx.Audio/OpenAL/OpenALAudioOut.cs
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
8
Ryujinx.Audio/PlaybackState.cs
Normal file
8
Ryujinx.Audio/PlaybackState.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Audio
|
||||
{
|
||||
public enum PlaybackState
|
||||
{
|
||||
Playing = 0,
|
||||
Stopped = 1
|
||||
}
|
||||
}
|
4
Ryujinx.Audio/ReleaseCallback.cs
Normal file
4
Ryujinx.Audio/ReleaseCallback.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace Ryujinx.Audio
|
||||
{
|
||||
public delegate void ReleaseCallback();
|
||||
}
|
11
Ryujinx.Audio/Ryujinx.Audio.csproj
Normal file
11
Ryujinx.Audio/Ryujinx.Audio.csproj
Normal file
|
@ -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>
|
|
@ -9,6 +9,7 @@ namespace Ryujinx.Core
|
|||
{
|
||||
public static class Config
|
||||
{
|
||||
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; }
|
||||
|
@ -16,7 +17,10 @@ namespace Ryujinx.Core
|
|||
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,6 +30,7 @@ namespace Ryujinx.Core
|
|||
var iniPath = Path.Combine(iniFolder, "Ryujinx.conf");
|
||||
IniParser Parser = new IniParser(iniPath);
|
||||
|
||||
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"));
|
||||
|
@ -33,7 +38,25 @@ namespace Ryujinx.Core
|
|||
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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -2,6 +2,8 @@ namespace Ryujinx.Core.Loaders.Executables
|
|||
{
|
||||
public interface IExecutable
|
||||
{
|
||||
string Name { get; }
|
||||
|
||||
byte[] Text { get; }
|
||||
byte[] RO { get; }
|
||||
byte[] Data { get; }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
38
Ryujinx.Core/LogClass.cs
Normal file
38
Ryujinx.Core/LogClass.cs
Normal file
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
@ -18,8 +20,22 @@ namespace Ryujinx.Core
|
|||
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()
|
||||
public static string GetExecutionTime() => ExecutionTime.ElapsedMilliseconds.ToString().PadLeft(8, '0') + "ms";
|
||||
|
||||
private static void LogMessage(LogEntry LogEntry)
|
||||
{
|
||||
return ExecutionTime.ElapsedMilliseconds.ToString().PadLeft(8, '0') + "ms";
|
||||
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;
|
||||
}
|
||||
|
||||
private static string WhoCalledMe()
|
||||
{
|
||||
return new StackTrace().GetFrame(2).GetMethod().Name;
|
||||
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()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
62
Ryujinx.Core/OsHle/AppletStateMgr.cs
Normal file
62
Ryujinx.Core/OsHle/AppletStateMgr.cs
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
class FileDesc
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public FileDesc(string Name)
|
||||
{
|
||||
this.Name = Name;
|
||||
}
|
||||
}
|
||||
}
|
69
Ryujinx.Core/OsHle/GlobalStateTable.cs
Normal file
69
Ryujinx.Core/OsHle/GlobalStateTable.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
namespace Ryujinx.Core.OsHle.Handles
|
||||
{
|
||||
class HEvent
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
4
Ryujinx.Core/OsHle/Handles/KEvent.cs
Normal file
4
Ryujinx.Core/OsHle/Handles/KEvent.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
namespace Ryujinx.Core.OsHle.Handles
|
||||
{
|
||||
class KEvent : KSynchronizationObject { }
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
Resume(Thread);
|
||||
}
|
||||
|
||||
public bool Resume(KThread Thread)
|
||||
{
|
||||
ActiveProcessors.Remove(Thread.ProcessorId);
|
||||
}
|
||||
|
||||
if (!AllThreads.TryGetValue(Thread, out SchedThread))
|
||||
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
||||
{
|
||||
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
|
||||
|
||||
return false;
|
||||
}
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
bool Result;
|
||||
return TryResumingExecution(SchedThread);
|
||||
}
|
||||
|
||||
if (Timeout >= 0)
|
||||
private bool TryResumingExecution(SchedulerThread SchedThread)
|
||||
{
|
||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
|
||||
KThread Thread = SchedThread.Thread;
|
||||
|
||||
Result = SchedThread.WaitEvent.WaitOne(Timeout);
|
||||
}
|
||||
else
|
||||
if (!SchedThread.Active || SchedThread.SyncTimeout != 0)
|
||||
{
|
||||
Result = SchedThread.WaitEvent.WaitOne();
|
||||
PrintDbgThreadInfo(Thread, "entering inactive wait state...");
|
||||
}
|
||||
|
||||
TryResumingExecution(SchedThread);
|
||||
bool Result = false;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
private void TryResumingExecution(SchedulerThread SchedThread)
|
||||
if (SchedThread.SyncTimeout != 0)
|
||||
{
|
||||
HThread Thread = SchedThread.Thread;
|
||||
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();
|
||||
}
|
||||
|
||||
private string GetDbgThreadInfo(HThread Thread)
|
||||
return IsActive(SchedThread);
|
||||
}
|
||||
|
||||
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()
|
||||
|
|
31
Ryujinx.Core/OsHle/Handles/KSession.cs
Normal file
31
Ryujinx.Core/OsHle/Handles/KSession.cs
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
Ryujinx.Core/OsHle/Handles/KSynchronizationObject.cs
Normal file
28
Ryujinx.Core/OsHle/Handles/KSynchronizationObject.cs
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
113
Ryujinx.Core/OsHle/Handles/KThread.cs
Normal file
113
Ryujinx.Core/OsHle/Handles/KThread.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
namespace Ryujinx.Core.OsHle.Ipc
|
||||
{
|
||||
enum IpcDomCmd
|
||||
{
|
||||
SendMsg = 1,
|
||||
DeleteObj = 2
|
||||
}
|
||||
}
|
|
@ -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,72 +23,8 @@ 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)
|
||||
{
|
||||
if (Request.DomCmd == IpcDomCmd.SendMsg)
|
||||
{
|
||||
long Magic = ReqReader.ReadInt64();
|
||||
int CmdId = (int)ReqReader.ReadInt64();
|
||||
|
||||
object Obj = Dom.GetObject(Request.DomObjId);
|
||||
|
||||
if (Obj is HDomain)
|
||||
{
|
||||
Session.Service.Commands.TryGetValue(CmdId, out ProcReq);
|
||||
|
||||
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);
|
||||
|
@ -109,14 +39,9 @@ namespace Ryujinx.Core.OsHle.Ipc
|
|||
ReqReader,
|
||||
ResWriter);
|
||||
|
||||
long Result = ProcReq(Context);
|
||||
Session.Service.CallMethod(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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
8
Ryujinx.Core/OsHle/Ipc/IpcMagic.cs
Normal file
8
Ryujinx.Core/OsHle/Ipc/IpcMagic.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Core/OsHle/Kernel/KernelErr.cs
Normal file
12
Ryujinx.Core/OsHle/Kernel/KernelErr.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
19
Ryujinx.Core/OsHle/Kernel/NsTimeConverter.cs
Normal file
19
Ryujinx.Core/OsHle/Kernel/NsTimeConverter.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
|
||||
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;
|
||||
long HandlesPtr = (long)ThreadState.X1;
|
||||
int HandlesCount = (int)ThreadState.X2;
|
||||
long Timeout = (long)ThreadState.X3;
|
||||
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,8 +153,7 @@ 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);
|
||||
|
||||
|
@ -89,65 +163,46 @@ namespace Ryujinx.Core.OsHle.Svc
|
|||
|
||||
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);
|
||||
|
||||
|
@ -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;
|
177
Ryujinx.Core/OsHle/Kernel/SvcThread.cs
Normal file
177
Ryujinx.Core/OsHle/Kernel/SvcThread.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
424
Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs
Normal file
424
Ryujinx.Core/OsHle/Kernel/SvcThreadSync.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 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,6 +125,8 @@ namespace Ryujinx.Core.OsHle
|
|||
return false;
|
||||
}
|
||||
|
||||
MakeSymbolTable();
|
||||
|
||||
MapRWMemRegion(
|
||||
MemoryRegions.MainStackAddress,
|
||||
MemoryRegions.MainStackSize,
|
||||
|
@ -125,14 +134,14 @@ namespace Ryujinx.Core.OsHle
|
|||
|
||||
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,11 +233,9 @@ namespace Ryujinx.Core.OsHle
|
|||
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
|
||||
}
|
||||
|
||||
private ATranslator GetTranslator()
|
||||
private void MakeSymbolTable()
|
||||
{
|
||||
if (Translator == null)
|
||||
{
|
||||
Dictionary<long, string> SymbolTable = new Dictionary<long, string>();
|
||||
SymbolTable = new Dictionary<long, string>();
|
||||
|
||||
foreach (Executable Exe in Executables)
|
||||
{
|
||||
|
@ -235,7 +244,12 @@ namespace Ryujinx.Core.OsHle
|
|||
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ATranslator GetTranslator()
|
||||
{
|
||||
if (Translator == null)
|
||||
{
|
||||
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;
|
||||
|
@ -258,17 +282,47 @@ namespace Ryujinx.Core.OsHle
|
|||
}
|
||||
}
|
||||
|
||||
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}";
|
||||
}
|
||||
|
||||
public void DisableCpuTracing()
|
||||
Trace.AppendLine(" " + SubName + " (" + GetNsoNameAndAddress(Position) + ")");
|
||||
}
|
||||
|
||||
Logging.Info(LogClass.CPU, Trace.ToString());
|
||||
}
|
||||
|
||||
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...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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()
|
||||
{
|
||||
|
@ -20,11 +20,15 @@ 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;
|
||||
|
|
|
@ -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);
|
||||
|
|
7
Ryujinx.Core/OsHle/Services/Am/AmErr.cs
Normal file
7
Ryujinx.Core/OsHle/Services/Am/AmErr.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Am
|
||||
{
|
||||
static class AmErr
|
||||
{
|
||||
public const int NoMessages = 3;
|
||||
}
|
||||
}
|
8
Ryujinx.Core/OsHle/Services/Am/FocusState.cs
Normal file
8
Ryujinx.Core/OsHle/Services/Am/FocusState.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Am
|
||||
{
|
||||
enum FocusState
|
||||
{
|
||||
InFocus = 1,
|
||||
OutOfFocus = 2
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
20
Ryujinx.Core/OsHle/Services/Am/IApplicationCreator.cs
Normal file
20
Ryujinx.Core/OsHle/Services/Am/IApplicationCreator.cs
Normal file
|
@ -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>()
|
||||
{
|
||||
//...
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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>()
|
||||
{
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
20
Ryujinx.Core/OsHle/Services/Am/IGlobalStateController.cs
Normal file
20
Ryujinx.Core/OsHle/Services/Am/IGlobalStateController.cs
Normal file
|
@ -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>()
|
||||
{
|
||||
//...
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
45
Ryujinx.Core/OsHle/Services/Am/IHomeMenuFunctions.cs
Normal file
45
Ryujinx.Core/OsHle/Services/Am/IHomeMenuFunctions.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
99
Ryujinx.Core/OsHle/Services/Am/ISystemAppletProxy.cs
Normal file
99
Ryujinx.Core/OsHle/Services/Am/ISystemAppletProxy.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
9
Ryujinx.Core/OsHle/Services/Am/MessageInfo.cs
Normal file
9
Ryujinx.Core/OsHle/Services/Am/MessageInfo.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Am
|
||||
{
|
||||
enum MessageInfo
|
||||
{
|
||||
FocusStateChanged = 0xf,
|
||||
OperationModeChanged = 0x1e,
|
||||
PerformanceModeChanged = 0x1f
|
||||
}
|
||||
}
|
8
Ryujinx.Core/OsHle/Services/Am/OperationMode.cs
Normal file
8
Ryujinx.Core/OsHle/Services/Am/OperationMode.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Core.OsHle.Services.Am
|
||||
{
|
||||
enum OperationMode
|
||||
{
|
||||
Handheld = 0,
|
||||
Docked = 1
|
||||
}
|
||||
}
|
|
@ -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>()
|
||||
{
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue