Merge pull request #15 from gdkchan/master

Working...
This commit is contained in:
Dr.Hacknik 2018-04-24 09:23:42 -04:00 committed by GitHub
commit fe94cd2037
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
283 changed files with 23071 additions and 3710 deletions

View file

@ -8,7 +8,7 @@ namespace ChocolArm64
{ {
static AOpCodeTable() static AOpCodeTable()
{ {
#region "OpCode Table" #region "OpCode Table"
//Integer //Integer
Set("x0011010000xxxxx000000xxxxxxxxxx", AInstEmit.Adc, typeof(AOpCodeAluRs)); Set("x0011010000xxxxx000000xxxxxxxxxx", AInstEmit.Adc, typeof(AOpCodeAluRs));
Set("x0111010000xxxxx000000xxxxxxxxxx", AInstEmit.Adcs, typeof(AOpCodeAluRs)); Set("x0111010000xxxxx000000xxxxxxxxxx", AInstEmit.Adcs, typeof(AOpCodeAluRs));
@ -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));
@ -68,7 +69,7 @@ namespace ChocolArm64
Set("xx111000010xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm)); Set("xx111000010xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm));
Set("xx11100101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm)); Set("xx11100101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm));
Set("xx111000011xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemReg)); Set("xx111000011xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemReg));
Set("xx011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeMemLit)); Set("xx011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeMemLit));
Set("0x1110001x0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); Set("0x1110001x0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm));
Set("0x1110011xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); Set("0x1110011xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm));
Set("10111000100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm)); Set("10111000100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm));
@ -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));
@ -182,16 +207,20 @@ namespace ChocolArm64
Set("x00111100x111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp, typeof(AOpCodeSimdCvt)); Set("x00111100x111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp, typeof(AOpCodeSimdCvt));
Set("x00111100x011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt)); Set("x00111100x011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt));
Set("0>1011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd)); Set("0>1011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd));
Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm)); Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm));
Set("000111100x1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg)); Set("000111100x1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg));
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
} }
@ -433,4 +491,4 @@ namespace ChocolArm64
return AInst.Undefined; return AInst.Undefined;
} }
} }
} }

View file

@ -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;
} }

View file

@ -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;
}
} }
} }

View file

@ -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 CallCount == MinCallCountForReJit; return false;
} }
return Type == ATranslatedSubType.SubTier1 && NeedsReJit; return NeedsReJit;
} }
public long Execute(AThreadState ThreadState, AMemory Memory) public long Execute(AThreadState ThreadState, AMemory Memory)
@ -125,10 +119,32 @@ 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 MarkForReJit() => NeedsReJit = true; public void SetType(ATranslatedSubType Type)
{
this.Type = Type;
if (Type == ATranslatedSubType.SubTier0)
{
NeedsReJit = true;
}
}
public void MarkForReJit() => NeedsReJit = true;
} }
} }

View file

@ -107,25 +107,31 @@ namespace ChocolArm64
ATranslatedSub Subroutine = Context.GetSubroutine(); ATranslatedSub Subroutine = Context.GetSubroutine();
if (SubBlocks.Contains(Position)) lock (SubBlocks)
{ {
SubBlocks.Remove(Position); if (SubBlocks.Contains(Position))
{
SubBlocks.Remove(Position);
Subroutine.SetType(ATranslatedSubType.SubBlock); Subroutine.SetType(ATranslatedSubType.SubBlock);
} }
else else
{ {
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();
if (LastOp.Emitter != AInstEmit.Ret && lock (SubBlocks)
LastOp.Emitter != AInstEmit.Br)
{ {
SubBlocks.Add(LastOp.Position + 4); if (LastOp.Emitter != AInstEmit.Ret &&
LastOp.Emitter != AInstEmit.Br)
{
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();
}
} }
} }

View file

@ -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;
} }
} }
} }

View file

@ -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;

View file

@ -18,7 +18,7 @@ namespace ChocolArm64.Decoder
int Scale = (OpCode >> 14) & 3; int Scale = (OpCode >> 14) & 3;
int L = (OpCode >> 22) & 1; int L = (OpCode >> 22) & 1;
int Q = (OpCode >> 30) & 1; int Q = (OpCode >> 30) & 1;
SElems |= (OpCode >> 21) & 1; SElems |= (OpCode >> 21) & 1;
SElems++; SElems++;
@ -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

