commit
fe94cd2037
283 changed files with 23071 additions and 3710 deletions
|
@ -41,6 +41,7 @@ namespace ChocolArm64
|
||||||
Set("x1111010010xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpImm));
|
Set("x1111010010xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpImm));
|
||||||
Set("x1111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg));
|
Set("x1111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg));
|
||||||
Set("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem));
|
Set("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem));
|
||||||
|
Set("x101101011000000000101xxxxxxxxxx", AInstEmit.Cls, typeof(AOpCodeAlu));
|
||||||
Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu));
|
Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu));
|
||||||
Set("x0011010110xxxxx010000xxxxxxxxxx", AInstEmit.Crc32b, typeof(AOpCodeAluRs));
|
Set("x0011010110xxxxx010000xxxxxxxxxx", AInstEmit.Crc32b, typeof(AOpCodeAluRs));
|
||||||
Set("x0011010110xxxxx010001xxxxxxxxxx", AInstEmit.Crc32h, typeof(AOpCodeAluRs));
|
Set("x0011010110xxxxx010001xxxxxxxxxx", AInstEmit.Crc32h, typeof(AOpCodeAluRs));
|
||||||
|
@ -91,6 +92,7 @@ namespace ChocolArm64
|
||||||
Set("x01100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluImm));
|
Set("x01100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluImm));
|
||||||
Set("x0101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluRs));
|
Set("x0101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluRs));
|
||||||
Set("1111100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm));
|
Set("1111100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm));
|
||||||
|
Set("11111000100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm));
|
||||||
Set("11011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemLit));
|
Set("11011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemLit));
|
||||||
Set("x101101011000000000000xxxxxxxxxx", AInstEmit.Rbit, typeof(AOpCodeAlu));
|
Set("x101101011000000000000xxxxxxxxxx", AInstEmit.Rbit, typeof(AOpCodeAlu));
|
||||||
Set("11010110010xxxxx000000xxxxxxxxxx", AInstEmit.Ret, typeof(AOpCodeBReg));
|
Set("11010110010xxxxx000000xxxxxxxxxx", AInstEmit.Ret, typeof(AOpCodeBReg));
|
||||||
|
@ -131,14 +133,20 @@ namespace ChocolArm64
|
||||||
Set("10011011110xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Umulh, typeof(AOpCodeMul));
|
Set("10011011110xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Umulh, typeof(AOpCodeMul));
|
||||||
|
|
||||||
//Vector
|
//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("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("0>001110<<1xxxxx101111xxxxxxxxxx", AInstEmit.Addp_V, typeof(AOpCodeSimdReg));
|
||||||
Set("000011100x110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd));
|
Set("000011100x110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd));
|
||||||
Set("01001110<<110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd));
|
Set("01001110<<110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd));
|
||||||
Set("0x001110001xxxxx000111xxxxxxxxxx", AInstEmit.And_V, typeof(AOpCodeSimdReg));
|
Set("0x001110001xxxxx000111xxxxxxxxxx", AInstEmit.And_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x001110011xxxxx000111xxxxxxxxxx", AInstEmit.Bic_V, typeof(AOpCodeSimdReg));
|
Set("0x001110011xxxxx000111xxxxxxxxxx", AInstEmit.Bic_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x10111100000xxx<<x101xxxxxxxxxx", AInstEmit.Bic_Vi, typeof(AOpCodeSimdImm));
|
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("0x101110011xxxxx000111xxxxxxxxxx", AInstEmit.Bsl_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0>101110<<1xxxxx100011xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimdReg));
|
Set("0>101110<<1xxxxx100011xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0>001110<<100000100110xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimd));
|
Set("0>001110<<100000100110xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimd));
|
||||||
|
@ -161,8 +169,25 @@ namespace ChocolArm64
|
||||||
Set("000111100x100000110000xxxxxxxxxx", AInstEmit.Fabs_S, typeof(AOpCodeSimd));
|
Set("000111100x100000110000xxxxxxxxxx", AInstEmit.Fabs_S, typeof(AOpCodeSimd));
|
||||||
Set("000111100x1xxxxx001010xxxxxxxxxx", AInstEmit.Fadd_S, typeof(AOpCodeSimdReg));
|
Set("000111100x1xxxxx001010xxxxxxxxxx", AInstEmit.Fadd_S, typeof(AOpCodeSimdReg));
|
||||||
Set("0>0011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Fadd_V, 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("000111100x1xxxxxxxxx01xxxxx0xxxx", AInstEmit.Fccmp_S, typeof(AOpCodeSimdFcond));
|
||||||
Set("000111100x1xxxxxxxxx01xxxxx1xxxx", AInstEmit.Fccmpe_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("000111100x1xxxxx001000xxxxx0x000", AInstEmit.Fcmp_S, typeof(AOpCodeSimdReg));
|
||||||
Set("000111100x1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg));
|
Set("000111100x1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg));
|
||||||
Set("000111100x1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond));
|
Set("000111100x1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond));
|
||||||
|
@ -187,11 +212,15 @@ namespace ChocolArm64
|
||||||
Set("0>1011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Fdiv_V, typeof(AOpCodeSimdReg));
|
Set("0>1011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Fdiv_V, typeof(AOpCodeSimdReg));
|
||||||
Set("000111110x0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fmadd_S, typeof(AOpCodeSimdReg));
|
Set("000111110x0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fmadd_S, typeof(AOpCodeSimdReg));
|
||||||
Set("000111100x1xxxxx010010xxxxxxxxxx", AInstEmit.Fmax_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("000111100x1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S, typeof(AOpCodeSimdReg));
|
||||||
Set("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_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("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg));
|
||||||
Set("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg));
|
Set("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElemF));
|
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("000111100x100000010000xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimd));
|
||||||
Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov));
|
Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov));
|
||||||
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
|
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
|
||||||
|
@ -201,16 +230,34 @@ namespace ChocolArm64
|
||||||
Set("1001111010101111000000xxxxxxxxxx", AInstEmit.Fmov_Itof1, typeof(AOpCodeSimdCvt));
|
Set("1001111010101111000000xxxxxxxxxx", AInstEmit.Fmov_Itof1, typeof(AOpCodeSimdCvt));
|
||||||
Set("000111110x0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
|
Set("000111110x0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
|
||||||
Set("000111100x1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_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("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElemF));
|
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("000111110x1xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fnmsub_S, typeof(AOpCodeSimdReg));
|
||||||
Set("000111100x1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_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("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("000111100x100101010000xxxxxxxxxx", AInstEmit.Frintm_S, typeof(AOpCodeSimd));
|
||||||
Set("0>0011100<100001100110xxxxxxxxxx", AInstEmit.Frintm_V, 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("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S, typeof(AOpCodeSimd));
|
||||||
|
Set("0>0011101<100001100010xxxxxxxxxx", AInstEmit.Frintp_V, typeof(AOpCodeSimd));
|
||||||
Set("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S, 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("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd));
|
||||||
Set("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg));
|
Set("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg));
|
||||||
Set("0>0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, 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("01101110000xxxxx0xxxx1xxxxxxxxxx", AInstEmit.Ins_V, typeof(AOpCodeSimdIns));
|
||||||
Set("0x00110001000000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs));
|
Set("0x00110001000000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs));
|
||||||
Set("0x001100110xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs));
|
Set("0x001100110xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs));
|
||||||
Set("0x00110101000000xx0xxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs));
|
Set("0x00110101x00000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs));
|
||||||
Set("0x001101110xxxxxxx0xxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs));
|
Set("0x00110111xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs));
|
||||||
Set("xx10110xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp, typeof(AOpCodeSimdMemPair));
|
Set("xx10110xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp, typeof(AOpCodeSimdMemPair));
|
||||||
Set("xx111100x10xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm));
|
Set("xx111100x10xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm));
|
||||||
Set("xx111100x10xxxxxxxxx01xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm));
|
Set("xx111100x10xxxxxxxxx01xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm));
|
||||||
|
@ -228,6 +275,7 @@ namespace ChocolArm64
|
||||||
Set("xx111100x11xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemReg));
|
Set("xx111100x11xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemReg));
|
||||||
Set("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeSimdMemLit));
|
Set("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeSimdMemLit));
|
||||||
Set("0x001110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mla_V, typeof(AOpCodeSimdReg));
|
Set("0x001110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mla_V, typeof(AOpCodeSimdReg));
|
||||||
|
Set("0x101111xxxxxxxx0000x0xxxxxxxxxx", AInstEmit.Mla_Ve, typeof(AOpCodeSimdRegElem));
|
||||||
Set("0x101110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mls_V, typeof(AOpCodeSimdReg));
|
Set("0x101110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mls_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x00111100000xxx0xx001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
Set("0x00111100000xxx0xx001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
||||||
Set("0x00111100000xxx10x001xxxxxxxxxx", 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("0x10111100000xxx0xx001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
|
||||||
Set("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
|
Set("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
|
||||||
Set("0x10111100000xxx110x01xxxxxxxxxx", 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("0x10111000100000010110xxxxxxxxxx", AInstEmit.Not_V, typeof(AOpCodeSimd));
|
||||||
Set("0x001110101xxxxx000111xxxxxxxxxx", AInstEmit.Orr_V, typeof(AOpCodeSimdReg));
|
Set("0x001110101xxxxx000111xxxxxxxxxx", AInstEmit.Orr_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x00111100000xxx<<x101xxxxxxxxxx", AInstEmit.Orr_Vi, typeof(AOpCodeSimdImm));
|
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("0x001110<<100000000010xxxxxxxxxx", AInstEmit.Rev64_V, typeof(AOpCodeSimd));
|
||||||
|
Set("0x101110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Rsubhn_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V, typeof(AOpCodeSimdReg));
|
Set("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V, typeof(AOpCodeSimdReg));
|
||||||
Set("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp, typeof(AOpCodeSimdCvt));
|
Set("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp, typeof(AOpCodeSimdCvt));
|
||||||
Set("010111100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_S, typeof(AOpCodeSimd));
|
Set("010111100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_S, typeof(AOpCodeSimd));
|
||||||
|
@ -263,23 +314,30 @@ namespace ChocolArm64
|
||||||
Set("0x0011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm));
|
Set("0x0011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm));
|
||||||
Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
|
Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
|
||||||
Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
|
Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
|
||||||
Set("0x00110100000000xx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
|
Set("0x00110100x00000xxxxxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
|
||||||
Set("0x001101100xxxxxxx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
|
Set("0x00110110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
|
||||||
Set("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeSimdMemPair));
|
Set("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeSimdMemPair));
|
||||||
Set("xx111100x00xxxxxxxxx00xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
|
Set("xx111100x00xxxxxxxxx00xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
|
||||||
Set("xx111100x00xxxxxxxxx01xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
|
Set("xx111100x00xxxxxxxxx01xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
|
||||||
Set("xx111100x00xxxxxxxxx11xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
|
Set("xx111100x00xxxxxxxxx11xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
|
||||||
Set("xx111101x0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
|
Set("xx111101x0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
|
||||||
Set("xx111100x01xxxxxxxxx10xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemReg));
|
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("0>101110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_V, typeof(AOpCodeSimdReg));
|
||||||
|
Set("0x001110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Subhn_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x001110000xxxxx0xx000xxxxxxxxxx", AInstEmit.Tbl_V, typeof(AOpCodeSimdTbl));
|
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("001011100x110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd));
|
||||||
Set("01101110<<110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd));
|
Set("01101110<<110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd));
|
||||||
Set("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V, typeof(AOpCodeSimdReg));
|
Set("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V, typeof(AOpCodeSimdReg));
|
||||||
Set("x0011110xx100011000000xxxxxxxxxx", AInstEmit.Ucvtf_Gp, typeof(AOpCodeSimdCvt));
|
Set("x0011110xx100011000000xxxxxxxxxx", AInstEmit.Ucvtf_Gp, typeof(AOpCodeSimdCvt));
|
||||||
Set("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd));
|
Set("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd));
|
||||||
Set("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, 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("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns));
|
||||||
Set("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg));
|
Set("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_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("011111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm));
|
||||||
Set("0x1011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm));
|
Set("0x1011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm));
|
||||||
Set("0x1011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm));
|
Set("0x1011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm));
|
||||||
Set("0x001110xx0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg));
|
Set("0>001110<<0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x001110xx0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg));
|
Set("0>001110<<0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd));
|
Set("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd));
|
||||||
Set("0x001110xx0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V, typeof(AOpCodeSimdReg));
|
Set("0>001110<<0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x001110xx0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V, typeof(AOpCodeSimdReg));
|
Set("0>001110<<0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V, typeof(AOpCodeSimdReg));
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
public static class AOptimizations
|
public static class AOptimizations
|
||||||
{
|
{
|
||||||
public static bool DisableMemoryChecks = false;
|
public static bool DisableMemoryChecks = false;
|
||||||
|
|
||||||
|
public static bool GenerateCallStack = true;
|
||||||
}
|
}
|
|
@ -54,6 +54,14 @@ namespace ChocolArm64
|
||||||
return true;
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ namespace ChocolArm64
|
||||||
|
|
||||||
public ReadOnlyCollection<ARegister> Params { get; private set; }
|
public ReadOnlyCollection<ARegister> Params { get; private set; }
|
||||||
|
|
||||||
private HashSet<long> Callees;
|
private HashSet<long> Callers;
|
||||||
|
|
||||||
private ATranslatedSubType Type;
|
private ATranslatedSubType Type;
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ namespace ChocolArm64
|
||||||
|
|
||||||
private int MinCallCountForReJit = 250;
|
private int MinCallCountForReJit = 250;
|
||||||
|
|
||||||
public ATranslatedSub(DynamicMethod Method, List<ARegister> Params, HashSet<long> Callees)
|
public ATranslatedSub(DynamicMethod Method, List<ARegister> Params)
|
||||||
{
|
{
|
||||||
if (Method == null)
|
if (Method == null)
|
||||||
{
|
{
|
||||||
|
@ -45,14 +46,10 @@ namespace ChocolArm64
|
||||||
throw new ArgumentNullException(nameof(Params));
|
throw new ArgumentNullException(nameof(Params));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Callees == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(Callees));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Method = Method;
|
this.Method = Method;
|
||||||
this.Params = Params.AsReadOnly();
|
this.Params = Params.AsReadOnly();
|
||||||
this.Callees = Callees;
|
|
||||||
|
Callers = new HashSet<long>();
|
||||||
|
|
||||||
PrepareDelegate();
|
PrepareDelegate();
|
||||||
}
|
}
|
||||||
|
@ -107,17 +104,14 @@ namespace ChocolArm64
|
||||||
|
|
||||||
public bool ShouldReJit()
|
public bool ShouldReJit()
|
||||||
{
|
{
|
||||||
if (Type == ATranslatedSubType.SubTier0)
|
if (NeedsReJit && CallCount < MinCallCountForReJit)
|
||||||
{
|
|
||||||
if (CallCount < MinCallCountForReJit)
|
|
||||||
{
|
{
|
||||||
CallCount++;
|
CallCount++;
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CallCount == MinCallCountForReJit;
|
return NeedsReJit;
|
||||||
}
|
|
||||||
|
|
||||||
return Type == ATranslatedSubType.SubTier1 && NeedsReJit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long Execute(AThreadState ThreadState, AMemory Memory)
|
public long Execute(AThreadState ThreadState, AMemory Memory)
|
||||||
|
@ -125,9 +119,31 @@ namespace ChocolArm64
|
||||||
return ExecDelegate(ThreadState, Memory);
|
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;
|
public void MarkForReJit() => NeedsReJit = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,8 @@ namespace ChocolArm64
|
||||||
|
|
||||||
ATranslatedSub Subroutine = Context.GetSubroutine();
|
ATranslatedSub Subroutine = Context.GetSubroutine();
|
||||||
|
|
||||||
|
lock (SubBlocks)
|
||||||
|
{
|
||||||
if (SubBlocks.Contains(Position))
|
if (SubBlocks.Contains(Position))
|
||||||
{
|
{
|
||||||
SubBlocks.Remove(Position);
|
SubBlocks.Remove(Position);
|
||||||
|
@ -117,16 +119,20 @@ namespace ChocolArm64
|
||||||
{
|
{
|
||||||
Subroutine.SetType(ATranslatedSubType.SubTier0);
|
Subroutine.SetType(ATranslatedSubType.SubTier0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine);
|
CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine);
|
||||||
|
|
||||||
AOpCode LastOp = Block.GetLastOp();
|
AOpCode LastOp = Block.GetLastOp();
|
||||||
|
|
||||||
|
lock (SubBlocks)
|
||||||
|
{
|
||||||
if (LastOp.Emitter != AInstEmit.Ret &&
|
if (LastOp.Emitter != AInstEmit.Ret &&
|
||||||
LastOp.Emitter != AInstEmit.Br)
|
LastOp.Emitter != AInstEmit.Br)
|
||||||
{
|
{
|
||||||
SubBlocks.Add(LastOp.Position + 4);
|
SubBlocks.Add(LastOp.Position + 4);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Subroutine;
|
return Subroutine;
|
||||||
}
|
}
|
||||||
|
@ -154,11 +160,14 @@ namespace ChocolArm64
|
||||||
|
|
||||||
//Mark all methods that calls this method for ReJiting,
|
//Mark all methods that calls this method for ReJiting,
|
||||||
//since we can now call it directly which is faster.
|
//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.Instruction;
|
||||||
|
using ChocolArm64.State;
|
||||||
|
|
||||||
namespace ChocolArm64.Decoder
|
namespace ChocolArm64.Decoder
|
||||||
{
|
{
|
||||||
|
@ -11,6 +12,10 @@ namespace ChocolArm64.Decoder
|
||||||
Rt = OpCode & 0x1f;
|
Rt = OpCode & 0x1f;
|
||||||
|
|
||||||
Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode);
|
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;
|
default: Inst = AInst.Undefined; return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Size = (OpCode >> 10) & 0x3;
|
Size = (OpCode >> 10) & 3;
|
||||||
WBack = ((OpCode >> 23) & 0x1) != 0;
|
WBack = ((OpCode >> 23) & 1) != 0;
|
||||||
|
|
||||||
bool Q = ((OpCode >> 30) & 1) != 0;
|
bool Q = ((OpCode >> 30) & 1) != 0;
|
||||||
|
|
||||||
|
|
|
@ -82,12 +82,13 @@ namespace ChocolArm64.Decoder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.Index = Index;
|
||||||
this.SElems = SElems;
|
this.SElems = SElems;
|
||||||
this.Size = Scale;
|
this.Size = Scale;
|
||||||
|
|
||||||
Extend64 = false;
|
Extend64 = false;
|
||||||
|
|
||||||
WBack = ((OpCode >> 23) & 0x1) != 0;
|
WBack = ((OpCode >> 23) & 1) != 0;
|
||||||
|
|
||||||
RegisterSize = Q != 0
|
RegisterSize = Q != 0
|
||||||
? ARegisterSize.SIMD128
|
? ARegisterSize.SIMD128
|
||||||
|
|
|
@ -11,9 +11,8 @@ namespace ChocolArm64.Decoder
|
||||||
switch (Size)
|
switch (Size)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
Index = (OpCode >> 21) & 1 |
|
Index = (OpCode >> 20) & 3 |
|
||||||
(OpCode >> 10) & 2 |
|
(OpCode >> 9) & 4;
|
||||||
(OpCode >> 18) & 4;
|
|
||||||
|
|
||||||
Rm &= 0xf;
|
Rm &= 0xf;
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,24 @@ namespace ChocolArm64.Instruction
|
||||||
EmitDataStore(Context, SetFlags);
|
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)
|
public static void Clz(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
|
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
using ChocolArm64.Decoder;
|
using ChocolArm64.Decoder;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using ChocolArm64.Translation;
|
using ChocolArm64.Translation;
|
||||||
using System.Reflection;
|
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
namespace ChocolArm64.Instruction
|
namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
static partial class AInstEmit
|
static partial class AInstEmit
|
||||||
{
|
{
|
||||||
private const BindingFlags Binding = BindingFlags.NonPublic | BindingFlags.Instance;
|
|
||||||
|
|
||||||
public static void Brk(AILEmitterCtx Context)
|
public static void Brk(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitExceptionCall(Context, nameof(AThreadState.OnBreak));
|
EmitExceptionCall(Context, nameof(AThreadState.OnBreak));
|
||||||
|
@ -30,9 +27,7 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
Context.EmitLdc_I4(Op.Id);
|
Context.EmitLdc_I4(Op.Id);
|
||||||
|
|
||||||
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
|
Context.EmitPrivateCall(typeof(AThreadState), MthdName);
|
||||||
|
|
||||||
Context.EmitCall(MthdInfo);
|
|
||||||
|
|
||||||
//Check if the thread should still be running, if it isn't then we return 0
|
//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.
|
//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_I8(Op.Position);
|
||||||
Context.EmitLdc_I4(Op.RawOpCode);
|
Context.EmitLdc_I4(Op.RawOpCode);
|
||||||
|
|
||||||
string MthdName = nameof(AThreadState.OnUndefined);
|
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.OnUndefined));
|
||||||
|
|
||||||
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
|
|
||||||
|
|
||||||
Context.EmitCall(MthdInfo);
|
|
||||||
|
|
||||||
if (Context.CurrBlock.Next != null)
|
if (Context.CurrBlock.Next != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,6 +35,14 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
|
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.EmitLdc_I(Op.Position + 4);
|
||||||
Context.EmitStint(AThreadState.LRIndex);
|
Context.EmitStint(AThreadState.LRIndex);
|
||||||
Context.EmitStoreState();
|
Context.EmitStoreState();
|
||||||
|
@ -72,6 +80,14 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
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.EmitLdc_I(Op.Position + 4);
|
||||||
Context.EmitStint(AThreadState.LRIndex);
|
Context.EmitStint(AThreadState.LRIndex);
|
||||||
Context.EmitStoreState();
|
Context.EmitStoreState();
|
||||||
|
@ -84,6 +100,14 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
|
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.EmitStoreState();
|
||||||
Context.EmitLdintzr(Op.Rn);
|
Context.EmitLdintzr(Op.Rn);
|
||||||
|
|
||||||
|
@ -105,6 +129,13 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void Ret(AILEmitterCtx Context)
|
public static void Ret(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
|
if (AOptimizations.GenerateCallStack)
|
||||||
|
{
|
||||||
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
|
|
||||||
|
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.ExitMethod));
|
||||||
|
}
|
||||||
|
|
||||||
Context.EmitStoreState();
|
Context.EmitStoreState();
|
||||||
Context.EmitLdint(AThreadState.LRIndex);
|
Context.EmitLdint(AThreadState.LRIndex);
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,44 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
static partial class AInstEmit
|
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)
|
public static void Add_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
|
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)
|
public static void Addp_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
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)
|
public static void Fabd_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarBinaryOpF(Context, () =>
|
EmitScalarBinaryOpF(Context, () =>
|
||||||
|
@ -129,6 +196,38 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Add));
|
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)
|
public static void Fdiv_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div));
|
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div));
|
||||||
|
@ -150,17 +249,87 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void Fmax_S(AILEmitterCtx Context)
|
public static void Fmax_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
|
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||||
|
|
||||||
EmitScalarBinaryOpF(Context, () =>
|
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)
|
public static void Fmin_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
|
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||||
|
|
||||||
EmitScalarBinaryOpF(Context, () =>
|
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)
|
public static void Fmsub_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarTernaryRaOpF(Context, () =>
|
EmitScalarTernaryRaOpF(Context, () =>
|
||||||
|
@ -206,6 +393,11 @@ namespace ChocolArm64.Instruction
|
||||||
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
|
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)
|
public static void Fmul_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
|
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
|
||||||
|
@ -221,13 +413,30 @@ namespace ChocolArm64.Instruction
|
||||||
EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg));
|
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);
|
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)
|
public static void Fnmsub_S(AILEmitterCtx Context)
|
||||||
|
@ -248,6 +457,119 @@ namespace ChocolArm64.Instruction
|
||||||
EmitScalarSetF(Context, Op.Rd, SizeF);
|
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)
|
public static void Frinta_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||||
|
@ -259,6 +581,66 @@ namespace ChocolArm64.Instruction
|
||||||
EmitScalarSetF(Context, Op.Rd, Op.Size);
|
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)
|
public static void Frintm_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarUnaryOpF(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)
|
public static void Frintp_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarUnaryOpF(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)
|
public static void Frintx_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
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)
|
public static void Fsqrt_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarUnaryOpF(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)
|
public static void Mls_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorTernaryOpZx(Context, () =>
|
EmitVectorTernaryOpZx(Context, () =>
|
||||||
|
@ -354,11 +877,26 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorBinaryOpByElemZx(Context, () => Context.Emit(OpCodes.Mul));
|
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)
|
public static void Neg_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
|
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)
|
public static void Saddw_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add));
|
EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add));
|
||||||
|
@ -406,6 +944,35 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
|
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)
|
public static void Uaddlv_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||||
|
@ -429,6 +996,18 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
|
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)
|
public static void Umull_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));
|
EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));
|
||||||
|
|
|
@ -110,13 +110,64 @@ namespace ChocolArm64.Instruction
|
||||||
Fccmp_S(Context);
|
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)
|
public static void Fcmp_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
|
|
||||||
bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false;
|
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)
|
if (CmpWithZero)
|
||||||
{
|
{
|
||||||
EmitNaNCheck(Context, Op.Rn);
|
EmitNaNCheck(Context, Op.Rn);
|
||||||
|
@ -140,7 +191,14 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
if (CmpWithZero)
|
if (CmpWithZero)
|
||||||
{
|
{
|
||||||
EmitLdcImmF(Context, 0, Op.Size);
|
if (Op.Size == 0)
|
||||||
|
{
|
||||||
|
Context.EmitLdc_R4(0);
|
||||||
|
}
|
||||||
|
else /* if (SizeF == 1) */
|
||||||
|
{
|
||||||
|
Context.EmitLdc_R8(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -190,22 +248,6 @@ namespace ChocolArm64.Instruction
|
||||||
Fcmp_S(Context);
|
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)
|
private static void EmitNaNCheck(AILEmitterCtx Context, int Reg)
|
||||||
{
|
{
|
||||||
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
||||||
|
@ -268,5 +310,84 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorZeroUpper(Context, Op.Rd);
|
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);
|
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)
|
public static void EmitScalarUnaryOpSx(AILEmitterCtx Context, Action Emit)
|
||||||
{
|
{
|
||||||
EmitScalarOp(Context, Emit, OperFlags.Rn, true);
|
EmitScalarOp(Context, Emit, OperFlags.Rn, true);
|
||||||
|
@ -381,13 +427,16 @@ namespace ChocolArm64.Instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
|
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();
|
Emit();
|
||||||
|
|
||||||
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
|
EmitVectorInsertTmp(Context, Index, Op.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Context.EmitLdvectmp();
|
||||||
|
Context.EmitStvec(Op.Rd);
|
||||||
|
|
||||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||||
{
|
{
|
||||||
EmitVectorZeroUpper(Context, Op.Rd);
|
EmitVectorZeroUpper(Context, Op.Rd);
|
||||||
|
@ -444,6 +493,9 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
|
|
||||||
|
Context.EmitLdvec(Op.Rd);
|
||||||
|
Context.EmitStvectmp();
|
||||||
|
|
||||||
int Elems = 8 >> Op.Size;
|
int Elems = 8 >> Op.Size;
|
||||||
|
|
||||||
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
|
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
|
||||||
|
@ -486,6 +538,9 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
|
|
||||||
|
Context.EmitLdvec(Op.Rd);
|
||||||
|
Context.EmitStvectmp();
|
||||||
|
|
||||||
int Elems = 8 >> Op.Size;
|
int Elems = 8 >> Op.Size;
|
||||||
|
|
||||||
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
|
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)
|
public static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed)
|
||||||
{
|
{
|
||||||
if (Size < 0 || Size > 3)
|
ThrowIfInvalid(Index, Size);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
|
||||||
}
|
|
||||||
|
|
||||||
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
|
||||||
|
|
||||||
|
@ -551,6 +603,8 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size)
|
public static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size)
|
||||||
{
|
{
|
||||||
|
ThrowIfInvalidF(Index, Size);
|
||||||
|
|
||||||
Context.EmitLdvec(Reg);
|
Context.EmitLdvec(Reg);
|
||||||
Context.EmitLdc_I4(Index);
|
Context.EmitLdc_I4(Index);
|
||||||
|
|
||||||
|
@ -586,10 +640,7 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size)
|
public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size)
|
||||||
{
|
{
|
||||||
if (Size < 0 || Size > 3)
|
ThrowIfInvalid(Index, Size);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
|
||||||
}
|
|
||||||
|
|
||||||
Context.EmitLdvec(Reg);
|
Context.EmitLdvec(Reg);
|
||||||
Context.EmitLdc_I4(Index);
|
Context.EmitLdc_I4(Index);
|
||||||
|
@ -602,10 +653,7 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size)
|
public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size)
|
||||||
{
|
{
|
||||||
if (Size < 0 || Size > 3)
|
ThrowIfInvalid(Index, Size);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
|
||||||
}
|
|
||||||
|
|
||||||
Context.EmitLdvectmp();
|
Context.EmitLdvectmp();
|
||||||
Context.EmitLdc_I4(Index);
|
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)
|
public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value)
|
||||||
{
|
{
|
||||||
if (Size < 0 || Size > 3)
|
ThrowIfInvalid(Index, Size);
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Size));
|
|
||||||
}
|
|
||||||
|
|
||||||
Context.EmitLdc_I8(Value);
|
Context.EmitLdc_I8(Value);
|
||||||
Context.EmitLdvec(Reg);
|
Context.EmitLdvec(Reg);
|
||||||
|
@ -635,6 +680,8 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size)
|
public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size)
|
||||||
{
|
{
|
||||||
|
ThrowIfInvalidF(Index, Size);
|
||||||
|
|
||||||
Context.EmitLdvec(Reg);
|
Context.EmitLdvec(Reg);
|
||||||
Context.EmitLdc_I4(Index);
|
Context.EmitLdc_I4(Index);
|
||||||
|
|
||||||
|
@ -656,6 +703,8 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void EmitVectorInsertTmpF(AILEmitterCtx Context, int Index, int Size)
|
public static void EmitVectorInsertTmpF(AILEmitterCtx Context, int Index, int Size)
|
||||||
{
|
{
|
||||||
|
ThrowIfInvalidF(Index, Size);
|
||||||
|
|
||||||
Context.EmitLdvectmp();
|
Context.EmitLdvectmp();
|
||||||
Context.EmitLdc_I4(Index);
|
Context.EmitLdc_I4(Index);
|
||||||
|
|
||||||
|
@ -674,5 +723,31 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
Context.EmitStvectmp();
|
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)
|
public static void Bsl_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorTernaryOpZx(Context, () =>
|
EmitVectorTernaryOpZx(Context, () =>
|
||||||
|
|
|
@ -61,6 +61,9 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
AOpCodeSimdExt Op = (AOpCodeSimdExt)Context.CurrOp;
|
AOpCodeSimdExt Op = (AOpCodeSimdExt)Context.CurrOp;
|
||||||
|
|
||||||
|
Context.EmitLdvec(Op.Rd);
|
||||||
|
Context.EmitStvectmp();
|
||||||
|
|
||||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||||
|
|
||||||
int Position = Op.Imm4;
|
int Position = Op.Imm4;
|
||||||
|
@ -75,10 +78,12 @@ namespace ChocolArm64.Instruction
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitVectorExtractZx(Context, Reg, Position++, 0);
|
EmitVectorExtractZx(Context, Reg, Position++, 0);
|
||||||
|
EmitVectorInsertTmp(Context, Index, 0);
|
||||||
EmitVectorInsert(Context, Op.Rd, Index, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Context.EmitLdvectmp();
|
||||||
|
Context.EmitStvec(Op.Rd);
|
||||||
|
|
||||||
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||||
{
|
{
|
||||||
EmitVectorZeroUpper(Context, Op.Rd);
|
EmitVectorZeroUpper(Context, Op.Rd);
|
||||||
|
@ -113,7 +118,7 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
EmitVectorExtractZx(Context, Op.Rn, 0, 3);
|
EmitVectorExtractZx(Context, Op.Rn, 0, 3);
|
||||||
|
|
||||||
EmitIntZeroHigherIfNeeded(Context);
|
EmitIntZeroUpperIfNeeded(Context);
|
||||||
|
|
||||||
Context.EmitStintzr(Op.Rd);
|
Context.EmitStintzr(Op.Rd);
|
||||||
}
|
}
|
||||||
|
@ -124,7 +129,7 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
EmitVectorExtractZx(Context, Op.Rn, 1, 3);
|
EmitVectorExtractZx(Context, Op.Rn, 1, 3);
|
||||||
|
|
||||||
EmitIntZeroHigherIfNeeded(Context);
|
EmitIntZeroUpperIfNeeded(Context);
|
||||||
|
|
||||||
Context.EmitStintzr(Op.Rd);
|
Context.EmitStintzr(Op.Rd);
|
||||||
}
|
}
|
||||||
|
@ -135,7 +140,7 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
Context.EmitLdintzr(Op.Rn);
|
Context.EmitLdintzr(Op.Rn);
|
||||||
|
|
||||||
EmitIntZeroHigherIfNeeded(Context);
|
EmitIntZeroUpperIfNeeded(Context);
|
||||||
|
|
||||||
EmitScalarSet(Context, Op.Rd, 3);
|
EmitScalarSet(Context, Op.Rd, 3);
|
||||||
}
|
}
|
||||||
|
@ -146,7 +151,7 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
Context.EmitLdintzr(Op.Rn);
|
Context.EmitLdintzr(Op.Rn);
|
||||||
|
|
||||||
EmitIntZeroHigherIfNeeded(Context);
|
EmitIntZeroUpperIfNeeded(Context);
|
||||||
|
|
||||||
EmitVectorInsert(Context, Op.Rd, 1, 3);
|
EmitVectorInsert(Context, Op.Rd, 1, 3);
|
||||||
}
|
}
|
||||||
|
@ -251,6 +256,16 @@ namespace ChocolArm64.Instruction
|
||||||
Context.EmitStvec(Op.Rd);
|
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)
|
public static void Umov_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
|
||||||
|
@ -301,7 +316,7 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorZip(Context, Part: 1);
|
EmitVectorZip(Context, Part: 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitIntZeroHigherIfNeeded(AILEmitterCtx Context)
|
private static void EmitIntZeroUpperIfNeeded(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
|
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)
|
private static void EmitVectorUnzip(AILEmitterCtx Context, int Part)
|
||||||
{
|
{
|
||||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
|
|
|
@ -20,6 +20,14 @@ namespace ChocolArm64.Instruction
|
||||||
Context.EmitCall(typeof(ASoftFallback), MthdName);
|
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 uint CountLeadingZeros32(uint Value) => (uint)CountLeadingZeros(Value, 32);
|
||||||
public static ulong CountLeadingZeros64(ulong Value) => (ulong)CountLeadingZeros(Value, 64);
|
public static ulong CountLeadingZeros64(ulong Value) => (ulong)CountLeadingZeros(Value, 64);
|
||||||
|
|
||||||
|
@ -42,14 +50,14 @@ namespace ChocolArm64.Instruction
|
||||||
private const uint Crc32cRevPoly = 0x82f63b78;
|
private const uint Crc32cRevPoly = 0x82f63b78;
|
||||||
|
|
||||||
public static uint Crc32b(uint Crc, byte Val) => Crc32 (Crc, Crc32RevPoly, Val);
|
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 Crc32h(uint Crc, ushort Val) => Crc32h(Crc, Crc32RevPoly, Val);
|
||||||
public static uint Crc32w(uint Crc, byte Val) => Crc32w(Crc, Crc32RevPoly, Val);
|
public static uint Crc32w(uint Crc, uint Val) => Crc32w(Crc, Crc32RevPoly, Val);
|
||||||
public static uint Crc32x(uint Crc, byte Val) => Crc32x(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 Crc32cb(uint Crc, byte Val) => Crc32 (Crc, Crc32cRevPoly, Val);
|
||||||
public static uint Crc32ch(uint Crc, byte Val) => Crc32h(Crc, Crc32cRevPoly, Val);
|
public static uint Crc32ch(uint Crc, ushort Val) => Crc32h(Crc, Crc32cRevPoly, Val);
|
||||||
public static uint Crc32cw(uint Crc, byte Val) => Crc32w(Crc, Crc32cRevPoly, Val);
|
public static uint Crc32cw(uint Crc, uint Val) => Crc32w(Crc, Crc32cRevPoly, Val);
|
||||||
public static uint Crc32cx(uint Crc, byte Val) => Crc32x(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)
|
private static uint Crc32h(uint Crc, uint Poly, ushort Val)
|
||||||
{
|
{
|
||||||
|
@ -242,10 +250,86 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static int CountSetBits8(byte Value)
|
public static int CountSetBits8(byte Value)
|
||||||
{
|
{
|
||||||
return (Value >> 0) & 1 + (Value >> 1) & 1 +
|
return ((Value >> 0) & 1) + ((Value >> 1) & 1) +
|
||||||
(Value >> 2) & 1 + (Value >> 3) & 1 +
|
((Value >> 2) & 1) + ((Value >> 3) & 1) +
|
||||||
(Value >> 4) & 1 + (Value >> 5) & 1 +
|
((Value >> 4) & 1) + ((Value >> 5) & 1) +
|
||||||
(Value >> 6) & 1 + (Value >> 7);
|
((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)
|
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.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace ChocolArm64.Memory
|
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];
|
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);
|
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())
|
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);
|
byte Value = (byte)Memory.ReadByte(Position + Offs);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using ChocolArm64.Events;
|
using ChocolArm64.Events;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace ChocolArm64.State
|
namespace ChocolArm64.State
|
||||||
|
@ -56,10 +57,17 @@ namespace ChocolArm64.State
|
||||||
public event EventHandler<AInstExceptionEventArgs> SvcCall;
|
public event EventHandler<AInstExceptionEventArgs> SvcCall;
|
||||||
public event EventHandler<AInstUndefinedEventArgs> Undefined;
|
public event EventHandler<AInstUndefinedEventArgs> Undefined;
|
||||||
|
|
||||||
|
private Stack<long> CallStack;
|
||||||
|
|
||||||
private static Stopwatch TickCounter;
|
private static Stopwatch TickCounter;
|
||||||
|
|
||||||
private static double HostTickFreq;
|
private static double HostTickFreq;
|
||||||
|
|
||||||
|
public AThreadState()
|
||||||
|
{
|
||||||
|
CallStack = new Stack<long>();
|
||||||
|
}
|
||||||
|
|
||||||
static AThreadState()
|
static AThreadState()
|
||||||
{
|
{
|
||||||
HostTickFreq = 1.0 / Stopwatch.Frequency;
|
HostTickFreq = 1.0 / Stopwatch.Frequency;
|
||||||
|
@ -83,5 +91,27 @@ namespace ChocolArm64.State
|
||||||
{
|
{
|
||||||
Undefined?.Invoke(this, new AInstUndefinedEventArgs(Position, RawOpCode));
|
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 AILBlock GetILBlock(int Index) => ILBlocks[Index];
|
||||||
|
|
||||||
public ATranslatedSub GetSubroutine(HashSet<long> Callees)
|
public ATranslatedSub GetSubroutine()
|
||||||
{
|
{
|
||||||
LocalAlloc = new ALocalAlloc(ILBlocks, Root);
|
LocalAlloc = new ALocalAlloc(ILBlocks, Root);
|
||||||
|
|
||||||
InitSubroutine(Callees);
|
InitSubroutine();
|
||||||
InitLocals();
|
InitLocals();
|
||||||
|
|
||||||
foreach (AILBlock ILBlock in ILBlocks)
|
foreach (AILBlock ILBlock in ILBlocks)
|
||||||
|
@ -75,7 +75,7 @@ namespace ChocolArm64.Translation
|
||||||
return Subroutine;
|
return Subroutine;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitSubroutine(HashSet<long> Callees)
|
private void InitSubroutine()
|
||||||
{
|
{
|
||||||
List<ARegister> Params = new List<ARegister>();
|
List<ARegister> Params = new List<ARegister>();
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
Generator = Mthd.GetILGenerator();
|
Generator = Mthd.GetILGenerator();
|
||||||
|
|
||||||
Subroutine = new ATranslatedSub(Mthd, Params, Callees);
|
Subroutine = new ATranslatedSub(Mthd, Params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitLocals()
|
private void InitLocals()
|
||||||
|
|
|
@ -12,8 +12,6 @@ namespace ChocolArm64.Translation
|
||||||
{
|
{
|
||||||
private ATranslator Translator;
|
private ATranslator Translator;
|
||||||
|
|
||||||
private HashSet<long> Callees;
|
|
||||||
|
|
||||||
private Dictionary<long, AILLabel> Labels;
|
private Dictionary<long, AILLabel> Labels;
|
||||||
|
|
||||||
private int BlkIndex;
|
private int BlkIndex;
|
||||||
|
@ -66,8 +64,6 @@ namespace ChocolArm64.Translation
|
||||||
this.Graph = Graph;
|
this.Graph = Graph;
|
||||||
this.Root = Root;
|
this.Root = Root;
|
||||||
|
|
||||||
Callees = new HashSet<long>();
|
|
||||||
|
|
||||||
Labels = new Dictionary<long, AILLabel>();
|
Labels = new Dictionary<long, AILLabel>();
|
||||||
|
|
||||||
Emitter = new AILEmitter(Graph, Root, SubName);
|
Emitter = new AILEmitter(Graph, Root, SubName);
|
||||||
|
@ -84,7 +80,7 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public ATranslatedSub GetSubroutine()
|
public ATranslatedSub GetSubroutine()
|
||||||
{
|
{
|
||||||
return Emitter.GetSubroutine(Callees);
|
return Emitter.GetSubroutine();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AdvanceOpCode()
|
public bool AdvanceOpCode()
|
||||||
|
@ -123,8 +119,6 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
public bool TryOptEmitSubroutineCall()
|
public bool TryOptEmitSubroutineCall()
|
||||||
{
|
{
|
||||||
Callees.Add(((AOpCodeBImm)CurrOp).Imm);
|
|
||||||
|
|
||||||
if (CurrBlock.Next == null)
|
if (CurrBlock.Next == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -152,6 +146,8 @@ namespace ChocolArm64.Translation
|
||||||
|
|
||||||
EmitCall(Sub.Method);
|
EmitCall(Sub.Method);
|
||||||
|
|
||||||
|
Sub.AddCaller(Root.Position);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,18 +256,24 @@ namespace ChocolArm64.Translation
|
||||||
case AIntType.Int64: Emit(OpCodes.Conv_I8); break;
|
case AIntType.Int64: Emit(OpCodes.Conv_I8); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IntType == AIntType.UInt64 ||
|
bool Sz64 = CurrOp.RegisterSize != ARegisterSize.Int32;
|
||||||
IntType == AIntType.Int64)
|
|
||||||
|
if (Sz64 == (IntType == AIntType.UInt64 ||
|
||||||
|
IntType == AIntType.Int64))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CurrOp.RegisterSize != ARegisterSize.Int32)
|
if (Sz64)
|
||||||
{
|
{
|
||||||
Emit(IntType >= AIntType.Int8
|
Emit(IntType >= AIntType.Int8
|
||||||
? OpCodes.Conv_I8
|
? OpCodes.Conv_I8
|
||||||
: OpCodes.Conv_U8);
|
: OpCodes.Conv_U8);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Emit(OpCodes.Conv_U4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EmitLsl(int Amount) => EmitILShift(Amount, OpCodes.Shl);
|
public void EmitLsl(int Amount) => EmitILShift(Amount, OpCodes.Shl);
|
||||||
|
@ -459,6 +461,21 @@ namespace ChocolArm64.Translation
|
||||||
EmitCall(ObjType.GetMethod(MthdName));
|
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)
|
public void EmitCall(MethodInfo MthdInfo)
|
||||||
{
|
{
|
||||||
if (MthdInfo == null)
|
if (MthdInfo == null)
|
||||||
|
|
|
@ -49,6 +49,9 @@ https://openal.org/downloads/OpenAL11CoreSDK.zip
|
||||||
- Config File: `Ryujinx.conf` should be present in executable folder.
|
- Config File: `Ryujinx.conf` should be present in executable folder.
|
||||||
For more informations [you can go here](CONFIG.md).
|
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**
|
**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.
|
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 class Config
|
||||||
{
|
{
|
||||||
|
public static bool EnableMemoryChecks { get; private set; }
|
||||||
public static bool LoggingEnableInfo { get; private set; }
|
public static bool LoggingEnableInfo { get; private set; }
|
||||||
public static bool LoggingEnableTrace { get; private set; }
|
public static bool LoggingEnableTrace { get; private set; }
|
||||||
public static bool LoggingEnableDebug { 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 LoggingEnableError { get; private set; }
|
||||||
public static bool LoggingEnableFatal { get; private set; }
|
public static bool LoggingEnableFatal { get; private set; }
|
||||||
public static bool LoggingEnableIpc { 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 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; }
|
public static JoyCon FakeJoyCon { get; private set; }
|
||||||
|
|
||||||
|
@ -26,6 +30,7 @@ namespace Ryujinx.Core
|
||||||
var iniPath = Path.Combine(iniFolder, "Ryujinx.conf");
|
var iniPath = Path.Combine(iniFolder, "Ryujinx.conf");
|
||||||
IniParser Parser = new IniParser(iniPath);
|
IniParser Parser = new IniParser(iniPath);
|
||||||
|
|
||||||
|
EnableMemoryChecks = Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
|
||||||
LoggingEnableInfo = Convert.ToBoolean(Parser.Value("Logging_Enable_Info"));
|
LoggingEnableInfo = Convert.ToBoolean(Parser.Value("Logging_Enable_Info"));
|
||||||
LoggingEnableTrace = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace"));
|
LoggingEnableTrace = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace"));
|
||||||
LoggingEnableDebug = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"));
|
LoggingEnableDebug = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"));
|
||||||
|
@ -33,7 +38,25 @@ namespace Ryujinx.Core
|
||||||
LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
|
LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
|
||||||
LoggingEnableFatal = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
|
LoggingEnableFatal = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
|
||||||
LoggingEnableIpc = Convert.ToBoolean(Parser.Value("Logging_Enable_Ipc"));
|
LoggingEnableIpc = Convert.ToBoolean(Parser.Value("Logging_Enable_Ipc"));
|
||||||
|
LoggingEnableStub = Convert.ToBoolean(Parser.Value("Logging_Enable_Stub"));
|
||||||
LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
|
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
|
FakeJoyCon = new JoyCon
|
||||||
{
|
{
|
||||||
|
|
|
@ -82,7 +82,7 @@ namespace Ryujinx.Core.Input
|
||||||
|
|
||||||
(AMemory Memory, long Position) ShMem = ShMemPositions[ShMemPositions.Length - 1];
|
(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);
|
Init(ShMem.Memory, ShMem.Position);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,16 @@ namespace Ryujinx.Core.Loaders
|
||||||
{
|
{
|
||||||
class Executable
|
class Executable
|
||||||
{
|
{
|
||||||
private AMemory Memory;
|
|
||||||
|
|
||||||
private List<ElfDyn> Dynamic;
|
private List<ElfDyn> Dynamic;
|
||||||
|
|
||||||
private Dictionary<long, string> m_SymbolTable;
|
private Dictionary<long, string> m_SymbolTable;
|
||||||
|
|
||||||
public IReadOnlyDictionary<long, string> SymbolTable => 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 ImageBase { get; private set; }
|
||||||
public long ImageEnd { get; private set; }
|
public long ImageEnd { get; private set; }
|
||||||
|
|
||||||
|
@ -24,6 +26,8 @@ namespace Ryujinx.Core.Loaders
|
||||||
|
|
||||||
m_SymbolTable = new Dictionary<long, string>();
|
m_SymbolTable = new Dictionary<long, string>();
|
||||||
|
|
||||||
|
Name = Exe.Name;
|
||||||
|
|
||||||
this.Memory = Memory;
|
this.Memory = Memory;
|
||||||
this.ImageBase = ImageBase;
|
this.ImageBase = ImageBase;
|
||||||
this.ImageEnd = ImageBase;
|
this.ImageEnd = ImageBase;
|
||||||
|
|
|
@ -2,6 +2,8 @@ namespace Ryujinx.Core.Loaders.Executables
|
||||||
{
|
{
|
||||||
public interface IExecutable
|
public interface IExecutable
|
||||||
{
|
{
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
byte[] Text { get; }
|
byte[] Text { get; }
|
||||||
byte[] RO { get; }
|
byte[] RO { get; }
|
||||||
byte[] Data { get; }
|
byte[] Data { get; }
|
||||||
|
|
|
@ -4,6 +4,8 @@ namespace Ryujinx.Core.Loaders.Executables
|
||||||
{
|
{
|
||||||
class Nro : IExecutable
|
class Nro : IExecutable
|
||||||
{
|
{
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
public byte[] Text { get; private set; }
|
public byte[] Text { get; private set; }
|
||||||
public byte[] RO { get; private set; }
|
public byte[] RO { get; private set; }
|
||||||
public byte[] Data { 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 DataOffset { get; private set; }
|
||||||
public int BssSize { 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);
|
BinaryReader Reader = new BinaryReader(Input);
|
||||||
|
|
||||||
Input.Seek(4, SeekOrigin.Begin);
|
Input.Seek(4, SeekOrigin.Begin);
|
||||||
|
|
|
@ -6,6 +6,8 @@ namespace Ryujinx.Core.Loaders.Executables
|
||||||
{
|
{
|
||||||
class Nso : IExecutable
|
class Nso : IExecutable
|
||||||
{
|
{
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
public byte[] Text { get; private set; }
|
public byte[] Text { get; private set; }
|
||||||
public byte[] RO { get; private set; }
|
public byte[] RO { get; private set; }
|
||||||
public byte[] Data { get; private set; }
|
public byte[] Data { get; private set; }
|
||||||
|
@ -27,8 +29,10 @@ namespace Ryujinx.Core.Loaders.Executables
|
||||||
HasDataHash = 1 << 5
|
HasDataHash = 1 << 5
|
||||||
}
|
}
|
||||||
|
|
||||||
public Nso(Stream Input)
|
public Nso(Stream Input, string Name)
|
||||||
{
|
{
|
||||||
|
this.Name = Name;
|
||||||
|
|
||||||
BinaryReader Reader = new BinaryReader(Input);
|
BinaryReader Reader = new BinaryReader(Input);
|
||||||
|
|
||||||
Input.Seek(0, SeekOrigin.Begin);
|
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;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Core
|
namespace Ryujinx.Core
|
||||||
{
|
{
|
||||||
|
@ -18,8 +20,22 @@ namespace Ryujinx.Core
|
||||||
private static bool EnableWarn = Config.LoggingEnableWarn;
|
private static bool EnableWarn = Config.LoggingEnableWarn;
|
||||||
private static bool EnableError = Config.LoggingEnableError;
|
private static bool EnableError = Config.LoggingEnableError;
|
||||||
private static bool EnableFatal = Config.LoggingEnableFatal;
|
private static bool EnableFatal = Config.LoggingEnableFatal;
|
||||||
|
private static bool EnableStub = Config.LoggingEnableStub;
|
||||||
private static bool EnableIpc = Config.LoggingEnableIpc;
|
private static bool EnableIpc = Config.LoggingEnableIpc;
|
||||||
|
private static bool EnableFilter = Config.LoggingEnableFilter;
|
||||||
private static bool EnableLogFile = Config.LoggingEnableLogFile;
|
private static bool EnableLogFile = Config.LoggingEnableLogFile;
|
||||||
|
private static bool[] FilteredLogClasses = Config.LoggingFilteredClasses;
|
||||||
|
|
||||||
|
private enum LogLevel
|
||||||
|
{
|
||||||
|
Debug,
|
||||||
|
Error,
|
||||||
|
Fatal,
|
||||||
|
Info,
|
||||||
|
Stub,
|
||||||
|
Trace,
|
||||||
|
Warn
|
||||||
|
}
|
||||||
|
|
||||||
static Logging()
|
static Logging()
|
||||||
{
|
{
|
||||||
|
@ -30,14 +46,51 @@ namespace Ryujinx.Core
|
||||||
ExecutionTime.Start();
|
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()
|
LogEntry.ManagedThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||||
{
|
|
||||||
return new StackTrace().GetFrame(2).GetMethod().Name;
|
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)
|
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)
|
if (EnableInfo)
|
||||||
{
|
{
|
||||||
string Text = $"{GetExecutionTime()} | INFO > {Message}";
|
LogMessage(new LogEntry
|
||||||
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.White;
|
CallingMember = CallingMember,
|
||||||
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
|
LogLevel = LogLevel.Info,
|
||||||
Console.ResetColor();
|
LogClass = LogClass,
|
||||||
|
Message = Message,
|
||||||
LogFile(Text);
|
ExecutionTime = GetExecutionTime()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Trace(string Message)
|
public static void Trace(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
|
||||||
{
|
{
|
||||||
if (EnableTrace)
|
if (EnableTrace)
|
||||||
{
|
{
|
||||||
string Text = $"{GetExecutionTime()} | TRACE > {WhoCalledMe()} - {Message}";
|
LogMessage(new LogEntry
|
||||||
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
CallingMember = CallingMember,
|
||||||
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
|
LogLevel = LogLevel.Trace,
|
||||||
Console.ResetColor();
|
LogClass = LogClass,
|
||||||
|
Message = Message,
|
||||||
LogFile(Text);
|
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)
|
if (EnableDebug)
|
||||||
{
|
{
|
||||||
string Text = $"{GetExecutionTime()} | DEBUG > {WhoCalledMe()} - {Message}";
|
LogMessage(new LogEntry
|
||||||
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Gray;
|
CallingMember = CallingMember,
|
||||||
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
|
LogLevel = LogLevel.Debug,
|
||||||
Console.ResetColor();
|
LogClass = LogClass,
|
||||||
|
Message = Message,
|
||||||
LogFile(Text);
|
ExecutionTime = GetExecutionTime()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Warn(string Message)
|
public static void Warn(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
|
||||||
{
|
{
|
||||||
if (EnableWarn)
|
if (EnableWarn)
|
||||||
{
|
{
|
||||||
string Text = $"{GetExecutionTime()} | WARN > {WhoCalledMe()} - {Message}";
|
LogMessage(new LogEntry
|
||||||
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
CallingMember = CallingMember,
|
||||||
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
|
LogLevel = LogLevel.Warn,
|
||||||
Console.ResetColor();
|
LogClass = LogClass,
|
||||||
|
Message = Message,
|
||||||
LogFile(Text);
|
ExecutionTime = GetExecutionTime()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Error(string Message)
|
public static void Error(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
|
||||||
{
|
{
|
||||||
if (EnableError)
|
if (EnableError)
|
||||||
{
|
{
|
||||||
string Text = $"{GetExecutionTime()} | ERROR > {WhoCalledMe()} - {Message}";
|
LogMessage(new LogEntry
|
||||||
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
CallingMember = CallingMember,
|
||||||
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
|
LogLevel = LogLevel.Error,
|
||||||
Console.ResetColor();
|
LogClass = LogClass,
|
||||||
|
Message = Message,
|
||||||
LogFile(Text);
|
ExecutionTime = GetExecutionTime()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Fatal(string Message)
|
public static void Fatal(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
|
||||||
{
|
{
|
||||||
if (EnableFatal)
|
if (EnableFatal)
|
||||||
{
|
{
|
||||||
string Text = $"{GetExecutionTime()} | FATAL > {WhoCalledMe()} - {Message}";
|
LogMessage(new LogEntry
|
||||||
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Magenta;
|
CallingMember = CallingMember,
|
||||||
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
|
LogLevel = LogLevel.Fatal,
|
||||||
Console.ResetColor();
|
LogClass = LogClass,
|
||||||
|
Message = Message,
|
||||||
LogFile(Text);
|
ExecutionTime = GetExecutionTime()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,5 +282,15 @@ namespace Ryujinx.Core
|
||||||
}
|
}
|
||||||
return result.ToString();
|
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
|
namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
class KProcessHandleTable : IDisposable
|
class KProcessHandleTable
|
||||||
{
|
{
|
||||||
private IdDictionary Handles;
|
private IdDictionary Handles;
|
||||||
|
|
||||||
|
@ -21,43 +21,14 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
return Handles.GetData<T>(Handle);
|
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);
|
return Handles.Delete(Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public ICollection<object> Clear()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
return Handles.Clear();
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool Disposing)
|
|
||||||
{
|
|
||||||
if (Disposing)
|
|
||||||
{
|
|
||||||
foreach (object Obj in Handles)
|
|
||||||
{
|
|
||||||
if (Obj is IDisposable DisposableObj)
|
|
||||||
{
|
|
||||||
DisposableObj.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,19 +5,31 @@ using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Handles
|
namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
public class KProcessScheduler : IDisposable
|
class KProcessScheduler : IDisposable
|
||||||
{
|
{
|
||||||
|
private const int LowestPriority = 0x40;
|
||||||
|
|
||||||
private class SchedulerThread : IDisposable
|
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;
|
this.Thread = Thread;
|
||||||
|
|
||||||
WaitEvent = new AutoResetEvent(false);
|
SyncWaitEvent = new ManualResetEvent(true);
|
||||||
|
SchedWaitEvent = new AutoResetEvent(false);
|
||||||
|
|
||||||
|
Active = true;
|
||||||
|
|
||||||
|
SyncTimeout = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -29,7 +41,8 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
if (Disposing)
|
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)
|
lock (Threads)
|
||||||
{
|
{
|
||||||
|
@ -65,9 +78,9 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
SchedThread = Threads[Index];
|
SchedThread = Threads[Index];
|
||||||
|
|
||||||
if (HighestPriority > SchedThread.Thread.Priority)
|
if (HighestPriority > SchedThread.Thread.ActualPriority)
|
||||||
{
|
{
|
||||||
HighestPriority = SchedThread.Thread.Priority;
|
HighestPriority = SchedThread.Thread.ActualPriority;
|
||||||
|
|
||||||
HighestPrioIndex = Index;
|
HighestPrioIndex = Index;
|
||||||
}
|
}
|
||||||
|
@ -93,9 +106,17 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
return Threads.Contains(SchedThread);
|
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;
|
private ThreadQueue[] WaitingToRun;
|
||||||
|
|
||||||
|
@ -105,7 +126,7 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
|
|
||||||
public KProcessScheduler()
|
public KProcessScheduler()
|
||||||
{
|
{
|
||||||
AllThreads = new ConcurrentDictionary<HThread, SchedulerThread>();
|
AllThreads = new ConcurrentDictionary<KThread, SchedulerThread>();
|
||||||
|
|
||||||
WaitingToRun = new ThreadQueue[4];
|
WaitingToRun = new ThreadQueue[4];
|
||||||
|
|
||||||
|
@ -119,7 +140,7 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
SchedLock = new object();
|
SchedLock = new object();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartThread(HThread Thread)
|
public void StartThread(KThread Thread)
|
||||||
{
|
{
|
||||||
lock (SchedLock)
|
lock (SchedLock)
|
||||||
{
|
{
|
||||||
|
@ -130,23 +151,119 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ActiveProcessors.Contains(Thread.ProcessorId))
|
if (ActiveProcessors.Add(Thread.ProcessorId))
|
||||||
{
|
{
|
||||||
ActiveProcessors.Add(Thread.ProcessorId);
|
|
||||||
|
|
||||||
Thread.Thread.Execute();
|
Thread.Thread.Execute();
|
||||||
|
|
||||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} running.");
|
PrintDbgThreadInfo(Thread, "running.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
|
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)
|
public void Suspend(int ProcessorId)
|
||||||
{
|
{
|
||||||
lock (SchedLock)
|
lock (SchedLock)
|
||||||
|
@ -159,164 +276,120 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logging.Debug(LogClass.KernelScheduler, $"Nothing to run on core {ProcessorId}!");
|
||||||
|
|
||||||
ActiveProcessors.Remove(ProcessorId);
|
ActiveProcessors.Remove(ProcessorId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Resume(HThread CurrThread)
|
public void Yield(KThread Thread)
|
||||||
{
|
{
|
||||||
SchedulerThread SchedThread;
|
PrintDbgThreadInfo(Thread, "yielded execution.");
|
||||||
|
|
||||||
Logging.Debug($"{GetDbgThreadInfo(CurrThread)} entering ipc delay wait state.");
|
|
||||||
|
|
||||||
lock (SchedLock)
|
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;
|
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)
|
if (SchedThread != null)
|
||||||
{
|
{
|
||||||
RunThread(SchedThread);
|
RunThread(SchedThread);
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
|
||||||
|
Resume(Thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Resume(KThread Thread)
|
||||||
{
|
{
|
||||||
ActiveProcessors.Remove(Thread.ProcessorId);
|
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
||||||
}
|
|
||||||
|
|
||||||
if (!AllThreads.TryGetValue(Thread, out SchedThread))
|
|
||||||
{
|
{
|
||||||
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
|
throw new InvalidOperationException();
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
if (!SchedThread.Active || SchedThread.SyncTimeout != 0)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
Result = SchedThread.WaitEvent.WaitOne();
|
PrintDbgThreadInfo(Thread, "entering inactive wait state...");
|
||||||
}
|
}
|
||||||
|
|
||||||
TryResumingExecution(SchedThread);
|
bool Result = false;
|
||||||
|
|
||||||
return Result;
|
if (SchedThread.SyncTimeout != 0)
|
||||||
}
|
|
||||||
|
|
||||||
private void TryResumingExecution(SchedulerThread SchedThread)
|
|
||||||
{
|
{
|
||||||
HThread Thread = SchedThread.Thread;
|
Result = SchedThread.SyncWaitEvent.WaitOne(SchedThread.SyncTimeout);
|
||||||
|
|
||||||
|
SchedThread.SyncTimeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
lock (SchedLock)
|
lock (SchedLock)
|
||||||
{
|
{
|
||||||
if (ActiveProcessors.Add(Thread.ProcessorId))
|
if (ActiveProcessors.Add(Thread.ProcessorId))
|
||||||
{
|
{
|
||||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
|
PrintDbgThreadInfo(Thread, "resuming execution...");
|
||||||
|
|
||||||
return;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
|
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)
|
return Result;
|
||||||
{
|
|
||||||
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...");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RunThread(SchedulerThread SchedThread)
|
private void RunThread(SchedulerThread SchedThread)
|
||||||
{
|
{
|
||||||
if (!SchedThread.Thread.Thread.Execute())
|
if (!SchedThread.Thread.Thread.Execute())
|
||||||
{
|
{
|
||||||
SchedThread.WaitEvent.Set();
|
SchedThread.SchedWaitEvent.Set();
|
||||||
}
|
}
|
||||||
else
|
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)
|
throw new InvalidOperationException();
|
||||||
{
|
|
||||||
if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
|
|
||||||
{
|
|
||||||
if (!WaitingToRun[Thread.ProcessorId].HasThread(SchedThread))
|
|
||||||
{
|
|
||||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} signaled.");
|
|
||||||
|
|
||||||
SchedThread.WaitEvent.Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
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 Value0 = Memory.ReadInt64(Position + 0x08);
|
||||||
long Value1 = Memory.ReadInt64(Position + 0x10);
|
long Value1 = Memory.ReadInt64(Position + 0x10);
|
||||||
|
|
||||||
FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, (int)(Value1 - Value0));
|
FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, Value1 - Value0);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,14 @@ namespace Ryujinx.Core.OsHle
|
||||||
internal const int HidSize = 0x40000;
|
internal const int HidSize = 0x40000;
|
||||||
internal const int FontSize = 0x50;
|
internal const int FontSize = 0x50;
|
||||||
|
|
||||||
internal ConcurrentDictionary<long, Mutex> Mutexes { get; private set; }
|
private KProcessScheduler Scheduler;
|
||||||
internal ConcurrentDictionary<long, CondVar> CondVars { get; private set; }
|
|
||||||
|
|
||||||
private ConcurrentDictionary<int, Process> Processes;
|
private ConcurrentDictionary<int, Process> Processes;
|
||||||
|
|
||||||
internal HSharedMem HidSharedMem;
|
internal HSharedMem HidSharedMem { get; private set; }
|
||||||
internal HSharedMem FontSharedMem;
|
internal HSharedMem FontSharedMem { get; private set; }
|
||||||
|
|
||||||
|
internal KEvent VsyncEvent { get; private set; }
|
||||||
|
|
||||||
private Switch Ns;
|
private Switch Ns;
|
||||||
|
|
||||||
|
@ -25,13 +26,14 @@ namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
this.Ns = Ns;
|
this.Ns = Ns;
|
||||||
|
|
||||||
Mutexes = new ConcurrentDictionary<long, Mutex>();
|
Scheduler = new KProcessScheduler();
|
||||||
CondVars = new ConcurrentDictionary<long, CondVar>();
|
|
||||||
|
|
||||||
Processes = new ConcurrentDictionary<int, Process>();
|
Processes = new ConcurrentDictionary<int, Process>();
|
||||||
|
|
||||||
HidSharedMem = new HSharedMem();
|
HidSharedMem = new HSharedMem();
|
||||||
FontSharedMem = new HSharedMem();
|
FontSharedMem = new HSharedMem();
|
||||||
|
|
||||||
|
VsyncEvent = new KEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
||||||
|
@ -52,11 +54,13 @@ namespace Ryujinx.Core.OsHle
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.Info($"Loading {Path.GetFileNameWithoutExtension(File)}...");
|
Logging.Info(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}...");
|
||||||
|
|
||||||
using (FileStream Input = new FileStream(File, FileMode.Open))
|
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);
|
MainProcess.LoadProgram(Program);
|
||||||
}
|
}
|
||||||
|
@ -78,19 +82,23 @@ namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
|
bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
|
||||||
|
|
||||||
|
string Name = Path.GetFileNameWithoutExtension(FileName);
|
||||||
|
|
||||||
Process MainProcess = MakeProcess();
|
Process MainProcess = MakeProcess();
|
||||||
|
|
||||||
using (FileStream Input = new FileStream(FileName, FileMode.Open))
|
using (FileStream Input = new FileStream(FileName, FileMode.Open))
|
||||||
{
|
{
|
||||||
MainProcess.LoadProgram(IsNro
|
MainProcess.LoadProgram(IsNro
|
||||||
? (IExecutable)new Nro(Input)
|
? (IExecutable)new Nro(Input, Name)
|
||||||
: (IExecutable)new Nso(Input));
|
: (IExecutable)new Nso(Input, Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
MainProcess.SetEmptyArgs();
|
MainProcess.SetEmptyArgs();
|
||||||
MainProcess.Run(IsNro);
|
MainProcess.Run(IsNro);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SignalVsync() => VsyncEvent.WaitEvent.Set();
|
||||||
|
|
||||||
private Process MakeProcess()
|
private Process MakeProcess()
|
||||||
{
|
{
|
||||||
Process Process;
|
Process Process;
|
||||||
|
@ -104,21 +112,28 @@ namespace Ryujinx.Core.OsHle
|
||||||
ProcessId++;
|
ProcessId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
Process = new Process(Ns, ProcessId);
|
Process = new Process(Ns, Scheduler, ProcessId);
|
||||||
|
|
||||||
Processes.TryAdd(ProcessId, Process);
|
Processes.TryAdd(ProcessId, Process);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InitializeProcess(Process);
|
||||||
|
|
||||||
return Process;
|
return Process;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InitializeProcess(Process Process)
|
||||||
|
{
|
||||||
|
Process.AppletState.SetFocus(true);
|
||||||
|
}
|
||||||
|
|
||||||
internal void ExitProcess(int ProcessId)
|
internal void ExitProcess(int ProcessId)
|
||||||
{
|
{
|
||||||
if (Processes.TryGetValue(ProcessId, out Process Process) && Process.NeedsHbAbi)
|
if (Processes.TryGetValue(ProcessId, out Process Process) && Process.NeedsHbAbi)
|
||||||
{
|
{
|
||||||
string NextNro = Homebrew.ReadHbAbiNextLoadPath(Process.Memory, Process.HbAbiDataPosition);
|
string NextNro = Homebrew.ReadHbAbiNextLoadPath(Process.Memory, Process.HbAbiDataPosition);
|
||||||
|
|
||||||
Logging.Info($"HbAbi NextLoadPath {NextNro}");
|
Logging.Info(LogClass.Loader, $"HbAbi NextLoadPath {NextNro}");
|
||||||
|
|
||||||
if (NextNro == string.Empty)
|
if (NextNro == string.Empty)
|
||||||
{
|
{
|
||||||
|
@ -131,11 +146,6 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
if (File.Exists(NextNro))
|
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);
|
LoadProgram(NextNro);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,6 +181,10 @@ namespace Ryujinx.Core.OsHle
|
||||||
Process.StopAllThreadsAsync();
|
Process.StopAllThreadsAsync();
|
||||||
Process.Dispose();
|
Process.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VsyncEvent.Dispose();
|
||||||
|
|
||||||
|
Scheduler.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle
|
namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
class IdDictionary : IEnumerable<object>
|
class IdDictionary
|
||||||
{
|
{
|
||||||
private ConcurrentDictionary<int, object> Objs;
|
private ConcurrentDictionary<int, object> Objs;
|
||||||
|
|
||||||
|
@ -16,6 +15,11 @@ namespace Ryujinx.Core.OsHle
|
||||||
Objs = new ConcurrentDictionary<int, object>();
|
Objs = new ConcurrentDictionary<int, object>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Add(int Id, object Data)
|
||||||
|
{
|
||||||
|
return Objs.TryAdd(Id, Data);
|
||||||
|
}
|
||||||
|
|
||||||
public int Add(object Data)
|
public int Add(object Data)
|
||||||
{
|
{
|
||||||
if (Objs.TryAdd(FreeIdHint, Data))
|
if (Objs.TryAdd(FreeIdHint, Data))
|
||||||
|
@ -39,18 +43,6 @@ namespace Ryujinx.Core.OsHle
|
||||||
throw new InvalidOperationException();
|
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)
|
public object GetData(int Id)
|
||||||
{
|
{
|
||||||
if (Objs.TryGetValue(Id, out object Data))
|
if (Objs.TryGetValue(Id, out object Data))
|
||||||
|
@ -71,31 +63,25 @@ namespace Ryujinx.Core.OsHle
|
||||||
return default(T);
|
return default(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Delete(int Id)
|
public object Delete(int Id)
|
||||||
{
|
{
|
||||||
if (Objs.TryRemove(Id, out object Obj))
|
if (Objs.TryRemove(Id, out object Obj))
|
||||||
{
|
{
|
||||||
if (Obj is IDisposable DisposableObj)
|
|
||||||
{
|
|
||||||
DisposableObj.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
FreeIdHint = Id;
|
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()
|
Objs.Clear();
|
||||||
{
|
|
||||||
return Objs.Values.GetEnumerator();
|
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 ChocolArm64.Memory;
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
using Ryujinx.Core.OsHle.Handles;
|
||||||
using Ryujinx.Core.OsHle.IpcServices;
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
|
@ -8,20 +7,15 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
{
|
{
|
||||||
static class IpcHandler
|
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(
|
public static void IpcCall(
|
||||||
Switch Ns,
|
Switch Ns,
|
||||||
Process Process,
|
Process Process,
|
||||||
AMemory Memory,
|
AMemory Memory,
|
||||||
HSession Session,
|
KSession Session,
|
||||||
IpcMessage Request,
|
IpcMessage Request,
|
||||||
int ThreadId,
|
long CmdPtr)
|
||||||
long CmdPtr,
|
|
||||||
int HndId)
|
|
||||||
{
|
{
|
||||||
IpcMessage Response = new IpcMessage(Request.IsDomain && Request.Type == IpcMessageType.Request);
|
IpcMessage Response = new IpcMessage();
|
||||||
|
|
||||||
using (MemoryStream Raw = new MemoryStream(Request.RawData))
|
using (MemoryStream Raw = new MemoryStream(Request.RawData))
|
||||||
{
|
{
|
||||||
|
@ -29,72 +23,8 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
|
|
||||||
if (Request.Type == IpcMessageType.Request)
|
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())
|
using (MemoryStream ResMS = new MemoryStream())
|
||||||
{
|
{
|
||||||
BinaryWriter ResWriter = new BinaryWriter(ResMS);
|
BinaryWriter ResWriter = new BinaryWriter(ResMS);
|
||||||
|
@ -109,14 +39,9 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
ReqReader,
|
ReqReader,
|
||||||
ResWriter);
|
ResWriter);
|
||||||
|
|
||||||
long Result = ProcReq(Context);
|
Session.Service.CallMethod(Context);
|
||||||
|
|
||||||
Response = FillResponse(Response, Result, ResMS.ToArray());
|
Response.RawData = ResMS.ToArray();
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!IgnoreNullPR)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException(DbgServiceName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Request.Type == IpcMessageType.Control)
|
else if (Request.Type == IpcMessageType.Control)
|
||||||
|
@ -128,11 +53,7 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
HDomain Dom = new HDomain(Session);
|
Request = FillResponse(Response, 0, Session.Service.ConvertToDomain());
|
||||||
|
|
||||||
Process.HandleTable.ReplaceData(HndId, Dom);
|
|
||||||
|
|
||||||
Request = FillResponse(Response, 0, Dom.Add(Dom));
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -198,7 +119,7 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
{
|
{
|
||||||
BinaryWriter Writer = new BinaryWriter(MS);
|
BinaryWriter Writer = new BinaryWriter(MS);
|
||||||
|
|
||||||
Writer.Write(SfcoMagic);
|
Writer.Write(IpcMagic.Sfco);
|
||||||
Writer.Write(Result);
|
Writer.Write(Result);
|
||||||
|
|
||||||
if (Data != null)
|
if (Data != null)
|
||||||
|
|
|
@ -125,8 +125,7 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
|
|
||||||
Reader.ReadInt64(); //Padding
|
Reader.ReadInt64(); //Padding
|
||||||
|
|
||||||
IpcMessage += Environment.NewLine + $" Domain:" + Environment.NewLine +
|
IpcMessage += Environment.NewLine + $" Domain:" + Environment.NewLine + Environment.NewLine +
|
||||||
$" DomCmd: {Enum.GetName(typeof(IpcDomCmd), DomCmd)}" + Environment.NewLine +
|
|
||||||
$" DomObjId: {DomObjId.ToString()}" + 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 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 byte[] RawData { get; set; }
|
||||||
|
|
||||||
public IpcMessage()
|
public IpcMessage()
|
||||||
|
@ -34,27 +30,18 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
ResponseObjIds = new List<int>();
|
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))
|
using (MemoryStream MS = new MemoryStream(Data))
|
||||||
{
|
{
|
||||||
BinaryReader Reader = new BinaryReader(MS);
|
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 Word0 = Reader.ReadInt32();
|
||||||
int Word1 = Reader.ReadInt32();
|
int Word1 = Reader.ReadInt32();
|
||||||
|
|
||||||
|
@ -110,19 +97,6 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
RecvListCount = 0;
|
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);
|
RawData = Reader.ReadBytes(RawDataSize);
|
||||||
|
|
||||||
Reader.BaseStream.Seek(RecvListPos, SeekOrigin.Begin);
|
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...
|
//This is the weirdest padding I've seen so far...
|
||||||
int Pad1 = 0x10 - Pad0;
|
int Pad1 = 0x10 - Pad0;
|
||||||
|
|
||||||
DataLength = (DataLength + Pad0 + Pad1 + (IsDomain ? 0x10 : 0)) / 4;
|
DataLength = (DataLength + Pad0 + Pad1) / 4;
|
||||||
|
|
||||||
DataLength += ResponseObjIds.Count;
|
|
||||||
|
|
||||||
Word1 = DataLength & 0x3ff;
|
Word1 = DataLength & 0x3ff;
|
||||||
|
|
||||||
|
@ -182,23 +154,11 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
|
|
||||||
MS.Seek(Pad0, SeekOrigin.Current);
|
MS.Seek(Pad0, SeekOrigin.Current);
|
||||||
|
|
||||||
if (IsDomain)
|
|
||||||
{
|
|
||||||
Writer.Write(ResponseObjIds.Count);
|
|
||||||
Writer.Write(0);
|
|
||||||
Writer.Write(0L);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RawData != null)
|
if (RawData != null)
|
||||||
{
|
{
|
||||||
Writer.Write(RawData);
|
Writer.Write(RawData);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (int Id in ResponseObjIds)
|
|
||||||
{
|
|
||||||
Writer.Write(Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Writer.Write(new byte[Pad1]);
|
Writer.Write(new byte[Pad1]);
|
||||||
|
|
||||||
return MS.ToArray();
|
return MS.ToArray();
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
{
|
{
|
||||||
public long Position { get; private set; }
|
public long Position { get; private set; }
|
||||||
public int Index { 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)
|
public IpcPtrBuffDesc(BinaryReader Reader)
|
||||||
{
|
{
|
||||||
|
@ -20,7 +20,7 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
Index = ((int)Word0 >> 0) & 0x03f;
|
Index = ((int)Word0 >> 0) & 0x03f;
|
||||||
Index |= ((int)Word0 >> 3) & 0x1c0;
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Svc
|
namespace Ryujinx.Core.OsHle.Kernel
|
||||||
{
|
{
|
||||||
partial class SvcHandler : IDisposable
|
partial class SvcHandler : IDisposable
|
||||||
{
|
{
|
||||||
|
@ -17,6 +17,8 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
private Process Process;
|
private Process Process;
|
||||||
private AMemory Memory;
|
private AMemory Memory;
|
||||||
|
|
||||||
|
private object CondVarLock;
|
||||||
|
|
||||||
private HashSet<(HSharedMem, long)> MappedSharedMems;
|
private HashSet<(HSharedMem, long)> MappedSharedMems;
|
||||||
|
|
||||||
private ulong CurrentHeapSize;
|
private ulong CurrentHeapSize;
|
||||||
|
@ -40,6 +42,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
{ 0x0c, SvcGetThreadPriority },
|
{ 0x0c, SvcGetThreadPriority },
|
||||||
{ 0x0d, SvcSetThreadPriority },
|
{ 0x0d, SvcSetThreadPriority },
|
||||||
{ 0x0f, SvcSetThreadCoreMask },
|
{ 0x0f, SvcSetThreadCoreMask },
|
||||||
|
{ 0x10, SvcGetCurrentProcessorNumber },
|
||||||
{ 0x12, SvcClearEvent },
|
{ 0x12, SvcClearEvent },
|
||||||
{ 0x13, SvcMapSharedMemory },
|
{ 0x13, SvcMapSharedMemory },
|
||||||
{ 0x14, SvcUnmapSharedMemory },
|
{ 0x14, SvcUnmapSharedMemory },
|
||||||
|
@ -58,13 +61,16 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
{ 0x25, SvcGetThreadId },
|
{ 0x25, SvcGetThreadId },
|
||||||
{ 0x26, SvcBreak },
|
{ 0x26, SvcBreak },
|
||||||
{ 0x27, SvcOutputDebugString },
|
{ 0x27, SvcOutputDebugString },
|
||||||
{ 0x29, SvcGetInfo }
|
{ 0x29, SvcGetInfo },
|
||||||
|
{ 0x32, SvcSetThreadActivity }
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Ns = Ns;
|
this.Ns = Ns;
|
||||||
this.Process = Process;
|
this.Process = Process;
|
||||||
this.Memory = Process.Memory;
|
this.Memory = Process.Memory;
|
||||||
|
|
||||||
|
CondVarLock = new object();
|
||||||
|
|
||||||
MappedSharedMems = new HashSet<(HSharedMem, long)>();
|
MappedSharedMems = new HashSet<(HSharedMem, long)>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,14 +85,16 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
|
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);
|
Func(ThreadState);
|
||||||
|
|
||||||
Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended.");
|
Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Process.PrintStackTrace(ThreadState);
|
||||||
|
|
||||||
throw new NotImplementedException(e.Id.ToString("x4"));
|
throw new NotImplementedException(e.Id.ToString("x4"));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ using Ryujinx.Core.OsHle.Handles;
|
||||||
|
|
||||||
using static Ryujinx.Core.OsHle.ErrorCode;
|
using static Ryujinx.Core.OsHle.ErrorCode;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Svc
|
namespace Ryujinx.Core.OsHle.Kernel
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler
|
||||||
{
|
{
|
||||||
|
@ -57,7 +57,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
if (!IsValidPosition(Src))
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
if (!IsValidMapPosition(Dst))
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
if (!IsValidPosition(Src))
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
if (!IsValidMapPosition(Dst))
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
if (!IsValidPosition(Src))
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
if (!IsValidPosition(Src))
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
|
@ -230,7 +230,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
if (!IsValidPosition(Src))
|
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);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
|
@ -3,12 +3,13 @@ using ChocolArm64.State;
|
||||||
using Ryujinx.Core.OsHle.Exceptions;
|
using Ryujinx.Core.OsHle.Exceptions;
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
using Ryujinx.Core.OsHle.Handles;
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
|
using Ryujinx.Core.OsHle.Services;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using static Ryujinx.Core.OsHle.ErrorCode;
|
using static Ryujinx.Core.OsHle.ErrorCode;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Svc
|
namespace Ryujinx.Core.OsHle.Kernel
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler
|
||||||
{
|
{
|
||||||
|
@ -34,7 +35,28 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
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;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
@ -43,25 +65,78 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
|
|
||||||
//TODO: Implement events.
|
KEvent Event = Process.HandleTable.GetData<KEvent>(Handle);
|
||||||
|
|
||||||
|
if (Event != null)
|
||||||
|
{
|
||||||
|
Event.WaitEvent.Reset();
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
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)
|
private void SvcWaitSynchronization(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long HandlesPtr = (long)ThreadState.X0;
|
long HandlesPtr = (long)ThreadState.X1;
|
||||||
int HandlesCount = (int)ThreadState.X2;
|
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);
|
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);
|
Process.Scheduler.Resume(CurrThread);
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
ThreadState.X0 = Result;
|
||||||
|
|
||||||
|
if (Result == 0)
|
||||||
|
{
|
||||||
|
ThreadState.X1 = (ulong)HandleIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcGetSystemTick(AThreadState ThreadState)
|
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
|
//TODO: Validate that app has perms to access the service, and that the service
|
||||||
//actually exists, return error codes otherwise.
|
//actually exists, return error codes otherwise.
|
||||||
|
KSession Session = new KSession(ServiceFactory.MakeService(Name), Name);
|
||||||
HSession Session = new HSession(Process.Services.GetService(Name));
|
|
||||||
|
|
||||||
ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session);
|
ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session);
|
||||||
|
|
||||||
|
@ -89,65 +163,46 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
private void SvcSendSyncRequest(AThreadState ThreadState)
|
private void SvcSendSyncRequest(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
SendSyncRequest(ThreadState, false);
|
SendSyncRequest(ThreadState, ThreadState.Tpidr, 0x100, (int)ThreadState.X0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcSendSyncRequestWithUserBuffer(AThreadState ThreadState)
|
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;
|
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||||
long Size = 0x100;
|
|
||||||
int Handle = 0;
|
|
||||||
|
|
||||||
if (UserBuffer)
|
byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, Size);
|
||||||
{
|
|
||||||
CmdPtr = (long)ThreadState.X0;
|
|
||||||
Size = (long)ThreadState.X1;
|
|
||||||
Handle = (int)ThreadState.X2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Handle = (int)ThreadState.X0;
|
|
||||||
}
|
|
||||||
|
|
||||||
HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
KSession Session = Process.HandleTable.GetData<KSession>(Handle);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (Session != null)
|
if (Session != null)
|
||||||
{
|
{
|
||||||
IpcHandler.IpcCall(
|
Process.Scheduler.Suspend(CurrThread.ProcessorId);
|
||||||
Ns,
|
|
||||||
Process,
|
|
||||||
Memory,
|
|
||||||
Session,
|
|
||||||
Cmd,
|
|
||||||
ThreadState.ThreadId,
|
|
||||||
CmdPtr,
|
|
||||||
Handle);
|
|
||||||
|
|
||||||
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;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
else
|
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)
|
private void SvcBreak(AThreadState ThreadState)
|
||||||
|
@ -156,6 +211,8 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
long Unknown = (long)ThreadState.X1;
|
long Unknown = (long)ThreadState.X1;
|
||||||
long Info = (long)ThreadState.X2;
|
long Info = (long)ThreadState.X2;
|
||||||
|
|
||||||
|
Process.PrintStackTrace(ThreadState);
|
||||||
|
|
||||||
throw new GuestBrokeExecutionException();
|
throw new GuestBrokeExecutionException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,9 +221,9 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
long Position = (long)ThreadState.X0;
|
long Position = (long)ThreadState.X0;
|
||||||
long Size = (long)ThreadState.X1;
|
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;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
@ -180,7 +237,8 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
//Fail for info not available on older Kernel versions.
|
//Fail for info not available on older Kernel versions.
|
||||||
if (InfoType == 18 ||
|
if (InfoType == 18 ||
|
||||||
InfoType == 19)
|
InfoType == 19 ||
|
||||||
|
InfoType == 20)
|
||||||
{
|
{
|
||||||
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo);
|
||||||
|
|
||||||
|
@ -241,7 +299,10 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
ThreadState.X1 = MemoryRegions.MapRegionSize;
|
ThreadState.X1 = MemoryRegions.MapRegionSize;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}");
|
default:
|
||||||
|
Process.PrintStackTrace(ThreadState);
|
||||||
|
|
||||||
|
throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle:x8} {InfoId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState.X0 = 0;
|
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 MainStackAddress = AMemoryMgr.AddrSize - MainStackSize;
|
||||||
|
|
||||||
public const long TlsPagesSize = 0x4000;
|
public const long TlsPagesSize = 0x20000;
|
||||||
|
|
||||||
public const long TlsPagesAddress = MainStackAddress - TlsPagesSize;
|
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;
|
||||||
using ChocolArm64.Events;
|
using ChocolArm64.Events;
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
|
using ChocolArm64.State;
|
||||||
using Ryujinx.Core.Loaders;
|
using Ryujinx.Core.Loaders;
|
||||||
using Ryujinx.Core.Loaders.Executables;
|
using Ryujinx.Core.Loaders.Executables;
|
||||||
using Ryujinx.Core.OsHle.Exceptions;
|
using Ryujinx.Core.OsHle.Exceptions;
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
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;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle
|
namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
class Process : IDisposable
|
class Process : IDisposable
|
||||||
{
|
{
|
||||||
private const int TlsSize = 0x200;
|
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;
|
private const int TickFreq = 19_200_000;
|
||||||
|
|
||||||
|
@ -31,21 +35,25 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
public AMemory Memory { get; private set; }
|
public AMemory Memory { get; private set; }
|
||||||
|
|
||||||
public ServiceMgr Services { get; private set; }
|
|
||||||
|
|
||||||
public KProcessScheduler Scheduler { get; private set; }
|
public KProcessScheduler Scheduler { get; private set; }
|
||||||
|
|
||||||
|
public KThread ThreadArbiterList { get; set; }
|
||||||
|
|
||||||
public KProcessHandleTable HandleTable { get; private set; }
|
public KProcessHandleTable HandleTable { get; private set; }
|
||||||
|
|
||||||
|
public AppletStateMgr AppletState { get; private set; }
|
||||||
|
|
||||||
private SvcHandler SvcHandler;
|
private SvcHandler SvcHandler;
|
||||||
|
|
||||||
private ConcurrentDictionary<int, AThread> TlsSlots;
|
private ConcurrentDictionary<int, AThread> TlsSlots;
|
||||||
|
|
||||||
private ConcurrentDictionary<long, HThread> ThreadsByTpidr;
|
private ConcurrentDictionary<long, KThread> Threads;
|
||||||
|
|
||||||
|
private KThread MainThread;
|
||||||
|
|
||||||
private List<Executable> Executables;
|
private List<Executable> Executables;
|
||||||
|
|
||||||
private HThread MainThread;
|
private Dictionary<long, string> SymbolTable;
|
||||||
|
|
||||||
private long ImageBase;
|
private long ImageBase;
|
||||||
|
|
||||||
|
@ -53,24 +61,23 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
private bool Disposed;
|
private bool Disposed;
|
||||||
|
|
||||||
public Process(Switch Ns, int ProcessId)
|
public Process(Switch Ns, KProcessScheduler Scheduler, int ProcessId)
|
||||||
{
|
{
|
||||||
this.Ns = Ns;
|
this.Ns = Ns;
|
||||||
|
this.Scheduler = Scheduler;
|
||||||
this.ProcessId = ProcessId;
|
this.ProcessId = ProcessId;
|
||||||
|
|
||||||
Memory = new AMemory();
|
Memory = new AMemory();
|
||||||
|
|
||||||
Services = new ServiceMgr();
|
|
||||||
|
|
||||||
HandleTable = new KProcessHandleTable();
|
HandleTable = new KProcessHandleTable();
|
||||||
|
|
||||||
Scheduler = new KProcessScheduler();
|
AppletState = new AppletStateMgr();
|
||||||
|
|
||||||
SvcHandler = new SvcHandler(Ns, this);
|
SvcHandler = new SvcHandler(Ns, this);
|
||||||
|
|
||||||
TlsSlots = new ConcurrentDictionary<int, AThread>();
|
TlsSlots = new ConcurrentDictionary<int, AThread>();
|
||||||
|
|
||||||
ThreadsByTpidr = new ConcurrentDictionary<long, HThread>();
|
Threads = new ConcurrentDictionary<long, KThread>();
|
||||||
|
|
||||||
Executables = new List<Executable>();
|
Executables = new List<Executable>();
|
||||||
|
|
||||||
|
@ -89,7 +96,7 @@ namespace Ryujinx.Core.OsHle
|
||||||
throw new ObjectDisposedException(nameof(Process));
|
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);
|
Executable Executable = new Executable(Program, Memory, ImageBase);
|
||||||
|
|
||||||
|
@ -118,6 +125,8 @@ namespace Ryujinx.Core.OsHle
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MakeSymbolTable();
|
||||||
|
|
||||||
MapRWMemRegion(
|
MapRWMemRegion(
|
||||||
MemoryRegions.MainStackAddress,
|
MemoryRegions.MainStackAddress,
|
||||||
MemoryRegions.MainStackSize,
|
MemoryRegions.MainStackSize,
|
||||||
|
@ -125,14 +134,14 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize;
|
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)
|
if (Handle == -1)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
MainThread = HandleTable.GetData<HThread>(Handle);
|
MainThread = HandleTable.GetData<KThread>(Handle);
|
||||||
|
|
||||||
if (NeedsHbAbi)
|
if (NeedsHbAbi)
|
||||||
{
|
{
|
||||||
|
@ -184,30 +193,32 @@ namespace Ryujinx.Core.OsHle
|
||||||
throw new ObjectDisposedException(nameof(Process));
|
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;
|
long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize;
|
||||||
|
|
||||||
Thread.ThreadState.Break += BreakHandler;
|
CpuThread.ThreadState.ProcessId = ProcessId;
|
||||||
Thread.ThreadState.SvcCall += SvcHandler.SvcCall;
|
CpuThread.ThreadState.ThreadId = ThreadId;
|
||||||
Thread.ThreadState.Undefined += UndefinedHandler;
|
CpuThread.ThreadState.CntfrqEl0 = TickFreq;
|
||||||
Thread.ThreadState.ProcessId = ProcessId;
|
CpuThread.ThreadState.Tpidr = Tpidr;
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
return Handle;
|
||||||
}
|
}
|
||||||
|
@ -222,11 +233,9 @@ namespace Ryujinx.Core.OsHle
|
||||||
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
|
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ATranslator GetTranslator()
|
private void MakeSymbolTable()
|
||||||
{
|
{
|
||||||
if (Translator == null)
|
SymbolTable = new Dictionary<long, string>();
|
||||||
{
|
|
||||||
Dictionary<long, string> SymbolTable = new Dictionary<long, string>();
|
|
||||||
|
|
||||||
foreach (Executable Exe in Executables)
|
foreach (Executable Exe in Executables)
|
||||||
{
|
{
|
||||||
|
@ -235,7 +244,12 @@ namespace Ryujinx.Core.OsHle
|
||||||
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
|
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ATranslator GetTranslator()
|
||||||
|
{
|
||||||
|
if (Translator == null)
|
||||||
|
{
|
||||||
Translator = new ATranslator(SymbolTable);
|
Translator = new ATranslator(SymbolTable);
|
||||||
|
|
||||||
Translator.CpuTrace += CpuTraceHandler;
|
Translator.CpuTrace += CpuTraceHandler;
|
||||||
|
@ -244,6 +258,16 @@ namespace Ryujinx.Core.OsHle
|
||||||
return Translator;
|
return Translator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void EnableCpuTracing()
|
||||||
|
{
|
||||||
|
Translator.EnableCpuTrace = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisableCpuTracing()
|
||||||
|
{
|
||||||
|
Translator.EnableCpuTrace = false;
|
||||||
|
}
|
||||||
|
|
||||||
private void CpuTraceHandler(object sender, ACpuTraceEventArgs e)
|
private void CpuTraceHandler(object sender, ACpuTraceEventArgs e)
|
||||||
{
|
{
|
||||||
string NsoName = string.Empty;
|
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)
|
private int GetFreeTlsSlot(AThread Thread)
|
||||||
|
@ -288,9 +342,15 @@ namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
if (sender is AThread Thread)
|
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 _);
|
TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
|
||||||
|
|
||||||
|
KThread KernelThread = GetThread(Thread.ThreadState.Tpidr);
|
||||||
|
|
||||||
|
Scheduler.RemoveThread(KernelThread);
|
||||||
|
|
||||||
|
KernelThread.WaitEvent.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TlsSlots.Count == 0)
|
if (TlsSlots.Count == 0)
|
||||||
|
@ -300,7 +360,7 @@ namespace Ryujinx.Core.OsHle
|
||||||
Dispose();
|
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);
|
Ns.Os.ExitProcess(ProcessId);
|
||||||
}
|
}
|
||||||
|
@ -311,11 +371,11 @@ namespace Ryujinx.Core.OsHle
|
||||||
return (int)((Position - MemoryRegions.TlsPagesAddress) / TlsSize);
|
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;
|
return Thread;
|
||||||
|
@ -338,20 +398,34 @@ namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
ShouldDispose = true;
|
ShouldDispose = true;
|
||||||
|
|
||||||
Logging.Info($"Process {ProcessId} waiting all threads terminate...");
|
Logging.Info(LogClass.KernelScheduler, $"Process {ProcessId} waiting all threads terminate...");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Disposed = true;
|
Disposed = true;
|
||||||
|
|
||||||
Services.Dispose();
|
foreach (object Obj in HandleTable.Clear())
|
||||||
HandleTable.Dispose();
|
{
|
||||||
Scheduler.Dispose();
|
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();
|
SvcHandler.Dispose();
|
||||||
|
|
||||||
Memory.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 Switch Ns { get; private set; }
|
||||||
public Process Process { get; private set; }
|
public Process Process { get; private set; }
|
||||||
public AMemory Memory { 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 Request { get; private set; }
|
||||||
public IpcMessage Response { get; private set; }
|
public IpcMessage Response { get; private set; }
|
||||||
public BinaryReader RequestData { get; private set; }
|
public BinaryReader RequestData { get; private set; }
|
||||||
|
@ -20,7 +20,7 @@ namespace Ryujinx.Core.OsHle
|
||||||
Switch Ns,
|
Switch Ns,
|
||||||
Process Process,
|
Process Process,
|
||||||
AMemory Memory,
|
AMemory Memory,
|
||||||
HSession Session,
|
KSession Session,
|
||||||
IpcMessage Request,
|
IpcMessage Request,
|
||||||
IpcMessage Response,
|
IpcMessage Response,
|
||||||
BinaryReader RequestData,
|
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 Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
|
namespace Ryujinx.Core.OsHle.Services.Acc
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.IpcServices.Acc
|
|
||||||
{
|
{
|
||||||
class ServiceAcc : IIpcService
|
class IAccountServiceForApplication : IpcService
|
||||||
{
|
{
|
||||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
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>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
{
|
{
|
||||||
|
{ 0, GetUserCount },
|
||||||
{ 3, ListOpenUsers },
|
{ 3, ListOpenUsers },
|
||||||
{ 5, GetProfile },
|
{ 5, GetProfile },
|
||||||
{ 100, InitializeApplicationInfo },
|
{ 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)
|
public long ListOpenUsers(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
|
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +46,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
|
||||||
|
|
||||||
public long InitializeApplicationInfo(ServiceCtx Context)
|
public long InitializeApplicationInfo(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
|
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
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;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
public IManagerForApplication()
|
public IManagerForApplication()
|
||||||
{
|
{
|
||||||
|
@ -20,11 +20,15 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
|
||||||
|
|
||||||
public long CheckAvailability(ServiceCtx Context)
|
public long CheckAvailability(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
|
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetAccountId(ServiceCtx Context)
|
public long GetAccountId(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
|
Logging.Stub(LogClass.ServiceAcc, "AccountId = 0xcafeL");
|
||||||
|
|
||||||
Context.ResponseData.Write(0xcafeL);
|
Context.ResponseData.Write(0xcafeL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
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;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
public IProfile()
|
public IProfile()
|
||||||
{
|
{
|
||||||
|
@ -19,6 +19,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
|
||||||
|
|
||||||
public long GetBase(ServiceCtx Context)
|
public long GetBase(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
|
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
|
||||||
|
|
||||||
Context.ResponseData.Write(0L);
|
Context.ResponseData.Write(0L);
|
||||||
Context.ResponseData.Write(0L);
|
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.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
|
namespace Ryujinx.Core.OsHle.Services.Am
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.IpcServices.Am
|
|
||||||
{
|
{
|
||||||
class IApplicationFunctions : IIpcService
|
class IApplicationFunctions : IpcService
|
||||||
{
|
{
|
||||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
public IApplicationFunctions()
|
public IApplicationFunctions()
|
||||||
{
|
{
|
||||||
|
@ -20,6 +18,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
{ 20, EnsureSaveData },
|
{ 20, EnsureSaveData },
|
||||||
{ 21, GetDesiredLanguage },
|
{ 21, GetDesiredLanguage },
|
||||||
{ 22, SetTerminateResult },
|
{ 22, SetTerminateResult },
|
||||||
|
{ 23, GetDisplayVersion },
|
||||||
{ 40, NotifyRunning }
|
{ 40, NotifyRunning }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -39,6 +38,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
long UIdLow = Context.RequestData.ReadInt64();
|
long UIdLow = Context.RequestData.ReadInt64();
|
||||||
long UIdHigh = Context.RequestData.ReadInt64();
|
long UIdHigh = Context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
Logging.Stub(LogClass.ServiceAm, $"UidLow = {UIdLow}, UidHigh = {UIdHigh}");
|
||||||
|
|
||||||
Context.ResponseData.Write(0L);
|
Context.ResponseData.Write(0L);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -46,6 +47,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
|
|
||||||
public long GetDesiredLanguage(ServiceCtx Context)
|
public long GetDesiredLanguage(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
|
Logging.Stub(LogClass.ServiceAm, "LanguageId = 1");
|
||||||
|
|
||||||
//This is an enumerator where each number is a differnet language.
|
//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.
|
//0 is Japanese and 1 is English, need to figure out the other codes.
|
||||||
Context.ResponseData.Write(1L);
|
Context.ResponseData.Write(1L);
|
||||||
|
@ -60,7 +63,16 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
int Module = ErrorCode & 0xFF;
|
int Module = ErrorCode & 0xFF;
|
||||||
int Description = (ErrorCode >> 9) & 0xFFF;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
|
namespace Ryujinx.Core.OsHle.Services.Am
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.IpcServices.Am
|
|
||||||
{
|
{
|
||||||
class IApplicationProxy : IIpcService
|
class IApplicationProxy : IpcService
|
||||||
{
|
{
|
||||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
public IApplicationProxy()
|
public IApplicationProxy()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
|
namespace Ryujinx.Core.OsHle.Services.Am
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.IpcServices.Am
|
|
||||||
{
|
{
|
||||||
class ServiceAppletOE : IIpcService
|
class IApplicationProxyService : IpcService
|
||||||
{
|
{
|
||||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
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>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
{
|
{
|
|
@ -1,20 +1,71 @@
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
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;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
public IAudioController()
|
public IAudioController()
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
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 Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
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;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
public ICommonStateGetter()
|
public ICommonStateGetter()
|
||||||
{
|
{
|
||||||
|
@ -17,37 +20,32 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
{ 1, ReceiveMessage },
|
{ 1, ReceiveMessage },
|
||||||
{ 5, GetOperationMode },
|
{ 5, GetOperationMode },
|
||||||
{ 6, GetPerformanceMode },
|
{ 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)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long ReceiveMessage(ServiceCtx Context)
|
public long ReceiveMessage(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
//Program expects 0xF at 0x17ae70 on puyo sdk,
|
if (!Context.Process.AppletState.TryDequeueMessage(out MessageInfo Message))
|
||||||
//otherwise runs on a infinite loop until it reads said value.
|
{
|
||||||
//What it means is still unknown.
|
return MakeError(ErrorModule.Am, AmErr.NoMessages);
|
||||||
Context.ResponseData.Write(0xfL);
|
}
|
||||||
|
|
||||||
return 0; //0x680;
|
Context.ResponseData.Write((int)Message);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetOperationMode(ServiceCtx Context)
|
public long GetOperationMode(ServiceCtx Context)
|
||||||
|
@ -59,14 +57,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
|
|
||||||
public long GetPerformanceMode(ServiceCtx Context)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetCurrentFocusState(ServiceCtx Context)
|
public long GetCurrentFocusState(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
Context.ResponseData.Write((byte)FocusState.InFocus);
|
Context.ResponseData.Write((byte)Context.Process.AppletState.FocusState);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
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;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
public IDebugFunctions()
|
public IDebugFunctions()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
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;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
public IDisplayController()
|
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 Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
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;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
public ILibraryAppletCreator()
|
public ILibraryAppletCreator()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,29 +1,30 @@
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
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;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
public ISelfController()
|
public ISelfController()
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
{
|
{
|
||||||
{ 1, Exit },
|
{ 1, LockExit },
|
||||||
{ 10, SetScreenShotPermission },
|
{ 10, SetScreenShotPermission },
|
||||||
{ 11, SetOperationModeChangedNotification },
|
{ 11, SetOperationModeChangedNotification },
|
||||||
{ 12, SetPerformanceModeChangedNotification },
|
{ 12, SetPerformanceModeChangedNotification },
|
||||||
{ 13, SetFocusHandlingMode },
|
{ 13, SetFocusHandlingMode },
|
||||||
{ 14, SetRestartMessageEnabled },
|
{ 14, SetRestartMessageEnabled },
|
||||||
{ 16, SetOutOfFocusSuspendingEnabled }
|
{ 16, SetOutOfFocusSuspendingEnabled },
|
||||||
|
{ 50, SetHandlesRequestToDisplay }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public long Exit(ServiceCtx Context)
|
public long LockExit(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -32,6 +33,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
{
|
{
|
||||||
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
|
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
|
||||||
|
|
||||||
|
Logging.Stub(LogClass.ServiceAm, $"ScreenShot Allowed = {Enable}");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +42,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
{
|
{
|
||||||
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
|
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
|
||||||
|
|
||||||
|
Logging.Stub(LogClass.ServiceAm, $"OperationMode Changed = {Enable}");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +51,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
{
|
{
|
||||||
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
|
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
|
||||||
|
|
||||||
|
Logging.Stub(LogClass.ServiceAm, $"PerformanceMode Changed = {Enable}");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +62,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
bool Flag2 = Context.RequestData.ReadByte() != 0 ? true : false;
|
bool Flag2 = Context.RequestData.ReadByte() != 0 ? true : false;
|
||||||
bool Flag3 = 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +71,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
{
|
{
|
||||||
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
|
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
|
||||||
|
|
||||||
|
Logging.Stub(LogClass.ServiceAm, $"Restart Message Enabled = {Enable}");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +80,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
{
|
{
|
||||||
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
|
namespace Ryujinx.Core.OsHle.Services.Am
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.IpcServices.Am
|
|
||||||
{
|
{
|
||||||
class IStorage : IIpcService
|
class IStorage : IpcService
|
||||||
{
|
{
|
||||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
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; }
|
public byte[] Data { get; private set; }
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,13 @@ using Ryujinx.Core.OsHle.Ipc;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
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;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
private IStorage Storage;
|
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 Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
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;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
public IWindowController()
|
public IWindowController()
|
||||||
{
|
{
|
||||||
|
@ -20,6 +20,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
|
|
||||||
public long GetAppletResourceUserId(ServiceCtx Context)
|
public long GetAppletResourceUserId(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
|
Logging.Stub(LogClass.ServiceAm, $"Applet Resource Id = 0");
|
||||||
|
|
||||||
Context.ResponseData.Write(0L);
|
Context.ResponseData.Write(0L);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -27,6 +29,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
|
||||||
|
|
||||||
public long AcquireForegroundRights(ServiceCtx Context)
|
public long AcquireForegroundRights(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
|
Logging.Stub(LogClass.ServiceAm, "Stubbed");
|
||||||
|
|
||||||
return 0;
|
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 Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
|
namespace Ryujinx.Core.OsHle.Services.Apm
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.IpcServices.Apm
|
|
||||||
{
|
{
|
||||||
class ServiceApm : IIpcService
|
class IManager : IpcService
|
||||||
{
|
{
|
||||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
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>()
|
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