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()
{
#region "OpCode Table"
#region "OpCode Table"
//Integer
Set("x0011010000xxxxx000000xxxxxxxxxx", AInstEmit.Adc, typeof(AOpCodeAluRs));
Set("x0111010000xxxxx000000xxxxxxxxxx", AInstEmit.Adcs, typeof(AOpCodeAluRs));
@ -41,6 +41,7 @@ namespace ChocolArm64
Set("x1111010010xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpImm));
Set("x1111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg));
Set("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem));
Set("x101101011000000000101xxxxxxxxxx", AInstEmit.Cls, typeof(AOpCodeAlu));
Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu));
Set("x0011010110xxxxx010000xxxxxxxxxx", AInstEmit.Crc32b, typeof(AOpCodeAluRs));
Set("x0011010110xxxxx010001xxxxxxxxxx", AInstEmit.Crc32h, typeof(AOpCodeAluRs));
@ -91,6 +92,7 @@ namespace ChocolArm64
Set("x01100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluImm));
Set("x0101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluRs));
Set("1111100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm));
Set("11111000100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm));
Set("11011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemLit));
Set("x101101011000000000000xxxxxxxxxx", AInstEmit.Rbit, typeof(AOpCodeAlu));
Set("11010110010xxxxx000000xxxxxxxxxx", AInstEmit.Ret, typeof(AOpCodeBReg));
@ -131,14 +133,20 @@ namespace ChocolArm64
Set("10011011110xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Umulh, typeof(AOpCodeMul));
//Vector
Set("0101111011100000101110xxxxxxxxxx", AInstEmit.Abs_S, typeof(AOpCodeSimd));
Set("0>001110<<100000101110xxxxxxxxxx", AInstEmit.Abs_V, typeof(AOpCodeSimd));
Set("01011110111xxxxx100001xxxxxxxxxx", AInstEmit.Add_S, typeof(AOpCodeSimdReg));
Set("0>001110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Add_V, typeof(AOpCodeSimdReg));
Set("01011110xx110001101110xxxxxxxxxx", AInstEmit.Addp_S, typeof(AOpCodeSimd));
Set("0x001110<<1xxxxx010000xxxxxxxxxx", AInstEmit.Addhn_V, typeof(AOpCodeSimdReg));
Set("0101111011110001101110xxxxxxxxxx", AInstEmit.Addp_S, typeof(AOpCodeSimd));
Set("0>001110<<1xxxxx101111xxxxxxxxxx", AInstEmit.Addp_V, typeof(AOpCodeSimdReg));
Set("000011100x110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd));
Set("01001110<<110001101110xxxxxxxxxx", AInstEmit.Addv_V, typeof(AOpCodeSimd));
Set("0x001110001xxxxx000111xxxxxxxxxx", AInstEmit.And_V, typeof(AOpCodeSimdReg));
Set("0x001110011xxxxx000111xxxxxxxxxx", AInstEmit.Bic_V, typeof(AOpCodeSimdReg));
Set("0x10111100000xxx<<x101xxxxxxxxxx", AInstEmit.Bic_Vi, typeof(AOpCodeSimdImm));
Set("0x101110111xxxxx000111xxxxxxxxxx", AInstEmit.Bif_V, typeof(AOpCodeSimdReg));
Set("0x101110101xxxxx000111xxxxxxxxxx", AInstEmit.Bit_V, typeof(AOpCodeSimdReg));
Set("0x101110011xxxxx000111xxxxxxxxxx", AInstEmit.Bsl_V, typeof(AOpCodeSimdReg));
Set("0>101110<<1xxxxx100011xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimdReg));
Set("0>001110<<100000100110xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimd));
@ -161,8 +169,25 @@ namespace ChocolArm64
Set("000111100x100000110000xxxxxxxxxx", AInstEmit.Fabs_S, typeof(AOpCodeSimd));
Set("000111100x1xxxxx001010xxxxxxxxxx", AInstEmit.Fadd_S, typeof(AOpCodeSimdReg));
Set("0>0011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Fadd_V, typeof(AOpCodeSimdReg));
Set("0>1011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Faddp_V, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxxxxxx01xxxxx0xxxx", AInstEmit.Fccmp_S, typeof(AOpCodeSimdFcond));
Set("000111100x1xxxxxxxxx01xxxxx1xxxx", AInstEmit.Fccmpe_S, typeof(AOpCodeSimdFcond));
Set("010111100x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmeq_S, typeof(AOpCodeSimdReg));
Set("0>0011100<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmeq_V, typeof(AOpCodeSimdReg));
Set("010111101x100000110110xxxxxxxxxx", AInstEmit.Fcmeq_S, typeof(AOpCodeSimd));
Set("0>0011101<100000110110xxxxxxxxxx", AInstEmit.Fcmeq_V, typeof(AOpCodeSimd));
Set("011111100x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmge_S, typeof(AOpCodeSimdReg));
Set("0>1011100<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmge_V, typeof(AOpCodeSimdReg));
Set("011111101x100000110010xxxxxxxxxx", AInstEmit.Fcmge_S, typeof(AOpCodeSimd));
Set("0>1011101<100000110010xxxxxxxxxx", AInstEmit.Fcmge_V, typeof(AOpCodeSimd));
Set("011111101x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmgt_S, typeof(AOpCodeSimdReg));
Set("0>1011101<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmgt_V, typeof(AOpCodeSimdReg));
Set("010111101x100000110010xxxxxxxxxx", AInstEmit.Fcmgt_S, typeof(AOpCodeSimd));
Set("0>0011101<100000110010xxxxxxxxxx", AInstEmit.Fcmgt_V, typeof(AOpCodeSimd));
Set("011111101x100000110110xxxxxxxxxx", AInstEmit.Fcmle_S, typeof(AOpCodeSimd));
Set("0>1011101<100000110110xxxxxxxxxx", AInstEmit.Fcmle_V, typeof(AOpCodeSimd));
Set("010111101x100000111010xxxxxxxxxx", AInstEmit.Fcmlt_S, typeof(AOpCodeSimd));
Set("0>0011101<100000111010xxxxxxxxxx", AInstEmit.Fcmlt_V, typeof(AOpCodeSimd));
Set("000111100x1xxxxx001000xxxxx0x000", AInstEmit.Fcmp_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond));
@ -187,11 +212,15 @@ namespace ChocolArm64
Set("0>1011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Fdiv_V, typeof(AOpCodeSimdReg));
Set("000111110x0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fmadd_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx010010xxxxxxxxxx", AInstEmit.Fmax_S, typeof(AOpCodeSimdReg));
Set("0x0011100x1xxxxx111101xxxxxxxxxx", AInstEmit.Fmax_V, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg));
Set("0x0011101x1xxxxx111101xxxxxxxxxx", AInstEmit.Fmin_V, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg));
Set("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg));
Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElemF));
Set("0>0011101<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmls_V, typeof(AOpCodeSimdReg));
Set("0x0011111<<xxxxx0101x0xxxxxxxxxx", AInstEmit.Fmls_Ve, typeof(AOpCodeSimdRegElemF));
Set("000111100x100000010000xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimd));
Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov));
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
@ -201,16 +230,34 @@ namespace ChocolArm64
Set("1001111010101111000000xxxxxxxxxx", AInstEmit.Fmov_Itof1, typeof(AOpCodeSimdCvt));
Set("000111110x0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, typeof(AOpCodeSimdReg));
Set("010111111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Se, typeof(AOpCodeSimdRegElemF));
Set("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
Set("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElemF));
Set("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimdReg));
Set("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimd));
Set("0>1011101<100000111110xxxxxxxxxx", AInstEmit.Fneg_V, typeof(AOpCodeSimd));
Set("000111110x1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fnmadd_S, typeof(AOpCodeSimdReg));
Set("000111110x1xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fnmsub_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg));
Set("010111101x100001110110xxxxxxxxxx", AInstEmit.Frecpe_S, typeof(AOpCodeSimd));
Set("0>0011101<100001110110xxxxxxxxxx", AInstEmit.Frecpe_V, typeof(AOpCodeSimd));
Set("010111100x1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_S, typeof(AOpCodeSimdReg));
Set("0>0011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_V, typeof(AOpCodeSimdReg));
Set("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
Set("0>1011100<100001100010xxxxxxxxxx", AInstEmit.Frinta_V, typeof(AOpCodeSimd));
Set("000111100x100111110000xxxxxxxxxx", AInstEmit.Frinti_S, typeof(AOpCodeSimd));
Set("0>1011101<100001100110xxxxxxxxxx", AInstEmit.Frinti_V, typeof(AOpCodeSimd));
Set("000111100x100101010000xxxxxxxxxx", AInstEmit.Frintm_S, typeof(AOpCodeSimd));
Set("0>0011100<100001100110xxxxxxxxxx", AInstEmit.Frintm_V, typeof(AOpCodeSimd));
Set("000111100x100100010000xxxxxxxxxx", AInstEmit.Frintn_S, typeof(AOpCodeSimd));
Set("0>0011100<100001100010xxxxxxxxxx", AInstEmit.Frintn_V, typeof(AOpCodeSimd));
Set("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S, typeof(AOpCodeSimd));
Set("0>0011101<100001100010xxxxxxxxxx", AInstEmit.Frintp_V, typeof(AOpCodeSimd));
Set("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S, typeof(AOpCodeSimd));
Set("0>1011100<100001100110xxxxxxxxxx", AInstEmit.Frintx_V, typeof(AOpCodeSimd));
Set("011111101x100001110110xxxxxxxxxx", AInstEmit.Frsqrte_S, typeof(AOpCodeSimd));
Set("0>1011101<100001110110xxxxxxxxxx", AInstEmit.Frsqrte_V, typeof(AOpCodeSimd));
Set("010111101x1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_S, typeof(AOpCodeSimdReg));
Set("0>0011101<1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_V, typeof(AOpCodeSimdReg));
Set("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd));
Set("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg));
Set("0>0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, typeof(AOpCodeSimdReg));
@ -218,8 +265,8 @@ namespace ChocolArm64
Set("01101110000xxxxx0xxxx1xxxxxxxxxx", AInstEmit.Ins_V, typeof(AOpCodeSimdIns));
Set("0x00110001000000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs));
Set("0x001100110xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs));
Set("0x00110101000000xx0xxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs));
Set("0x001101110xxxxxxx0xxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs));
Set("0x00110101x00000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs));
Set("0x00110111xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs));
Set("xx10110xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp, typeof(AOpCodeSimdMemPair));
Set("xx111100x10xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm));
Set("xx111100x10xxxxxxxxx01xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm));
@ -228,6 +275,7 @@ namespace ChocolArm64
Set("xx111100x11xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemReg));
Set("xx011100xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeSimdMemLit));
Set("0x001110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mla_V, typeof(AOpCodeSimdReg));
Set("0x101111xxxxxxxx0000x0xxxxxxxxxx", AInstEmit.Mla_Ve, typeof(AOpCodeSimdRegElem));
Set("0x101110<<1xxxxx100101xxxxxxxxxx", AInstEmit.Mls_V, typeof(AOpCodeSimdReg));
Set("0x00111100000xxx0xx001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
Set("0x00111100000xxx10x001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
@ -238,11 +286,14 @@ namespace ChocolArm64
Set("0x10111100000xxx0xx001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
Set("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
Set("0x10111100000xxx110x01xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
Set("0>101110<<100000101110xxxxxxxxxx", AInstEmit.Neg_V, typeof(AOpCodeSimdReg));
Set("0111111011100000101110xxxxxxxxxx", AInstEmit.Neg_S, typeof(AOpCodeSimd));
Set("0>101110<<100000101110xxxxxxxxxx", AInstEmit.Neg_V, typeof(AOpCodeSimd));
Set("0x10111000100000010110xxxxxxxxxx", AInstEmit.Not_V, typeof(AOpCodeSimd));
Set("0x001110101xxxxx000111xxxxxxxxxx", AInstEmit.Orr_V, typeof(AOpCodeSimdReg));
Set("0x00111100000xxx<<x101xxxxxxxxxx", AInstEmit.Orr_Vi, typeof(AOpCodeSimdImm));
Set("0x101110<<1xxxxx010000xxxxxxxxxx", AInstEmit.Raddhn_V, typeof(AOpCodeSimdReg));
Set("0x001110<<100000000010xxxxxxxxxx", AInstEmit.Rev64_V, typeof(AOpCodeSimd));
Set("0x101110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Rsubhn_V, typeof(AOpCodeSimdReg));
Set("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V, typeof(AOpCodeSimdReg));
Set("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp, typeof(AOpCodeSimdCvt));
Set("010111100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_S, typeof(AOpCodeSimd));
@ -263,23 +314,30 @@ namespace ChocolArm64
Set("0x0011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm));
Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
Set("0x00110100000000xx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
Set("0x001101100xxxxxxx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
Set("0x00110100x00000xxxxxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
Set("0x00110110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
Set("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeSimdMemPair));
Set("xx111100x00xxxxxxxxx00xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
Set("xx111100x00xxxxxxxxx01xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
Set("xx111100x00xxxxxxxxx11xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
Set("xx111101x0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
Set("xx111100x01xxxxxxxxx10xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemReg));
Set("01111110xx1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_S, typeof(AOpCodeSimdReg));
Set("01111110111xxxxx100001xxxxxxxxxx", AInstEmit.Sub_S, typeof(AOpCodeSimdReg));
Set("0>101110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_V, typeof(AOpCodeSimdReg));
Set("0x001110<<1xxxxx011000xxxxxxxxxx", AInstEmit.Subhn_V, typeof(AOpCodeSimdReg));
Set("0x001110000xxxxx0xx000xxxxxxxxxx", AInstEmit.Tbl_V, typeof(AOpCodeSimdTbl));
Set("0>001110<<0xxxxx001010xxxxxxxxxx", AInstEmit.Trn1_V, typeof(AOpCodeSimdReg));
Set("0>001110<<0xxxxx011010xxxxxxxxxx", AInstEmit.Trn2_V, typeof(AOpCodeSimdReg));
Set("0x101110<<1xxxxx011101xxxxxxxxxx", AInstEmit.Uabd_V, typeof(AOpCodeSimdReg));
Set("0x101110<<1xxxxx011100xxxxxxxxxx", AInstEmit.Uabdl_V, typeof(AOpCodeSimdReg));
Set("0x101110<<1xxxxx000000xxxxxxxxxx", AInstEmit.Uaddl_V, typeof(AOpCodeSimdReg));
Set("001011100x110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd));
Set("01101110<<110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd));
Set("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V, typeof(AOpCodeSimdReg));
Set("x0011110xx100011000000xxxxxxxxxx", AInstEmit.Ucvtf_Gp, typeof(AOpCodeSimdCvt));
Set("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd));
Set("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimd));
Set("0x101110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Uhadd_V, typeof(AOpCodeSimdReg));
Set("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns));
Set("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg));
Set("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg));
@ -287,11 +345,11 @@ namespace ChocolArm64
Set("011111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm));
Set("0x1011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm));
Set("0x1011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm));
Set("0x001110xx0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg));
Set("0x001110xx0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg));
Set("0>001110<<0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg));
Set("0>001110<<0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg));
Set("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd));
Set("0x001110xx0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V, typeof(AOpCodeSimdReg));
Set("0x001110xx0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V, typeof(AOpCodeSimdReg));
Set("0>001110<<0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V, typeof(AOpCodeSimdReg));
Set("0>001110<<0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V, typeof(AOpCodeSimdReg));
#endregion
}

View file

@ -1,4 +1,6 @@
public static class AOptimizations
{
public static bool DisableMemoryChecks = false;
public static bool GenerateCallStack = true;
}

View file

@ -54,6 +54,14 @@ namespace ChocolArm64
return true;
}
public void StopExecution() => ThreadState.Running = false;
public void StopExecution()
{
ThreadState.Running = false;
}
public bool IsCurrentThread()
{
return Thread.CurrentThread == Work;
}
}
}

View file

@ -3,6 +3,7 @@ using ChocolArm64.State;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
@ -23,7 +24,7 @@ namespace ChocolArm64
public ReadOnlyCollection<ARegister> Params { get; private set; }
private HashSet<long> Callees;
private HashSet<long> Callers;
private ATranslatedSubType Type;
@ -33,7 +34,7 @@ namespace ChocolArm64
private int MinCallCountForReJit = 250;
public ATranslatedSub(DynamicMethod Method, List<ARegister> Params, HashSet<long> Callees)
public ATranslatedSub(DynamicMethod Method, List<ARegister> Params)
{
if (Method == null)
{
@ -45,14 +46,10 @@ namespace ChocolArm64
throw new ArgumentNullException(nameof(Params));
}
if (Callees == null)
{
throw new ArgumentNullException(nameof(Callees));
}
this.Method = Method;
this.Params = Params.AsReadOnly();
this.Callees = Callees;
Callers = new HashSet<long>();
PrepareDelegate();
}
@ -107,17 +104,14 @@ namespace ChocolArm64
public bool ShouldReJit()
{
if (Type == ATranslatedSubType.SubTier0)
if (NeedsReJit && CallCount < MinCallCountForReJit)
{
if (CallCount < MinCallCountForReJit)
{
CallCount++;
}
CallCount++;
return CallCount == MinCallCountForReJit;
return false;
}
return Type == ATranslatedSubType.SubTier1 && NeedsReJit;
return NeedsReJit;
}
public long Execute(AThreadState ThreadState, AMemory Memory)
@ -125,9 +119,31 @@ namespace ChocolArm64
return ExecDelegate(ThreadState, Memory);
}
public void SetType(ATranslatedSubType Type) => this.Type = Type;
public void AddCaller(long Position)
{
lock (Callers)
{
Callers.Add(Position);
}
}
public bool HasCallee(long Position) => Callees.Contains(Position);
public long[] GetCallerPositions()
{
lock (Callers)
{
return Callers.ToArray();
}
}
public void SetType(ATranslatedSubType Type)
{
this.Type = Type;
if (Type == ATranslatedSubType.SubTier0)
{
NeedsReJit = true;
}
}
public void MarkForReJit() => NeedsReJit = true;
}

View file

@ -107,25 +107,31 @@ namespace ChocolArm64
ATranslatedSub Subroutine = Context.GetSubroutine();
if (SubBlocks.Contains(Position))
lock (SubBlocks)
{
SubBlocks.Remove(Position);
if (SubBlocks.Contains(Position))
{
SubBlocks.Remove(Position);
Subroutine.SetType(ATranslatedSubType.SubBlock);
}
else
{
Subroutine.SetType(ATranslatedSubType.SubTier0);
Subroutine.SetType(ATranslatedSubType.SubBlock);
}
else
{
Subroutine.SetType(ATranslatedSubType.SubTier0);
}
}
CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine);
AOpCode LastOp = Block.GetLastOp();
if (LastOp.Emitter != AInstEmit.Ret &&
LastOp.Emitter != AInstEmit.Br)
lock (SubBlocks)
{
SubBlocks.Add(LastOp.Position + 4);
if (LastOp.Emitter != AInstEmit.Ret &&
LastOp.Emitter != AInstEmit.Br)
{
SubBlocks.Add(LastOp.Position + 4);
}
}
return Subroutine;
@ -154,11 +160,14 @@ namespace ChocolArm64
//Mark all methods that calls this method for ReJiting,
//since we can now call it directly which is faster.
foreach (ATranslatedSub TS in CachedSubs.Values)
if (CachedSubs.TryGetValue(Position, out ATranslatedSub OldSub))
{
if (TS.HasCallee(Position))
foreach (long CallerPos in OldSub.GetCallerPositions())
{
TS.MarkForReJit();
if (CachedSubs.TryGetValue(Position, out ATranslatedSub CallerSub))
{
CallerSub.MarkForReJit();
}
}
}

View file

@ -1,4 +1,5 @@
using ChocolArm64.Instruction;
using ChocolArm64.State;
namespace ChocolArm64.Decoder
{
@ -11,6 +12,10 @@ namespace ChocolArm64.Decoder
Rt = OpCode & 0x1f;
Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode);
RegisterSize = (OpCode >> 31) != 0
? ARegisterSize.Int64
: ARegisterSize.Int32;
}
}
}

View file

@ -25,8 +25,8 @@ namespace ChocolArm64.Decoder
default: Inst = AInst.Undefined; return;
}
Size = (OpCode >> 10) & 0x3;
WBack = ((OpCode >> 23) & 0x1) != 0;
Size = (OpCode >> 10) & 3;
WBack = ((OpCode >> 23) & 1) != 0;
bool Q = ((OpCode >> 30) & 1) != 0;

View file

@ -82,12 +82,13 @@ namespace ChocolArm64.Decoder
}
}
this.Index = Index;
this.SElems = SElems;
this.Size = Scale;
Extend64 = false;
WBack = ((OpCode >> 23) & 0x1) != 0;
WBack = ((OpCode >> 23) & 1) != 0;
RegisterSize = Q != 0
? ARegisterSize.SIMD128

View file

@ -11,9 +11,8 @@ namespace ChocolArm64.Decoder
switch (Size)
{
case 1:
Index = (OpCode >> 21) & 1 |
(OpCode >> 10) & 2 |
(OpCode >> 18) & 4;
Index = (OpCode >> 20) & 3 |
(OpCode >> 9) & 4;
Rm &= 0xf;

View file

@ -100,6 +100,24 @@ namespace ChocolArm64.Instruction
EmitDataStore(Context, SetFlags);
}
public static void Cls(AILEmitterCtx Context)
{
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
Context.EmitLdintzr(Op.Rn);
if (Op.RegisterSize == ARegisterSize.Int32)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingSigns32));
}
else
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingSigns64));
}
Context.EmitStintzr(Op.Rd);
}
public static void Clz(AILEmitterCtx Context)
{
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;

View file

@ -1,15 +1,12 @@
using ChocolArm64.Decoder;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System.Reflection;
using System.Reflection.Emit;
namespace ChocolArm64.Instruction
{
static partial class AInstEmit
{
private const BindingFlags Binding = BindingFlags.NonPublic | BindingFlags.Instance;
public static void Brk(AILEmitterCtx Context)
{
EmitExceptionCall(Context, nameof(AThreadState.OnBreak));
@ -30,9 +27,7 @@ namespace ChocolArm64.Instruction
Context.EmitLdc_I4(Op.Id);
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
Context.EmitCall(MthdInfo);
Context.EmitPrivateCall(typeof(AThreadState), MthdName);
//Check if the thread should still be running, if it isn't then we return 0
//to force a return to the dispatcher and then exit the thread.
@ -73,11 +68,7 @@ namespace ChocolArm64.Instruction
Context.EmitLdc_I8(Op.Position);
Context.EmitLdc_I4(Op.RawOpCode);
string MthdName = nameof(AThreadState.OnUndefined);
MethodInfo MthdInfo = typeof(AThreadState).GetMethod(MthdName, Binding);
Context.EmitCall(MthdInfo);
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.OnUndefined));
if (Context.CurrBlock.Next != null)
{

View file

@ -35,6 +35,14 @@ namespace ChocolArm64.Instruction
{
AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp;
if (AOptimizations.GenerateCallStack)
{
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitLdc_I8(Op.Imm);
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod));
}
Context.EmitLdc_I(Op.Position + 4);
Context.EmitStint(AThreadState.LRIndex);
Context.EmitStoreState();
@ -72,6 +80,14 @@ namespace ChocolArm64.Instruction
{
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
if (AOptimizations.GenerateCallStack)
{
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitLdintzr(Op.Rn);
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.EnterMethod));
}
Context.EmitLdc_I(Op.Position + 4);
Context.EmitStint(AThreadState.LRIndex);
Context.EmitStoreState();
@ -84,6 +100,14 @@ namespace ChocolArm64.Instruction
{
AOpCodeBReg Op = (AOpCodeBReg)Context.CurrOp;
if (AOptimizations.GenerateCallStack)
{
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitLdintzr(Op.Rn);
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.JumpMethod));
}
Context.EmitStoreState();
Context.EmitLdintzr(Op.Rn);
@ -105,6 +129,13 @@ namespace ChocolArm64.Instruction
public static void Ret(AILEmitterCtx Context)
{
if (AOptimizations.GenerateCallStack)
{
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitPrivateCall(typeof(AThreadState), nameof(AThreadState.ExitMethod));
}
Context.EmitStoreState();
Context.EmitLdint(AThreadState.LRIndex);

View file

@ -11,11 +11,44 @@ namespace ChocolArm64.Instruction
{
static partial class AInstEmit
{
public static void Abs_S(AILEmitterCtx Context)
{
EmitScalarUnaryOpSx(Context, () => EmitAbs(Context));
}
public static void Abs_V(AILEmitterCtx Context)
{
EmitVectorUnaryOpSx(Context, () => EmitAbs(Context));
}
private static void EmitAbs(AILEmitterCtx Context)
{
AILLabel LblTrue = new AILLabel();
Context.Emit(OpCodes.Dup);
Context.Emit(OpCodes.Ldc_I4_0);
Context.Emit(OpCodes.Bge_S, LblTrue);
Context.Emit(OpCodes.Neg);
Context.MarkLabel(LblTrue);
}
public static void Add_S(AILEmitterCtx Context)
{
EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
}
public static void Add_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
}
public static void Addhn_V(AILEmitterCtx Context)
{
EmitHighNarrow(Context, () => Context.Emit(OpCodes.Add), Round: false);
}
public static void Addp_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@ -101,6 +134,40 @@ namespace ChocolArm64.Instruction
}
}
private static void EmitHighNarrow(AILEmitterCtx Context, Action Emit, bool Round)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int Elems = 8 >> Op.Size;
int ESize = 8 << Op.Size;
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
for (int Index = 0; Index < Elems; Index++)
{
EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1);
EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size + 1);
Emit();
if (Round)
{
Context.EmitLdc_I8(1L << (ESize - 1));
Context.Emit(OpCodes.Add);
}
Context.EmitLsr(ESize);
EmitVectorInsert(Context, Op.Rd, Part + Index, Op.Size);
}
if (Part == 0)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
public static void Fabd_S(AILEmitterCtx Context)
{
EmitScalarBinaryOpF(Context, () =>
@ -129,6 +196,38 @@ namespace ChocolArm64.Instruction
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Add));
}
public static void Faddp_V(AILEmitterCtx Context)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
int Elems = Bytes >> SizeF + 2;
int Half = Elems >> 1;
for (int Index = 0; Index < Elems; Index++)
{
int Elem = (Index & (Half - 1)) << 1;
EmitVectorExtractF(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, SizeF);
EmitVectorExtractF(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, SizeF);
Context.Emit(OpCodes.Add);
EmitVectorInsertTmpF(Context, Index, SizeF);
}
Context.EmitLdvectmp();
Context.EmitStvec(Op.Rd);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
public static void Fdiv_S(AILEmitterCtx Context)
{
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div));
@ -150,17 +249,87 @@ namespace ChocolArm64.Instruction
public static void Fmax_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitScalarBinaryOpF(Context, () =>
{
EmitBinaryMathCall(Context, nameof(Math.Max));
if (Op.Size == 0)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.MaxF));
}
else if (Op.Size == 1)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Max));
}
else
{
throw new InvalidOperationException();
}
});
}
public static void Fmax_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitVectorBinaryOpF(Context, () =>
{
if (Op.Size == 0)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.MaxF));
}
else if (Op.Size == 1)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Max));
}
else
{
throw new InvalidOperationException();
}
});
}
public static void Fmin_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitScalarBinaryOpF(Context, () =>
{
EmitBinaryMathCall(Context, nameof(Math.Min));
if (Op.Size == 0)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.MinF));
}
else if (Op.Size == 1)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Min));
}
else
{
throw new InvalidOperationException();
}
});
}
public static void Fmin_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
EmitVectorBinaryOpF(Context, () =>
{
if (SizeF == 0)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.MinF));
}
else if (SizeF == 1)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Min));
}
else
{
throw new InvalidOperationException();
}
});
}
@ -192,6 +361,24 @@ namespace ChocolArm64.Instruction
});
}
public static void Fmls_V(AILEmitterCtx Context)
{
EmitVectorTernaryOpF(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Sub);
});
}
public static void Fmls_Ve(AILEmitterCtx Context)
{
EmitVectorTernaryOpByElemF(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Sub);
});
}
public static void Fmsub_S(AILEmitterCtx Context)
{
EmitScalarTernaryRaOpF(Context, () =>
@ -206,6 +393,11 @@ namespace ChocolArm64.Instruction
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
}
public static void Fmul_Se(AILEmitterCtx Context)
{
EmitScalarBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul));
}
public static void Fmul_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
@ -221,13 +413,30 @@ namespace ChocolArm64.Instruction
EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg));
}
public static void Fnmul_S(AILEmitterCtx Context)
public static void Fneg_V(AILEmitterCtx Context)
{
EmitScalarBinaryOpF(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Neg);
});
EmitVectorUnaryOpF(Context, () => Context.Emit(OpCodes.Neg));
}
public static void Fnmadd_S(AILEmitterCtx Context)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
Context.Emit(OpCodes.Neg);
EmitVectorExtractF(Context, Op.Rm, 0, SizeF);
Context.Emit(OpCodes.Mul);
EmitVectorExtractF(Context, Op.Ra, 0, SizeF);
Context.Emit(OpCodes.Sub);
EmitScalarSetF(Context, Op.Rd, SizeF);
}
public static void Fnmsub_S(AILEmitterCtx Context)
@ -248,6 +457,119 @@ namespace ChocolArm64.Instruction
EmitScalarSetF(Context, Op.Rd, SizeF);
}
public static void Fnmul_S(AILEmitterCtx Context)
{
EmitScalarBinaryOpF(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Neg);
});
}
public static void Frecpe_S(AILEmitterCtx Context)
{
EmitFrecpe(Context, 0, Scalar: true);
}
public static void Frecpe_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
{
EmitFrecpe(Context, Index, Scalar: false);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitFrecpe(AILEmitterCtx Context, int Index, bool Scalar)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
if (SizeF == 0)
{
Context.EmitLdc_R4(1);
}
else /* if (SizeF == 1) */
{
Context.EmitLdc_R8(1);
}
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
Context.Emit(OpCodes.Div);
if (Scalar)
{
EmitVectorZeroAll(Context, Op.Rd);
}
EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
}
public static void Frecps_S(AILEmitterCtx Context)
{
EmitFrecps(Context, 0, Scalar: true);
}
public static void Frecps_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
{
EmitFrecps(Context, Index, Scalar: false);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitFrecps(AILEmitterCtx Context, int Index, bool Scalar)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
if (SizeF == 0)
{
Context.EmitLdc_R4(2);
}
else /* if (SizeF == 1) */
{
Context.EmitLdc_R8(2);
}
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
EmitVectorExtractF(Context, Op.Rm, Index, SizeF);
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Sub);
if (Scalar)
{
EmitVectorZeroAll(Context, Op.Rd);
}
EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
}
public static void Frinta_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@ -259,6 +581,66 @@ namespace ChocolArm64.Instruction
EmitScalarSetF(Context, Op.Rd, Op.Size);
}
public static void Frinta_V(AILEmitterCtx Context)
{
EmitVectorUnaryOpF(Context, () =>
{
EmitRoundMathCall(Context, MidpointRounding.AwayFromZero);
});
}
public static void Frinti_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitScalarUnaryOpF(Context, () =>
{
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr));
if (Op.Size == 0)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.RoundF));
}
else if (Op.Size == 1)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Round));
}
else
{
throw new InvalidOperationException();
}
});
}
public static void Frinti_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
EmitVectorUnaryOpF(Context, () =>
{
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr));
if (SizeF == 0)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.RoundF));
}
else if (SizeF == 1)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Round));
}
else
{
throw new InvalidOperationException();
}
});
}
public static void Frintm_S(AILEmitterCtx Context)
{
EmitScalarUnaryOpF(Context, () =>
@ -275,6 +657,25 @@ namespace ChocolArm64.Instruction
});
}
public static void Frintn_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
EmitRoundMathCall(Context, MidpointRounding.ToEven);
EmitScalarSetF(Context, Op.Rd, Op.Size);
}
public static void Frintn_V(AILEmitterCtx Context)
{
EmitVectorUnaryOpF(Context, () =>
{
EmitRoundMathCall(Context, MidpointRounding.ToEven);
});
}
public static void Frintp_S(AILEmitterCtx Context)
{
EmitScalarUnaryOpF(Context, () =>
@ -283,6 +684,14 @@ namespace ChocolArm64.Instruction
});
}
public static void Frintp_V(AILEmitterCtx Context)
{
EmitVectorUnaryOpF(Context, () =>
{
EmitUnaryMathCall(Context, nameof(Math.Ceiling));
});
}
public static void Frintx_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@ -308,6 +717,111 @@ namespace ChocolArm64.Instruction
});
}
public static void Frintx_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitVectorUnaryOpF(Context, () =>
{
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr));
if (Op.Size == 0)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.RoundF));
}
else if (Op.Size == 1)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Round));
}
else
{
throw new InvalidOperationException();
}
});
}
public static void Frsqrte_S(AILEmitterCtx Context)
{
EmitScalarUnaryOpF(Context, () =>
{
EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.InvSqrtEstimate));
});
}
public static void Frsqrte_V(AILEmitterCtx Context)
{
EmitVectorUnaryOpF(Context, () =>
{
EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.InvSqrtEstimate));
});
}
public static void Frsqrts_S(AILEmitterCtx Context)
{
EmitFrsqrts(Context, 0, Scalar: true);
}
public static void Frsqrts_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
{
EmitFrsqrts(Context, Index, Scalar: false);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitFrsqrts(AILEmitterCtx Context, int Index, bool Scalar)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
if (SizeF == 0)
{
Context.EmitLdc_R4(3);
}
else /* if (SizeF == 1) */
{
Context.EmitLdc_R8(3);
}
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
EmitVectorExtractF(Context, Op.Rm, Index, SizeF);
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Sub);
if (SizeF == 0)
{
Context.EmitLdc_R4(0.5f);
}
else /* if (SizeF == 1) */
{
Context.EmitLdc_R8(0.5);
}
Context.Emit(OpCodes.Mul);
if (Scalar)
{
EmitVectorZeroAll(Context, Op.Rd);
}
EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
}
public static void Fsqrt_S(AILEmitterCtx Context)
{
EmitScalarUnaryOpF(Context, () =>
@ -335,6 +849,15 @@ namespace ChocolArm64.Instruction
});
}
public static void Mla_Ve(AILEmitterCtx Context)
{
EmitVectorTernaryOpByElemZx(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Add);
});
}
public static void Mls_V(AILEmitterCtx Context)
{
EmitVectorTernaryOpZx(Context, () =>
@ -354,11 +877,26 @@ namespace ChocolArm64.Instruction
EmitVectorBinaryOpByElemZx(Context, () => Context.Emit(OpCodes.Mul));
}
public static void Neg_S(AILEmitterCtx Context)
{
EmitScalarUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
}
public static void Neg_V(AILEmitterCtx Context)
{
EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
}
public static void Raddhn_V(AILEmitterCtx Context)
{
EmitHighNarrow(Context, () => Context.Emit(OpCodes.Add), Round: true);
}
public static void Rsubhn_V(AILEmitterCtx Context)
{
EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: true);
}
public static void Saddw_V(AILEmitterCtx Context)
{
EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add));
@ -406,6 +944,35 @@ namespace ChocolArm64.Instruction
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
}
public static void Subhn_V(AILEmitterCtx Context)
{
EmitHighNarrow(Context, () => Context.Emit(OpCodes.Sub), Round: false);
}
public static void Uabd_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpZx(Context, () => EmitAbd(Context));
}
public static void Uabdl_V(AILEmitterCtx Context)
{
EmitVectorWidenRnRmBinaryOpZx(Context, () => EmitAbd(Context));
}
private static void EmitAbd(AILEmitterCtx Context)
{
Context.Emit(OpCodes.Sub);
Type[] Types = new Type[] { typeof(long) };
Context.EmitCall(typeof(Math).GetMethod(nameof(Math.Abs), Types));
}
public static void Uaddl_V(AILEmitterCtx Context)
{
EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
}
public static void Uaddlv_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@ -429,6 +996,18 @@ namespace ChocolArm64.Instruction
EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
}
public static void Uhadd_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpZx(Context, () =>
{
Context.Emit(OpCodes.Add);
Context.EmitLdc_I4(1);
Context.Emit(OpCodes.Shr_Un);
});
}
public static void Umull_V(AILEmitterCtx Context)
{
EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));