View file

@ -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;

View file

@ -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;
@ -383,4 +401,4 @@ namespace ChocolArm64.Instruction
Context.EmitStflg((int)APState.CBit); Context.EmitStflg((int)APState.CBit);
} }
} }
} }

View file

@ -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)
{ {

View file

@ -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);

View file

@ -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));
{ }
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Neg); public static void Fnmadd_S(AILEmitterCtx Context)
}); {
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
Context.Emit(OpCodes.Neg);
EmitVectorExtractF(Context, Op.Rm, 0, SizeF);
Context.Emit(OpCodes.Mul);
EmitVectorExtractF(Context, Op.Ra, 0, SizeF);
Context.Emit(OpCodes.Sub);
EmitScalarSetF(Context, Op.Rd, SizeF);
} }
public static void Fnmsub_S(AILEmitterCtx Context) public static void Fnmsub_S(AILEmitterCtx Context)
@ -235,7 +444,7 @@ namespace ChocolArm64.Instruction
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1; int SizeF = Op.Size & 1;
EmitVectorExtractF(Context, Op.Rn, 0, SizeF); EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
EmitVectorExtractF(Context, Op.Rm, 0, SizeF); EmitVectorExtractF(Context, Op.Rm, 0, SizeF);
@ -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,9 +996,21 @@ 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));
} }
} }
} }

View file

@ -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);
}
} }
} }

View file

@ -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));
}
}
} }
} }

View file

@ -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, () =>

View file

@ -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;
@ -322,7 +360,7 @@ namespace ChocolArm64.Instruction
for (int Index = 0; Index < Elems; Index++) for (int Index = 0; Index < Elems; Index++)
{ {
int Elem = Part + ((Index & (Half - 1)) << 1); int Elem = Part + ((Index & (Half - 1)) << 1);
EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem, Op.Size); EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem, Op.Size);
EmitVectorInsert(Context, Op.Rd, Index, Op.Size); EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
@ -358,4 +396,4 @@ namespace ChocolArm64.Instruction
} }
} }
} }
} }

View file

@ -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);
@ -41,15 +49,15 @@ namespace ChocolArm64.Instruction
private const uint Crc32RevPoly = 0xedb88320; private const uint Crc32RevPoly = 0xedb88320;
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)
{ {
@ -113,7 +121,7 @@ namespace ChocolArm64.Instruction
Value = ((Value & 0xcccccccccccccccc) >> 2) | ((Value & 0x3333333333333333) << 2); Value = ((Value & 0xcccccccccccccccc) >> 2) | ((Value & 0x3333333333333333) << 2);
Value = ((Value & 0xf0f0f0f0f0f0f0f0) >> 4) | ((Value & 0x0f0f0f0f0f0f0f0f) << 4); Value = ((Value & 0xf0f0f0f0f0f0f0f0) >> 4) | ((Value & 0x0f0f0f0f0f0f0f0f) << 4);
Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8); Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8);
Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16); Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16);
return (Value >> 32) | (Value << 32); return (Value >> 32) | (Value << 32);
} }
@ -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)
@ -398,4 +482,4 @@ namespace ChocolArm64.Instruction
throw new ArgumentOutOfRangeException(nameof(Size)); throw new ArgumentOutOfRangeException(nameof(Size));
} }
} }
} }

View 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);
}
}
}

View file

@ -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);

View file

@ -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();
}
} }
} }

View file

@ -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()
@ -115,7 +115,7 @@ namespace ChocolArm64.Translation
Generator.EmitLdarg(Index + ParamsStart); Generator.EmitLdarg(Index + ParamsStart);
Generator.EmitStloc(GetLocalIndex(Reg)); Generator.EmitStloc(GetLocalIndex(Reg));
} }
} }
private Type[] GetParamTypes(IList<ARegister> Params) private Type[] GetParamTypes(IList<ARegister> Params)
{ {

View file

@ -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,13 +64,11 @@ 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);
ILBlock = Emitter.GetILBlock(0); ILBlock = Emitter.GetILBlock(0);
OpcIndex = -1; OpcIndex = -1;
@ -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);
@ -298,7 +300,7 @@ namespace ChocolArm64.Translation
EmitLdc_I4(Amount); EmitLdc_I4(Amount);
Emit(OpCodes.Shr_Un); Emit(OpCodes.Shr_Un);
Ldloc(Tmp2Index, AIoType.Int); Ldloc(Tmp2Index, AIoType.Int);
EmitLdc_I4(CurrOp.GetBitsCount() - Amount); EmitLdc_I4(CurrOp.GetBitsCount() - Amount);
@ -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)