View file

@ -110,13 +110,64 @@ namespace ChocolArm64.Instruction
Fccmp_S(Context);
}
public static void Fcmeq_S(AILEmitterCtx Context)
{
EmitScalarFcmp(Context, OpCodes.Beq_S);
}
public static void Fcmeq_V(AILEmitterCtx Context)
{
EmitVectorFcmp(Context, OpCodes.Beq_S);
}
public static void Fcmge_S(AILEmitterCtx Context)
{
EmitScalarFcmp(Context, OpCodes.Bge_S);
}
public static void Fcmge_V(AILEmitterCtx Context)
{
EmitVectorFcmp(Context, OpCodes.Bge_S);
}
public static void Fcmgt_S(AILEmitterCtx Context)
{
EmitScalarFcmp(Context, OpCodes.Bgt_S);
}
public static void Fcmgt_V(AILEmitterCtx Context)
{
EmitVectorFcmp(Context, OpCodes.Bgt_S);
}
public static void Fcmle_S(AILEmitterCtx Context)
{
EmitScalarFcmp(Context, OpCodes.Ble_S);
}
public static void Fcmle_V(AILEmitterCtx Context)
{
EmitVectorFcmp(Context, OpCodes.Ble_S);
}
public static void Fcmlt_S(AILEmitterCtx Context)
{
EmitScalarFcmp(Context, OpCodes.Blt_S);
}
public static void Fcmlt_V(AILEmitterCtx Context)
{
EmitVectorFcmp(Context, OpCodes.Blt_S);
}
public static void Fcmp_S(AILEmitterCtx Context)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false;
//Handle NaN case. If any number is NaN, then NZCV = 0011.
//Handle NaN case.
//If any number is NaN, then NZCV = 0011.
if (CmpWithZero)
{
EmitNaNCheck(Context, Op.Rn);
@ -140,7 +191,14 @@ namespace ChocolArm64.Instruction
if (CmpWithZero)
{
EmitLdcImmF(Context, 0, Op.Size);
if (Op.Size == 0)
{
Context.EmitLdc_R4(0);
}
else /* if (SizeF == 1) */
{
Context.EmitLdc_R8(0);
}
}
else
{
@ -190,22 +248,6 @@ namespace ChocolArm64.Instruction
Fcmp_S(Context);
}
private static void EmitLdcImmF(AILEmitterCtx Context, double ImmF, int Size)
{
if (Size == 0)
{
Context.EmitLdc_R4((float)ImmF);
}
else if (Size == 1)
{
Context.EmitLdc_R8(ImmF);
}
else
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
}
private static void EmitNaNCheck(AILEmitterCtx Context, int Reg)
{
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
@ -268,5 +310,84 @@ namespace ChocolArm64.Instruction
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitScalarFcmp(AILEmitterCtx Context, OpCode ILOp)
{
EmitFcmp(Context, ILOp, 0, Scalar: true);
}
private static void EmitVectorFcmp(AILEmitterCtx Context, OpCode ILOp)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
{
EmitFcmp(Context, ILOp, Index, Scalar: false);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitFcmp(AILEmitterCtx Context, OpCode ILOp, int Index, bool Scalar)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
ulong SzMask = ulong.MaxValue >> (64 - (32 << SizeF));
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
if (Op is AOpCodeSimdReg BinOp)
{
EmitVectorExtractF(Context, BinOp.Rm, Index, SizeF);
}
else if (SizeF == 0)
{
Context.EmitLdc_R4(0);
}
else /* if (SizeF == 1) */
{
Context.EmitLdc_R8(0);
}
AILLabel LblTrue = new AILLabel();
AILLabel LblEnd = new AILLabel();
Context.Emit(ILOp, LblTrue);
if (Scalar)
{
EmitVectorZeroAll(Context, Op.Rd);
}
else
{
EmitVectorInsert(Context, Op.Rd, Index, SizeF + 2, 0);
}
Context.Emit(OpCodes.Br_S, LblEnd);
Context.MarkLabel(LblTrue);
if (Scalar)
{
EmitVectorInsert(Context, Op.Rd, Index, 3, (long)SzMask);
EmitVectorZeroUpper(Context, Op.Rd);
}
else
{
EmitVectorInsert(Context, Op.Rd, Index, SizeF + 2, (long)SzMask);
}
Context.MarkLabel(LblEnd);
}
}
}

View file

@ -100,6 +100,52 @@ namespace ChocolArm64.Instruction
Context.EmitCall(MthdInfo);
}
public static void EmitUnarySoftFloatCall(AILEmitterCtx Context, string Name)
{
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
MethodInfo MthdInfo;
if (SizeF == 0)
{
MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(float) });
}
else /* if (SizeF == 1) */
{
MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(double) });
}
Context.EmitCall(MthdInfo);
}
public static void EmitScalarBinaryOpByElemF(AILEmitterCtx Context, Action Emit)
{
AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp;
EmitScalarOpByElemF(Context, Emit, Op.Index, Ternary: false);
}
public static void EmitScalarOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
if (Ternary)
{
EmitVectorExtractF(Context, Op.Rd, 0, SizeF);
}
EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
EmitVectorExtractF(Context, Op.Rm, Elem, SizeF);
Emit();
EmitScalarSetF(Context, Op.Rd, SizeF);
}
public static void EmitScalarUnaryOpSx(AILEmitterCtx Context, Action Emit)
{
EmitScalarOp(Context, Emit, OperFlags.Rn, true);
@ -381,13 +427,16 @@ namespace ChocolArm64.Instruction
}
EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
EmitVectorExtract(Context, Op.Rm, Index, Op.Size, Signed);
EmitVectorExtract(Context, Op.Rm, Elem, Op.Size, Signed);
Emit();
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
EmitVectorInsertTmp(Context, Index, Op.Size);
}
Context.EmitLdvectmp();
Context.EmitStvec(Op.Rd);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
@ -444,6 +493,9 @@ namespace ChocolArm64.Instruction
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
Context.EmitLdvec(Op.Rd);
Context.EmitStvectmp();
int Elems = 8 >> Op.Size;
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
@ -486,6 +538,9 @@ namespace ChocolArm64.Instruction
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
Context.EmitLdvec(Op.Rd);
Context.EmitStvectmp();
int Elems = 8 >> Op.Size;
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
@ -533,10 +588,7 @@ namespace ChocolArm64.Instruction
public static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed)
{
if (Size < 0 || Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
ThrowIfInvalid(Index, Size);
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
@ -551,6 +603,8 @@ namespace ChocolArm64.Instruction
public static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size)
{
ThrowIfInvalidF(Index, Size);
Context.EmitLdvec(Reg);
Context.EmitLdc_I4(Index);
@ -586,10 +640,7 @@ namespace ChocolArm64.Instruction
public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size)
{
if (Size < 0 || Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
ThrowIfInvalid(Index, Size);
Context.EmitLdvec(Reg);
Context.EmitLdc_I4(Index);
@ -602,10 +653,7 @@ namespace ChocolArm64.Instruction
public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size)
{
if (Size < 0 || Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
ThrowIfInvalid(Index, Size);
Context.EmitLdvectmp();
Context.EmitLdc_I4(Index);
@ -618,10 +666,7 @@ namespace ChocolArm64.Instruction
public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value)
{
if (Size < 0 || Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
ThrowIfInvalid(Index, Size);
Context.EmitLdc_I8(Value);
Context.EmitLdvec(Reg);
@ -635,6 +680,8 @@ namespace ChocolArm64.Instruction
public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size)
{
ThrowIfInvalidF(Index, Size);
Context.EmitLdvec(Reg);
Context.EmitLdc_I4(Index);
@ -656,6 +703,8 @@ namespace ChocolArm64.Instruction
public static void EmitVectorInsertTmpF(AILEmitterCtx Context, int Index, int Size)
{
ThrowIfInvalidF(Index, Size);
Context.EmitLdvectmp();
Context.EmitLdc_I4(Index);
@ -674,5 +723,31 @@ namespace ChocolArm64.Instruction
Context.EmitStvectmp();
}
private static void ThrowIfInvalid(int Index, int Size)
{
if ((uint)Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
if ((uint)Index >= 16 >> Size)
{
throw new ArgumentOutOfRangeException(nameof(Index));
}
}
private static void ThrowIfInvalidF(int Index, int Size)
{
if ((uint)Size > 1)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
if ((uint)Index >= 4 >> Size)
{
throw new ArgumentOutOfRangeException(nameof(Index));
}
}
}
}

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)
{
EmitVectorTernaryOpZx(Context, () =>

View file

@ -61,6 +61,9 @@ namespace ChocolArm64.Instruction
{
AOpCodeSimdExt Op = (AOpCodeSimdExt)Context.CurrOp;
Context.EmitLdvec(Op.Rd);
Context.EmitStvectmp();
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
int Position = Op.Imm4;
@ -75,10 +78,12 @@ namespace ChocolArm64.Instruction
}
EmitVectorExtractZx(Context, Reg, Position++, 0);
EmitVectorInsert(Context, Op.Rd, Index, 0);
EmitVectorInsertTmp(Context, Index, 0);
}
Context.EmitLdvectmp();
Context.EmitStvec(Op.Rd);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
@ -113,7 +118,7 @@ namespace ChocolArm64.Instruction
EmitVectorExtractZx(Context, Op.Rn, 0, 3);
EmitIntZeroHigherIfNeeded(Context);
EmitIntZeroUpperIfNeeded(Context);
Context.EmitStintzr(Op.Rd);
}
@ -124,7 +129,7 @@ namespace ChocolArm64.Instruction
EmitVectorExtractZx(Context, Op.Rn, 1, 3);
EmitIntZeroHigherIfNeeded(Context);
EmitIntZeroUpperIfNeeded(Context);
Context.EmitStintzr(Op.Rd);
}
@ -135,7 +140,7 @@ namespace ChocolArm64.Instruction
Context.EmitLdintzr(Op.Rn);
EmitIntZeroHigherIfNeeded(Context);
EmitIntZeroUpperIfNeeded(Context);
EmitScalarSet(Context, Op.Rd, 3);
}
@ -146,7 +151,7 @@ namespace ChocolArm64.Instruction
Context.EmitLdintzr(Op.Rn);
EmitIntZeroHigherIfNeeded(Context);
EmitIntZeroUpperIfNeeded(Context);
EmitVectorInsert(Context, Op.Rd, 1, 3);
}
@ -251,6 +256,16 @@ namespace ChocolArm64.Instruction
Context.EmitStvec(Op.Rd);
}
public static void Trn1_V(AILEmitterCtx Context)
{
EmitVectorTranspose(Context, Part: 0);
}
public static void Trn2_V(AILEmitterCtx Context)
{
EmitVectorTranspose(Context, Part: 1);
}
public static void Umov_S(AILEmitterCtx Context)
{
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
@ -301,7 +316,7 @@ namespace ChocolArm64.Instruction
EmitVectorZip(Context, Part: 1);
}
private static void EmitIntZeroHigherIfNeeded(AILEmitterCtx Context)
private static void EmitIntZeroUpperIfNeeded(AILEmitterCtx Context)
{
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
{
@ -310,6 +325,29 @@ namespace ChocolArm64.Instruction
}
}
private static void EmitVectorTranspose(AILEmitterCtx Context, int Part)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
int Elems = Bytes >> Op.Size;
for (int Index = 0; Index < Elems; Index++)
{
int Elem = (Index & ~1) + Part;
EmitVectorExtractZx(Context, (Index & 1) == 0 ? Op.Rn : Op.Rm, Elem, Op.Size);
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitVectorUnzip(AILEmitterCtx Context, int Part)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;

View file

@ -20,6 +20,14 @@ namespace ChocolArm64.Instruction
Context.EmitCall(typeof(ASoftFallback), MthdName);
}
public static uint CountLeadingSigns32(uint Value) => (uint)CountLeadingSigns(Value, 32);
public static ulong CountLeadingSigns64(ulong Value) => (ulong)CountLeadingSigns(Value, 64);
private static ulong CountLeadingSigns(ulong Value, int Size)
{
return CountLeadingZeros((Value >> 1) ^ Value, Size - 1);
}
public static uint CountLeadingZeros32(uint Value) => (uint)CountLeadingZeros(Value, 32);
public static ulong CountLeadingZeros64(ulong Value) => (ulong)CountLeadingZeros(Value, 64);
@ -41,15 +49,15 @@ namespace ChocolArm64.Instruction
private const uint Crc32RevPoly = 0xedb88320;
private const uint Crc32cRevPoly = 0x82f63b78;
public static uint Crc32b(uint Crc, byte Val) => Crc32 (Crc, Crc32RevPoly, Val);
public static uint Crc32h(uint Crc, byte Val) => Crc32h(Crc, Crc32RevPoly, Val);
public static uint Crc32w(uint Crc, byte Val) => Crc32w(Crc, Crc32RevPoly, Val);
public static uint Crc32x(uint Crc, byte Val) => Crc32x(Crc, Crc32RevPoly, Val);
public static uint Crc32b(uint Crc, byte Val) => Crc32 (Crc, Crc32RevPoly, Val);
public static uint Crc32h(uint Crc, ushort Val) => Crc32h(Crc, Crc32RevPoly, Val);
public static uint Crc32w(uint Crc, uint Val) => Crc32w(Crc, Crc32RevPoly, Val);
public static uint Crc32x(uint Crc, ulong Val) => Crc32x(Crc, Crc32RevPoly, Val);
public static uint Crc32cb(uint Crc, byte Val) => Crc32 (Crc, Crc32cRevPoly, Val);
public static uint Crc32ch(uint Crc, byte Val) => Crc32h(Crc, Crc32cRevPoly, Val);
public static uint Crc32cw(uint Crc, byte Val) => Crc32w(Crc, Crc32cRevPoly, Val);
public static uint Crc32cx(uint Crc, byte Val) => Crc32x(Crc, Crc32cRevPoly, Val);
public static uint Crc32cb(uint Crc, byte Val) => Crc32 (Crc, Crc32cRevPoly, Val);
public static uint Crc32ch(uint Crc, ushort Val) => Crc32h(Crc, Crc32cRevPoly, Val);
public static uint Crc32cw(uint Crc, uint Val) => Crc32w(Crc, Crc32cRevPoly, Val);
public static uint Crc32cx(uint Crc, ulong Val) => Crc32x(Crc, Crc32cRevPoly, Val);
private static uint Crc32h(uint Crc, uint Poly, ushort Val)
{
@ -242,10 +250,86 @@ namespace ChocolArm64.Instruction
public static int CountSetBits8(byte Value)
{
return (Value >> 0) & 1 + (Value >> 1) & 1 +
(Value >> 2) & 1 + (Value >> 3) & 1 +
(Value >> 4) & 1 + (Value >> 5) & 1 +
(Value >> 6) & 1 + (Value >> 7);
return ((Value >> 0) & 1) + ((Value >> 1) & 1) +
((Value >> 2) & 1) + ((Value >> 3) & 1) +
((Value >> 4) & 1) + ((Value >> 5) & 1) +
((Value >> 6) & 1) + (Value >> 7);
}
public static float MaxF(float val1, float val2)
{
if (val1 == 0.0 && val2 == 0.0)
{
if (BitConverter.SingleToInt32Bits(val1) < 0 && BitConverter.SingleToInt32Bits(val2) < 0)
return -0.0f;
return 0.0f;
}
if (val1 > val2)
return val1;
if (float.IsNaN(val1))
return val1;
return val2;
}
public static double Max(double val1, double val2)
{
if (val1 == 0.0 && val2 == 0.0)
{
if (BitConverter.DoubleToInt64Bits(val1) < 0 && BitConverter.DoubleToInt64Bits(val2) < 0)
return -0.0;
return 0.0;
}
if (val1 > val2)
return val1;
if (double.IsNaN(val1))
return val1;
return val2;
}
public static float MinF(float val1, float val2)
{
if (val1 == 0.0 && val2 == 0.0)
{
if (BitConverter.SingleToInt32Bits(val1) < 0 || BitConverter.SingleToInt32Bits(val2) < 0)
return -0.0f;
return 0.0f;
}
if (val1 < val2)
return val1;
if (float.IsNaN(val1))
return val1;
return val2;
}
public static double Min(double val1, double val2)
{
if (val1 == 0.0 && val2 == 0.0)
{
if (BitConverter.DoubleToInt64Bits(val1) < 0 || BitConverter.DoubleToInt64Bits(val2) < 0)
return -0.0;
return 0.0;
}
if (val1 < val2)
return val1;
if (double.IsNaN(val1))
return val1;
return val2;
}
public static float RoundF(float Value, int Fpcr)

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.Runtime.InteropServices;
using System.Text;
namespace ChocolArm64.Memory
@ -20,11 +22,11 @@ namespace ChocolArm64.Memory
}
}
public static byte[] ReadBytes(AMemory Memory, long Position, int Size)
public static byte[] ReadBytes(AMemory Memory, long Position, long Size)
{
byte[] Data = new byte[Size];
for (int Offs = 0; Offs < Size; Offs++)
for (long Offs = 0; Offs < Size; Offs++)
{
Data[Offs] = (byte)Memory.ReadByte(Position + Offs);
}
@ -40,11 +42,39 @@ namespace ChocolArm64.Memory
}
}
public static string ReadAsciiString(AMemory Memory, long Position, int MaxSize = -1)
public unsafe static T Read<T>(AMemory Memory, long Position) where T : struct
{
long Size = Marshal.SizeOf<T>();
if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
{
throw new ArgumentOutOfRangeException(nameof(Position));
}
IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
return Marshal.PtrToStructure<T>(Ptr);
}
public unsafe static void Write<T>(AMemory Memory, long Position, T Value) where T : struct
{
long Size = Marshal.SizeOf<T>();
if ((ulong)(Position + Size) > AMemoryMgr.AddrSize)
{
throw new ArgumentOutOfRangeException(nameof(Position));
}
IntPtr Ptr = new IntPtr((byte*)Memory.Ram + Position);
Marshal.StructureToPtr<T>(Value, Ptr, false);
}
public static string ReadAsciiString(AMemory Memory, long Position, long MaxSize = -1)
{
using (MemoryStream MS = new MemoryStream())
{
for (int Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++)
for (long Offs = 0; Offs < MaxSize || MaxSize == -1; Offs++)
{
byte Value = (byte)Memory.ReadByte(Position + Offs);

View file

@ -1,5 +1,6 @@
using ChocolArm64.Events;
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ChocolArm64.State
@ -56,10 +57,17 @@ namespace ChocolArm64.State
public event EventHandler<AInstExceptionEventArgs> SvcCall;
public event EventHandler<AInstUndefinedEventArgs> Undefined;
private Stack<long> CallStack;
private static Stopwatch TickCounter;
private static double HostTickFreq;
public AThreadState()
{
CallStack = new Stack<long>();
}
static AThreadState()
{
HostTickFreq = 1.0 / Stopwatch.Frequency;
@ -83,5 +91,27 @@ namespace ChocolArm64.State
{
Undefined?.Invoke(this, new AInstUndefinedEventArgs(Position, RawOpCode));
}
internal void EnterMethod(long Position)
{
CallStack.Push(Position);
}
internal void ExitMethod()
{
CallStack.TryPop(out _);
}
internal void JumpMethod(long Position)
{
CallStack.TryPop(out _);
CallStack.Push(Position);
}
public long[] GetCallStack()
{
return CallStack.ToArray();
}
}
}

View file

@ -60,11 +60,11 @@ namespace ChocolArm64.Translation
public AILBlock GetILBlock(int Index) => ILBlocks[Index];
public ATranslatedSub GetSubroutine(HashSet<long> Callees)
public ATranslatedSub GetSubroutine()
{
LocalAlloc = new ALocalAlloc(ILBlocks, Root);
InitSubroutine(Callees);
InitSubroutine();
InitLocals();
foreach (AILBlock ILBlock in ILBlocks)
@ -75,7 +75,7 @@ namespace ChocolArm64.Translation
return Subroutine;
}
private void InitSubroutine(HashSet<long> Callees)
private void InitSubroutine()
{
List<ARegister> Params = new List<ARegister>();
@ -99,7 +99,7 @@ namespace ChocolArm64.Translation
Generator = Mthd.GetILGenerator();
Subroutine = new ATranslatedSub(Mthd, Params, Callees);
Subroutine = new ATranslatedSub(Mthd, Params);
}
private void InitLocals()

View file

@ -12,8 +12,6 @@ namespace ChocolArm64.Translation
{
private ATranslator Translator;
private HashSet<long> Callees;
private Dictionary<long, AILLabel> Labels;
private int BlkIndex;
@ -66,8 +64,6 @@ namespace ChocolArm64.Translation
this.Graph = Graph;
this.Root = Root;
Callees = new HashSet<long>();
Labels = new Dictionary<long, AILLabel>();
Emitter = new AILEmitter(Graph, Root, SubName);
@ -84,7 +80,7 @@ namespace ChocolArm64.Translation
public ATranslatedSub GetSubroutine()
{
return Emitter.GetSubroutine(Callees);
return Emitter.GetSubroutine();
}
public bool AdvanceOpCode()
@ -123,8 +119,6 @@ namespace ChocolArm64.Translation
public bool TryOptEmitSubroutineCall()
{
Callees.Add(((AOpCodeBImm)CurrOp).Imm);
if (CurrBlock.Next == null)
{
return false;
@ -152,6 +146,8 @@ namespace ChocolArm64.Translation
EmitCall(Sub.Method);
Sub.AddCaller(Root.Position);
return true;
}
@ -260,18 +256,24 @@ namespace ChocolArm64.Translation
case AIntType.Int64: Emit(OpCodes.Conv_I8); break;
}
if (IntType == AIntType.UInt64 ||
IntType == AIntType.Int64)
bool Sz64 = CurrOp.RegisterSize != ARegisterSize.Int32;
if (Sz64 == (IntType == AIntType.UInt64 ||
IntType == AIntType.Int64))
{
return;
}
if (CurrOp.RegisterSize != ARegisterSize.Int32)
if (Sz64)
{
Emit(IntType >= AIntType.Int8
? OpCodes.Conv_I8
: OpCodes.Conv_U8);
}
else
{
Emit(OpCodes.Conv_U4);
}
}
public void EmitLsl(int Amount) => EmitILShift(Amount, OpCodes.Shl);
@ -459,6 +461,21 @@ namespace ChocolArm64.Translation
EmitCall(ObjType.GetMethod(MthdName));
}
public void EmitPrivateCall(Type ObjType, string MthdName)
{
if (ObjType == null)
{
throw new ArgumentNullException(nameof(ObjType));
}
if (MthdName == null)
{
throw new ArgumentNullException(nameof(MthdName));
}
EmitCall(ObjType.GetMethod(MthdName, BindingFlags.Instance | BindingFlags.NonPublic));
}
public void EmitCall(MethodInfo MthdInfo)
{
if (MthdInfo == null)

View file

@ -49,6 +49,9 @@ https://openal.org/downloads/OpenAL11CoreSDK.zip
- Config File: `Ryujinx.conf` should be present in executable folder.
For more informations [you can go here](CONFIG.md).
- If you are a Windows user, you can configure your keys, the logs, install OpenAL, etc... with Ryujinx-Setting.
[Download it, right here](https://github.com/AcK77/Ryujinx-Settings)
**Help**
If you have some homebrew that currently don't work on the emulator, you can contact us through Discord with the compiled NRO/NSO (and source code if possible) and then we'll make changes to make the requested app / game work.

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

View file

@ -82,7 +82,7 @@ namespace Ryujinx.Core.Input
(AMemory Memory, long Position) ShMem = ShMemPositions[ShMemPositions.Length - 1];
Logging.Info($"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!");
Logging.Info(LogClass.ServiceHid, $"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!");
Init(ShMem.Memory, ShMem.Position);
}

View file

@ -7,14 +7,16 @@ namespace Ryujinx.Core.Loaders
{
class Executable
{
private AMemory Memory;
private List<ElfDyn> Dynamic;
private Dictionary<long, string> m_SymbolTable;
public IReadOnlyDictionary<long, string> SymbolTable => m_SymbolTable;
public string Name { get; private set; }
private AMemory Memory;
public long ImageBase { get; private set; }
public long ImageEnd { get; private set; }
@ -24,6 +26,8 @@ namespace Ryujinx.Core.Loaders
m_SymbolTable = new Dictionary<long, string>();
Name = Exe.Name;
this.Memory = Memory;
this.ImageBase = ImageBase;
this.ImageEnd = ImageBase;

View file

@ -2,6 +2,8 @@ namespace Ryujinx.Core.Loaders.Executables
{
public interface IExecutable
{
string Name { get; }
byte[] Text { get; }
byte[] RO { get; }
byte[] Data { get; }

View file

@ -4,6 +4,8 @@ namespace Ryujinx.Core.Loaders.Executables
{
class Nro : IExecutable
{
public string Name { get; private set; }
public byte[] Text { get; private set; }
public byte[] RO { get; private set; }
public byte[] Data { get; private set; }
@ -14,8 +16,10 @@ namespace Ryujinx.Core.Loaders.Executables
public int DataOffset { get; private set; }
public int BssSize { get; private set; }
public Nro(Stream Input)
public Nro(Stream Input, string Name)
{
this.Name = Name;
BinaryReader Reader = new BinaryReader(Input);
Input.Seek(4, SeekOrigin.Begin);

View file

@ -6,6 +6,8 @@ namespace Ryujinx.Core.Loaders.Executables
{
class Nso : IExecutable
{
public string Name { get; private set; }
public byte[] Text { get; private set; }
public byte[] RO { get; private set; }
public byte[] Data { get; private set; }
@ -27,8 +29,10 @@ namespace Ryujinx.Core.Loaders.Executables
HasDataHash = 1 << 5
}
public Nso(Stream Input)
public Nso(Stream Input, string Name)
{
this.Name = Name;
BinaryReader Reader = new BinaryReader(Input);
Input.Seek(0, SeekOrigin.Begin);

38
Ryujinx.Core/LogClass.cs Normal file
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.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
namespace Ryujinx.Core
{
@ -12,14 +14,28 @@ namespace Ryujinx.Core
private const string LogFileName = "Ryujinx.log";
private static bool EnableInfo = Config.LoggingEnableInfo;
private static bool EnableTrace = Config.LoggingEnableTrace;
private static bool EnableDebug = Config.LoggingEnableDebug;
private static bool EnableWarn = Config.LoggingEnableWarn;
private static bool EnableError = Config.LoggingEnableError;
private static bool EnableFatal = Config.LoggingEnableFatal;
private static bool EnableIpc = Config.LoggingEnableIpc;
private static bool EnableLogFile = Config.LoggingEnableLogFile;
private static bool EnableInfo = Config.LoggingEnableInfo;
private static bool EnableTrace = Config.LoggingEnableTrace;
private static bool EnableDebug = Config.LoggingEnableDebug;
private static bool EnableWarn = Config.LoggingEnableWarn;
private static bool EnableError = Config.LoggingEnableError;
private static bool EnableFatal = Config.LoggingEnableFatal;
private static bool EnableStub = Config.LoggingEnableStub;
private static bool EnableIpc = Config.LoggingEnableIpc;
private static bool EnableFilter = Config.LoggingEnableFilter;
private static bool EnableLogFile = Config.LoggingEnableLogFile;
private static bool[] FilteredLogClasses = Config.LoggingFilteredClasses;
private enum LogLevel
{
Debug,
Error,
Fatal,
Info,
Stub,
Trace,
Warn
}
static Logging()
{
@ -30,14 +46,51 @@ namespace Ryujinx.Core
ExecutionTime.Start();
}
public static string GetExecutionTime()
{
return ExecutionTime.ElapsedMilliseconds.ToString().PadLeft(8, '0') + "ms";
}
public static string GetExecutionTime() => ExecutionTime.ElapsedMilliseconds.ToString().PadLeft(8, '0') + "ms";
private static string WhoCalledMe()
private static void LogMessage(LogEntry LogEntry)
{
return new StackTrace().GetFrame(2).GetMethod().Name;
if (EnableFilter)
if (!FilteredLogClasses[(int)LogEntry.LogClass])
return;
ConsoleColor consoleColor = ConsoleColor.White;
switch (LogEntry.LogLevel)
{
case LogLevel.Debug:
consoleColor = ConsoleColor.Gray;
break;
case LogLevel.Error:
consoleColor = ConsoleColor.Red;
break;
case LogLevel.Fatal:
consoleColor = ConsoleColor.Magenta;
break;
case LogLevel.Info:
consoleColor = ConsoleColor.White;
break;
case LogLevel.Stub:
consoleColor = ConsoleColor.DarkYellow;
break;
case LogLevel.Trace:
consoleColor = ConsoleColor.DarkGray;
break;
case LogLevel.Warn:
consoleColor = ConsoleColor.Yellow;
break;
}
LogEntry.ManagedThreadId = Thread.CurrentThread.ManagedThreadId;
string Text = $"{LogEntry.ExecutionTime} | {LogEntry.ManagedThreadId} > {LogEntry.LogClass} > " +
$"{LogEntry.LogLevel.ToString()} > {LogEntry.CallingMember} > {LogEntry.Message}";
Console.ForegroundColor = consoleColor;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
}
private static void LogFile(string Message)
@ -51,87 +104,108 @@ namespace Ryujinx.Core
}
}
public static void Info(string Message)
public static void Info(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableInfo)
{
string Text = $"{GetExecutionTime()} | INFO > {Message}";
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Info,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
public static void Trace(string Message)
public static void Trace(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableTrace)
{
string Text = $"{GetExecutionTime()} | TRACE > {WhoCalledMe()} - {Message}";
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Trace,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
public static void Debug(string Message)
public static void Stub(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableStub)
{
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Stub,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
public static void Debug(LogClass LogClass,string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableDebug)
{
string Text = $"{GetExecutionTime()} | DEBUG > {WhoCalledMe()} - {Message}";
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Debug,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
public static void Warn(string Message)
public static void Warn(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableWarn)
{
string Text = $"{GetExecutionTime()} | WARN > {WhoCalledMe()} - {Message}";
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Warn,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
public static void Error(string Message)
public static void Error(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableError)
{
string Text = $"{GetExecutionTime()} | ERROR > {WhoCalledMe()} - {Message}";
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Error,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
public static void Fatal(string Message)
public static void Fatal(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableFatal)
{
string Text = $"{GetExecutionTime()} | FATAL > {WhoCalledMe()} - {Message}";
Console.ForegroundColor = ConsoleColor.Magenta;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Fatal,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
@ -208,5 +282,15 @@ namespace Ryujinx.Core
}
return result.ToString();
}
private struct LogEntry
{
public string CallingMember;
public string ExecutionTime;
public string Message;
public int ManagedThreadId;
public LogClass LogClass;
public LogLevel LogLevel;
}
}
}

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
{
class KProcessHandleTable : IDisposable
class KProcessHandleTable
{
private IdDictionary Handles;
@ -21,43 +21,14 @@ namespace Ryujinx.Core.OsHle.Handles
return Handles.GetData<T>(Handle);
}
public bool ReplaceData(int Id, object Data)
public object CloseHandle(int Handle)
{
return Handles.ReplaceData(Id, Data);
}
public bool CloseHandle(int Handle)
{
object Data = Handles.GetData(Handle);
if (Data is HTransferMem TMem)
{
TMem.Memory.Manager.Reprotect(
TMem.Position,
TMem.Size,
TMem.Perm);
}
return Handles.Delete(Handle);
}
public void Dispose()
public ICollection<object> Clear()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
foreach (object Obj in Handles)
{
if (Obj is IDisposable DisposableObj)
{
DisposableObj.Dispose();
}
}
}
return Handles.Clear();
}
}
}

View file

@ -5,19 +5,31 @@ using System.Threading;
namespace Ryujinx.Core.OsHle.Handles
{
public class KProcessScheduler : IDisposable
class KProcessScheduler : IDisposable
{
private const int LowestPriority = 0x40;
private class SchedulerThread : IDisposable
{
public HThread Thread { get; private set; }
public KThread Thread { get; private set; }
public AutoResetEvent WaitEvent { get; private set; }
public ManualResetEvent SyncWaitEvent { get; private set; }
public AutoResetEvent SchedWaitEvent { get; private set; }
public SchedulerThread(HThread Thread)
public bool Active { get; set; }
public int SyncTimeout { get; set; }
public SchedulerThread(KThread Thread)
{
this.Thread = Thread;
WaitEvent = new AutoResetEvent(false);
SyncWaitEvent = new ManualResetEvent(true);
SchedWaitEvent = new AutoResetEvent(false);
Active = true;
SyncTimeout = 0;
}
public void Dispose()
@ -29,7 +41,8 @@ namespace Ryujinx.Core.OsHle.Handles
{
if (Disposing)
{
WaitEvent.Dispose();
SyncWaitEvent.Dispose();
SchedWaitEvent.Dispose();
}
}
}
@ -51,7 +64,7 @@ namespace Ryujinx.Core.OsHle.Handles
}
}
public SchedulerThread Pop(int MinPriority = 0x40)
public SchedulerThread Pop(int MinPriority = LowestPriority)
{
lock (Threads)
{
@ -65,9 +78,9 @@ namespace Ryujinx.Core.OsHle.Handles
{
SchedThread = Threads[Index];
if (HighestPriority > SchedThread.Thread.Priority)
if (HighestPriority > SchedThread.Thread.ActualPriority)
{
HighestPriority = SchedThread.Thread.Priority;
HighestPriority = SchedThread.Thread.ActualPriority;
HighestPrioIndex = Index;
}
@ -93,9 +106,17 @@ namespace Ryujinx.Core.OsHle.Handles
return Threads.Contains(SchedThread);
}
}
public bool Remove(SchedulerThread SchedThread)
{
lock (Threads)
{
return Threads.Remove(SchedThread);
}
}
}
private ConcurrentDictionary<HThread, SchedulerThread> AllThreads;
private ConcurrentDictionary<KThread, SchedulerThread> AllThreads;
private ThreadQueue[] WaitingToRun;
@ -105,7 +126,7 @@ namespace Ryujinx.Core.OsHle.Handles
public KProcessScheduler()
{
AllThreads = new ConcurrentDictionary<HThread, SchedulerThread>();
AllThreads = new ConcurrentDictionary<KThread, SchedulerThread>();
WaitingToRun = new ThreadQueue[4];
@ -119,7 +140,7 @@ namespace Ryujinx.Core.OsHle.Handles
SchedLock = new object();
}
public void StartThread(HThread Thread)
public void StartThread(KThread Thread)
{
lock (SchedLock)
{
@ -130,23 +151,119 @@ namespace Ryujinx.Core.OsHle.Handles
return;
}
if (!ActiveProcessors.Contains(Thread.ProcessorId))
if (ActiveProcessors.Add(Thread.ProcessorId))
{
ActiveProcessors.Add(Thread.ProcessorId);
Thread.Thread.Execute();
Logging.Debug($"{GetDbgThreadInfo(Thread)} running.");
PrintDbgThreadInfo(Thread, "running.");
}
else
{
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
PrintDbgThreadInfo(Thread, "waiting to run.");
}
}
}
public void RemoveThread(KThread Thread)
{
PrintDbgThreadInfo(Thread, "exited.");
lock (SchedLock)
{
if (AllThreads.TryRemove(Thread, out SchedulerThread SchedThread))
{
WaitingToRun[Thread.ProcessorId].Remove(SchedThread);
SchedThread.Dispose();
}
SchedulerThread NewThread = WaitingToRun[Thread.ProcessorId].Pop();
if (NewThread == null)
{
Logging.Debug(LogClass.KernelScheduler, $"Nothing to run on core {Thread.ProcessorId}!");
ActiveProcessors.Remove(Thread.ProcessorId);
return;
}
RunThread(NewThread);
}
}
public void SetThreadActivity(KThread Thread, bool Active)
{
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
{
throw new InvalidOperationException();
}
SchedThread.Active = Active;
UpdateSyncWaitEvent(SchedThread);
WaitIfNeeded(SchedThread);
}
public bool EnterWait(KThread Thread, int Timeout = -1)
{
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
{
throw new InvalidOperationException();
}
SchedThread.SyncTimeout = Timeout;
UpdateSyncWaitEvent(SchedThread);
return WaitIfNeeded(SchedThread);
}
public void WakeUp(KThread Thread)
{
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
{
throw new InvalidOperationException();
}
SchedThread.SyncTimeout = 0;
UpdateSyncWaitEvent(SchedThread);
WaitIfNeeded(SchedThread);
}
private void UpdateSyncWaitEvent(SchedulerThread SchedThread)
{
if (SchedThread.Active && SchedThread.SyncTimeout == 0)
{
SchedThread.SyncWaitEvent.Set();
}
else
{
SchedThread.SyncWaitEvent.Reset();
}
}
private bool WaitIfNeeded(SchedulerThread SchedThread)
{
KThread Thread = SchedThread.Thread;
if (!IsActive(SchedThread) && Thread.Thread.IsCurrentThread())
{
Suspend(Thread.ProcessorId);
return Resume(Thread);
}
else
{
return false;
}
}
public void Suspend(int ProcessorId)
{
lock (SchedLock)
@ -159,164 +276,120 @@ namespace Ryujinx.Core.OsHle.Handles
}
else
{
Logging.Debug(LogClass.KernelScheduler, $"Nothing to run on core {ProcessorId}!");
ActiveProcessors.Remove(ProcessorId);
}
}
}
public void Resume(HThread CurrThread)
public void Yield(KThread Thread)
{
SchedulerThread SchedThread;
Logging.Debug($"{GetDbgThreadInfo(CurrThread)} entering ipc delay wait state.");
PrintDbgThreadInfo(Thread, "yielded execution.");
lock (SchedLock)
{
if (!AllThreads.TryGetValue(CurrThread, out SchedThread))
SchedulerThread SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.ActualPriority);
if (IsActive(Thread) && SchedThread == null)
{
Logging.Error($"{GetDbgThreadInfo(CurrThread)} was not found on the scheduler queue!");
PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run.");
return;
}
}
TryResumingExecution(SchedThread);
}
public bool WaitForSignal(HThread Thread, int Timeout = -1)
{
SchedulerThread SchedThread;
Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state.");
lock (SchedLock)
{
SchedThread = WaitingToRun[Thread.ProcessorId].Pop();
if (SchedThread != null)
{
RunThread(SchedThread);
}
else
{
ActiveProcessors.Remove(Thread.ProcessorId);
}
if (!AllThreads.TryGetValue(Thread, out SchedThread))
{
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
return false;
}
}
bool Result;
if (Timeout >= 0)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
Result = SchedThread.WaitEvent.WaitOne(Timeout);
}
else
{
Result = SchedThread.WaitEvent.WaitOne();
}
TryResumingExecution(SchedThread);
return Result;
Resume(Thread);
}
private void TryResumingExecution(SchedulerThread SchedThread)
public bool Resume(KThread Thread)
{
HThread Thread = SchedThread.Thread;
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
{
throw new InvalidOperationException();
}
return TryResumingExecution(SchedThread);
}
private bool TryResumingExecution(SchedulerThread SchedThread)
{
KThread Thread = SchedThread.Thread;
if (!SchedThread.Active || SchedThread.SyncTimeout != 0)
{
PrintDbgThreadInfo(Thread, "entering inactive wait state...");
}
bool Result = false;
if (SchedThread.SyncTimeout != 0)
{
Result = SchedThread.SyncWaitEvent.WaitOne(SchedThread.SyncTimeout);
SchedThread.SyncTimeout = 0;
}
lock (SchedLock)
{
if (ActiveProcessors.Add(Thread.ProcessorId))
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
PrintDbgThreadInfo(Thread, "resuming execution...");
return;
return Result;
}
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
PrintDbgThreadInfo(Thread, "entering wait state...");
}
SchedThread.WaitEvent.WaitOne();
SchedThread.SchedWaitEvent.WaitOne();
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
}
PrintDbgThreadInfo(Thread, "resuming execution...");
public void Yield(HThread Thread)
{
SchedulerThread SchedThread;
Logging.Debug($"{GetDbgThreadInfo(Thread)} yielded execution.");
lock (SchedLock)
{
SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.Priority);
if (SchedThread == null)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} resumed because theres nothing better to run.");
return;
}
RunThread(SchedThread);
if (!AllThreads.TryGetValue(Thread, out SchedThread))
{
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
return;
}
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
}
SchedThread.WaitEvent.WaitOne();
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
return Result;
}
private void RunThread(SchedulerThread SchedThread)
{
if (!SchedThread.Thread.Thread.Execute())
{
SchedThread.WaitEvent.Set();
SchedThread.SchedWaitEvent.Set();
}
else
{
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} running.");
PrintDbgThreadInfo(SchedThread.Thread, "running.");
}
}
public void Signal(params HThread[] Threads)
private bool IsActive(KThread Thread)
{
lock (SchedLock)
if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
{
foreach (HThread Thread in Threads)
{
if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
{
if (!WaitingToRun[Thread.ProcessorId].HasThread(SchedThread))
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} signaled.");
SchedThread.WaitEvent.Set();
}
}
}
throw new InvalidOperationException();
}
return IsActive(SchedThread);
}
private string GetDbgThreadInfo(HThread Thread)
private bool IsActive(SchedulerThread SchedThread)
{
return $"Thread {Thread.ThreadId} (core {Thread.ProcessorId}) prio {Thread.Priority}";
return SchedThread.Active && SchedThread.SyncTimeout == 0;
}
private void PrintDbgThreadInfo(KThread Thread, string Message)
{
Logging.Debug(LogClass.KernelScheduler, "(" +
"ThreadId: " + Thread.ThreadId + ", " +
"ProcessorId: " + Thread.ProcessorId + ", " +
"ActualPriority: " + Thread.ActualPriority + ", " +
"WantedPriority: " + Thread.WantedPriority + ") " + Message);
}
public void Dispose()

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 Value1 = Memory.ReadInt64(Position + 0x10);
FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, (int)(Value1 - Value0));
FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, Value1 - Value0);
break;
}