View file

@ -3,7 +3,7 @@ using System;
namespace ChocolArm64 namespace ChocolArm64
{ {
using System.Reflection.Emit; using System.Reflection.Emit;
static class ILGeneratorEx static class ILGeneratorEx
{ {
public static void EmitLdc_I4(this ILGenerator Generator,int Value) public static void EmitLdc_I4(this ILGenerator Generator,int Value)

View file

@ -48,6 +48,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**

View 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
}
}

View 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);
}
}

View 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;
}
}
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Audio
{
public enum PlaybackState
{
Playing = 0,
Stopped = 1
}
}

View file

@ -0,0 +1,4 @@
namespace Ryujinx.Audio
{
public delegate void ReleaseCallback();
}

View 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>

View file

@ -9,14 +9,18 @@ namespace Ryujinx.Core
{ {
public static class Config public static class Config
{ {
public static bool LoggingEnableInfo { get; private set; } public static bool EnableMemoryChecks { get; private set; }
public static bool LoggingEnableTrace { get; private set; } public static bool LoggingEnableInfo { get; private set; }
public static bool LoggingEnableDebug { get; private set; } public static bool LoggingEnableTrace { get; private set; }
public static bool LoggingEnableWarn { get; private set; } public static bool LoggingEnableDebug { get; private set; }
public static bool LoggingEnableError { get; private set; } public static bool LoggingEnableWarn { get; private set; }
public static bool LoggingEnableFatal { get; private set; } public static bool LoggingEnableError { get; private set; }
public static bool LoggingEnableIpc { get; private set; } public static bool LoggingEnableFatal { get; private set; }
public static bool LoggingEnableLogFile { get; private set; } public static bool LoggingEnableIpc { get; private set; }
public static bool LoggingEnableStub { get; private set; }
public static bool LoggingEnableLogFile { get; private set; }
public static bool LoggingEnableFilter { get; private set; }
public static bool[] LoggingFilteredClasses { get; private set; }
public static JoyCon FakeJoyCon { get; private set; } public static JoyCon FakeJoyCon { get; private set; }
@ -26,14 +30,33 @@ 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);
LoggingEnableInfo = Convert.ToBoolean(Parser.Value("Logging_Enable_Info")); EnableMemoryChecks = Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
LoggingEnableTrace = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace")); LoggingEnableInfo = Convert.ToBoolean(Parser.Value("Logging_Enable_Info"));
LoggingEnableDebug = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug")); LoggingEnableTrace = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace"));
LoggingEnableWarn = Convert.ToBoolean(Parser.Value("Logging_Enable_Warn")); LoggingEnableDebug = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"));
LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error")); LoggingEnableWarn = Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"));
LoggingEnableFatal = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal")); LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
LoggingEnableIpc = Convert.ToBoolean(Parser.Value("Logging_Enable_Ipc")); LoggingEnableFatal = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile")); LoggingEnableIpc = Convert.ToBoolean(Parser.Value("Logging_Enable_Ipc"));
LoggingEnableStub = Convert.ToBoolean(Parser.Value("Logging_Enable_Stub"));
LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
LoggingEnableFilter = Convert.ToBoolean(Parser.Value("Logging_Enable_Filter"));
LoggingFilteredClasses = new bool[(int)LogClass.Count];
string[] FilteredLogClasses = Parser.Value("Logging_Filtered_Classes", string.Empty).Split(',');
foreach (string LogClass in FilteredLogClasses)
{
if (!string.IsNullOrEmpty(LogClass.Trim()))
{
foreach (LogClass EnumItemName in Enum.GetValues(typeof(LogClass)))
{
if (EnumItemName.ToString().ToLower().Contains(LogClass.Trim().ToLower()))
{
LoggingFilteredClasses[(int)EnumItemName] = true;
}
}
}
}
FakeJoyCon = new JoyCon FakeJoyCon = new JoyCon
{ {

View file

@ -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);
} }
@ -219,7 +219,7 @@ namespace Ryujinx.Core.Input
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x8, HidEntryCount); Memory.WriteInt64Unchecked(TouchScreenOffset + 0x8, HidEntryCount);
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x10, CurrEntry); Memory.WriteInt64Unchecked(TouchScreenOffset + 0x10, CurrEntry);
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x18, HidEntryCount - 1); Memory.WriteInt64Unchecked(TouchScreenOffset + 0x18, HidEntryCount - 1);
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp); Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp);
long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize; long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize;