View file

@ -11,13 +11,14 @@ namespace Ryujinx.Core.OsHle
internal const int HidSize = 0x40000;
internal const int FontSize = 0x50;
internal ConcurrentDictionary<long, Mutex> Mutexes { get; private set; }
internal ConcurrentDictionary<long, CondVar> CondVars { get; private set; }
private KProcessScheduler Scheduler;
private ConcurrentDictionary<int, Process> Processes;
internal HSharedMem HidSharedMem;
internal HSharedMem FontSharedMem;
internal HSharedMem HidSharedMem { get; private set; }
internal HSharedMem FontSharedMem { get; private set; }
internal KEvent VsyncEvent { get; private set; }
private Switch Ns;
@ -25,13 +26,14 @@ namespace Ryujinx.Core.OsHle
{
this.Ns = Ns;
Mutexes = new ConcurrentDictionary<long, Mutex>();
CondVars = new ConcurrentDictionary<long, CondVar>();
Scheduler = new KProcessScheduler();
Processes = new ConcurrentDictionary<int, Process>();
HidSharedMem = new HSharedMem();
FontSharedMem = new HSharedMem();
VsyncEvent = new KEvent();
}
public void LoadCart(string ExeFsDir, string RomFsFile = null)
@ -52,11 +54,13 @@ namespace Ryujinx.Core.OsHle
continue;
}
Logging.Info($"Loading {Path.GetFileNameWithoutExtension(File)}...");
Logging.Info(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}...");
using (FileStream Input = new FileStream(File, FileMode.Open))
{
Nso Program = new Nso(Input);
string Name = Path.GetFileNameWithoutExtension(File);
Nso Program = new Nso(Input, Name);
MainProcess.LoadProgram(Program);
}
@ -78,19 +82,23 @@ namespace Ryujinx.Core.OsHle
{
bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
string Name = Path.GetFileNameWithoutExtension(FileName);
Process MainProcess = MakeProcess();
using (FileStream Input = new FileStream(FileName, FileMode.Open))
{
MainProcess.LoadProgram(IsNro
? (IExecutable)new Nro(Input)
: (IExecutable)new Nso(Input));
? (IExecutable)new Nro(Input, Name)
: (IExecutable)new Nso(Input, Name));
}
MainProcess.SetEmptyArgs();
MainProcess.Run(IsNro);
}
public void SignalVsync() => VsyncEvent.WaitEvent.Set();
private Process MakeProcess()
{
Process Process;
@ -104,21 +112,28 @@ namespace Ryujinx.Core.OsHle
ProcessId++;
}
Process = new Process(Ns, ProcessId);
Process = new Process(Ns, Scheduler, ProcessId);
Processes.TryAdd(ProcessId, Process);
}
InitializeProcess(Process);
return Process;
}
private void InitializeProcess(Process Process)
{
Process.AppletState.SetFocus(true);
}
internal void ExitProcess(int ProcessId)
{
if (Processes.TryGetValue(ProcessId, out Process Process) && Process.NeedsHbAbi)
{
string NextNro = Homebrew.ReadHbAbiNextLoadPath(Process.Memory, Process.HbAbiDataPosition);
Logging.Info($"HbAbi NextLoadPath {NextNro}");
Logging.Info(LogClass.Loader, $"HbAbi NextLoadPath {NextNro}");
if (NextNro == string.Empty)
{
@ -131,11 +146,6 @@ namespace Ryujinx.Core.OsHle
if (File.Exists(NextNro))
{
//TODO: Those dictionaries shouldn't even exist,
//the Mutex and CondVar helper classes should be static.
Mutexes.Clear();
CondVars.Clear();
LoadProgram(NextNro);
}
}
@ -171,6 +181,10 @@ namespace Ryujinx.Core.OsHle
Process.StopAllThreadsAsync();
Process.Dispose();
}
VsyncEvent.Dispose();
Scheduler.Dispose();
}
}
}