View file

@ -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;

View file

@ -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; }

View file

@ -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);

View file

@ -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
View 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,
}
}

View file

@ -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
{ {
@ -12,14 +14,28 @@ namespace Ryujinx.Core
private const string LogFileName = "Ryujinx.log"; private const string LogFileName = "Ryujinx.log";
private static bool EnableInfo = Config.LoggingEnableInfo; private static bool EnableInfo = Config.LoggingEnableInfo;
private static bool EnableTrace = Config.LoggingEnableTrace; private static bool EnableTrace = Config.LoggingEnableTrace;
private static bool EnableDebug = Config.LoggingEnableDebug; private static bool EnableDebug = Config.LoggingEnableDebug;
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 EnableIpc = Config.LoggingEnableIpc; private static bool EnableStub = Config.LoggingEnableStub;
private static bool EnableLogFile = Config.LoggingEnableLogFile; private static bool EnableIpc = Config.LoggingEnableIpc;
private static bool EnableFilter = Config.LoggingEnableFilter;
private static bool EnableLogFile = Config.LoggingEnableLogFile;
private static bool[] FilteredLogClasses = Config.LoggingFilteredClasses;
private enum LogLevel
{
Debug,
Error,
Fatal,
Info,
Stub,
Trace,
Warn
}
static Logging() 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";
{
return ExecutionTime.ElapsedMilliseconds.ToString().PadLeft(8, '0') + "ms";
}
private static string WhoCalledMe() private static void LogMessage(LogEntry LogEntry)
{ {
return new StackTrace().GetFrame(2).GetMethod().Name; if (EnableFilter)
if (!FilteredLogClasses[(int)LogEntry.LogClass])
return;
ConsoleColor consoleColor = ConsoleColor.White;
switch (LogEntry.LogLevel)
{
case LogLevel.Debug:
consoleColor = ConsoleColor.Gray;
break;
case LogLevel.Error:
consoleColor = ConsoleColor.Red;
break;
case LogLevel.Fatal:
consoleColor = ConsoleColor.Magenta;
break;
case LogLevel.Info:
consoleColor = ConsoleColor.White;
break;
case LogLevel.Stub:
consoleColor = ConsoleColor.DarkYellow;
break;
case LogLevel.Trace:
consoleColor = ConsoleColor.DarkGray;
break;
case LogLevel.Warn:
consoleColor = ConsoleColor.Yellow;
break;
}
LogEntry.ManagedThreadId = Thread.CurrentThread.ManagedThreadId;
string Text = $"{LogEntry.ExecutionTime} | {LogEntry.ManagedThreadId} > {LogEntry.LogClass} > " +
$"{LogEntry.LogLevel.ToString()} > {LogEntry.CallingMember} > {LogEntry.Message}";
Console.ForegroundColor = consoleColor;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
} }
private static void LogFile(string Message) 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()
});
} }
} }
@ -160,7 +234,7 @@ namespace Ryujinx.Core
int firstCharColumn = firstHexColumn int firstCharColumn = firstHexColumn
+ bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space + bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space
+ (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th + (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th
+ 2; // 2 spaces + 2; // 2 spaces
int lineLength = firstCharColumn int lineLength = firstCharColumn
+ bytesPerLine // - characters to show the ascii value + bytesPerLine // - characters to show the ascii value
@ -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;
}
} }
} }

View 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();
}
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -1,12 +0,0 @@
namespace Ryujinx.Core.OsHle
{
class FileDesc
{
public string Name { get; private set; }
public FileDesc(string Name)
{
this.Name = Name;
}
}
}