View file

@ -1,11 +1,10 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle
{
class IdDictionary : IEnumerable<object>
class IdDictionary
{
private ConcurrentDictionary<int, object> Objs;
@ -16,6 +15,11 @@ namespace Ryujinx.Core.OsHle
Objs = new ConcurrentDictionary<int, object>();
}
public bool Add(int Id, object Data)
{
return Objs.TryAdd(Id, Data);
}
public int Add(object Data)
{
if (Objs.TryAdd(FreeIdHint, Data))
@ -39,18 +43,6 @@ namespace Ryujinx.Core.OsHle
throw new InvalidOperationException();
}
public bool ReplaceData(int Id, object Data)
{
if (Objs.ContainsKey(Id))
{
Objs[Id] = Data;
return true;
}
return false;
}
public object GetData(int Id)
{
if (Objs.TryGetValue(Id, out object Data))
@ -71,31 +63,25 @@ namespace Ryujinx.Core.OsHle
return default(T);
}
public bool Delete(int Id)
public object Delete(int Id)
{
if (Objs.TryRemove(Id, out object Obj))
{
if (Obj is IDisposable DisposableObj)
{
DisposableObj.Dispose();
}
FreeIdHint = Id;
return true;
return Obj;
}
return false;
return null;
}
IEnumerator<object> IEnumerable<object>.GetEnumerator()
public ICollection<object> Clear()
{
return Objs.Values.GetEnumerator();
}
ICollection<object> Values = Objs.Values;
IEnumerator IEnumerable.GetEnumerator()
{
return Objs.Values.GetEnumerator();
Objs.Clear();
return Values;
}
}
}

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 Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.IpcServices;
using System;
using System.IO;
@ -8,20 +7,15 @@ namespace Ryujinx.Core.OsHle.Ipc
{
static class IpcHandler
{
private const long SfciMagic = 'S' << 0 | 'F' << 8 | 'C' << 16 | 'I' << 24;
private const long SfcoMagic = 'S' << 0 | 'F' << 8 | 'C' << 16 | 'O' << 24;
public static void IpcCall(
Switch Ns,
Process Process,
AMemory Memory,
HSession Session,
KSession Session,
IpcMessage Request,
int ThreadId,
long CmdPtr,
int HndId)
long CmdPtr)
{
IpcMessage Response = new IpcMessage(Request.IsDomain && Request.Type == IpcMessageType.Request);
IpcMessage Response = new IpcMessage();
using (MemoryStream Raw = new MemoryStream(Request.RawData))
{
@ -29,94 +23,25 @@ namespace Ryujinx.Core.OsHle.Ipc
if (Request.Type == IpcMessageType.Request)
{
string ServiceName = Session.Service.GetType().Name;
Response.Type = IpcMessageType.Response;
ServiceProcessRequest ProcReq = null;
bool IgnoreNullPR = false;
string DbgServiceName = string.Empty;
if (Session is HDomain Dom)
using (MemoryStream ResMS = new MemoryStream())
{
if (Request.DomCmd == IpcDomCmd.SendMsg)
{
long Magic = ReqReader.ReadInt64();
int CmdId = (int)ReqReader.ReadInt64();
BinaryWriter ResWriter = new BinaryWriter(ResMS);
object Obj = Dom.GetObject(Request.DomObjId);
ServiceCtx Context = new ServiceCtx(
Ns,
Process,
Memory,
Session,
Request,
Response,
ReqReader,
ResWriter);
if (Obj is HDomain)
{
Session.Service.Commands.TryGetValue(CmdId, out ProcReq);
Session.Service.CallMethod(Context);
DbgServiceName = $"{ProcReq?.Method.Name ?? CmdId.ToString()}";
}
else if (Obj != null)
{
((IIpcService)Obj).Commands.TryGetValue(CmdId, out ProcReq);
DbgServiceName = $"{Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}";
}
}
else if (Request.DomCmd == IpcDomCmd.DeleteObj)
{
Dom.Delete(Request.DomObjId);
Response = FillResponse(Response, 0);
IgnoreNullPR = true;
}
}
else
{
long Magic = ReqReader.ReadInt64();
int CmdId = (int)ReqReader.ReadInt64();
if (Session is HSessionObj)
{
object Obj = ((HSessionObj)Session).Obj;
((IIpcService)Obj).Commands.TryGetValue(CmdId, out ProcReq);
DbgServiceName = $"{Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}";
}
else
{
Session.Service.Commands.TryGetValue(CmdId, out ProcReq);
DbgServiceName = $"{ProcReq?.Method.Name ?? CmdId.ToString()}";
}
}
DbgServiceName = $"Tid {ThreadId} {ServiceName} {DbgServiceName}";
Logging.Debug($"IpcMessage: {DbgServiceName}");
if (ProcReq != null)
{
using (MemoryStream ResMS = new MemoryStream())
{
BinaryWriter ResWriter = new BinaryWriter(ResMS);
ServiceCtx Context = new ServiceCtx(
Ns,
Process,
Memory,
Session,
Request,
Response,
ReqReader,
ResWriter);
long Result = ProcReq(Context);
Response = FillResponse(Response, Result, ResMS.ToArray());
}
}
else if (!IgnoreNullPR)
{
throw new NotImplementedException(DbgServiceName);
Response.RawData = ResMS.ToArray();
}
}
else if (Request.Type == IpcMessageType.Control)
@ -128,11 +53,7 @@ namespace Ryujinx.Core.OsHle.Ipc
{
case 0:
{
HDomain Dom = new HDomain(Session);
Process.HandleTable.ReplaceData(HndId, Dom);
Request = FillResponse(Response, 0, Dom.Add(Dom));
Request = FillResponse(Response, 0, Session.Service.ConvertToDomain());
break;
}
@ -198,7 +119,7 @@ namespace Ryujinx.Core.OsHle.Ipc
{
BinaryWriter Writer = new BinaryWriter(MS);
Writer.Write(SfcoMagic);
Writer.Write(IpcMagic.Sfco);
Writer.Write(Result);
if (Data != null)

View file

@ -125,8 +125,7 @@ namespace Ryujinx.Core.OsHle.Ipc
Reader.ReadInt64(); //Padding
IpcMessage += Environment.NewLine + $" Domain:" + Environment.NewLine +
$" DomCmd: {Enum.GetName(typeof(IpcDomCmd), DomCmd)}" + Environment.NewLine +
IpcMessage += Environment.NewLine + $" Domain:" + Environment.NewLine + Environment.NewLine +
$" DomObjId: {DomObjId.ToString()}" + Environment.NewLine;
}

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 bool IsDomain { get; private set; }
public IpcDomCmd DomCmd { get; private set; }
public int DomObjId { get; private set; }
public byte[] RawData { get; set; }
public IpcMessage()
@ -34,27 +30,18 @@ namespace Ryujinx.Core.OsHle.Ipc
ResponseObjIds = new List<int>();
}
public IpcMessage(bool Domain) : this()
public IpcMessage(byte[] Data, long CmdPtr) : this()
{
IsDomain = Domain;
}
public IpcMessage(byte[] Data, long CmdPtr, bool Domain) : this()
{
Logging.Ipc(Data, CmdPtr, Domain);
using (MemoryStream MS = new MemoryStream(Data))
{
BinaryReader Reader = new BinaryReader(MS);
Initialize(Reader, CmdPtr, Domain);
Initialize(Reader, CmdPtr);
}
}
private void Initialize(BinaryReader Reader, long CmdPtr, bool Domain)
private void Initialize(BinaryReader Reader, long CmdPtr)
{
IsDomain = Domain;
int Word0 = Reader.ReadInt32();
int Word1 = Reader.ReadInt32();
@ -110,19 +97,6 @@ namespace Ryujinx.Core.OsHle.Ipc
RecvListCount = 0;
}
if (Domain && Type == IpcMessageType.Request)
{
int DomWord0 = Reader.ReadInt32();
DomCmd = (IpcDomCmd)(DomWord0 & 0xff);
RawDataSize = (DomWord0 >> 16) & 0xffff;
DomObjId = Reader.ReadInt32();
Reader.ReadInt64(); //Padding
}
RawData = Reader.ReadBytes(RawDataSize);
Reader.BaseStream.Seek(RecvListPos, SeekOrigin.Begin);
@ -165,9 +139,7 @@ namespace Ryujinx.Core.OsHle.Ipc
//This is the weirdest padding I've seen so far...
int Pad1 = 0x10 - Pad0;
DataLength = (DataLength + Pad0 + Pad1 + (IsDomain ? 0x10 : 0)) / 4;
DataLength += ResponseObjIds.Count;
DataLength = (DataLength + Pad0 + Pad1) / 4;
Word1 = DataLength & 0x3ff;
@ -182,23 +154,11 @@ namespace Ryujinx.Core.OsHle.Ipc
MS.Seek(Pad0, SeekOrigin.Current);
if (IsDomain)
{
Writer.Write(ResponseObjIds.Count);
Writer.Write(0);
Writer.Write(0L);
}
if (RawData != null)
{
Writer.Write(RawData);
}
foreach (int Id in ResponseObjIds)
{
Writer.Write(Id);
}
Writer.Write(new byte[Pad1]);
return MS.ToArray();

View file

@ -6,7 +6,7 @@ namespace Ryujinx.Core.OsHle.Ipc
{
public long Position { get; private set; }
public int Index { get; private set; }
public short Size { get; private set; }
public long Size { get; private set; }
public IpcPtrBuffDesc(BinaryReader Reader)
{
@ -20,7 +20,7 @@ namespace Ryujinx.Core.OsHle.Ipc
Index = ((int)Word0 >> 0) & 0x03f;
Index |= ((int)Word0 >> 3) & 0x1c0;
Size = (short)(Word0 >> 16);
Size = (ushort)(Word0 >> 16);
}
}
}

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.Collections.Generic;
namespace Ryujinx.Core.OsHle.Svc
namespace Ryujinx.Core.OsHle.Kernel
{
partial class SvcHandler : IDisposable
{
@ -17,6 +17,8 @@ namespace Ryujinx.Core.OsHle.Svc
private Process Process;
private AMemory Memory;
private object CondVarLock;
private HashSet<(HSharedMem, long)> MappedSharedMems;
private ulong CurrentHeapSize;
@ -40,6 +42,7 @@ namespace Ryujinx.Core.OsHle.Svc
{ 0x0c, SvcGetThreadPriority },
{ 0x0d, SvcSetThreadPriority },
{ 0x0f, SvcSetThreadCoreMask },
{ 0x10, SvcGetCurrentProcessorNumber },
{ 0x12, SvcClearEvent },
{ 0x13, SvcMapSharedMemory },
{ 0x14, SvcUnmapSharedMemory },
@ -58,13 +61,16 @@ namespace Ryujinx.Core.OsHle.Svc
{ 0x25, SvcGetThreadId },
{ 0x26, SvcBreak },
{ 0x27, SvcOutputDebugString },
{ 0x29, SvcGetInfo }
{ 0x29, SvcGetInfo },
{ 0x32, SvcSetThreadActivity }
};
this.Ns = Ns;
this.Process = Process;
this.Memory = Process.Memory;
CondVarLock = new object();
MappedSharedMems = new HashSet<(HSharedMem, long)>();
}
@ -79,14 +85,16 @@ namespace Ryujinx.Core.OsHle.Svc
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
{
Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} called.");
Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} called.");
Func(ThreadState);
Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended.");
Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended.");
}
else
{
Process.PrintStackTrace(ThreadState);
throw new NotImplementedException(e.Id.ToString("x4"));
}
}