View 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;
}
}
}

View file

@ -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();
}
}
}
}
}
}

View file

@ -1,7 +0,0 @@
namespace Ryujinx.Core.OsHle.Handles
{
class HEvent
{
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}
}
}
}

View file

@ -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;
}
}
}

View file

@ -0,0 +1,4 @@
namespace Ryujinx.Core.OsHle.Handles
{
class KEvent : KSynchronizationObject { }
}

View file

@ -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();
}
}
}
} }
} }
} }

View file

@ -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
{
ActiveProcessors.Remove(Thread.ProcessorId);
}
if (!AllThreads.TryGetValue(Thread, out SchedThread))
{
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
return false;
}
} }
bool Result; Resume(Thread);
if (Timeout >= 0)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
Result = SchedThread.WaitEvent.WaitOne(Timeout);
}
else
{
Result = SchedThread.WaitEvent.WaitOne();
}
TryResumingExecution(SchedThread);
return Result;
} }
private void TryResumingExecution(SchedulerThread SchedThread) public bool Resume(KThread Thread)
{ {
HThread Thread = SchedThread.Thread; if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
{
throw new InvalidOperationException();
}
return TryResumingExecution(SchedThread);
}
private bool TryResumingExecution(SchedulerThread SchedThread)
{
KThread Thread = SchedThread.Thread;
if (!SchedThread.Active || SchedThread.SyncTimeout != 0)
{
PrintDbgThreadInfo(Thread, "entering inactive wait state...");
}
bool Result = false;
if (SchedThread.SyncTimeout != 0)
{
Result = SchedThread.SyncWaitEvent.WaitOne(SchedThread.SyncTimeout);
SchedThread.SyncTimeout = 0;
}
lock (SchedLock) 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();
}
}
}
} }
return IsActive(SchedThread);
} }
private string GetDbgThreadInfo(HThread Thread) private bool IsActive(SchedulerThread SchedThread)
{ {
return $"Thread {Thread.ThreadId} (core {Thread.ProcessorId}) prio {Thread.Priority}"; return SchedThread.Active && SchedThread.SyncTimeout == 0;
}
private void PrintDbgThreadInfo(KThread Thread, string Message)
{
Logging.Debug(LogClass.KernelScheduler, "(" +
"ThreadId: " + Thread.ThreadId + ", " +
"ProcessorId: " + Thread.ProcessorId + ", " +
"ActualPriority: " + Thread.ActualPriority + ", " +
"WantedPriority: " + Thread.WantedPriority + ") " + Message);
} }
public void Dispose() public void Dispose()

View 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();
}
}
}
}

View 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();
}
}
}
}

View 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;
}
}
}
}
}

View file

@ -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;
} }

View file

@ -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();
} }
} }
} }

View file

@ -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;
} }
} }
} }

View file

@ -1,8 +0,0 @@
namespace Ryujinx.Core.OsHle.Ipc
{
enum IpcDomCmd
{
SendMsg = 1,
DeleteObj = 2
}
}

View file

@ -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,94 +23,25 @@ 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; using (MemoryStream ResMS = new MemoryStream())
bool IgnoreNullPR = false;
string DbgServiceName = string.Empty;
if (Session is HDomain Dom)
{ {
if (Request.DomCmd == IpcDomCmd.SendMsg) BinaryWriter ResWriter = new BinaryWriter(ResMS);
{
long Magic = ReqReader.ReadInt64();
int CmdId = (int)ReqReader.ReadInt64();
object Obj = Dom.GetObject(Request.DomObjId); ServiceCtx Context = new ServiceCtx(
Ns,
Process,
Memory,
Session,
Request,
Response,
ReqReader,
ResWriter);
if (Obj is HDomain) Session.Service.CallMethod(Context);
{
Session.Service.Commands.TryGetValue(CmdId, out ProcReq);
DbgServiceName = $"{ProcReq?.Method.Name ?? CmdId.ToString()}"; Response.RawData = ResMS.ToArray();
}
else if (Obj != null)
{
((IIpcService)Obj).Commands.TryGetValue(CmdId, out ProcReq);
DbgServiceName = $"{Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}";
}
}
else if (Request.DomCmd == IpcDomCmd.DeleteObj)
{
Dom.Delete(Request.DomObjId);
Response = FillResponse(Response, 0);
IgnoreNullPR = true;
}
}
else
{
long Magic = ReqReader.ReadInt64();
int CmdId = (int)ReqReader.ReadInt64();
if (Session is HSessionObj)
{
object Obj = ((HSessionObj)Session).Obj;
((IIpcService)Obj).Commands.TryGetValue(CmdId, out ProcReq);
DbgServiceName = $"{Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}";
}
else
{
Session.Service.Commands.TryGetValue(CmdId, out ProcReq);
DbgServiceName = $"{ProcReq?.Method.Name ?? CmdId.ToString()}";
}
}
DbgServiceName = $"Tid {ThreadId} {ServiceName} {DbgServiceName}";
Logging.Debug($"IpcMessage: {DbgServiceName}");
if (ProcReq != null)
{
using (MemoryStream ResMS = new MemoryStream())
{
BinaryWriter ResWriter = new BinaryWriter(ResMS);
ServiceCtx Context = new ServiceCtx(
Ns,
Process,
Memory,
Session,
Request,
Response,
ReqReader,
ResWriter);
long Result = ProcReq(Context);
Response = FillResponse(Response, Result, ResMS.ToArray());
}
}
else if (!IgnoreNullPR)
{
throw new NotImplementedException(DbgServiceName);
} }
} }
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;
} }
@ -140,12 +61,12 @@ namespace Ryujinx.Core.OsHle.Ipc
case 3: case 3:
{ {
Request = FillResponse(Response, 0, 0x500); Request = FillResponse(Response, 0, 0x500);
break; break;
} }
//TODO: Whats the difference between IpcDuplicateSession/Ex? //TODO: Whats the difference between IpcDuplicateSession/Ex?
case 2: case 2:
case 4: case 4:
{ {
int Unknown = ReqReader.ReadInt32(); int Unknown = ReqReader.ReadInt32();
@ -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)

View file

@ -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;
} }

View 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;
}
}

View file

@ -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();

View file

@ -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);
} }
} }
} }

View 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;
}
}

View 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;
}
}
}
}

View file

@ -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"));
} }
} }

View file

@ -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);
@ -244,7 +244,7 @@ namespace Ryujinx.Core.OsHle.Svc
HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size); HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size);
ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem); ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem);
ThreadState.X0 = 0; ThreadState.X0 = 0;
ThreadState.X1 = Handle; ThreadState.X1 = Handle;
} }
@ -252,13 +252,13 @@ namespace Ryujinx.Core.OsHle.Svc
private static bool IsValidPosition(long Position) private static bool IsValidPosition(long Position)
{ {
return Position >= MemoryRegions.AddrSpaceStart && return Position >= MemoryRegions.AddrSpaceStart &&
Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
} }
private static bool IsValidMapPosition(long Position) private static bool IsValidMapPosition(long Position)
{ {
return Position >= MemoryRegions.MapRegionAddress && return Position >= MemoryRegions.MapRegionAddress &&
Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize; Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize;
} }
} }
} }

View file

@ -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);
ThreadState.X0 = 0; if (Event != null)
{
Event.WaitEvent.Reset();
ThreadState.X0 = 0;
}
else
{
Logging.Warn(LogClass.KernelSvc, $"Tried to ResetSignal on invalid event handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
} }
private void SvcWaitSynchronization(AThreadState ThreadState) 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,76 +153,56 @@ 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);
ThreadState.X0 = 0; ThreadState.X0 = 0;
ThreadState.X1 = Handle; ThreadState.X1 = Handle;
} }
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);
@ -212,7 +270,7 @@ namespace Ryujinx.Core.OsHle.Svc
case 6: case 6:
ThreadState.X1 = MemoryRegions.TotalMemoryAvailable; ThreadState.X1 = MemoryRegions.TotalMemoryAvailable;
break; break;
case 7: case 7:
ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize; ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize;
break; break;
@ -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;

View 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);
}
}
}
}

View 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;
}
}
}

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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);
}
}
}
}

View file