View file

@ -4,7 +4,7 @@ using Ryujinx.Core.OsHle.Handles;
using static Ryujinx.Core.OsHle.ErrorCode;
namespace Ryujinx.Core.OsHle.Svc
namespace Ryujinx.Core.OsHle.Kernel
{
partial class SvcHandler
{
@ -57,7 +57,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to map Memory at invalid src address {Src:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to map Memory at invalid src address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -66,7 +66,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidMapPosition(Dst))
{
Logging.Warn($"Tried to map Memory at invalid dst address {Dst:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to map Memory at invalid dst address {Dst:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -92,7 +92,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to unmap Memory at invalid src address {Src:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to unmap Memory at invalid src address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -101,7 +101,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidMapPosition(Dst))
{
Logging.Warn($"Tried to unmap Memory at invalid dst address {Dst:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to unmap Memory at invalid dst address {Dst:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -158,7 +158,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to map SharedMemory at invalid address {Src:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to map SharedMemory at invalid address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -196,7 +196,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to unmap SharedMemory at invalid address {Src:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to unmap SharedMemory at invalid address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -230,7 +230,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to create TransferMemory at invalid address {Src:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to create TransferMemory at invalid address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);

View file

@ -3,12 +3,13 @@ using ChocolArm64.State;
using Ryujinx.Core.OsHle.Exceptions;
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.Services;
using System;
using System.Threading;
using static Ryujinx.Core.OsHle.ErrorCode;
namespace Ryujinx.Core.OsHle.Svc
namespace Ryujinx.Core.OsHle.Kernel
{
partial class SvcHandler
{
@ -34,7 +35,28 @@ namespace Ryujinx.Core.OsHle.Svc
{
int Handle = (int)ThreadState.X0;
Process.HandleTable.CloseHandle(Handle);
object Obj = Process.HandleTable.CloseHandle(Handle);
if (Obj == null)
{
Logging.Warn(LogClass.KernelSvc, $"Tried to CloseHandle on invalid handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
return;
}
if (Obj is KSession Session)
{
Session.Dispose();
}
else if (Obj is HTransferMem TMem)
{
TMem.Memory.Manager.Reprotect(
TMem.Position,
TMem.Size,
TMem.Perm);
}
ThreadState.X0 = 0;
}
@ -43,25 +65,78 @@ namespace Ryujinx.Core.OsHle.Svc
{
int Handle = (int)ThreadState.X0;
//TODO: Implement events.
KEvent Event = Process.HandleTable.GetData<KEvent>(Handle);
ThreadState.X0 = 0;
if (Event != null)
{
Event.WaitEvent.Reset();
ThreadState.X0 = 0;
}
else
{
Logging.Warn(LogClass.KernelSvc, $"Tried to ResetSignal on invalid event handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
}
private void SvcWaitSynchronization(AThreadState ThreadState)
{
long HandlesPtr = (long)ThreadState.X0;
int HandlesCount = (int)ThreadState.X2;
long Timeout = (long)ThreadState.X3;
long HandlesPtr = (long)ThreadState.X1;
int HandlesCount = (int)ThreadState.X2;
ulong Timeout = ThreadState.X3;
//TODO: Implement events.
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
WaitHandle[] Handles = new WaitHandle[HandlesCount];
for (int Index = 0; Index < HandlesCount; Index++)
{
int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
KSynchronizationObject SyncObj = Process.HandleTable.GetData<KSynchronizationObject>(Handle);
if (SyncObj == null)
{
Logging.Warn(LogClass.KernelSvc, $"Tried to WaitSynchronization on invalid handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
return;
}
Handles[Index] = SyncObj.WaitEvent;
}
Process.Scheduler.Suspend(CurrThread.ProcessorId);
int HandleIndex;
ulong Result = 0;
if (Timeout != ulong.MaxValue)
{
HandleIndex = WaitHandle.WaitAny(Handles, NsTimeConverter.GetTimeMs(Timeout));
if (HandleIndex == WaitHandle.WaitTimeout)
{
Result = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
}
}
else
{
HandleIndex = WaitHandle.WaitAny(Handles);
}
Process.Scheduler.Resume(CurrThread);
ThreadState.X0 = 0;
ThreadState.X0 = Result;
if (Result == 0)
{
ThreadState.X1 = (ulong)HandleIndex;
}
}
private void SvcGetSystemTick(AThreadState ThreadState)
@ -78,8 +153,7 @@ namespace Ryujinx.Core.OsHle.Svc
//TODO: Validate that app has perms to access the service, and that the service
//actually exists, return error codes otherwise.
HSession Session = new HSession(Process.Services.GetService(Name));
KSession Session = new KSession(ServiceFactory.MakeService(Name), Name);
ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session);
@ -89,65 +163,46 @@ namespace Ryujinx.Core.OsHle.Svc
private void SvcSendSyncRequest(AThreadState ThreadState)
{
SendSyncRequest(ThreadState, false);
SendSyncRequest(ThreadState, ThreadState.Tpidr, 0x100, (int)ThreadState.X0);
}
private void SvcSendSyncRequestWithUserBuffer(AThreadState ThreadState)
{
SendSyncRequest(ThreadState, true);
SendSyncRequest(
ThreadState,
(long)ThreadState.X0,
(long)ThreadState.X1,
(int)ThreadState.X2);
}
private void SendSyncRequest(AThreadState ThreadState, bool UserBuffer)
private void SendSyncRequest(AThreadState ThreadState, long CmdPtr, long Size, int Handle)
{
long CmdPtr = ThreadState.Tpidr;
long Size = 0x100;
int Handle = 0;
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
if (UserBuffer)
{
CmdPtr = (long)ThreadState.X0;
Size = (long)ThreadState.X1;
Handle = (int)ThreadState.X2;
}
else
{
Handle = (int)ThreadState.X0;
}
byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, Size);
HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
Process.Scheduler.Suspend(CurrThread.ProcessorId);
byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
HSession Session = Process.HandleTable.GetData<HSession>(Handle);
IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr, Session is HDomain);
KSession Session = Process.HandleTable.GetData<KSession>(Handle);
if (Session != null)
{
IpcHandler.IpcCall(
Ns,
Process,
Memory,
Session,
Cmd,
ThreadState.ThreadId,
CmdPtr,
Handle);
Process.Scheduler.Suspend(CurrThread.ProcessorId);
byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr);
IpcHandler.IpcCall(Ns, Process, Memory, Session, Cmd, CmdPtr);
Thread.Yield();
Process.Scheduler.Resume(CurrThread);
ThreadState.X0 = 0;
}
else
{
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidIpcReq);
Logging.Warn(LogClass.KernelSvc, $"Tried to SendSyncRequest on invalid session handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
Thread.Yield();
Process.Scheduler.Resume(CurrThread);
}
private void SvcBreak(AThreadState ThreadState)
@ -156,6 +211,8 @@ namespace Ryujinx.Core.OsHle.Svc
long Unknown = (long)ThreadState.X1;
long Info = (long)ThreadState.X2;
Process.PrintStackTrace(ThreadState);
throw new GuestBrokeExecutionException();
}
@ -164,9 +221,9 @@ namespace Ryujinx.Core.OsHle.Svc
long Position = (long)ThreadState.X0;
long Size = (long)ThreadState.X1;
string Str = AMemoryHelper.ReadAsciiString(Memory, Position, (int)Size);
string Str = AMemoryHelper.ReadAsciiString(Memory, Position, Size);
Logging.Info($"SvcOutputDebugString: {Str}");
Logging.Info(LogClass.KernelSvc, Str);
ThreadState.X0 = 0;
}
@ -180,7 +237,8 @@ namespace Ryujinx.Core.OsHle.Svc
//Fail for info not available on older Kernel versions.
if (InfoType == 18 ||
InfoType == 19)
InfoType == 19 ||
InfoType == 20)
{
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo);
@ -241,7 +299,10 @@ namespace Ryujinx.Core.OsHle.Svc
ThreadState.X1 = MemoryRegions.MapRegionSize;
break;
default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}");
default:
Process.PrintStackTrace(ThreadState);
throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle:x8} {InfoId}");
}
ThreadState.X0 = 0;

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 TlsPagesSize = 0x4000;
public const long TlsPagesSize = 0x20000;
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.Events;
using ChocolArm64.Memory;
using ChocolArm64.State;
using Ryujinx.Core.Loaders;
using Ryujinx.Core.Loaders.Executables;
using Ryujinx.Core.OsHle.Exceptions;
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Svc;
using Ryujinx.Core.OsHle.Kernel;
using Ryujinx.Core.OsHle.Services.Nv;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
namespace Ryujinx.Core.OsHle
{
class Process : IDisposable
{
private const int TlsSize = 0x200;
private const int TotalTlsSlots = 32;
private const int TlsSize = 0x200;
private const int TotalTlsSlots = (int)MemoryRegions.TlsPagesSize / TlsSize;
private const int TickFreq = 19_200_000;
@ -31,21 +35,25 @@ namespace Ryujinx.Core.OsHle
public AMemory Memory { get; private set; }
public ServiceMgr Services { get; private set; }
public KProcessScheduler Scheduler { get; private set; }
public KThread ThreadArbiterList { get; set; }
public KProcessHandleTable HandleTable { get; private set; }
public AppletStateMgr AppletState { get; private set; }
private SvcHandler SvcHandler;
private ConcurrentDictionary<int, AThread> TlsSlots;
private ConcurrentDictionary<long, HThread> ThreadsByTpidr;
private ConcurrentDictionary<long, KThread> Threads;
private KThread MainThread;
private List<Executable> Executables;
private HThread MainThread;
private Dictionary<long, string> SymbolTable;
private long ImageBase;
@ -53,24 +61,23 @@ namespace Ryujinx.Core.OsHle
private bool Disposed;
public Process(Switch Ns, int ProcessId)
public Process(Switch Ns, KProcessScheduler Scheduler, int ProcessId)
{
this.Ns = Ns;
this.Scheduler = Scheduler;
this.ProcessId = ProcessId;
Memory = new AMemory();
Services = new ServiceMgr();
HandleTable = new KProcessHandleTable();
Scheduler = new KProcessScheduler();
AppletState = new AppletStateMgr();
SvcHandler = new SvcHandler(Ns, this);
TlsSlots = new ConcurrentDictionary<int, AThread>();
ThreadsByTpidr = new ConcurrentDictionary<long, HThread>();
Threads = new ConcurrentDictionary<long, KThread>();
Executables = new List<Executable>();
@ -89,7 +96,7 @@ namespace Ryujinx.Core.OsHle
throw new ObjectDisposedException(nameof(Process));
}
Logging.Info($"Image base at 0x{ImageBase:x16}.");
Logging.Info(LogClass.Loader, $"Image base at 0x{ImageBase:x16}.");
Executable Executable = new Executable(Program, Memory, ImageBase);
@ -118,6 +125,8 @@ namespace Ryujinx.Core.OsHle
return false;
}
MakeSymbolTable();
MapRWMemRegion(
MemoryRegions.MainStackAddress,
MemoryRegions.MainStackSize,
@ -125,14 +134,14 @@ namespace Ryujinx.Core.OsHle
long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize;
int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 0, 0);
int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 44, 0);
if (Handle == -1)
{
return false;
}
MainThread = HandleTable.GetData<HThread>(Handle);
MainThread = HandleTable.GetData<KThread>(Handle);
if (NeedsHbAbi)
{
@ -184,30 +193,32 @@ namespace Ryujinx.Core.OsHle
throw new ObjectDisposedException(nameof(Process));
}
AThread Thread = new AThread(GetTranslator(), Memory, EntryPoint);
AThread CpuThread = new AThread(GetTranslator(), Memory, EntryPoint);
HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority);
KThread Thread = new KThread(CpuThread, ProcessorId, Priority);
int Handle = HandleTable.OpenHandle(ThreadHnd);
int Handle = HandleTable.OpenHandle(Thread);
int ThreadId = GetFreeTlsSlot(Thread);
int ThreadId = GetFreeTlsSlot(CpuThread);
long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize;
Thread.ThreadState.Break += BreakHandler;
Thread.ThreadState.SvcCall += SvcHandler.SvcCall;
Thread.ThreadState.Undefined += UndefinedHandler;
Thread.ThreadState.ProcessId = ProcessId;
Thread.ThreadState.ThreadId = ThreadId;
Thread.ThreadState.CntfrqEl0 = TickFreq;
Thread.ThreadState.Tpidr = Tpidr;
Thread.ThreadState.X0 = (ulong)ArgsPtr;
Thread.ThreadState.X1 = (ulong)Handle;
Thread.ThreadState.X31 = (ulong)StackTop;
CpuThread.ThreadState.ProcessId = ProcessId;
CpuThread.ThreadState.ThreadId = ThreadId;
CpuThread.ThreadState.CntfrqEl0 = TickFreq;
CpuThread.ThreadState.Tpidr = Tpidr;
Thread.WorkFinished += ThreadFinished;
CpuThread.ThreadState.X0 = (ulong)ArgsPtr;
CpuThread.ThreadState.X1 = (ulong)Handle;
CpuThread.ThreadState.X31 = (ulong)StackTop;
ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, ThreadHnd);
CpuThread.ThreadState.Break += BreakHandler;
CpuThread.ThreadState.SvcCall += SvcHandler.SvcCall;
CpuThread.ThreadState.Undefined += UndefinedHandler;
CpuThread.WorkFinished += ThreadFinished;
Threads.TryAdd(CpuThread.ThreadState.Tpidr, Thread);
return Handle;
}
@ -222,20 +233,23 @@ namespace Ryujinx.Core.OsHle
throw new UndefinedInstructionException(e.Position, e.RawOpCode);
}
private void MakeSymbolTable()
{
SymbolTable = new Dictionary<long, string>();
foreach (Executable Exe in Executables)
{
foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
{
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
}
}
}
private ATranslator GetTranslator()
{
if (Translator == null)
{
Dictionary<long, string> SymbolTable = new Dictionary<long, string>();
foreach (Executable Exe in Executables)
{
foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
{
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
}
}
Translator = new ATranslator(SymbolTable);
Translator.CpuTrace += CpuTraceHandler;
@ -244,6 +258,16 @@ namespace Ryujinx.Core.OsHle
return Translator;
}
public void EnableCpuTracing()
{
Translator.EnableCpuTrace = true;
}
public void DisableCpuTracing()
{
Translator.EnableCpuTrace = false;
}
private void CpuTraceHandler(object sender, ACpuTraceEventArgs e)
{
string NsoName = string.Empty;
@ -258,17 +282,47 @@ namespace Ryujinx.Core.OsHle
}
}
Logging.Trace($"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
Logging.Trace(LogClass.CPU, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
}
public void EnableCpuTracing()
public void PrintStackTrace(AThreadState ThreadState)
{
Translator.EnableCpuTrace = true;
long[] Positions = ThreadState.GetCallStack();
StringBuilder Trace = new StringBuilder();
Trace.AppendLine("Guest stack trace:");
foreach (long Position in Positions)
{
if (!SymbolTable.TryGetValue(Position, out string SubName))
{
SubName = $"Sub{Position:x16}";
}
Trace.AppendLine(" " + SubName + " (" + GetNsoNameAndAddress(Position) + ")");
}
Logging.Info(LogClass.CPU, Trace.ToString());
}
public void DisableCpuTracing()
private string GetNsoNameAndAddress(long Position)
{
Translator.EnableCpuTrace = false;
string Name = string.Empty;
for (int Index = Executables.Count - 1; Index >= 0; Index--)
{
if (Position >= Executables[Index].ImageBase)
{
long Offset = Position - Executables[Index].ImageBase;
Name = $"{Executables[Index].Name}:{Offset:x8}";
break;
}
}
return Name;
}
private int GetFreeTlsSlot(AThread Thread)
@ -288,9 +342,15 @@ namespace Ryujinx.Core.OsHle
{
if (sender is AThread Thread)
{
Logging.Info($"Thread {Thread.ThreadId} exiting...");
Logging.Info(LogClass.KernelScheduler, $"Thread {Thread.ThreadId} exiting...");
TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
KThread KernelThread = GetThread(Thread.ThreadState.Tpidr);
Scheduler.RemoveThread(KernelThread);
KernelThread.WaitEvent.Set();
}
if (TlsSlots.Count == 0)
@ -300,7 +360,7 @@ namespace Ryujinx.Core.OsHle
Dispose();
}
Logging.Info($"No threads running, now exiting Process {ProcessId}...");
Logging.Info(LogClass.KernelScheduler, $"No threads running, now exiting Process {ProcessId}...");
Ns.Os.ExitProcess(ProcessId);
}
@ -311,11 +371,11 @@ namespace Ryujinx.Core.OsHle
return (int)((Position - MemoryRegions.TlsPagesAddress) / TlsSize);
}
public HThread GetThread(long Tpidr)
public KThread GetThread(long Tpidr)
{
if (!ThreadsByTpidr.TryGetValue(Tpidr, out HThread Thread))
if (!Threads.TryGetValue(Tpidr, out KThread Thread))
{
Logging.Error($"Thread with TPIDR 0x{Tpidr:x16} not found!");
Logging.Error(LogClass.KernelScheduler, $"Thread with TPIDR 0x{Tpidr:x16} not found!");
}
return Thread;
@ -338,20 +398,34 @@ namespace Ryujinx.Core.OsHle
{
ShouldDispose = true;
Logging.Info($"Process {ProcessId} waiting all threads terminate...");
Logging.Info(LogClass.KernelScheduler, $"Process {ProcessId} waiting all threads terminate...");
return;
}
Disposed = true;
Services.Dispose();
HandleTable.Dispose();
Scheduler.Dispose();
foreach (object Obj in HandleTable.Clear())
{
if (Obj is KSession Session)
{
Session.Dispose();
}
}
INvDrvServices.Fds.DeleteProcess(this);
INvDrvServices.NvMaps .DeleteProcess(this);
INvDrvServices.NvMapsById.DeleteProcess(this);
INvDrvServices.NvMapsFb .DeleteProcess(this);
AppletState.Dispose();
SvcHandler.Dispose();
Memory.Dispose();
Logging.Info($"Process {ProcessId} exiting...");
Logging.Info(LogClass.KernelScheduler, $"Process {ProcessId} exiting...");
}
}
}