@ -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,21 +125,23 @@ namespace Ryujinx.Core.OsHle
return false; return false;
} }
MakeSymbolTable();
MapRWMemRegion( MapRWMemRegion(
MemoryRegions.MainStackAddress, MemoryRegions.MainStackAddress,
MemoryRegions.MainStackSize, MemoryRegions.MainStackSize,
MemoryType.Normal); MemoryType.Normal);
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,20 +233,23 @@ namespace Ryujinx.Core.OsHle
throw new UndefinedInstructionException(e.Position, e.RawOpCode); throw new UndefinedInstructionException(e.Position, e.RawOpCode);
} }
private void MakeSymbolTable()
{
SymbolTable = new Dictionary<long, string>();
foreach (Executable Exe in Executables)
{
foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
{
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
}
}
}
private ATranslator GetTranslator() private ATranslator GetTranslator()
{ {
if (Translator == null) if (Translator == null)
{ {
Dictionary<long, string> SymbolTable = new Dictionary<long, string>();
foreach (Executable Exe in Executables)
{
foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
{
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
}
}
Translator = new ATranslator(SymbolTable); Translator = 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;
@ -253,22 +277,52 @@ namespace Ryujinx.Core.OsHle
if (e.Position >= Executables[Index].ImageBase) if (e.Position >= Executables[Index].ImageBase)
{ {
NsoName = $"{(e.Position - Executables[Index].ImageBase):x16}"; NsoName = $"{(e.Position - Executables[Index].ImageBase):x16}";
break; break;
} }
} }
Logging.Trace($"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}"); Logging.Trace(LogClass.CPU, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
} }
public void EnableCpuTracing() public void PrintStackTrace(AThreadState ThreadState)
{ {
Translator.EnableCpuTrace = true; long[] Positions = ThreadState.GetCallStack();
StringBuilder Trace = new StringBuilder();
Trace.AppendLine("Guest stack trace:");
foreach (long Position in Positions)
{
if (!SymbolTable.TryGetValue(Position, out string SubName))
{
SubName = $"Sub{Position:x16}";
}
Trace.AppendLine(" " + SubName + " (" + GetNsoNameAndAddress(Position) + ")");
}
Logging.Info(LogClass.CPU, Trace.ToString());
} }
public void DisableCpuTracing() private string GetNsoNameAndAddress(long Position)
{ {
Translator.EnableCpuTrace = false; string Name = string.Empty;
for (int Index = Executables.Count - 1; Index >= 0; Index--)
{
if (Position >= Executables[Index].ImageBase)
{
long Offset = Position - Executables[Index].ImageBase;
Name = $"{Executables[Index].Name}:{Offset:x8}";
break;
}
}
return Name;
} }
private int GetFreeTlsSlot(AThread Thread) 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...");
} }
} }
} }

View file

@ -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,

View file

@ -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();
}
}
}
}
}

View file

@ -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;
} }
@ -46,4 +58,4 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
return 0; return 0;
} }
} }
} }

View file

@ -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()
{ {
@ -19,12 +19,16 @@ 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;

View file

@ -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);

View file

@ -0,0 +1,7 @@
namespace Ryujinx.Core.OsHle.Services.Am
{
static class AmErr
{
public const int NoMessages = 3;
}
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Core.OsHle.Services.Am
{
enum FocusState
{
InFocus = 1,
OutOfFocus = 2
}
}

View file

@ -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;
}
}
}

View 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>()
{
//...
};
}
}
}

View file

@ -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;
} }

View file

@ -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()
{ {

View file

@ -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>()
{ {

View file

@ -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;
}
} }
} }

View file

@ -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;
} }

View file

@ -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()
{ {

View file

@ -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()
{ {

View 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>()
{
//...
};
}
}
}

View 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;
}
}
}

View file

@ -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()
{ {

View file

@ -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;
} }
} }

View file

@ -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; }

View file

@ -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;

View 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;
}
}
}

View file

@ -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;
} }
} }

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Core.OsHle.Services.Am
{
enum MessageInfo
{
FocusStateChanged = 0xf,
OperationModeChanged = 0x1e,
PerformanceModeChanged = 0x1f
}
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Core.OsHle.Services.Am
{
enum OperationMode
{
Handheld = 0,
Docked = 1
}
}

Some files were not shown because too many files have changed in this diff Show more