View file

@ -10,7 +10,7 @@ namespace Ryujinx.Core.OsHle
public Switch Ns { get; private set; }
public Process Process { get; private set; }
public AMemory Memory { get; private set; }
public HSession Session { get; private set; }
public KSession Session { get; private set; }
public IpcMessage Request { get; private set; }
public IpcMessage Response { get; private set; }
public BinaryReader RequestData { get; private set; }
@ -20,7 +20,7 @@ namespace Ryujinx.Core.OsHle
Switch Ns,
Process Process,
AMemory Memory,
HSession Session,
KSession Session,
IpcMessage Request,
IpcMessage Response,
BinaryReader RequestData,

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 System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Acc
namespace Ryujinx.Core.OsHle.Services.Acc
{
class ServiceAcc : IIpcService
class IAccountServiceForApplication : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceAcc()
public IAccountServiceForApplication()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, GetUserCount },
{ 3, ListOpenUsers },
{ 5, GetProfile },
{ 100, InitializeApplicationInfo },
@ -22,8 +21,19 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
};
}
public long GetUserCount(ServiceCtx Context)
{
Context.ResponseData.Write(0);
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
return 0;
}
public long ListOpenUsers(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
return 0;
}
@ -36,6 +46,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
public long InitializeApplicationInfo(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
return 0;
}

View file

@ -1,13 +1,13 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Acc
namespace Ryujinx.Core.OsHle.Services.Acc
{
class IManagerForApplication : IIpcService
class IManagerForApplication : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IManagerForApplication()
{
@ -20,11 +20,15 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
public long CheckAvailability(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
return 0;
}
public long GetAccountId(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAcc, "AccountId = 0xcafeL");
Context.ResponseData.Write(0xcafeL);
return 0;

View file

@ -1,13 +1,13 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Acc
namespace Ryujinx.Core.OsHle.Services.Acc
{
class IProfile : IIpcService
class IProfile : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IProfile()
{
@ -19,6 +19,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Acc
public long GetBase(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
Context.ResponseData.Write(0L);
Context.ResponseData.Write(0L);
Context.ResponseData.Write(0L);

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.IO;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Am
namespace Ryujinx.Core.OsHle.Services.Am
{
class IApplicationFunctions : IIpcService
class IApplicationFunctions : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IApplicationFunctions()
{
@ -20,6 +18,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
{ 20, EnsureSaveData },
{ 21, GetDesiredLanguage },
{ 22, SetTerminateResult },
{ 23, GetDisplayVersion },
{ 40, NotifyRunning }
};
}
@ -39,6 +38,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
long UIdLow = Context.RequestData.ReadInt64();
long UIdHigh = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceAm, $"UidLow = {UIdLow}, UidHigh = {UIdHigh}");
Context.ResponseData.Write(0L);
return 0;
@ -46,6 +47,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
public long GetDesiredLanguage(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAm, "LanguageId = 1");
//This is an enumerator where each number is a differnet language.
//0 is Japanese and 1 is English, need to figure out the other codes.
Context.ResponseData.Write(1L);
@ -60,7 +63,16 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
int Module = ErrorCode & 0xFF;
int Description = (ErrorCode >> 9) & 0xFFF;
Logging.Info($"({(ErrorModule)Module}){2000 + Module}-{Description}");
Logging.Info(LogClass.ServiceAm, $"({(ErrorModule)Module}){2000 + Module}-{Description}");
return 0;
}
public long GetDisplayVersion(ServiceCtx Context)
{
//FIXME: Need to check correct version on a switch.
Context.ResponseData.Write(1L);
Context.ResponseData.Write(0L);
return 0;
}

View file

@ -1,15 +1,13 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Am
namespace Ryujinx.Core.OsHle.Services.Am
{
class IApplicationProxy : IIpcService
class IApplicationProxy : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IApplicationProxy()
{

View file

@ -1,17 +1,15 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Am
namespace Ryujinx.Core.OsHle.Services.Am
{
class ServiceAppletOE : IIpcService
class IApplicationProxyService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceAppletOE()
public IApplicationProxyService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{

View file

@ -1,20 +1,71 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
namespace Ryujinx.Core.OsHle.Services.Am
{
class IAudioController : IIpcService
class IAudioController : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IAudioController()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//...
{ 0, SetExpectedMasterVolume },
{ 1, GetMainAppletExpectedMasterVolume },
{ 2, GetLibraryAppletExpectedMasterVolume },
{ 3, ChangeMainAppletMasterVolume },
{ 4, SetTransparentVolumeRate }
};
}
public long SetExpectedMasterVolume(ServiceCtx Context)
{
float AppletVolume = Context.RequestData.ReadSingle();
float LibraryAppletVolume = Context.RequestData.ReadSingle();
Logging.Stub(LogClass.ServiceAm, "Stubbed");
return 0;
}
public long GetMainAppletExpectedMasterVolume(ServiceCtx Context)
{
Context.ResponseData.Write(1f);
Logging.Stub(LogClass.ServiceAm, "Stubbed");
return 0;
}
public long GetLibraryAppletExpectedMasterVolume(ServiceCtx Context)
{
Context.ResponseData.Write(1f);
Logging.Stub(LogClass.ServiceAm, "Stubbed");
return 0;
}
public long ChangeMainAppletMasterVolume(ServiceCtx Context)
{
float Unknown0 = Context.RequestData.ReadSingle();
long Unknown1 = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceAm, "Stubbed");
return 0;
}
public long SetTransparentVolumeRate(ServiceCtx Context)
{
float Unknown0 = Context.RequestData.ReadSingle();
Logging.Stub(LogClass.ServiceAm, "Stubbed");
return 0;
}
}
}

View file

@ -1,13 +1,16 @@
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
using static Ryujinx.Core.OsHle.ErrorCode;
namespace Ryujinx.Core.OsHle.Services.Am
{
class ICommonStateGetter : IIpcService
class ICommonStateGetter : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ICommonStateGetter()
{
@ -17,37 +20,32 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
{ 1, ReceiveMessage },
{ 5, GetOperationMode },
{ 6, GetPerformanceMode },
{ 9, GetCurrentFocusState },
{ 8, GetBootMode },
{ 9, GetCurrentFocusState }
};
}
private enum FocusState
{
InFocus = 1,
OutOfFocus = 2
}
private enum OperationMode
{
Handheld = 0,
Docked = 1
}
public long GetEventHandle(ServiceCtx Context)
{
Context.ResponseData.Write(0L);
KEvent Event = Context.Process.AppletState.MessageEvent;
int Handle = Context.Process.HandleTable.OpenHandle(Event);
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
return 0;
}
public long ReceiveMessage(ServiceCtx Context)
{
//Program expects 0xF at 0x17ae70 on puyo sdk,
//otherwise runs on a infinite loop until it reads said value.
//What it means is still unknown.
Context.ResponseData.Write(0xfL);
if (!Context.Process.AppletState.TryDequeueMessage(out MessageInfo Message))
{
return MakeError(ErrorModule.Am, AmErr.NoMessages);
}
return 0; //0x680;
Context.ResponseData.Write((int)Message);
return 0;
}
public long GetOperationMode(ServiceCtx Context)
@ -59,14 +57,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
public long GetPerformanceMode(ServiceCtx Context)
{
Context.ResponseData.Write((byte)0);
Context.ResponseData.Write((byte)Apm.PerformanceMode.Handheld);
return 0;
}
public long GetBootMode(ServiceCtx Context)
{
Context.ResponseData.Write((byte)0); //Unknown value.
Logging.Stub(LogClass.ServiceAm, "Stubbed");
return 0;
}
public long GetCurrentFocusState(ServiceCtx Context)
{
Context.ResponseData.Write((byte)FocusState.InFocus);
Context.ResponseData.Write((byte)Context.Process.AppletState.FocusState);
return 0;
}

View file

@ -1,13 +1,13 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
namespace Ryujinx.Core.OsHle.Services.Am
{
class IDebugFunctions : IIpcService
class IDebugFunctions : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IDebugFunctions()
{

View file

@ -1,13 +1,13 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
namespace Ryujinx.Core.OsHle.Services.Am
{
class IDisplayController : IIpcService
class IDisplayController : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IDisplayController()
{

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 System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
namespace Ryujinx.Core.OsHle.Services.Am
{
class ILibraryAppletCreator : IIpcService
class ILibraryAppletCreator : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ILibraryAppletCreator()
{

View file

@ -1,29 +1,30 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
namespace Ryujinx.Core.OsHle.Services.Am
{
class ISelfController : IIpcService
class ISelfController : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ISelfController()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, Exit },
{ 1, LockExit },
{ 10, SetScreenShotPermission },
{ 11, SetOperationModeChangedNotification },
{ 12, SetPerformanceModeChangedNotification },
{ 13, SetFocusHandlingMode },
{ 14, SetRestartMessageEnabled },
{ 16, SetOutOfFocusSuspendingEnabled }
{ 16, SetOutOfFocusSuspendingEnabled },
{ 50, SetHandlesRequestToDisplay }
};
}
public long Exit(ServiceCtx Context)
public long LockExit(ServiceCtx Context)
{
return 0;
}
@ -32,6 +33,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"ScreenShot Allowed = {Enable}");
return 0;
}
@ -39,6 +42,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"OperationMode Changed = {Enable}");
return 0;
}
@ -46,6 +51,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"PerformanceMode Changed = {Enable}");
return 0;
}
@ -55,6 +62,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
bool Flag2 = Context.RequestData.ReadByte() != 0 ? true : false;
bool Flag3 = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"Focus Handling Mode Flags = {{{Flag1}|{Flag2}|{Flag3}}}");
return 0;
}
@ -62,6 +71,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"Restart Message Enabled = {Enable}");
return 0;
}
@ -69,6 +80,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"Out Of Focus Suspending Enabled = {Enable}");
return 0;
}
public long SetHandlesRequestToDisplay(ServiceCtx Context)
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"HandlesRequestToDisplay Allowed = {Enable}");
return 0;
}
}

View file

@ -1,15 +1,13 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Am
namespace Ryujinx.Core.OsHle.Services.Am
{
class IStorage : IIpcService
class IStorage : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public byte[] Data { get; private set; }

View file

@ -3,13 +3,13 @@ using Ryujinx.Core.OsHle.Ipc;
using System;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
namespace Ryujinx.Core.OsHle.Services.Am
{
class IStorageAccessor : IIpcService
class IStorageAccessor : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private IStorage Storage;

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 System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Am
namespace Ryujinx.Core.OsHle.Services.Am
{
class IWindowController : IIpcService
class IWindowController : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IWindowController()
{
@ -20,6 +20,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
public long GetAppletResourceUserId(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAm, $"Applet Resource Id = 0");
Context.ResponseData.Write(0L);
return 0;
@ -27,6 +29,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Am
public long AcquireForegroundRights(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAm, "Stubbed");
return 0;
}
}

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

View file

@ -1,17 +1,15 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.Apm
namespace Ryujinx.Core.OsHle.Services.Apm
{
class ServiceApm : IIpcService
class IManager : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceApm()
public IManager()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{

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