diff --git a/CONFIG.md b/CONFIG.md index 09fa13b7b..764eb528d 100644 --- a/CONFIG.md +++ b/CONFIG.md @@ -26,6 +26,10 @@ Enable the Fatal Logging (Enabled in Debug recommanded). +- `Logging_Enable_Ipc` *(bool)* + + Enable the Ipc Message Logging. + - `Logging_Enable_LogFile` *(bool)* Enable writing the logging inside a Ryujinx.log file. diff --git a/Ryujinx/Cpu/ABitUtils.cs b/ChocolArm64/ABitUtils.cs similarity index 100% rename from Ryujinx/Cpu/ABitUtils.cs rename to ChocolArm64/ABitUtils.cs diff --git a/Ryujinx/Cpu/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs similarity index 83% rename from Ryujinx/Cpu/AOpCodeTable.cs rename to ChocolArm64/AOpCodeTable.cs index e42136b8c..60646d0ee 100644 --- a/Ryujinx/Cpu/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -11,6 +11,7 @@ namespace ChocolArm64 #region "OpCode Table" //Integer Set("x0011010000xxxxx000000xxxxxxxxxx", AInstEmit.Adc, typeof(AOpCodeAluRs)); + Set("x0111010000xxxxx000000xxxxxxxxxx", AInstEmit.Adcs, typeof(AOpCodeAluRs)); Set("x00100010xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluImm)); Set("x0001011<<0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRs)); Set("x0001011001xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Add, typeof(AOpCodeAluRx)); @@ -47,9 +48,11 @@ namespace ChocolArm64 Set("x1011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csneg, typeof(AOpCodeCsel)); Set("11010101000000110011xxxx10111111", AInstEmit.Dmb, typeof(AOpCodeSystem)); Set("11010101000000110011xxxx10011111", AInstEmit.Dsb, typeof(AOpCodeSystem)); + Set("x1001010xx1xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eon, typeof(AOpCodeAluRs)); Set("x10100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluImm)); Set("x1001010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Eor, typeof(AOpCodeAluRs)); Set("x00100111x0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Extr, typeof(AOpCodeAluRs)); + Set("11010101000000110010xxxxxxx11111", AInstEmit.Hint, typeof(AOpCodeSystem)); Set("xx001000110xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldar, typeof(AOpCodeMemEx)); Set("1x001000011xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldaxp, typeof(AOpCodeMemEx)); Set("xx001000010xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Ldaxr, typeof(AOpCodeMemEx)); @@ -88,6 +91,7 @@ namespace ChocolArm64 Set("1101101011000000000011xxxxxxxxxx", AInstEmit.Rev64, typeof(AOpCodeAlu)); Set("x0011010110xxxxx001011xxxxxxxxxx", AInstEmit.Rorv, typeof(AOpCodeAluRs)); Set("x1011010000xxxxx000000xxxxxxxxxx", AInstEmit.Sbc, typeof(AOpCodeAluRs)); + Set("x1111010000xxxxx000000xxxxxxxxxx", AInstEmit.Sbcs, typeof(AOpCodeAluRs)); Set("x00100110xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Sbfm, typeof(AOpCodeBfm)); Set("x0011010110xxxxx000011xxxxxxxxxx", AInstEmit.Sdiv, typeof(AOpCodeAluRs)); Set("10011011001xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Smaddl, typeof(AOpCodeMul)); @@ -138,57 +142,70 @@ namespace ChocolArm64 Set("0>101110<<1xxxxx001111xxxxxxxxxx", AInstEmit.Cmhs_V, typeof(AOpCodeSimdReg)); Set("0>101110<<100000100110xxxxxxxxxx", AInstEmit.Cmle_V, typeof(AOpCodeSimd)); Set("0>001110<<100000101010xxxxxxxxxx", AInstEmit.Cmlt_V, typeof(AOpCodeSimd)); + Set("0>001110<<1xxxxx100011xxxxxxxxxx", AInstEmit.Cmtst_V, typeof(AOpCodeSimdReg)); Set("0x00111000100000010110xxxxxxxxxx", AInstEmit.Cnt_V, typeof(AOpCodeSimd)); Set("0x001110000xxxxx000011xxxxxxxxxx", AInstEmit.Dup_Gp, typeof(AOpCodeSimdIns)); Set("01011110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_S, typeof(AOpCodeSimdIns)); Set("0x001110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_V, typeof(AOpCodeSimdIns)); Set("0x101110001xxxxx000111xxxxxxxxxx", AInstEmit.Eor_V, typeof(AOpCodeSimdReg)); - Set("00011110xx100000110000xxxxxxxxxx", AInstEmit.Fabs_S, typeof(AOpCodeSimd)); - Set("00011110xx1xxxxx001010xxxxxxxxxx", AInstEmit.Fadd_S, typeof(AOpCodeSimdReg)); - Set("0x0011100x1xxxxx110101xxxxxxxxxx", AInstEmit.Fadd_V, typeof(AOpCodeSimdReg)); - Set("00011110xx1xxxxxxxxx01xxxxx0xxxx", AInstEmit.Fccmp_S, typeof(AOpCodeSimdFcond)); - Set("00011110xx1xxxxx001000xxxxx0x000", AInstEmit.Fcmp_S, typeof(AOpCodeSimdReg)); - Set("00011110xx1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg)); - Set("00011110xx1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond)); - Set("00011110xx10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd)); - Set("x0011110xx100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt)); - Set("x0011110xx100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt)); - Set("x0011110xx110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt)); - Set("x0011110xx101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt)); - Set("x0011110xx111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt)); - Set("x0011110xx011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Gp_Fix, typeof(AOpCodeSimdCvt)); - Set("0x0011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimd)); + Set("0>101110000xxxxx00011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Fadd_V, typeof(AOpCodeSimdReg)); + Set("000111100x1xxxxxxxxx01xxxxx0xxxx", AInstEmit.Fccmp_S, typeof(AOpCodeSimdFcond)); + Set("000111100x1xxxxxxxxx01xxxxx1xxxx", AInstEmit.Fccmpe_S, typeof(AOpCodeSimdFcond)); + Set("000111100x1xxxxx001000xxxxx0x000", AInstEmit.Fcmp_S, typeof(AOpCodeSimdReg)); + Set("000111100x1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg)); + Set("000111100x1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond)); + Set("000111100x10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd)); + Set("x00111100x100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt)); + Set("x00111100x100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt)); + Set("0x0011100x100001011110xxxxxxxxxx", AInstEmit.Fcvtl_V, typeof(AOpCodeSimd)); + Set("x00111100x110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt)); + Set("x00111100x110001000000xxxxxxxxxx", AInstEmit.Fcvtmu_Gp, typeof(AOpCodeSimdCvt)); + Set("0x0011100x100001011010xxxxxxxxxx", AInstEmit.Fcvtn_V, typeof(AOpCodeSimd)); + Set("x00111100x101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt)); + Set("x00111100x101001000000xxxxxxxxxx", AInstEmit.Fcvtpu_Gp, typeof(AOpCodeSimdCvt)); + Set("x00111100x111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt)); + Set("x00111100x011000xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzs_Gp_Fix, typeof(AOpCodeSimdCvt)); + Set("0>0011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimd)); Set("0x0011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzs_V, typeof(AOpCodeSimdShImm)); - Set("x0011110xx111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp, typeof(AOpCodeSimdCvt)); - Set("x0011110xx011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt)); - Set("0x1011101x100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd)); + Set("x00111100x111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp, typeof(AOpCodeSimdCvt)); + Set("x00111100x011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt)); + Set("0>1011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd)); Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm)); - Set("00011110xx1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg)); - Set("00011111xx0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fmadd_S, typeof(AOpCodeSimdReg)); - Set("00011110xx1xxxxx010010xxxxxxxxxx", AInstEmit.Fmax_S, typeof(AOpCodeSimdReg)); - Set("00011110xx1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S, typeof(AOpCodeSimdReg)); - Set("00011110xx1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg)); - Set("00011110xx1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg)); - Set("0x0011100x1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg)); - Set("0x0011111<1011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Fdiv_V, typeof(AOpCodeSimdReg)); + Set("000111110x0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fmadd_S, typeof(AOpCodeSimdReg)); + Set("000111100x1xxxxx010010xxxxxxxxxx", AInstEmit.Fmax_S, typeof(AOpCodeSimdReg)); + Set("000111100x1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S, typeof(AOpCodeSimdReg)); + Set("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg)); + Set("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg)); + Set("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg)); + Set("0x0011111<1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg)); + Set("0x0011111<0011100<100001100110xxxxxxxxxx", AInstEmit.Frintm_V, typeof(AOpCodeSimd)); + Set("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S, typeof(AOpCodeSimd)); + Set("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S, typeof(AOpCodeSimd)); + Set("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd)); + Set("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg)); + Set("0>0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, typeof(AOpCodeSimdReg)); Set("01001110000xxxxx000111xxxxxxxxxx", AInstEmit.Ins_Gp, typeof(AOpCodeSimdIns)); Set("01101110000xxxxx0xxxx1xxxxxxxxxx", AInstEmit.Ins_V, typeof(AOpCodeSimdIns)); Set("0x00110001000000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs)); @@ -209,6 +226,7 @@ namespace ChocolArm64 Set("0x00111100000xxx110x01xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); Set("0xx0111100000xxx111001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); Set("0x001110<<1xxxxx100111xxxxxxxxxx", AInstEmit.Mul_V, typeof(AOpCodeSimdReg)); + Set("0x001111xxxxxxxx1000x0xxxxxxxxxx", AInstEmit.Mul_Ve, typeof(AOpCodeSimdRegElem)); Set("0x10111100000xxx0xx001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); Set("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); Set("0x10111100000xxx110x01xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); @@ -216,19 +234,24 @@ namespace ChocolArm64 Set("0x10111000100000010110xxxxxxxxxx", AInstEmit.Not_V, typeof(AOpCodeSimd)); Set("0x001110101xxxxx000111xxxxxxxxxx", AInstEmit.Orr_V, typeof(AOpCodeSimdReg)); Set("0x00111100000xxx<>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm)); Set("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm)); + Set("0x101110<<100001001110xxxxxxxxxx", AInstEmit.Shll_V, typeof(AOpCodeSimd)); Set("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm)); Set("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg)); + Set("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg)); + Set("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg)); Set("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg)); Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm)); Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm)); Set("0x0011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm)); + 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)); @@ -249,13 +272,17 @@ namespace ChocolArm64 Set("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd)); Set("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimd)); Set("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns)); + Set("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg)); Set("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg)); Set("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm)); + 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("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd)); + Set("0x001110xx0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V, typeof(AOpCodeSimdReg)); + Set("0x001110xx0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V, typeof(AOpCodeSimdReg)); #endregion } diff --git a/ChocolArm64/AOptimizations.cs b/ChocolArm64/AOptimizations.cs new file mode 100644 index 000000000..a3c82dccd --- /dev/null +++ b/ChocolArm64/AOptimizations.cs @@ -0,0 +1,4 @@ +public static class AOptimizations +{ + public static bool DisableMemoryChecks = false; +} \ No newline at end of file diff --git a/Ryujinx/Cpu/AThread.cs b/ChocolArm64/AThread.cs similarity index 54% rename from Ryujinx/Cpu/AThread.cs rename to ChocolArm64/AThread.cs index 5c0322894..62f9d2d39 100644 --- a/Ryujinx/Cpu/AThread.cs +++ b/ChocolArm64/AThread.cs @@ -10,63 +10,50 @@ namespace ChocolArm64 public AThreadState ThreadState { get; private set; } public AMemory Memory { get; private set; } - public long EntryPoint { get; private set; } + private long EntryPoint; private ATranslator Translator; - private ThreadPriority Priority; - private Thread Work; public event EventHandler WorkFinished; public int ThreadId => ThreadState.ThreadId; - public bool IsAlive => Work.IsAlive; + private int IsExecuting; - private bool IsExecuting; - - private object ExecuteLock; - - public AThread(AMemory Memory, ThreadPriority Priority, long EntryPoint) + public AThread(ATranslator Translator, AMemory Memory, long EntryPoint) { + this.Translator = Translator; this.Memory = Memory; - this.Priority = Priority; this.EntryPoint = EntryPoint; ThreadState = new AThreadState(); - Translator = new ATranslator(this); - ExecuteLock = new object(); - } - public void StopExecution() => Translator.StopExecution(); + ThreadState.Running = true; + } public bool Execute() { - lock (ExecuteLock) + if (Interlocked.Exchange(ref IsExecuting, 1) == 1) { - if (IsExecuting) - { - return false; - } - - IsExecuting = true; + return false; } Work = new Thread(delegate() { - Translator.ExecuteSubroutine(EntryPoint); + Translator.ExecuteSubroutine(this, EntryPoint); Memory.RemoveMonitor(ThreadId); WorkFinished?.Invoke(this, EventArgs.Empty); }); - Work.Priority = Priority; - Work.Start(); return true; } + + public void StopExecution() => ThreadState.Running = false; } } \ No newline at end of file diff --git a/ChocolArm64/ATranslatedSub.cs b/ChocolArm64/ATranslatedSub.cs new file mode 100644 index 000000000..414038ab6 --- /dev/null +++ b/ChocolArm64/ATranslatedSub.cs @@ -0,0 +1,134 @@ +using ChocolArm64.Memory; +using ChocolArm64.State; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Reflection.Emit; + +namespace ChocolArm64 +{ + class ATranslatedSub + { + private delegate long AA64Subroutine(AThreadState Register, AMemory Memory); + + private AA64Subroutine ExecDelegate; + + public static int StateArgIdx { get; private set; } + public static int MemoryArgIdx { get; private set; } + + public static Type[] FixedArgTypes { get; private set; } + + public DynamicMethod Method { get; private set; } + + public ReadOnlyCollection Params { get; private set; } + + private HashSet Callees; + + private ATranslatedSubType Type; + + private int CallCount; + + private bool NeedsReJit; + + private int MinCallCountForReJit = 250; + + public ATranslatedSub(DynamicMethod Method, List Params, HashSet Callees) + { + if (Method == null) + { + throw new ArgumentNullException(nameof(Method)); + } + + if (Params == null) + { + throw new ArgumentNullException(nameof(Params)); + } + + if (Callees == null) + { + throw new ArgumentNullException(nameof(Callees)); + } + + this.Method = Method; + this.Params = Params.AsReadOnly(); + this.Callees = Callees; + + PrepareDelegate(); + } + + static ATranslatedSub() + { + MethodInfo MthdInfo = typeof(AA64Subroutine).GetMethod("Invoke"); + + ParameterInfo[] Params = MthdInfo.GetParameters(); + + FixedArgTypes = new Type[Params.Length]; + + for (int Index = 0; Index < Params.Length; Index++) + { + Type ParamType = Params[Index].ParameterType; + + FixedArgTypes[Index] = ParamType; + + if (ParamType == typeof(AThreadState)) + { + StateArgIdx = Index; + } + else if (ParamType == typeof(AMemory)) + { + MemoryArgIdx = Index; + } + } + } + + private void PrepareDelegate() + { + string Name = $"{Method.Name}_Dispatch"; + + DynamicMethod Mthd = new DynamicMethod(Name, typeof(long), FixedArgTypes); + + ILGenerator Generator = Mthd.GetILGenerator(); + + Generator.EmitLdargSeq(FixedArgTypes.Length); + + foreach (ARegister Reg in Params) + { + Generator.EmitLdarg(StateArgIdx); + + Generator.Emit(OpCodes.Ldfld, Reg.GetField()); + } + + Generator.Emit(OpCodes.Call, Method); + Generator.Emit(OpCodes.Ret); + + ExecDelegate = (AA64Subroutine)Mthd.CreateDelegate(typeof(AA64Subroutine)); + } + + public bool ShouldReJit() + { + if (Type == ATranslatedSubType.SubTier0) + { + if (CallCount < MinCallCountForReJit) + { + CallCount++; + } + + return CallCount == MinCallCountForReJit; + } + + return Type == ATranslatedSubType.SubTier1 && NeedsReJit; + } + + public long Execute(AThreadState ThreadState, AMemory Memory) + { + return ExecDelegate(ThreadState, Memory); + } + + public void SetType(ATranslatedSubType Type) => this.Type = Type; + + public bool HasCallee(long Position) => Callees.Contains(Position); + + public void MarkForReJit() => NeedsReJit = true; + } +} \ No newline at end of file diff --git a/ChocolArm64/ATranslatedSubType.cs b/ChocolArm64/ATranslatedSubType.cs new file mode 100644 index 000000000..e9f3e0bf0 --- /dev/null +++ b/ChocolArm64/ATranslatedSubType.cs @@ -0,0 +1,9 @@ +namespace ChocolArm64 +{ + enum ATranslatedSubType + { + SubBlock, + SubTier0, + SubTier1 + } +} \ No newline at end of file diff --git a/ChocolArm64/ATranslator.cs b/ChocolArm64/ATranslator.cs new file mode 100644 index 000000000..02c18efd2 --- /dev/null +++ b/ChocolArm64/ATranslator.cs @@ -0,0 +1,192 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Events; +using ChocolArm64.Instruction; +using ChocolArm64.Memory; +using ChocolArm64.Translation; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Reflection.Emit; + +namespace ChocolArm64 +{ + public class ATranslator + { + private HashSet SubBlocks; + + private ConcurrentDictionary CachedSubs; + + private ConcurrentDictionary SymbolTable; + + public event EventHandler CpuTrace; + + public bool EnableCpuTrace { get; set; } + + public ATranslator(IReadOnlyDictionary SymbolTable = null) + { + SubBlocks = new HashSet(); + + CachedSubs = new ConcurrentDictionary(); + + if (SymbolTable != null) + { + this.SymbolTable = new ConcurrentDictionary(SymbolTable); + } + else + { + this.SymbolTable = new ConcurrentDictionary(); + } + } + + internal void ExecuteSubroutine(AThread Thread, long Position) + { + do + { + if (EnableCpuTrace) + { + if (!SymbolTable.TryGetValue(Position, out string SubName)) + { + SubName = string.Empty; + } + + CpuTrace?.Invoke(this, new ACpuTraceEventArgs(Position, SubName)); + } + + if (!CachedSubs.TryGetValue(Position, out ATranslatedSub Sub)) + { + Sub = TranslateTier0(Thread.Memory, Position); + } + + if (Sub.ShouldReJit()) + { + TranslateTier1(Thread.Memory, Position); + } + + Position = Sub.Execute(Thread.ThreadState, Thread.Memory); + } + while (Position != 0 && Thread.ThreadState.Running); + } + + internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub) + { + if (OpCode.Emitter != AInstEmit.Bl) + { + Sub = null; + + return false; + } + + return TryGetCachedSub(((AOpCodeBImmAl)OpCode).Imm, out Sub); + } + + internal bool TryGetCachedSub(long Position, out ATranslatedSub Sub) + { + return CachedSubs.TryGetValue(Position, out Sub); + } + + internal bool HasCachedSub(long Position) + { + return CachedSubs.ContainsKey(Position); + } + + private ATranslatedSub TranslateTier0(AMemory Memory, long Position) + { + ABlock Block = ADecoder.DecodeBasicBlock(this, Memory, Position); + + ABlock[] Graph = new ABlock[] { Block }; + + string SubName = GetSubName(Position); + + AILEmitterCtx Context = new AILEmitterCtx(this, Graph, Block, SubName); + + do + { + Context.EmitOpCode(); + } + while (Context.AdvanceOpCode()); + + ATranslatedSub Subroutine = Context.GetSubroutine(); + + if (SubBlocks.Contains(Position)) + { + SubBlocks.Remove(Position); + + 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) + { + SubBlocks.Add(LastOp.Position + 4); + } + + return Subroutine; + } + + private void TranslateTier1(AMemory Memory, long Position) + { + (ABlock[] Graph, ABlock Root) Cfg = ADecoder.DecodeSubroutine(this, Memory, Position); + + string SubName = GetSubName(Position); + + PropagateName(Cfg.Graph, SubName); + + AILEmitterCtx Context = new AILEmitterCtx(this, Cfg.Graph, Cfg.Root, SubName); + + if (Context.CurrBlock.Position != Position) + { + Context.Emit(OpCodes.Br, Context.GetLabel(Position)); + } + + do + { + Context.EmitOpCode(); + } + while (Context.AdvanceOpCode()); + + //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 (TS.HasCallee(Position)) + { + TS.MarkForReJit(); + } + } + + ATranslatedSub Subroutine = Context.GetSubroutine(); + + Subroutine.SetType(ATranslatedSubType.SubTier1); + + CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine); + } + + private string GetSubName(long Position) + { + return SymbolTable.GetOrAdd(Position, $"Sub{Position:x16}"); + } + + private void PropagateName(ABlock[] Graph, string Name) + { + foreach (ABlock Block in Graph) + { + AOpCode LastOp = Block.GetLastOp(); + + if (LastOp != null && + (LastOp.Emitter == AInstEmit.Bl || + LastOp.Emitter == AInstEmit.Blr)) + { + SymbolTable.TryAdd(LastOp.Position + 4, Name); + } + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/ChocolArm64.csproj b/ChocolArm64/ChocolArm64.csproj new file mode 100644 index 000000000..47d66cf15 --- /dev/null +++ b/ChocolArm64/ChocolArm64.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp2.0 + + + + true + + + + true + + + diff --git a/Ryujinx/Cpu/Decoder/ABlock.cs b/ChocolArm64/Decoder/ABlock.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/ABlock.cs rename to ChocolArm64/Decoder/ABlock.cs diff --git a/Ryujinx/Cpu/Decoder/ACond.cs b/ChocolArm64/Decoder/ACond.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/ACond.cs rename to ChocolArm64/Decoder/ACond.cs diff --git a/Ryujinx/Cpu/Decoder/ADataOp.cs b/ChocolArm64/Decoder/ADataOp.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/ADataOp.cs rename to ChocolArm64/Decoder/ADataOp.cs diff --git a/Ryujinx/Cpu/Decoder/ADecoder.cs b/ChocolArm64/Decoder/ADecoder.cs similarity index 76% rename from Ryujinx/Cpu/Decoder/ADecoder.cs rename to ChocolArm64/Decoder/ADecoder.cs index 06a535c1b..2375c1857 100644 --- a/Ryujinx/Cpu/Decoder/ADecoder.cs +++ b/ChocolArm64/Decoder/ADecoder.cs @@ -1,6 +1,7 @@ using ChocolArm64.Instruction; using ChocolArm64.Memory; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection.Emit; @@ -8,7 +9,31 @@ namespace ChocolArm64.Decoder { static class ADecoder { - public static (ABlock[] Graph, ABlock Root) DecodeSubroutine(ATranslator Translator, long Start) + private delegate object OpActivator(AInst Inst, long Position, int OpCode); + + private static ConcurrentDictionary OpActivators; + + static ADecoder() + { + OpActivators = new ConcurrentDictionary(); + } + + public static ABlock DecodeBasicBlock( + ATranslator Translator, + AMemory Memory, + long Start) + { + ABlock Block = new ABlock(Start); + + FillBlock(Memory, Block); + + return Block; + } + + public static (ABlock[] Graph, ABlock Root) DecodeSubroutine( + ATranslator Translator, + AMemory Memory, + long Start) { Dictionary Visited = new Dictionary(); Dictionary VisitedEnd = new Dictionary(); @@ -35,7 +60,7 @@ namespace ChocolArm64.Decoder { ABlock Current = Blocks.Dequeue(); - FillBlock(Translator.Thread.Memory, Current); + FillBlock(Memory, Current); //Set child blocks. "Branch" is the block the branch instruction //points to (when taken), "Next" is the block at the next address, @@ -59,8 +84,8 @@ namespace ChocolArm64.Decoder } } - if ((!(LastOp is AOpCodeBImmAl) && - !(LastOp is AOpCodeBReg)) || HasCachedSub) + if (!((LastOp is AOpCodeBImmAl) || + (LastOp is AOpCodeBReg)) || HasCachedSub) { Current.Next = Enqueue(Current.EndPosition); } @@ -165,43 +190,39 @@ namespace ChocolArm64.Decoder if (Inst.Type != null) { - DecodedOpCode = CreateOpCode(Inst.Type, Inst, Position, OpCode); + DecodedOpCode = MakeOpCode(Inst.Type, Inst, Position, OpCode); } return DecodedOpCode; } - private delegate object OpActivator(AInst Inst, long Position, int OpCode); - - private static Dictionary Activators = new Dictionary(); - - private static AOpCode CreateOpCode(Type Type, AInst Inst, long Position, int OpCode) + private static AOpCode MakeOpCode(Type Type, AInst Inst, long Position, int OpCode) { if (Type == null) { throw new ArgumentNullException(nameof(Type)); } - if (!Activators.TryGetValue(Type, out OpActivator CreateInstance)) - { - Type[] ArgTypes = new Type[] { typeof(AInst), typeof(long), typeof(int) }; - - DynamicMethod Mthd = new DynamicMethod($"{Type.Name}_Create", Type, ArgTypes); - - ILGenerator Generator = Mthd.GetILGenerator(); - - Generator.Emit(OpCodes.Ldarg_0); - Generator.Emit(OpCodes.Ldarg_1); - Generator.Emit(OpCodes.Ldarg_2); - Generator.Emit(OpCodes.Newobj, Type.GetConstructor(ArgTypes)); - Generator.Emit(OpCodes.Ret); - - CreateInstance = (OpActivator)Mthd.CreateDelegate(typeof(OpActivator)); - - Activators.Add(Type, CreateInstance); - } + OpActivator CreateInstance = OpActivators.GetOrAdd(Type, CacheOpActivator); return (AOpCode)CreateInstance(Inst, Position, OpCode); } + + private static OpActivator CacheOpActivator(Type Type) + { + Type[] ArgTypes = new Type[] { typeof(AInst), typeof(long), typeof(int) }; + + DynamicMethod Mthd = new DynamicMethod($"Make{Type.Name}", Type, ArgTypes); + + ILGenerator Generator = Mthd.GetILGenerator(); + + Generator.Emit(OpCodes.Ldarg_0); + Generator.Emit(OpCodes.Ldarg_1); + Generator.Emit(OpCodes.Ldarg_2); + Generator.Emit(OpCodes.Newobj, Type.GetConstructor(ArgTypes)); + Generator.Emit(OpCodes.Ret); + + return (OpActivator)Mthd.CreateDelegate(typeof(OpActivator)); + } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Decoder/ADecoderHelper.cs b/ChocolArm64/Decoder/ADecoderHelper.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/ADecoderHelper.cs rename to ChocolArm64/Decoder/ADecoderHelper.cs diff --git a/Ryujinx/Cpu/Decoder/AIntType.cs b/ChocolArm64/Decoder/AIntType.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AIntType.cs rename to ChocolArm64/Decoder/AIntType.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCode.cs b/ChocolArm64/Decoder/AOpCode.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCode.cs rename to ChocolArm64/Decoder/AOpCode.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeAdr.cs b/ChocolArm64/Decoder/AOpCodeAdr.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeAdr.cs rename to ChocolArm64/Decoder/AOpCodeAdr.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeAlu.cs b/ChocolArm64/Decoder/AOpCodeAlu.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeAlu.cs rename to ChocolArm64/Decoder/AOpCodeAlu.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeAluImm.cs b/ChocolArm64/Decoder/AOpCodeAluImm.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeAluImm.cs rename to ChocolArm64/Decoder/AOpCodeAluImm.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeAluRs.cs b/ChocolArm64/Decoder/AOpCodeAluRs.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeAluRs.cs rename to ChocolArm64/Decoder/AOpCodeAluRs.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeAluRx.cs b/ChocolArm64/Decoder/AOpCodeAluRx.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeAluRx.cs rename to ChocolArm64/Decoder/AOpCodeAluRx.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBImm.cs b/ChocolArm64/Decoder/AOpCodeBImm.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeBImm.cs rename to ChocolArm64/Decoder/AOpCodeBImm.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBImmAl.cs b/ChocolArm64/Decoder/AOpCodeBImmAl.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeBImmAl.cs rename to ChocolArm64/Decoder/AOpCodeBImmAl.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBImmCmp.cs b/ChocolArm64/Decoder/AOpCodeBImmCmp.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeBImmCmp.cs rename to ChocolArm64/Decoder/AOpCodeBImmCmp.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBImmCond.cs b/ChocolArm64/Decoder/AOpCodeBImmCond.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeBImmCond.cs rename to ChocolArm64/Decoder/AOpCodeBImmCond.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBImmTest.cs b/ChocolArm64/Decoder/AOpCodeBImmTest.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeBImmTest.cs rename to ChocolArm64/Decoder/AOpCodeBImmTest.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBReg.cs b/ChocolArm64/Decoder/AOpCodeBReg.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeBReg.cs rename to ChocolArm64/Decoder/AOpCodeBReg.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeBfm.cs b/ChocolArm64/Decoder/AOpCodeBfm.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeBfm.cs rename to ChocolArm64/Decoder/AOpCodeBfm.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeCcmp.cs b/ChocolArm64/Decoder/AOpCodeCcmp.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeCcmp.cs rename to ChocolArm64/Decoder/AOpCodeCcmp.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeCcmpImm.cs b/ChocolArm64/Decoder/AOpCodeCcmpImm.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeCcmpImm.cs rename to ChocolArm64/Decoder/AOpCodeCcmpImm.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeCcmpReg.cs b/ChocolArm64/Decoder/AOpCodeCcmpReg.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeCcmpReg.cs rename to ChocolArm64/Decoder/AOpCodeCcmpReg.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeCsel.cs b/ChocolArm64/Decoder/AOpCodeCsel.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeCsel.cs rename to ChocolArm64/Decoder/AOpCodeCsel.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeException.cs b/ChocolArm64/Decoder/AOpCodeException.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeException.cs rename to ChocolArm64/Decoder/AOpCodeException.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMem.cs b/ChocolArm64/Decoder/AOpCodeMem.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeMem.cs rename to ChocolArm64/Decoder/AOpCodeMem.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMemEx.cs b/ChocolArm64/Decoder/AOpCodeMemEx.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeMemEx.cs rename to ChocolArm64/Decoder/AOpCodeMemEx.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMemImm.cs b/ChocolArm64/Decoder/AOpCodeMemImm.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeMemImm.cs rename to ChocolArm64/Decoder/AOpCodeMemImm.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMemLit.cs b/ChocolArm64/Decoder/AOpCodeMemLit.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeMemLit.cs rename to ChocolArm64/Decoder/AOpCodeMemLit.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMemPair.cs b/ChocolArm64/Decoder/AOpCodeMemPair.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeMemPair.cs rename to ChocolArm64/Decoder/AOpCodeMemPair.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMemReg.cs b/ChocolArm64/Decoder/AOpCodeMemReg.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeMemReg.cs rename to ChocolArm64/Decoder/AOpCodeMemReg.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMov.cs b/ChocolArm64/Decoder/AOpCodeMov.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeMov.cs rename to ChocolArm64/Decoder/AOpCodeMov.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeMul.cs b/ChocolArm64/Decoder/AOpCodeMul.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeMul.cs rename to ChocolArm64/Decoder/AOpCodeMul.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimd.cs b/ChocolArm64/Decoder/AOpCodeSimd.cs similarity index 95% rename from Ryujinx/Cpu/Decoder/AOpCodeSimd.cs rename to ChocolArm64/Decoder/AOpCodeSimd.cs index 5f940b220..417085172 100644 --- a/Ryujinx/Cpu/Decoder/AOpCodeSimd.cs +++ b/ChocolArm64/Decoder/AOpCodeSimd.cs @@ -10,8 +10,6 @@ namespace ChocolArm64.Decoder public int Opc { get; private set; } public int Size { get; protected set; } - public int SizeF => Size & 1; - public AOpCodeSimd(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { Rd = (OpCode >> 0) & 0x1f; diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdCvt.cs b/ChocolArm64/Decoder/AOpCodeSimdCvt.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdCvt.cs rename to ChocolArm64/Decoder/AOpCodeSimdCvt.cs diff --git a/ChocolArm64/Decoder/AOpCodeSimdExt.cs b/ChocolArm64/Decoder/AOpCodeSimdExt.cs new file mode 100644 index 000000000..888e44703 --- /dev/null +++ b/ChocolArm64/Decoder/AOpCodeSimdExt.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdExt : AOpCodeSimdReg + { + public int Imm4 { get; private set; } + + public AOpCodeSimdExt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + Imm4 = (OpCode >> 11) & 0xf; + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdFcond.cs b/ChocolArm64/Decoder/AOpCodeSimdFcond.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdFcond.cs rename to ChocolArm64/Decoder/AOpCodeSimdFcond.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdFmov.cs b/ChocolArm64/Decoder/AOpCodeSimdFmov.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdFmov.cs rename to ChocolArm64/Decoder/AOpCodeSimdFmov.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdImm.cs b/ChocolArm64/Decoder/AOpCodeSimdImm.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdImm.cs rename to ChocolArm64/Decoder/AOpCodeSimdImm.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdIns.cs b/ChocolArm64/Decoder/AOpCodeSimdIns.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdIns.cs rename to ChocolArm64/Decoder/AOpCodeSimdIns.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemImm.cs b/ChocolArm64/Decoder/AOpCodeSimdMemImm.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdMemImm.cs rename to ChocolArm64/Decoder/AOpCodeSimdMemImm.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemLit.cs b/ChocolArm64/Decoder/AOpCodeSimdMemLit.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdMemLit.cs rename to ChocolArm64/Decoder/AOpCodeSimdMemLit.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemMs.cs b/ChocolArm64/Decoder/AOpCodeSimdMemMs.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdMemMs.cs rename to ChocolArm64/Decoder/AOpCodeSimdMemMs.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemPair.cs b/ChocolArm64/Decoder/AOpCodeSimdMemPair.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdMemPair.cs rename to ChocolArm64/Decoder/AOpCodeSimdMemPair.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemReg.cs b/ChocolArm64/Decoder/AOpCodeSimdMemReg.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdMemReg.cs rename to ChocolArm64/Decoder/AOpCodeSimdMemReg.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdMemSs.cs b/ChocolArm64/Decoder/AOpCodeSimdMemSs.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdMemSs.cs rename to ChocolArm64/Decoder/AOpCodeSimdMemSs.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdReg.cs b/ChocolArm64/Decoder/AOpCodeSimdReg.cs similarity index 71% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdReg.cs rename to ChocolArm64/Decoder/AOpCodeSimdReg.cs index 10a4aff8b..702ffed1e 100644 --- a/Ryujinx/Cpu/Decoder/AOpCodeSimdReg.cs +++ b/ChocolArm64/Decoder/AOpCodeSimdReg.cs @@ -4,9 +4,9 @@ namespace ChocolArm64.Decoder { class AOpCodeSimdReg : AOpCodeSimd { - public bool Bit3 { get; private set; } - public int Ra { get; private set; } - public int Rm { get; private set; } + public bool Bit3 { get; private set; } + public int Ra { get; private set; } + public int Rm { get; protected set; } public AOpCodeSimdReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { diff --git a/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs b/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs new file mode 100644 index 000000000..721da88fa --- /dev/null +++ b/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs @@ -0,0 +1,32 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdRegElem : AOpCodeSimdReg + { + public int Index { get; private set; } + + public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + switch (Size) + { + case 1: + Index = (OpCode >> 21) & 1 | + (OpCode >> 10) & 2 | + (OpCode >> 18) & 4; + + Rm &= 0xf; + + break; + + case 2: + Index = (OpCode >> 21) & 1 | + (OpCode >> 10) & 2; + + break; + + default: Emitter = AInstEmit.Und; return; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdRegElem.cs b/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs similarity index 71% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdRegElem.cs rename to ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs index d878b12ea..e61d7093a 100644 --- a/Ryujinx/Cpu/Decoder/AOpCodeSimdRegElem.cs +++ b/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs @@ -2,11 +2,11 @@ using ChocolArm64.Instruction; namespace ChocolArm64.Decoder { - class AOpCodeSimdRegElem : AOpCodeSimdReg + class AOpCodeSimdRegElemF : AOpCodeSimdReg { public int Index { get; private set; } - public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + public AOpCodeSimdRegElemF(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { if ((Size & 1) != 0) { diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdShImm.cs b/ChocolArm64/Decoder/AOpCodeSimdShImm.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdShImm.cs rename to ChocolArm64/Decoder/AOpCodeSimdShImm.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSimdTbl.cs b/ChocolArm64/Decoder/AOpCodeSimdTbl.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSimdTbl.cs rename to ChocolArm64/Decoder/AOpCodeSimdTbl.cs diff --git a/Ryujinx/Cpu/Decoder/AOpCodeSystem.cs b/ChocolArm64/Decoder/AOpCodeSystem.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AOpCodeSystem.cs rename to ChocolArm64/Decoder/AOpCodeSystem.cs diff --git a/Ryujinx/Cpu/Decoder/AShiftType.cs b/ChocolArm64/Decoder/AShiftType.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/AShiftType.cs rename to ChocolArm64/Decoder/AShiftType.cs diff --git a/Ryujinx/Cpu/Decoder/IAOpCode.cs b/ChocolArm64/Decoder/IAOpCode.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/IAOpCode.cs rename to ChocolArm64/Decoder/IAOpCode.cs diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeAlu.cs b/ChocolArm64/Decoder/IAOpCodeAlu.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/IAOpCodeAlu.cs rename to ChocolArm64/Decoder/IAOpCodeAlu.cs diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeAluImm.cs b/ChocolArm64/Decoder/IAOpCodeAluImm.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/IAOpCodeAluImm.cs rename to ChocolArm64/Decoder/IAOpCodeAluImm.cs diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeAluRs.cs b/ChocolArm64/Decoder/IAOpCodeAluRs.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/IAOpCodeAluRs.cs rename to ChocolArm64/Decoder/IAOpCodeAluRs.cs diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeAluRx.cs b/ChocolArm64/Decoder/IAOpCodeAluRx.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/IAOpCodeAluRx.cs rename to ChocolArm64/Decoder/IAOpCodeAluRx.cs diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeCond.cs b/ChocolArm64/Decoder/IAOpCodeCond.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/IAOpCodeCond.cs rename to ChocolArm64/Decoder/IAOpCodeCond.cs diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeLit.cs b/ChocolArm64/Decoder/IAOpCodeLit.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/IAOpCodeLit.cs rename to ChocolArm64/Decoder/IAOpCodeLit.cs diff --git a/Ryujinx/Cpu/Decoder/IAOpCodeSimd.cs b/ChocolArm64/Decoder/IAOpCodeSimd.cs similarity index 100% rename from Ryujinx/Cpu/Decoder/IAOpCodeSimd.cs rename to ChocolArm64/Decoder/IAOpCodeSimd.cs diff --git a/ChocolArm64/Events/ACpuTraceEventArgs.cs b/ChocolArm64/Events/ACpuTraceEventArgs.cs new file mode 100644 index 000000000..fedf3865b --- /dev/null +++ b/ChocolArm64/Events/ACpuTraceEventArgs.cs @@ -0,0 +1,17 @@ +using System; + +namespace ChocolArm64.Events +{ + public class ACpuTraceEventArgs : EventArgs + { + public long Position { get; private set; } + + public string SubName { get; private set; } + + public ACpuTraceEventArgs(long Position, string SubName) + { + this.Position = Position; + this.SubName = SubName; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Events/AInstExceptionEventArgs.cs b/ChocolArm64/Events/AInstExceptionEventArgs.cs new file mode 100644 index 000000000..34f90c8ee --- /dev/null +++ b/ChocolArm64/Events/AInstExceptionEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace ChocolArm64.Events +{ + public class AInstExceptionEventArgs : EventArgs + { + public int Id { get; private set; } + + public AInstExceptionEventArgs(int Id) + { + this.Id = Id; + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/State/AInstUndEventArgs.cs b/ChocolArm64/Events/AInstUndefinedEventArgs.cs similarity index 60% rename from Ryujinx/Cpu/State/AInstUndEventArgs.cs rename to ChocolArm64/Events/AInstUndefinedEventArgs.cs index 53de65a33..cdc1728bd 100644 --- a/Ryujinx/Cpu/State/AInstUndEventArgs.cs +++ b/ChocolArm64/Events/AInstUndefinedEventArgs.cs @@ -1,13 +1,13 @@ using System; -namespace ChocolArm64.State +namespace ChocolArm64.Events { - public class AInstUndEventArgs : EventArgs + public class AInstUndefinedEventArgs : EventArgs { public long Position { get; private set; } public int RawOpCode { get; private set; } - public AInstUndEventArgs(long Position, int RawOpCode) + public AInstUndefinedEventArgs(long Position, int RawOpCode) { this.Position = Position; this.RawOpCode = RawOpCode; diff --git a/Ryujinx/Cpu/Exceptions/VmmAccessViolationException.cs b/ChocolArm64/Exceptions/VmmAccessViolationException.cs similarity index 100% rename from Ryujinx/Cpu/Exceptions/VmmAccessViolationException.cs rename to ChocolArm64/Exceptions/VmmAccessViolationException.cs diff --git a/Ryujinx/Cpu/Exceptions/VmmOutOfMemoryException.cs b/ChocolArm64/Exceptions/VmmOutOfMemoryException.cs similarity index 100% rename from Ryujinx/Cpu/Exceptions/VmmOutOfMemoryException.cs rename to ChocolArm64/Exceptions/VmmOutOfMemoryException.cs diff --git a/Ryujinx/Cpu/Exceptions/VmmPageFaultException.cs b/ChocolArm64/Exceptions/VmmPageFaultException.cs similarity index 100% rename from Ryujinx/Cpu/Exceptions/VmmPageFaultException.cs rename to ChocolArm64/Exceptions/VmmPageFaultException.cs diff --git a/Ryujinx/Cpu/Instruction/AInst.cs b/ChocolArm64/Instruction/AInst.cs similarity index 100% rename from Ryujinx/Cpu/Instruction/AInst.cs rename to ChocolArm64/Instruction/AInst.cs diff --git a/Ryujinx/Cpu/Instruction/AInstEmitAlu.cs b/ChocolArm64/Instruction/AInstEmitAlu.cs similarity index 89% rename from Ryujinx/Cpu/Instruction/AInstEmitAlu.cs rename to ChocolArm64/Instruction/AInstEmitAlu.cs index 073a45e7a..57020364b 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitAlu.cs +++ b/ChocolArm64/Instruction/AInstEmitAlu.cs @@ -11,7 +11,10 @@ namespace ChocolArm64.Instruction { static partial class AInstEmit { - public static void Adc(AILEmitterCtx Context) + public static void Adc(AILEmitterCtx Context) => EmitAdc(Context, false); + public static void Adcs(AILEmitterCtx Context) => EmitAdc(Context, true); + + private static void EmitAdc(AILEmitterCtx Context, bool SetFlags) { EmitDataLoadOpers(Context); @@ -27,11 +30,19 @@ namespace ChocolArm64.Instruction if (Context.CurrOp.RegisterSize != ARegisterSize.Int32) { - Context.Emit(OpCodes.Conv_I8); + Context.Emit(OpCodes.Conv_U8); } Context.Emit(OpCodes.Add); + if (SetFlags) + { + Context.EmitZNFlagCheck(); + + EmitAdcsCCheck(Context); + EmitAddsVCheck(Context); + } + EmitDataStore(Context); } @@ -107,6 +118,16 @@ namespace ChocolArm64.Instruction Context.EmitStintzr(Op.Rd); } + public static void Eon(AILEmitterCtx Context) + { + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.Xor); + + EmitDataStore(Context); + } + public static void Eor(AILEmitterCtx Context) => EmitDataOp(Context, OpCodes.Xor); public static void Extr(AILEmitterCtx Context) @@ -135,7 +156,10 @@ namespace ChocolArm64.Instruction public static void Lslv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shl); public static void Lsrv(AILEmitterCtx Context) => EmitDataOpShift(Context, OpCodes.Shr_Un); - public static void Sbc(AILEmitterCtx Context) + public static void Sbc(AILEmitterCtx Context) => EmitSbc(Context, false); + public static void Sbcs(AILEmitterCtx Context) => EmitSbc(Context, true); + + private static void EmitSbc(AILEmitterCtx Context, bool SetFlags) { EmitDataLoadOpers(Context); @@ -155,11 +179,19 @@ namespace ChocolArm64.Instruction if (Context.CurrOp.RegisterSize != ARegisterSize.Int32) { - Context.Emit(OpCodes.Conv_I8); + Context.Emit(OpCodes.Conv_U8); } Context.Emit(OpCodes.Sub); + if (SetFlags) + { + Context.EmitZNFlagCheck(); + + EmitSbcsCCheck(Context); + EmitSubsVCheck(Context); + } + EmitDataStore(Context); } diff --git a/Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs b/ChocolArm64/Instruction/AInstEmitAluHelper.cs similarity index 72% rename from Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs rename to ChocolArm64/Instruction/AInstEmitAluHelper.cs index 57ec25dd0..ef9dd7a7a 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitAluHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitAluHelper.cs @@ -7,6 +7,31 @@ namespace ChocolArm64.Instruction { static class AInstEmitAluHelper { + public static void EmitAdcsCCheck(AILEmitterCtx Context) + { + //C = (Rd == Rn && CIn) || Rd < Rn + Context.EmitSttmp(); + Context.EmitLdtmp(); + Context.EmitLdtmp(); + + EmitDataLoadRn(Context); + + Context.Emit(OpCodes.Ceq); + + Context.EmitLdflg((int)APState.CBit); + + Context.Emit(OpCodes.And); + + Context.EmitLdtmp(); + + EmitDataLoadRn(Context); + + Context.Emit(OpCodes.Clt_Un); + Context.Emit(OpCodes.Or); + + Context.EmitStflg((int)APState.CBit); + } + public static void EmitAddsCCheck(AILEmitterCtx Context) { //C = Rd < Rn @@ -41,6 +66,25 @@ namespace ChocolArm64.Instruction Context.EmitStflg((int)APState.VBit); } + public static void EmitSbcsCCheck(AILEmitterCtx Context) + { + //C = (Rn == Rm && CIn) || Rn > Rm + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Ceq); + + Context.EmitLdflg((int)APState.CBit); + + Context.Emit(OpCodes.And); + + EmitDataLoadOpers(Context); + + Context.Emit(OpCodes.Cgt_Un); + Context.Emit(OpCodes.Or); + + Context.EmitStflg((int)APState.CBit); + } + public static void EmitSubsCCheck(AILEmitterCtx Context) { //C = Rn == Rm || Rn > Rm = !(Rn < Rm) @@ -145,5 +189,24 @@ namespace ChocolArm64.Instruction Context.EmitStint(Op.Rd); } } + + public static void EmitSetNZCV(AILEmitterCtx Context, int NZCV) + { + Context.EmitLdc_I4((NZCV >> 0) & 1); + + Context.EmitStflg((int)APState.VBit); + + Context.EmitLdc_I4((NZCV >> 1) & 1); + + Context.EmitStflg((int)APState.CBit); + + Context.EmitLdc_I4((NZCV >> 2) & 1); + + Context.EmitStflg((int)APState.ZBit); + + Context.EmitLdc_I4((NZCV >> 3) & 1); + + Context.EmitStflg((int)APState.NBit); + } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitBfm.cs b/ChocolArm64/Instruction/AInstEmitBfm.cs similarity index 89% rename from Ryujinx/Cpu/Instruction/AInstEmitBfm.cs rename to ChocolArm64/Instruction/AInstEmitBfm.cs index 4eff013d1..2e8f25085 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitBfm.cs +++ b/ChocolArm64/Instruction/AInstEmitBfm.cs @@ -36,7 +36,7 @@ namespace ChocolArm64.Instruction if (Op.Pos + 1 == BitsCount) { - EmitBfmShift(Context, OpCodes.Shr); + EmitSbfmShift(Context); } else if (Op.Pos < Op.Shift) { @@ -54,15 +54,6 @@ namespace ChocolArm64.Instruction { EmitSbfmCast(Context, OpCodes.Conv_I4); } - else if (Op.Shift == 0) - { - Context.EmitLdintzr(Op.Rn); - - Context.EmitLsl(BitsCount - 1 - Op.Pos); - Context.EmitAsr(BitsCount - 1); - - Context.EmitStintzr(Op.Rd); - } else { EmitBfmLoadRn(Context); @@ -87,7 +78,7 @@ namespace ChocolArm64.Instruction if (Op.Pos + 1 == Op.GetBitsCount()) { - EmitBfmShift(Context, OpCodes.Shr_Un); + EmitUbfmShift(Context); } else if (Op.Pos < Op.Shift) { @@ -166,19 +157,28 @@ namespace ChocolArm64.Instruction Context.EmitStintzr(Op.Rd); } - private static void EmitBfmShift(AILEmitterCtx Context, OpCode ILOp) + private static void EmitSbfmShift(AILEmitterCtx Context) + { + EmitBfmShift(Context, true); + } + + private static void EmitUbfmShift(AILEmitterCtx Context) + { + EmitBfmShift(Context, false); + } + + private static void EmitBfmShift(AILEmitterCtx Context, bool Signed) { AOpCodeBfm Op = (AOpCodeBfm)Context.CurrOp; - if (Op.Shift > 0) - { - Context.EmitLdintzr(Op.Rn); - Context.EmitLdc_I4(Op.Shift); + Context.EmitLdintzr(Op.Rn); + Context.EmitLdc_I4(Op.Shift); - Context.Emit(ILOp); + Context.Emit(Signed + ? OpCodes.Shr + : OpCodes.Shr_Un); - Context.EmitStintzr(Op.Rd); - } + Context.EmitStintzr(Op.Rd); } private static void EmitBfmLsl(AILEmitterCtx Context) diff --git a/Ryujinx/Cpu/Instruction/AInstEmitCcmp.cs b/ChocolArm64/Instruction/AInstEmitCcmp.cs similarity index 100% rename from Ryujinx/Cpu/Instruction/AInstEmitCcmp.cs rename to ChocolArm64/Instruction/AInstEmitCcmp.cs diff --git a/Ryujinx/Cpu/Instruction/AInstEmitCsel.cs b/ChocolArm64/Instruction/AInstEmitCsel.cs similarity index 97% rename from Ryujinx/Cpu/Instruction/AInstEmitCsel.cs rename to ChocolArm64/Instruction/AInstEmitCsel.cs index 330809806..218767524 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitCsel.cs +++ b/ChocolArm64/Instruction/AInstEmitCsel.cs @@ -44,16 +44,15 @@ namespace ChocolArm64.Instruction Context.Emit(OpCodes.Neg); } - Context.EmitStintzr(Op.Rd); - Context.Emit(OpCodes.Br_S, LblEnd); Context.MarkLabel(LblTrue); Context.EmitLdintzr(Op.Rn); - Context.EmitStintzr(Op.Rd); Context.MarkLabel(LblEnd); + + Context.EmitStintzr(Op.Rd); } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitException.cs b/ChocolArm64/Instruction/AInstEmitException.cs similarity index 68% rename from Ryujinx/Cpu/Instruction/AInstEmitException.cs rename to ChocolArm64/Instruction/AInstEmitException.cs index 209ba56f5..3964c9497 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitException.cs +++ b/ChocolArm64/Instruction/AInstEmitException.cs @@ -2,6 +2,7 @@ using ChocolArm64.Decoder; using ChocolArm64.State; using ChocolArm64.Translation; using System.Reflection; +using System.Reflection.Emit; namespace ChocolArm64.Instruction { @@ -33,10 +34,32 @@ namespace ChocolArm64.Instruction Context.EmitCall(MthdInfo); + //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. + Context.EmitLdarg(ATranslatedSub.StateArgIdx); + + Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Running)); + + AILLabel LblEnd = new AILLabel(); + + Context.Emit(OpCodes.Brtrue_S, LblEnd); + + Context.EmitLdc_I8(0); + + Context.Emit(OpCodes.Ret); + + Context.MarkLabel(LblEnd); + if (Context.CurrBlock.Next != null) { Context.EmitLoadState(Context.CurrBlock.Next); } + else + { + Context.EmitLdc_I8(Op.Position + 4); + + Context.Emit(OpCodes.Ret); + } } public static void Und(AILEmitterCtx Context) @@ -60,6 +83,12 @@ namespace ChocolArm64.Instruction { Context.EmitLoadState(Context.CurrBlock.Next); } + else + { + Context.EmitLdc_I8(Op.Position + 4); + + Context.Emit(OpCodes.Ret); + } } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitFlow.cs b/ChocolArm64/Instruction/AInstEmitFlow.cs similarity index 62% rename from Ryujinx/Cpu/Instruction/AInstEmitFlow.cs rename to ChocolArm64/Instruction/AInstEmitFlow.cs index be68aa6c9..91262834f 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitFlow.cs +++ b/ChocolArm64/Instruction/AInstEmitFlow.cs @@ -11,14 +11,24 @@ namespace ChocolArm64.Instruction { AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp; - Context.Emit(OpCodes.Br, Context.GetLabel(Op.Imm)); + if (Context.CurrBlock.Branch != null) + { + Context.Emit(OpCodes.Br, Context.GetLabel(Op.Imm)); + } + else + { + Context.EmitStoreState(); + Context.EmitLdc_I8(Op.Imm); + + Context.Emit(OpCodes.Ret); + } } public static void B_Cond(AILEmitterCtx Context) { AOpCodeBImmCond Op = (AOpCodeBImmCond)Context.CurrOp; - Context.EmitCondBranch(Context.GetLabel(Op.Imm), Op.Cond); + EmitBranch(Context, Op.Cond); } public static void Bl(AILEmitterCtx Context) @@ -48,10 +58,7 @@ namespace ChocolArm64.Instruction Context.Emit(OpCodes.Pop); - if (Context.CurrBlock.Next != null) - { - Context.EmitLoadState(Context.CurrBlock.Next); - } + Context.EmitLoadState(Context.CurrBlock.Next); } else { @@ -93,7 +100,7 @@ namespace ChocolArm64.Instruction Context.EmitLdintzr(Op.Rt); Context.EmitLdc_I(0); - Context.Emit(ILOp, Context.GetLabel(Op.Imm)); + EmitBranch(Context, ILOp); } public static void Ret(AILEmitterCtx Context) @@ -118,7 +125,65 @@ namespace ChocolArm64.Instruction Context.EmitLdc_I(0); - Context.Emit(ILOp, Context.GetLabel(Op.Imm)); + EmitBranch(Context, ILOp); + } + + private static void EmitBranch(AILEmitterCtx Context, ACond Cond) + { + AOpCodeBImm Op = (AOpCodeBImm)Context.CurrOp; + + if (Context.CurrBlock.Next != null && + Context.CurrBlock.Branch != null) + { + Context.EmitCondBranch(Context.GetLabel(Op.Imm), Cond); + } + else + { + Context.EmitStoreState(); + + AILLabel LblTaken = new AILLabel(); + + Context.EmitCondBranch(LblTaken, Cond); + + Context.EmitLdc_I8(Op.Position + 4); + + Context.Emit(OpCodes.Ret); + + Context.MarkLabel(LblTaken); + + Context.EmitLdc_I8(Op.Imm); + + Context.Emit(OpCodes.Ret); + } + } + + private static void EmitBranch(AILEmitterCtx Context, OpCode ILOp) + { + AOpCodeBImm Op = (AOpCodeBImm)Context.CurrOp; + + if (Context.CurrBlock.Next != null && + Context.CurrBlock.Branch != null) + { + Context.Emit(ILOp, Context.GetLabel(Op.Imm)); + } + else + { + Context.EmitStoreState(); + + AILLabel LblTaken = new AILLabel(); + + Context.Emit(ILOp, LblTaken); + + Context.EmitLdc_I8(Op.Position + 4); + + Context.Emit(OpCodes.Ret); + + Context.MarkLabel(LblTaken); + + Context.EmitLdc_I8(Op.Imm); + + Context.Emit(OpCodes.Ret); + } } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMemory.cs b/ChocolArm64/Instruction/AInstEmitMemory.cs similarity index 100% rename from Ryujinx/Cpu/Instruction/AInstEmitMemory.cs rename to ChocolArm64/Instruction/AInstEmitMemory.cs diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs b/ChocolArm64/Instruction/AInstEmitMemoryEx.cs similarity index 100% rename from Ryujinx/Cpu/Instruction/AInstEmitMemoryEx.cs rename to ChocolArm64/Instruction/AInstEmitMemoryEx.cs diff --git a/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs new file mode 100644 index 000000000..6ffcf2dc9 --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs @@ -0,0 +1,188 @@ +using ChocolArm64.Decoder; +using ChocolArm64.Memory; +using ChocolArm64.Translation; +using System; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static class AInstEmitMemoryHelper + { + private enum Extension + { + Zx, + Sx32, + Sx64 + } + + public static void EmitReadZxCall(AILEmitterCtx Context, int Size) + { + EmitReadCall(Context, Extension.Zx, Size); + } + + public static void EmitReadSx32Call(AILEmitterCtx Context, int Size) + { + EmitReadCall(Context, Extension.Sx32, Size); + } + + public static void EmitReadSx64Call(AILEmitterCtx Context, int Size) + { + EmitReadCall(Context, Extension.Sx64, Size); + } + + private static void EmitReadCall(AILEmitterCtx Context, Extension Ext, int Size) + { + bool IsSimd = GetIsSimd(Context); + + string Name = null; + + if (Size < 0 || Size > (IsSimd ? 4 : 3)) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + if (IsSimd) + { + switch (Size) + { + case 0: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadVector8Unchecked) + : nameof(AMemory.ReadVector8); break; + + case 1: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadVector16Unchecked) + : nameof(AMemory.ReadVector16); break; + + case 2: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadVector32Unchecked) + : nameof(AMemory.ReadVector32); break; + + case 3: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadVector64Unchecked) + : nameof(AMemory.ReadVector64); break; + + case 4: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadVector128Unchecked) + : nameof(AMemory.ReadVector128); break; + } + } + else + { + switch (Size) + { + case 0: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadByteUnchecked) + : nameof(AMemory.ReadByte); break; + + case 1: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadUInt16Unchecked) + : nameof(AMemory.ReadUInt16); break; + + case 2: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadUInt32Unchecked) + : nameof(AMemory.ReadUInt32); break; + + case 3: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.ReadUInt64Unchecked) + : nameof(AMemory.ReadUInt64); break; + } + } + + Context.EmitCall(typeof(AMemory), Name); + + if (!IsSimd) + { + if (Ext == Extension.Sx32 || + Ext == Extension.Sx64) + { + switch (Size) + { + case 0: Context.Emit(OpCodes.Conv_I1); break; + case 1: Context.Emit(OpCodes.Conv_I2); break; + case 2: Context.Emit(OpCodes.Conv_I4); break; + } + } + + if (Size < 3) + { + Context.Emit(Ext == Extension.Sx64 + ? OpCodes.Conv_I8 + : OpCodes.Conv_U8); + } + } + } + + public static void EmitWriteCall(AILEmitterCtx Context, int Size) + { + bool IsSimd = GetIsSimd(Context); + + string Name = null; + + if (Size < 0 || Size > (IsSimd ? 4 : 3)) + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } + + if (Size < 3 && !IsSimd) + { + Context.Emit(OpCodes.Conv_I4); + } + + if (IsSimd) + { + switch (Size) + { + case 0: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteVector8Unchecked) + : nameof(AMemory.WriteVector8); break; + + case 1: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteVector16Unchecked) + : nameof(AMemory.WriteVector16); break; + + case 2: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteVector32Unchecked) + : nameof(AMemory.WriteVector32); break; + + case 3: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteVector64Unchecked) + : nameof(AMemory.WriteVector64); break; + + case 4: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteVector128Unchecked) + : nameof(AMemory.WriteVector128); break; + } + } + else + { + switch (Size) + { + case 0: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteByteUnchecked) + : nameof(AMemory.WriteByte); break; + + case 1: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteUInt16Unchecked) + : nameof(AMemory.WriteUInt16); break; + + case 2: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteUInt32Unchecked) + : nameof(AMemory.WriteUInt32); break; + + case 3: Name = AOptimizations.DisableMemoryChecks + ? nameof(AMemory.WriteUInt64Unchecked) + : nameof(AMemory.WriteUInt64); break; + } + } + + Context.EmitCall(typeof(AMemory), Name); + } + + private static bool GetIsSimd(AILEmitterCtx Context) + { + return Context.CurrOp is IAOpCodeSimd && + !(Context.CurrOp is AOpCodeSimdMemMs || + Context.CurrOp is AOpCodeSimdMemSs); + } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMove.cs b/ChocolArm64/Instruction/AInstEmitMove.cs similarity index 100% rename from Ryujinx/Cpu/Instruction/AInstEmitMove.cs rename to ChocolArm64/Instruction/AInstEmitMove.cs diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMul.cs b/ChocolArm64/Instruction/AInstEmitMul.cs similarity index 100% rename from Ryujinx/Cpu/Instruction/AInstEmitMul.cs rename to ChocolArm64/Instruction/AInstEmitMul.cs diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs similarity index 75% rename from Ryujinx/Cpu/Instruction/AInstEmitSimdArithmetic.cs rename to ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index d6ebcc3ff..bf980a581 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -101,6 +101,16 @@ namespace ChocolArm64.Instruction } } + public static void Fabd_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => + { + Context.Emit(OpCodes.Sub); + + EmitUnaryMathCall(Context, nameof(Math.Abs)); + }); + } + public static void Fabs_S(AILEmitterCtx Context) { EmitScalarUnaryOpF(Context, () => @@ -124,6 +134,11 @@ namespace ChocolArm64.Instruction EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div)); } + public static void Fdiv_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Div)); + } + public static void Fmadd_S(AILEmitterCtx Context) { EmitScalarTernaryRaOpF(Context, () => @@ -215,6 +230,24 @@ namespace ChocolArm64.Instruction }); } + public static void Fnmsub_S(AILEmitterCtx Context) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int SizeF = Op.Size & 1; + + EmitVectorExtractF(Context, Op.Rn, 0, SizeF); + 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 Frinta_S(AILEmitterCtx Context) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; @@ -234,6 +267,47 @@ namespace ChocolArm64.Instruction }); } + public static void Frintm_V(AILEmitterCtx Context) + { + EmitVectorUnaryOpF(Context, () => + { + EmitUnaryMathCall(Context, nameof(Math.Floor)); + }); + } + + public static void Frintp_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => + { + EmitUnaryMathCall(Context, nameof(Math.Ceiling)); + }); + } + + public static void Frintx_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 Fsqrt_S(AILEmitterCtx Context) { EmitScalarUnaryOpF(Context, () => @@ -275,6 +349,11 @@ namespace ChocolArm64.Instruction EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); } + public static void Mul_Ve(AILEmitterCtx Context) + { + EmitVectorBinaryOpByElemZx(Context, () => Context.Emit(OpCodes.Mul)); + } + public static void Neg_V(AILEmitterCtx Context) { EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); @@ -282,7 +361,7 @@ namespace ChocolArm64.Instruction public static void Saddw_V(AILEmitterCtx Context) { - EmitVectorWidenBinaryOpSx(Context, () => Context.Emit(OpCodes.Add)); + EmitVectorWidenRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Add)); } public static void Smax_V(AILEmitterCtx Context) @@ -303,6 +382,20 @@ namespace ChocolArm64.Instruction EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); } + public static void Smlal_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmTernaryOpSx(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } + + public static void Smull_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul)); + } + public static void Sub_S(AILEmitterCtx Context) { EmitScalarBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); @@ -333,7 +426,12 @@ namespace ChocolArm64.Instruction public static void Uaddw_V(AILEmitterCtx Context) { - EmitVectorWidenBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); + EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); + } + + public static void Umull_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdCmp.cs b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs similarity index 76% rename from Ryujinx/Cpu/Instruction/AInstEmitSimdCmp.cs rename to ChocolArm64/Instruction/AInstEmitSimdCmp.cs index 92361fbd3..76861b73b 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitSimdCmp.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs @@ -4,6 +4,7 @@ using ChocolArm64.Translation; using System; using System.Reflection.Emit; +using static ChocolArm64.Instruction.AInstEmitAluHelper; using static ChocolArm64.Instruction.AInstEmitSimdHelper; namespace ChocolArm64.Instruction @@ -45,6 +46,45 @@ namespace ChocolArm64.Instruction EmitVectorCmp(Context, OpCodes.Blt_S); } + public static void Cmtst_V(AILEmitterCtx Context) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size)); + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); + EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size); + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.Emit(OpCodes.And); + + Context.EmitLdc_I4(0); + + Context.Emit(OpCodes.Bne_Un_S, LblTrue); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size, 0); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size, (long)SzMask); + + Context.MarkLabel(LblEnd); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + public static void Fccmp_S(AILEmitterCtx Context) { AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; @@ -54,24 +94,9 @@ namespace ChocolArm64.Instruction Context.EmitCondBranch(LblTrue, Op.Cond); - //TODO: Share this logic with Ccmp. - Context.EmitLdc_I4((Op.NZCV >> 0) & 1); + EmitSetNZCV(Context, Op.NZCV); - Context.EmitStflg((int)APState.VBit); - - Context.EmitLdc_I4((Op.NZCV >> 1) & 1); - - Context.EmitStflg((int)APState.CBit); - - Context.EmitLdc_I4((Op.NZCV >> 2) & 1); - - Context.EmitStflg((int)APState.ZBit); - - Context.EmitLdc_I4((Op.NZCV >> 3) & 1); - - Context.EmitStflg((int)APState.NBit); - - Context.Emit(OpCodes.Br_S, LblEnd); + Context.Emit(OpCodes.Br, LblEnd); Context.MarkLabel(LblTrue); @@ -80,12 +105,35 @@ namespace ChocolArm64.Instruction Context.MarkLabel(LblEnd); } + public static void Fccmpe_S(AILEmitterCtx Context) + { + Fccmp_S(Context); + } + 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. + if (CmpWithZero) + { + EmitNaNCheck(Context, Op.Rn); + } + else + { + EmitNaNCheck(Context, Op.Rn); + EmitNaNCheck(Context, Op.Rm); + + Context.Emit(OpCodes.Or); + } + + AILLabel LblNaN = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.Emit(OpCodes.Brtrue_S, LblNaN); + void EmitLoadOpers() { EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); @@ -102,7 +150,7 @@ namespace ChocolArm64.Instruction //Z = Rn == Rm EmitLoadOpers(); - + Context.Emit(OpCodes.Ceq); Context.Emit(OpCodes.Dup); @@ -123,30 +171,18 @@ namespace ChocolArm64.Instruction Context.EmitStflg((int)APState.NBit); - //Handle NaN case. If any number is NaN, then NZCV = 0011. - AILLabel LblNotNaN = new AILLabel(); + //V = 0 + Context.EmitLdc_I4(0); - if (CmpWithZero) - { - EmitNaNCheck(Context, Op.Rn); - } - else - { - EmitNaNCheck(Context, Op.Rn); - EmitNaNCheck(Context, Op.Rm); - - Context.Emit(OpCodes.Or); - } - - Context.Emit(OpCodes.Brfalse_S, LblNotNaN); - - Context.EmitLdc_I4(1); - Context.EmitLdc_I4(1); - - Context.EmitStflg((int)APState.CBit); Context.EmitStflg((int)APState.VBit); - Context.MarkLabel(LblNotNaN); + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblNaN); + + EmitSetNZCV(Context, 0b0011); + + Context.MarkLabel(LblEnd); } public static void Fcmpe_S(AILEmitterCtx Context) diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdCvt.cs b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs similarity index 71% rename from Ryujinx/Cpu/Instruction/AInstEmitSimdCvt.cs rename to ChocolArm64/Instruction/AInstEmitSimdCvt.cs index fbb0dfda5..b9944e565 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitSimdCvt.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs @@ -23,52 +23,127 @@ namespace ChocolArm64.Instruction public static void Fcvtas_Gp(AILEmitterCtx Context) { - Fcvta__Gp(Context, Signed: true); + EmitFcvt_s_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero)); } public static void Fcvtau_Gp(AILEmitterCtx Context) { - Fcvta__Gp(Context, Signed: false); + EmitFcvt_u_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero)); + } + + public static void Fcvtl_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + + int Elems = 4 >> SizeF; + + int Part = Context.CurrOp.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + if (SizeF == 0) + { + //TODO: This need the half precision floating point type, + //that is not yet supported on .NET. We should probably + //do our own implementation on the meantime. + throw new NotImplementedException(); + } + else /* if (SizeF == 1) */ + { + EmitVectorExtractF(Context, Op.Rn, Part + Index, 0); + + Context.Emit(OpCodes.Conv_R8); + } + + EmitVectorInsertF(Context, Op.Rd, Index, SizeF); + } } public static void Fcvtms_Gp(AILEmitterCtx Context) { - EmitFcvt_s_Gp(Context, nameof(Math.Floor)); + EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor))); + } + + public static void Fcvtmu_Gp(AILEmitterCtx Context) + { + EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor))); + } + + public static void Fcvtn_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int SizeF = Op.Size & 1; + + int Elems = 4 >> SizeF; + + int Part = Context.CurrOp.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractF(Context, Op.Rd, Index, SizeF); + + if (SizeF == 0) + { + //TODO: This need the half precision floating point type, + //that is not yet supported on .NET. We should probably + //do our own implementation on the meantime. + throw new NotImplementedException(); + } + else /* if (SizeF == 1) */ + { + Context.Emit(OpCodes.Conv_R4); + + EmitVectorInsertF(Context, Op.Rd, Part + Index, 0); + } + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } } public static void Fcvtps_Gp(AILEmitterCtx Context) { - EmitFcvt_s_Gp(Context, nameof(Math.Ceiling)); + EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling))); + } + + public static void Fcvtpu_Gp(AILEmitterCtx Context) + { + EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling))); } public static void Fcvtzs_Gp(AILEmitterCtx Context) { - EmitFcvtz__Gp(Context, Signed: true); + EmitFcvt_s_Gp(Context, () => { }); } public static void Fcvtzs_Gp_Fix(AILEmitterCtx Context) { - EmitFcvtz__Gp_Fix(Context, Signed: true); + EmitFcvtzs_Gp_Fix(Context); } public static void Fcvtzs_V(AILEmitterCtx Context) { - EmitVectorFcvt(Context, Signed: true); + EmitVectorFcvtzs(Context); } public static void Fcvtzu_Gp(AILEmitterCtx Context) { - EmitFcvtz__Gp(Context, Signed: false); + EmitFcvt_u_Gp(Context, () => { }); } public static void Fcvtzu_Gp_Fix(AILEmitterCtx Context) { - EmitFcvtz__Gp_Fix(Context, Signed: false); + EmitFcvtzu_Gp_Fix(Context); } public static void Fcvtzu_V(AILEmitterCtx Context) { - EmitVectorFcvt(Context, Signed: false); + EmitVectorFcvtzu(Context); } public static void Scvtf_Gp(AILEmitterCtx Context) @@ -91,7 +166,7 @@ namespace ChocolArm64.Instruction { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - EmitVectorExtractSx(Context, Op.Rd, 0, Op.Size + 2); + EmitVectorExtractSx(Context, Op.Rn, 0, Op.Size + 2); EmitFloatCast(Context, Op.Size); @@ -165,13 +240,23 @@ namespace ChocolArm64.Instruction } } - private static void Fcvta__Gp(AILEmitterCtx Context, bool Signed) + private static void EmitFcvt_s_Gp(AILEmitterCtx Context, Action Emit) + { + EmitFcvt___Gp(Context, Emit, true); + } + + private static void EmitFcvt_u_Gp(AILEmitterCtx Context, Action Emit) + { + EmitFcvt___Gp(Context, Emit, false); + } + + private static void EmitFcvt___Gp(AILEmitterCtx Context, Action Emit, bool Signed) { AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - EmitRoundMathCall(Context, MidpointRounding.AwayFromZero); + Emit(); if (Signed) { @@ -190,45 +275,14 @@ namespace ChocolArm64.Instruction Context.EmitStintzr(Op.Rd); } - private static void EmitFcvt_s_Gp(AILEmitterCtx Context, string Name) + private static void EmitFcvtzs_Gp_Fix(AILEmitterCtx Context) { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - EmitUnaryMathCall(Context, Name); - - EmitScalarFcvts(Context, Op.Size, 0); - - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U8); - } - - Context.EmitStintzr(Op.Rd); + EmitFcvtz__Gp_Fix(Context, true); } - private static void EmitFcvtz__Gp(AILEmitterCtx Context, bool Signed) + private static void EmitFcvtzu_Gp_Fix(AILEmitterCtx Context) { - AOpCodeSimdCvt Op = (AOpCodeSimdCvt)Context.CurrOp; - - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); - - if (Signed) - { - EmitScalarFcvts(Context, Op.Size, 0); - } - else - { - EmitScalarFcvtu(Context, Op.Size, 0); - } - - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) - { - Context.Emit(OpCodes.Conv_U8); - } - - Context.EmitStintzr(Op.Rd); + EmitFcvtz__Gp_Fix(Context, false); } private static void EmitFcvtz__Gp_Fix(AILEmitterCtx Context, bool Signed) @@ -254,6 +308,16 @@ namespace ChocolArm64.Instruction Context.EmitStintzr(Op.Rd); } + private static void EmitVectorScvtf(AILEmitterCtx Context) + { + EmitVectorCvtf(Context, true); + } + + private static void EmitVectorUcvtf(AILEmitterCtx Context) + { + EmitVectorCvtf(Context, false); + } + private static void EmitVectorCvtf(AILEmitterCtx Context, bool Signed) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; @@ -289,7 +353,17 @@ namespace ChocolArm64.Instruction } } - private static void EmitVectorFcvt(AILEmitterCtx Context, bool Signed) + private static void EmitVectorFcvtzs(AILEmitterCtx Context) + { + EmitVectorFcvtz(Context, true); + } + + private static void EmitVectorFcvtzu(AILEmitterCtx Context) + { + EmitVectorFcvtz(Context, false); + } + + private static void EmitVectorFcvtz(AILEmitterCtx Context, bool Signed) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs similarity index 79% rename from Ryujinx/Cpu/Instruction/AInstEmitSimdHelper.cs rename to ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 97f288161..4e45a11d6 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -36,20 +36,18 @@ namespace ChocolArm64.Instruction { IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + int SizeF = Op.Size & 1; + MethodInfo MthdInfo; - if (Op.Size == 0) + if (SizeF == 0) { MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float) }); } - else if (Op.Size == 1) + else /* if (SizeF == 1) */ { MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double) }); } - else - { - throw new InvalidOperationException(); - } Context.EmitCall(MthdInfo); } @@ -58,20 +56,18 @@ namespace ChocolArm64.Instruction { IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + int SizeF = Op.Size & 1; + MethodInfo MthdInfo; - if (Op.Size == 0) + if (SizeF == 0) { MthdInfo = typeof(MathF).GetMethod(Name, new Type[] { typeof(float), typeof(float) }); } - else if (Op.Size == 1) + else /* if (SizeF == 1) */ { MthdInfo = typeof(Math).GetMethod(Name, new Type[] { typeof(double), typeof(double) }); } - else - { - throw new InvalidOperationException(); - } Context.EmitCall(MthdInfo); } @@ -80,28 +76,26 @@ namespace ChocolArm64.Instruction { IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; + int SizeF = Op.Size & 1; + Context.EmitLdc_I4((int)RoundMode); MethodInfo MthdInfo; Type[] Types = new Type[] { null, typeof(MidpointRounding) }; - Types[0] = Op.Size == 0 + Types[0] = SizeF == 0 ? typeof(float) : typeof(double); - if (Op.Size == 0) + if (SizeF == 0) { MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types); } - else if (Op.Size == 1) + else /* if (SizeF == 1) */ { MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types); } - else - { - throw new InvalidOperationException(); - } Context.EmitCall(MthdInfo); } @@ -133,7 +127,7 @@ namespace ChocolArm64.Instruction public static void EmitScalarOp(AILEmitterCtx Context, Action Emit, OperFlags Opers, bool Signed) { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; if (Opers.HasFlag(OperFlags.Rd)) { @@ -147,7 +141,7 @@ namespace ChocolArm64.Instruction if (Opers.HasFlag(OperFlags.Rm)) { - EmitVectorExtract(Context, Op.Rm, 0, Op.Size, Signed); + EmitVectorExtract(Context, ((AOpCodeSimdReg)Op).Rm, 0, Op.Size, Signed); } Emit(); @@ -196,6 +190,11 @@ namespace ChocolArm64.Instruction EmitScalarSetF(Context, Op.Rd, SizeF); } + public static void EmitVectorUnaryOpF(AILEmitterCtx Context, Action Emit) + { + EmitVectorOpF(Context, Emit, OperFlags.Rn); + } + public static void EmitVectorBinaryOpF(AILEmitterCtx Context, Action Emit) { EmitVectorOpF(Context, Emit, OperFlags.RnRm); @@ -206,23 +205,9 @@ namespace ChocolArm64.Instruction EmitVectorOpF(Context, Emit, OperFlags.RdRnRm); } - public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; - - EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false); - } - - public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit) - { - AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; - - EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true); - } - public static void EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers) { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int SizeF = Op.Size & 1; @@ -242,7 +227,7 @@ namespace ChocolArm64.Instruction if (Opers.HasFlag(OperFlags.Rm)) { - EmitVectorExtractF(Context, Op.Rm, Index, SizeF); + EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, Index, SizeF); } Emit(); @@ -256,6 +241,20 @@ namespace ChocolArm64.Instruction } } + public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; + + EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false); + } + + public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp; + + EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true); + } + public static void EmitVectorOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary) { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; @@ -347,6 +346,54 @@ namespace ChocolArm64.Instruction } } + public static void EmitVectorBinaryOpByElemSx(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; + + EmitVectorOpByElem(Context, Emit, Op.Index, false, true); + } + + public static void EmitVectorBinaryOpByElemZx(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; + + EmitVectorOpByElem(Context, Emit, Op.Index, false, false); + } + + public static void EmitVectorTernaryOpByElemZx(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp; + + EmitVectorOpByElem(Context, Emit, Op.Index, true, false); + } + + public static void EmitVectorOpByElem(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary, bool Signed) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + if (Ternary) + { + EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); + } + + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); + EmitVectorExtract(Context, Op.Rm, Index, Op.Size, Signed); + + Emit(); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + public static void EmitVectorImmUnaryOp(AILEmitterCtx Context, Action Emit) { EmitVectorImmOp(Context, Emit, false); @@ -383,17 +430,17 @@ namespace ChocolArm64.Instruction } } - public static void EmitVectorWidenBinaryOpSx(AILEmitterCtx Context, Action Emit) + public static void EmitVectorWidenRmBinaryOpSx(AILEmitterCtx Context, Action Emit) { - EmitVectorWidenBinaryOp(Context, Emit, true); + EmitVectorWidenRmBinaryOp(Context, Emit, true); } - public static void EmitVectorWidenBinaryOpZx(AILEmitterCtx Context, Action Emit) + public static void EmitVectorWidenRmBinaryOpZx(AILEmitterCtx Context, Action Emit) { - EmitVectorWidenBinaryOp(Context, Emit, false); + EmitVectorWidenRmBinaryOp(Context, Emit, false); } - public static void EmitVectorWidenBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed) + public static void EmitVectorWidenRmBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed) { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; @@ -415,6 +462,53 @@ namespace ChocolArm64.Instruction Context.EmitStvec(Op.Rd); } + public static void EmitVectorWidenRnRmBinaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRnRmOp(Context, Emit, false, true); + } + + public static void EmitVectorWidenRnRmBinaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRnRmOp(Context, Emit, false, false); + } + + public static void EmitVectorWidenRnRmTernaryOpSx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRnRmOp(Context, Emit, true, true); + } + + public static void EmitVectorWidenRnRmTernaryOpZx(AILEmitterCtx Context, Action Emit) + { + EmitVectorWidenRnRmOp(Context, Emit, true, false); + } + + public static void EmitVectorWidenRnRmOp(AILEmitterCtx Context, Action Emit, bool Ternary, bool Signed) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + for (int Index = 0; Index < Elems; Index++) + { + if (Ternary) + { + EmitVectorExtract(Context, Op.Rd, Index, Op.Size + 1, Signed); + } + + EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed); + EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed); + + Emit(); + + EmitVectorInsertTmp(Context, Index, Op.Size + 1); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + } + public static void EmitScalarSet(AILEmitterCtx Context, int Reg, int Size) { EmitVectorZeroAll(Context, Reg); diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdLogical.cs b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs similarity index 72% rename from Ryujinx/Cpu/Instruction/AInstEmitSimdLogical.cs rename to ChocolArm64/Instruction/AInstEmitSimdLogical.cs index ea4b17b3d..5b71a0bbb 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitSimdLogical.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs @@ -1,3 +1,5 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; using ChocolArm64.Translation; using System.Reflection.Emit; @@ -65,5 +67,28 @@ namespace ChocolArm64.Instruction { EmitVectorImmBinaryOp(Context, () => Context.Emit(OpCodes.Or)); } + + public static void Rev64_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + + int RevIndex = Elems - 1; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, RevIndex--, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdMemory.cs b/ChocolArm64/Instruction/AInstEmitSimdMemory.cs similarity index 100% rename from Ryujinx/Cpu/Instruction/AInstEmitSimdMemory.cs rename to ChocolArm64/Instruction/AInstEmitSimdMemory.cs diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs b/ChocolArm64/Instruction/AInstEmitSimdMove.cs similarity index 83% rename from Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs rename to ChocolArm64/Instruction/AInstEmitSimdMove.cs index c8f690328..3f427ad8a 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitSimdMove.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdMove.cs @@ -57,6 +57,34 @@ namespace ChocolArm64.Instruction } } + public static void Ext_V(AILEmitterCtx Context) + { + AOpCodeSimdExt Op = (AOpCodeSimdExt)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Position = Op.Imm4; + + for (int Index = 0; Index < Bytes; Index++) + { + int Reg = Op.Imm4 + Index < Bytes ? Op.Rn : Op.Rm; + + if (Position == Bytes) + { + Position = 0; + } + + EmitVectorExtractZx(Context, Reg, Position++, 0); + + EmitVectorInsert(Context, Op.Rd, Index, 0); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + public static void Fcsel_S(AILEmitterCtx Context) { AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; @@ -263,6 +291,16 @@ namespace ChocolArm64.Instruction } } + public static void Zip1_V(AILEmitterCtx Context) + { + EmitVectorZip(Context, Part: 0); + } + + public static void Zip2_V(AILEmitterCtx Context) + { + EmitVectorZip(Context, Part: 1); + } + private static void EmitIntZeroHigherIfNeeded(AILEmitterCtx Context) { if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) @@ -295,5 +333,29 @@ namespace ChocolArm64.Instruction EmitVectorZeroUpper(Context, Op.Rd); } } + + private static void EmitVectorZip(AILEmitterCtx Context, int Part) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + int Half = Elems >> 1; + + for (int Index = 0; Index < Elems; Index++) + { + int Elem = Part * Half + (Index >> 1); + + 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); + } + } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSimdShift.cs b/ChocolArm64/Instruction/AInstEmitSimdShift.cs similarity index 77% rename from Ryujinx/Cpu/Instruction/AInstEmitSimdShift.cs rename to ChocolArm64/Instruction/AInstEmitSimdShift.cs index 165642342..bb8a8f178 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitSimdShift.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdShift.cs @@ -10,15 +10,6 @@ namespace ChocolArm64.Instruction { static partial class AInstEmit { - [Flags] - private enum ShrFlags - { - None = 0, - Signed = 1 << 0, - Rounding = 1 << 1, - Accumulate = 1 << 2 - } - public static void Shl_S(AILEmitterCtx Context) { AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; @@ -38,7 +29,16 @@ namespace ChocolArm64.Instruction int Shift = Op.Imm - (8 << Op.Size); - EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); + EmitVectorBinaryShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); + } + + public static void Shll_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Shift = 8 << Op.Size; + + EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); } public static void Shrn_V(AILEmitterCtx Context) @@ -83,7 +83,22 @@ namespace ChocolArm64.Instruction int Shift = (8 << (Op.Size + 1)) - Op.Imm; - EmitVectorShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift); + EmitVectorBinaryShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift); + } + + public static void Ssra_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Shift = (8 << (Op.Size + 1)) - Op.Imm; + + Action Emit = () => + { + Context.Emit(OpCodes.Shr); + Context.Emit(OpCodes.Add); + }; + + EmitVectorTernaryShImmBinarySx(Context, Emit, Shift); } public static void Ushl_V(AILEmitterCtx Context) @@ -100,14 +115,43 @@ namespace ChocolArm64.Instruction EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); } + public static void Ushr_S(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + EmitScalarUnaryOpZx(Context, () => + { + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.Emit(OpCodes.Shr_Un); + }); + } + public static void Ushr_V(AILEmitterCtx Context) { - EmitVectorShr(Context, ShrFlags.None); + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + EmitVectorUnaryOpZx(Context, () => + { + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.Emit(OpCodes.Shr_Un); + }); } public static void Usra_V(AILEmitterCtx Context) { - EmitVectorShr(Context, ShrFlags.Accumulate); + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + Action Emit = () => + { + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.Emit(OpCodes.Shr_Un); + Context.Emit(OpCodes.Add); + }; + + EmitVectorOp(Context, Emit, OperFlags.RdRn, Signed: false); } private static void EmitVectorShl(AILEmitterCtx Context, bool Signed) @@ -173,53 +217,34 @@ namespace ChocolArm64.Instruction } } - private static void EmitVectorShr(AILEmitterCtx Context, ShrFlags Flags) + private static void EmitVectorBinaryShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - - int Shift = (8 << (Op.Size + 1)) - Op.Imm; - - if (Flags.HasFlag(ShrFlags.Accumulate)) - { - Action Emit = () => - { - Context.EmitLdc_I4(Shift); - - Context.Emit(OpCodes.Shr_Un); - Context.Emit(OpCodes.Add); - }; - - EmitVectorOp(Context, Emit, OperFlags.RdRn, Signed: false); - } - else - { - EmitVectorUnaryOpZx(Context, () => - { - Context.EmitLdc_I4(Shift); - - Context.Emit(OpCodes.Shr_Un); - }); - } + EmitVectorShImmBinaryOp(Context, Emit, Imm, false, true); } - private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) + private static void EmitVectorTernaryShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) { - EmitVectorShImmBinaryOp(Context, Emit, Imm, true); + EmitVectorShImmBinaryOp(Context, Emit, Imm, true, true); } - private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) + private static void EmitVectorBinaryShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) { - EmitVectorShImmBinaryOp(Context, Emit, Imm, false); + EmitVectorShImmBinaryOp(Context, Emit, Imm, false, false); } - private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) + private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Ternary, bool Signed) { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int Bytes = Context.CurrOp.GetBitsCount() >> 3; for (int Index = 0; Index < (Bytes >> Op.Size); Index++) { + if (Ternary) + { + EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed); + } + EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); Context.EmitLdc_I4(Imm); @@ -247,7 +272,7 @@ namespace ChocolArm64.Instruction private static void EmitVectorShImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int Elems = 8 >> Op.Size; @@ -282,7 +307,7 @@ namespace ChocolArm64.Instruction private static void EmitVectorShImmWidenBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) { - AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; int Elems = 8 >> Op.Size; diff --git a/Ryujinx/Cpu/Instruction/AInstEmitSystem.cs b/ChocolArm64/Instruction/AInstEmitSystem.cs similarity index 97% rename from Ryujinx/Cpu/Instruction/AInstEmitSystem.cs rename to ChocolArm64/Instruction/AInstEmitSystem.cs index 6754be7a8..f9d186020 100644 --- a/Ryujinx/Cpu/Instruction/AInstEmitSystem.cs +++ b/ChocolArm64/Instruction/AInstEmitSystem.cs @@ -9,6 +9,11 @@ namespace ChocolArm64.Instruction { static partial class AInstEmit { + public static void Hint(AILEmitterCtx Context) + { + //Execute as no-op. + } + public static void Mrs(AILEmitterCtx Context) { AOpCodeSystem Op = (AOpCodeSystem)Context.CurrOp; diff --git a/Ryujinx/Cpu/Instruction/AInstEmitter.cs b/ChocolArm64/Instruction/AInstEmitter.cs similarity index 100% rename from Ryujinx/Cpu/Instruction/AInstEmitter.cs rename to ChocolArm64/Instruction/AInstEmitter.cs diff --git a/Ryujinx/Cpu/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs similarity index 85% rename from Ryujinx/Cpu/Instruction/ASoftFallback.cs rename to ChocolArm64/Instruction/ASoftFallback.cs index b70d0b4f4..797d81573 100644 --- a/Ryujinx/Cpu/Instruction/ASoftFallback.cs +++ b/ChocolArm64/Instruction/ASoftFallback.cs @@ -1,6 +1,7 @@ using ChocolArm64.State; using ChocolArm64.Translation; using System; +using System.Numerics; using System.Runtime.CompilerServices; namespace ChocolArm64.Instruction @@ -74,7 +75,7 @@ namespace ChocolArm64.Instruction private static ulong ReverseBytes(ulong Value, RevSize Size) { - Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8); + Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8); if (Size == RevSize.Rev16) { @@ -101,6 +102,8 @@ namespace ChocolArm64.Instruction [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int SatF32ToS32(float Value) { + if (float.IsNaN(Value)) return 0; + return Value > int.MaxValue ? int.MaxValue : Value < int.MinValue ? int.MinValue : (int)Value; } @@ -108,6 +111,8 @@ namespace ChocolArm64.Instruction [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long SatF32ToS64(float Value) { + if (float.IsNaN(Value)) return 0; + return Value > long.MaxValue ? long.MaxValue : Value < long.MinValue ? long.MinValue : (long)Value; } @@ -115,6 +120,8 @@ namespace ChocolArm64.Instruction [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint SatF32ToU32(float Value) { + if (float.IsNaN(Value)) return 0; + return Value > uint.MaxValue ? uint.MaxValue : Value < uint.MinValue ? uint.MinValue : (uint)Value; } @@ -122,6 +129,8 @@ namespace ChocolArm64.Instruction [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong SatF32ToU64(float Value) { + if (float.IsNaN(Value)) return 0; + return Value > ulong.MaxValue ? ulong.MaxValue : Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; } @@ -129,6 +138,8 @@ namespace ChocolArm64.Instruction [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int SatF64ToS32(double Value) { + if (double.IsNaN(Value)) return 0; + return Value > int.MaxValue ? int.MaxValue : Value < int.MinValue ? int.MinValue : (int)Value; } @@ -136,6 +147,8 @@ namespace ChocolArm64.Instruction [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long SatF64ToS64(double Value) { + if (double.IsNaN(Value)) return 0; + return Value > long.MaxValue ? long.MaxValue : Value < long.MinValue ? long.MinValue : (long)Value; } @@ -143,6 +156,8 @@ namespace ChocolArm64.Instruction [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint SatF64ToU32(double Value) { + if (double.IsNaN(Value)) return 0; + return Value > uint.MaxValue ? uint.MaxValue : Value < uint.MinValue ? uint.MinValue : (uint)Value; } @@ -150,46 +165,20 @@ namespace ChocolArm64.Instruction [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong SatF64ToU64(double Value) { + if (double.IsNaN(Value)) return 0; + return Value > ulong.MaxValue ? ulong.MaxValue : Value < ulong.MinValue ? ulong.MinValue : (ulong)Value; } - public static ulong SMulHi128(ulong LHS, ulong RHS) + public static long SMulHi128(long LHS, long RHS) { - long LLo = (uint)(LHS >> 0); - long LHi = (int)(LHS >> 32); - long RLo = (uint)(RHS >> 0); - long RHi = (int)(RHS >> 32); - - long LHiRHi = LHi * RHi; - long LHiRLo = LHi * RLo; - long LLoRHi = LLo * RHi; - long LLoRLo = LLo * RLo; - - long Carry = ((uint)LHiRLo + ((uint)LLoRHi + (LLoRLo >> 32))) >> 32; - - long ResHi = LHiRHi + (LHiRLo >> 32) + (LLoRHi >> 32) + Carry; - - return (ulong)ResHi; + return (long)(BigInteger.Multiply(LHS, RHS) >> 64); } public static ulong UMulHi128(ulong LHS, ulong RHS) { - ulong LLo = (uint)(LHS >> 0); - ulong LHi = (uint)(LHS >> 32); - ulong RLo = (uint)(RHS >> 0); - ulong RHi = (uint)(RHS >> 32); - - ulong LHiRHi = LHi * RHi; - ulong LHiRLo = LHi * RLo; - ulong LLoRHi = LLo * RHi; - ulong LLoRLo = LLo * RLo; - - ulong Carry = ((uint)LHiRLo + ((uint)LLoRHi + (LLoRLo >> 32))) >> 32; - - ulong ResHi = LHiRHi + (LHiRLo >> 32) + (LLoRHi >> 32) + Carry; - - return ResHi; + return (ulong)(BigInteger.Multiply(LHS, RHS) >> 64); } public static int CountSetBits8(byte Value) @@ -200,6 +189,32 @@ namespace ChocolArm64.Instruction (Value >> 6) & 1 + (Value >> 7); } + public static float RoundF(float Value, int Fpcr) + { + switch ((ARoundMode)((Fpcr >> 22) & 3)) + { + case ARoundMode.ToNearest: return MathF.Round (Value); + case ARoundMode.TowardsPlusInfinity: return MathF.Ceiling (Value); + case ARoundMode.TowardsMinusInfinity: return MathF.Floor (Value); + case ARoundMode.TowardsZero: return MathF.Truncate(Value); + } + + throw new InvalidOperationException(); + } + + public static double Round(double Value, int Fpcr) + { + switch ((ARoundMode)((Fpcr >> 22) & 3)) + { + case ARoundMode.ToNearest: return Math.Round (Value); + case ARoundMode.TowardsPlusInfinity: return Math.Ceiling (Value); + case ARoundMode.TowardsMinusInfinity: return Math.Floor (Value); + case ARoundMode.TowardsZero: return Math.Truncate(Value); + } + + throw new InvalidOperationException(); + } + public static AVec Tbl1_V64(AVec Vector, AVec Tb0) { return Tbl(Vector, 8, Tb0); diff --git a/Ryujinx/Cpu/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs similarity index 50% rename from Ryujinx/Cpu/Memory/AMemory.cs rename to ChocolArm64/Memory/AMemory.cs index f2abffbf4..d7e11189f 100644 --- a/Ryujinx/Cpu/Memory/AMemory.cs +++ b/ChocolArm64/Memory/AMemory.cs @@ -2,11 +2,11 @@ using ChocolArm64.Exceptions; using ChocolArm64.State; using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace ChocolArm64.Memory { - public unsafe class AMemory + public unsafe class AMemory : IDisposable { private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1; @@ -39,16 +39,20 @@ namespace ChocolArm64.Memory private HashSet ExAddrs; + public IntPtr Ram { get; private set; } + private byte* RamPtr; - public AMemory(IntPtr Ram, AMemoryAlloc Allocator) + public AMemory() { - Manager = new AMemoryMgr(Allocator); + Manager = new AMemoryMgr(); Monitors = new Dictionary(); ExAddrs = new HashSet(); + Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize + AMemoryMgr.PageSize); + RamPtr = (byte*)Ram; } @@ -134,98 +138,79 @@ namespace ChocolArm64.Memory } } - public sbyte ReadSByte(long Position) => (sbyte)ReadByte (Position); - public short ReadInt16(long Position) => (short)ReadUInt16(Position); - public int ReadInt32(long Position) => (int)ReadUInt32(Position); - public long ReadInt64(long Position) => (long)ReadUInt64(Position); + public sbyte ReadSByte(long Position) + { + return (sbyte)ReadByte(Position); + } + + public short ReadInt16(long Position) + { + return (short)ReadUInt16(Position); + } + + public int ReadInt32(long Position) + { + return (int)ReadUInt32(Position); + } + + public long ReadInt64(long Position) + { + return (long)ReadUInt64(Position); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public byte ReadByte(long Position) { -#if DEBUG EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif - return *((byte*)(RamPtr + (uint)Position)); + return ReadByteUnchecked(Position); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public ushort ReadUInt16(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif + EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); + EnsureAccessIsValid(Position + 1, AMemoryPerm.Read); - return *((ushort*)(RamPtr + (uint)Position)); + return ReadUInt16Unchecked(Position); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public uint ReadUInt32(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif + EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); + EnsureAccessIsValid(Position + 3, AMemoryPerm.Read); - return *((uint*)(RamPtr + (uint)Position)); + return ReadUInt32Unchecked(Position); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public ulong ReadUInt64(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif + EnsureAccessIsValid(Position + 0, AMemoryPerm.Read); + EnsureAccessIsValid(Position + 7, AMemoryPerm.Read); - return *((ulong*)(RamPtr + (uint)Position)); + return ReadUInt64Unchecked(Position); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector8(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif - return new AVec() { B0 = ReadByte(Position) }; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector16(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif - return new AVec() { H0 = ReadUInt16(Position) }; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector32(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif - return new AVec() { W0 = ReadUInt32(Position) }; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector64(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif - return new AVec() { X0 = ReadUInt64(Position) }; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public AVec ReadVector128(long Position) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Read); -#endif - return new AVec() { X0 = ReadUInt64(Position + 0), @@ -233,102 +218,218 @@ namespace ChocolArm64.Memory }; } - public void WriteSByte(long Position, sbyte Value) => WriteByte (Position, (byte)Value); - public void WriteInt16(long Position, short Value) => WriteUInt16(Position, (ushort)Value); - public void WriteInt32(long Position, int Value) => WriteUInt32(Position, (uint)Value); - public void WriteInt64(long Position, long Value) => WriteUInt64(Position, (ulong)Value); + public sbyte ReadSByteUnchecked(long Position) + { + return (sbyte)ReadByteUnchecked(Position); + } + + public short ReadInt16Unchecked(long Position) + { + return (short)ReadUInt16Unchecked(Position); + } + + public int ReadInt32Unchecked(long Position) + { + return (int)ReadUInt32Unchecked(Position); + } + + public long ReadInt64Unchecked(long Position) + { + return (long)ReadUInt64Unchecked(Position); + } + + public byte ReadByteUnchecked(long Position) + { + return *((byte*)(RamPtr + (uint)Position)); + } + + public ushort ReadUInt16Unchecked(long Position) + { + return *((ushort*)(RamPtr + (uint)Position)); + } + + public uint ReadUInt32Unchecked(long Position) + { + return *((uint*)(RamPtr + (uint)Position)); + } + + public ulong ReadUInt64Unchecked(long Position) + { + return *((ulong*)(RamPtr + (uint)Position)); + } + + public AVec ReadVector8Unchecked(long Position) + { + return new AVec() { B0 = ReadByteUnchecked(Position) }; + } + + public AVec ReadVector16Unchecked(long Position) + { + return new AVec() { H0 = ReadUInt16Unchecked(Position) }; + } + + public AVec ReadVector32Unchecked(long Position) + { + return new AVec() { W0 = ReadUInt32Unchecked(Position) }; + } + + public AVec ReadVector64Unchecked(long Position) + { + return new AVec() { X0 = ReadUInt64Unchecked(Position) }; + } + + public AVec ReadVector128Unchecked(long Position) + { + return new AVec() + { + X0 = ReadUInt64Unchecked(Position + 0), + X1 = ReadUInt64Unchecked(Position + 8) + }; + } + + public void WriteSByte(long Position, sbyte Value) + { + WriteByte(Position, (byte)Value); + } + + public void WriteInt16(long Position, short Value) + { + WriteUInt16(Position, (ushort)Value); + } + + public void WriteInt32(long Position, int Value) + { + WriteUInt32(Position, (uint)Value); + } + + public void WriteInt64(long Position, long Value) + { + WriteUInt64(Position, (ulong)Value); + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteByte(long Position, byte Value) { -#if DEBUG EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif - *((byte*)(RamPtr + (uint)Position)) = Value; + WriteByteUnchecked(Position, Value); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt16(long Position, ushort Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif + EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); + EnsureAccessIsValid(Position + 1, AMemoryPerm.Write); - *((ushort*)(RamPtr + (uint)Position)) = Value; + WriteUInt16Unchecked(Position, Value); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt32(long Position, uint Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif + EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); + EnsureAccessIsValid(Position + 3, AMemoryPerm.Write); - *((uint*)(RamPtr + (uint)Position)) = Value; + WriteUInt32Unchecked(Position, Value); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteUInt64(long Position, ulong Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif + EnsureAccessIsValid(Position + 0, AMemoryPerm.Write); + EnsureAccessIsValid(Position + 7, AMemoryPerm.Write); - *((ulong*)(RamPtr + (uint)Position)) = Value; + WriteUInt64Unchecked(Position, Value); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector8(long Position, AVec Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif - WriteByte(Position, Value.B0); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector16(long Position, AVec Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif - WriteUInt16(Position, Value.H0); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector32(long Position, AVec Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif - WriteUInt32(Position, Value.W0); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector64(long Position, AVec Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif - WriteUInt64(Position, Value.X0); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector128(long Position, AVec Value) { -#if DEBUG - EnsureAccessIsValid(Position, AMemoryPerm.Write); -#endif - WriteUInt64(Position + 0, Value.X0); WriteUInt64(Position + 8, Value.X1); } + public void WriteSByteUnchecked(long Position, sbyte Value) + { + WriteByteUnchecked(Position, (byte)Value); + } + + public void WriteInt16Unchecked(long Position, short Value) + { + WriteUInt16Unchecked(Position, (ushort)Value); + } + + public void WriteInt32Unchecked(long Position, int Value) + { + WriteUInt32Unchecked(Position, (uint)Value); + } + + public void WriteInt64Unchecked(long Position, long Value) + { + WriteUInt64Unchecked(Position, (ulong)Value); + } + + public void WriteByteUnchecked(long Position, byte Value) + { + *((byte*)(RamPtr + (uint)Position)) = Value; + } + + public void WriteUInt16Unchecked(long Position, ushort Value) + { + *((ushort*)(RamPtr + (uint)Position)) = Value; + } + + public void WriteUInt32Unchecked(long Position, uint Value) + { + *((uint*)(RamPtr + (uint)Position)) = Value; + } + + public void WriteUInt64Unchecked(long Position, ulong Value) + { + *((ulong*)(RamPtr + (uint)Position)) = Value; + } + + public void WriteVector8Unchecked(long Position, AVec Value) + { + WriteByteUnchecked(Position, Value.B0); + } + + public void WriteVector16Unchecked(long Position, AVec Value) + { + WriteUInt16Unchecked(Position, Value.H0); + } + + public void WriteVector32Unchecked(long Position, AVec Value) + { + WriteUInt32Unchecked(Position, Value.W0); + } + + public void WriteVector64Unchecked(long Position, AVec Value) + { + WriteUInt64Unchecked(Position, Value.X0); + } + + public void WriteVector128Unchecked(long Position, AVec Value) + { + WriteUInt64Unchecked(Position + 0, Value.X0); + WriteUInt64Unchecked(Position + 8, Value.X1); + } + private void EnsureAccessIsValid(long Position, AMemoryPerm Perm) { if (!Manager.IsMapped(Position)) @@ -341,5 +442,20 @@ namespace ChocolArm64.Memory throw new VmmAccessViolationException(Position, Perm); } } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (Ram != IntPtr.Zero) + { + Marshal.FreeHGlobal(Ram); + + Ram = IntPtr.Zero; + } + } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/Memory/AMemoryHelper.cs b/ChocolArm64/Memory/AMemoryHelper.cs similarity index 100% rename from Ryujinx/Cpu/Memory/AMemoryHelper.cs rename to ChocolArm64/Memory/AMemoryHelper.cs diff --git a/Ryujinx/Cpu/Memory/AMemoryMapInfo.cs b/ChocolArm64/Memory/AMemoryMapInfo.cs similarity index 94% rename from Ryujinx/Cpu/Memory/AMemoryMapInfo.cs rename to ChocolArm64/Memory/AMemoryMapInfo.cs index 44b2cc079..02dd3055c 100644 --- a/Ryujinx/Cpu/Memory/AMemoryMapInfo.cs +++ b/ChocolArm64/Memory/AMemoryMapInfo.cs @@ -1,6 +1,6 @@ namespace ChocolArm64.Memory { - public struct AMemoryMapInfo + public class AMemoryMapInfo { public long Position { get; private set; } public long Size { get; private set; } diff --git a/Ryujinx/Cpu/Memory/AMemoryMgr.cs b/ChocolArm64/Memory/AMemoryMgr.cs similarity index 53% rename from Ryujinx/Cpu/Memory/AMemoryMgr.cs rename to ChocolArm64/Memory/AMemoryMgr.cs index 05284059e..8a165b079 100644 --- a/Ryujinx/Cpu/Memory/AMemoryMgr.cs +++ b/ChocolArm64/Memory/AMemoryMgr.cs @@ -1,26 +1,26 @@ +using System; + namespace ChocolArm64.Memory { public class AMemoryMgr { - public const long AddrSize = RamSize; public const long RamSize = 4L * 1024 * 1024 * 1024; + public const long AddrSize = RamSize; - private const int PTLvl0Bits = 11; - private const int PTLvl1Bits = 13; - private const int PTPageBits = 12; + private const int PTLvl0Bits = 10; + private const int PTLvl1Bits = 10; + private const int PTPageBits = 12; - private const int PTLvl0Size = 1 << PTLvl0Bits; - private const int PTLvl1Size = 1 << PTLvl1Bits; - public const int PageSize = 1 << PTPageBits; + private const int PTLvl0Size = 1 << PTLvl0Bits; + private const int PTLvl1Size = 1 << PTLvl1Bits; + public const int PageSize = 1 << PTPageBits; - private const int PTLvl0Mask = PTLvl0Size - 1; - private const int PTLvl1Mask = PTLvl1Size - 1; - public const int PageMask = PageSize - 1; + private const int PTLvl0Mask = PTLvl0Size - 1; + private const int PTLvl1Mask = PTLvl1Size - 1; + public const int PageMask = PageSize - 1; - private const int PTLvl0Bit = PTPageBits + PTLvl0Bits; - private const int PTLvl1Bit = PTPageBits; - - private AMemoryAlloc Allocator; + private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; + private const int PTLvl1Bit = PTPageBits; private enum PTMap { @@ -47,132 +47,24 @@ namespace ChocolArm64.Memory private PTEntry[][] PageTable; - private bool IsHeapInitialized; - - public long HeapAddr { get; private set; } - public long HeapSize { get; private set; } - - public AMemoryMgr(AMemoryAlloc Allocator) + public AMemoryMgr() { - this.Allocator = Allocator; - PageTable = new PTEntry[PTLvl0Size][]; } - public long GetTotalMemorySize() + public void Map(long Position, long Size, int Type, AMemoryPerm Perm) { - return Allocator.GetFreeMem() + GetUsedMemorySize(); + SetPTEntry(Position, Size, new PTEntry(PTMap.Mapped, Perm, Type, 0)); } - public long GetUsedMemorySize() + public void Unmap(long Position, long Size) { - long Size = 0; - - for (int L0 = 0; L0 < PageTable.Length; L0++) - { - if (PageTable[L0] == null) - { - continue; - } - - for (int L1 = 0; L1 < PageTable[L0].Length; L1++) - { - Size += PageTable[L0][L1].Map != PTMap.Unmapped ? PageSize : 0; - } - } - - return Size; + SetPTEntry(Position, Size, new PTEntry(PTMap.Unmapped, 0, 0, 0)); } - public bool SetHeapAddr(long Position) + public void Unmap(long Position, long Size, int Type) { - if (!IsHeapInitialized) - { - HeapAddr = Position; - - IsHeapInitialized = true; - - return true; - } - - return false; - } - - public void SetHeapSize(long Size, int Type) - { - //TODO: Return error when theres no enough space to allocate heap. - Size = AMemoryHelper.PageRoundUp(Size); - - long Position = HeapAddr; - - if ((ulong)Size < (ulong)HeapSize) - { - //Try to free now free area if size is smaller than old size. - Position += Size; - - while ((ulong)Size < (ulong)HeapSize) - { - Allocator.Free(Position); - - Position += PageSize; - } - } - else - { - //Allocate extra needed size. - Position += HeapSize; - Size -= HeapSize; - - MapPhys(Position, Size, Type, AMemoryPerm.RW); - } - - HeapSize = Size; - } - - public void MapPhys(long Position, long Size, int Type, AMemoryPerm Perm) - { - while (Size > 0) - { - if (!IsMapped(Position)) - { - SetPTEntry(Position, new PTEntry(PTMap.Mapped, Perm, Type, 0)); - } - - long CPgSize = PageSize - (Position & PageMask); - - Position += CPgSize; - Size -= CPgSize; - } - } - - public void MapMirror(long Src, long Dst, long Size, int Type) - { - Src = AMemoryHelper.PageRoundDown(Src); - Dst = AMemoryHelper.PageRoundDown(Dst); - - Size = AMemoryHelper.PageRoundUp(Size); - - long PagesCount = Size / PageSize; - - while (PagesCount-- > 0) - { - PTEntry SrcEntry = GetPTEntry(Src); - PTEntry DstEntry = GetPTEntry(Dst); - - DstEntry.Map = PTMap.Mapped; - DstEntry.Type = Type; - DstEntry.Perm = SrcEntry.Perm; - - SrcEntry.Perm = AMemoryPerm.None; - - SrcEntry.Attr |= 1; - - SetPTEntry(Src, SrcEntry); - SetPTEntry(Dst, DstEntry); - - Src += PageSize; - Dst += PageSize; - } + SetPTEntry(Position, Size, Type, new PTEntry(PTMap.Unmapped, 0, 0, 0)); } public void Reprotect(long Position, long Size, AMemoryPerm Perm) @@ -197,12 +89,22 @@ namespace ChocolArm64.Memory public AMemoryMapInfo GetMapInfo(long Position) { + if (!IsValidPosition(Position)) + { + return null; + } + Position = AMemoryHelper.PageRoundDown(Position); PTEntry BaseEntry = GetPTEntry(Position); bool IsSameSegment(long Pos) { + if (!IsValidPosition(Pos)) + { + return false; + } + PTEntry Entry = GetPTEntry(Pos); return Entry.Map == BaseEntry.Map && @@ -234,11 +136,51 @@ namespace ChocolArm64.Memory BaseEntry.Perm); } + public void ClearAttrBit(long Position, long Size, int Bit) + { + while (Size > 0) + { + PTEntry Entry = GetPTEntry(Position); + + Entry.Attr &= ~(1 << Bit); + + SetPTEntry(Position, Entry); + + Position += PageSize; + Size -= PageSize; + } + } + + public void SetAttrBit(long Position, long Size, int Bit) + { + while (Size > 0) + { + PTEntry Entry = GetPTEntry(Position); + + Entry.Attr |= (1 << Bit); + + SetPTEntry(Position, Entry); + + Position += PageSize; + Size -= PageSize; + } + } + public bool HasPermission(long Position, AMemoryPerm Perm) { return GetPTEntry(Position).Perm.HasFlag(Perm); } + public bool IsValidPosition(long Position) + { + if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0) + { + return false; + } + + return true; + } + public bool IsMapped(long Position) { if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0) @@ -270,8 +212,38 @@ namespace ChocolArm64.Memory return PageTable[L0][L1]; } + private void SetPTEntry(long Position, long Size, PTEntry Entry) + { + while (Size > 0) + { + SetPTEntry(Position, Entry); + + Position += PageSize; + Size -= PageSize; + } + } + + private void SetPTEntry(long Position, long Size, int Type, PTEntry Entry) + { + while (Size > 0) + { + if (GetPTEntry(Position).Type == Type) + { + SetPTEntry(Position, Entry); + } + + Position += PageSize; + Size -= PageSize; + } + } + private void SetPTEntry(long Position, PTEntry Entry) { + if (!IsValidPosition(Position)) + { + throw new ArgumentOutOfRangeException(nameof(Position)); + } + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; diff --git a/Ryujinx/Cpu/Memory/AMemoryPerm.cs b/ChocolArm64/Memory/AMemoryPerm.cs similarity index 100% rename from Ryujinx/Cpu/Memory/AMemoryPerm.cs rename to ChocolArm64/Memory/AMemoryPerm.cs diff --git a/Ryujinx/Cpu/State/APState.cs b/ChocolArm64/State/APState.cs similarity index 100% rename from Ryujinx/Cpu/State/APState.cs rename to ChocolArm64/State/APState.cs diff --git a/Ryujinx/Cpu/State/ARegister.cs b/ChocolArm64/State/ARegister.cs similarity index 100% rename from Ryujinx/Cpu/State/ARegister.cs rename to ChocolArm64/State/ARegister.cs diff --git a/Ryujinx/Cpu/State/ARegisterSize.cs b/ChocolArm64/State/ARegisterSize.cs similarity index 100% rename from Ryujinx/Cpu/State/ARegisterSize.cs rename to ChocolArm64/State/ARegisterSize.cs diff --git a/Ryujinx/Cpu/State/ARegisterType.cs b/ChocolArm64/State/ARegisterType.cs similarity index 100% rename from Ryujinx/Cpu/State/ARegisterType.cs rename to ChocolArm64/State/ARegisterType.cs diff --git a/ChocolArm64/State/ARoundMode.cs b/ChocolArm64/State/ARoundMode.cs new file mode 100644 index 000000000..9896f3075 --- /dev/null +++ b/ChocolArm64/State/ARoundMode.cs @@ -0,0 +1,10 @@ +namespace ChocolArm64.State +{ + public enum ARoundMode + { + ToNearest = 0, + TowardsPlusInfinity = 1, + TowardsMinusInfinity = 2, + TowardsZero = 3 + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/State/AThreadState.cs b/ChocolArm64/State/AThreadState.cs similarity index 66% rename from Ryujinx/Cpu/State/AThreadState.cs rename to ChocolArm64/State/AThreadState.cs index cdab4034e..ec8621b89 100644 --- a/Ryujinx/Cpu/State/AThreadState.cs +++ b/ChocolArm64/State/AThreadState.cs @@ -1,3 +1,4 @@ +using ChocolArm64.Events; using System; namespace ChocolArm64.State @@ -28,6 +29,8 @@ namespace ChocolArm64.State public int ProcessId; public int ThreadId; + public bool Running { get; set; } + public long TpidrEl0 { get; set; } public long Tpidr { get; set; } @@ -37,28 +40,28 @@ namespace ChocolArm64.State public uint CtrEl0 => 0x8444c004; public uint DczidEl0 => 0x00000004; - private const long TicksPerS = 19_200_000; - private const long TicksPerMS = TicksPerS / 1_000; + private const ulong TicksPerS = 19_200_000; + private const ulong TicksPerMS = TicksPerS / 1_000; - public long CntpctEl0 => Environment.TickCount * TicksPerMS; + public ulong CntpctEl0 => (ulong)Environment.TickCount * TicksPerMS; - public event EventHandler Break; - public event EventHandler SvcCall; - public event EventHandler Undefined; + public event EventHandler Break; + public event EventHandler SvcCall; + public event EventHandler Undefined; internal void OnBreak(int Imm) { - Break?.Invoke(this, new AInstExceptEventArgs(Imm)); + Break?.Invoke(this, new AInstExceptionEventArgs(Imm)); } internal void OnSvcCall(int Imm) { - SvcCall?.Invoke(this, new AInstExceptEventArgs(Imm)); + SvcCall?.Invoke(this, new AInstExceptionEventArgs(Imm)); } internal void OnUndefined(long Position, int RawOpCode) { - Undefined?.Invoke(this, new AInstUndEventArgs(Position, RawOpCode)); + Undefined?.Invoke(this, new AInstUndefinedEventArgs(Position, RawOpCode)); } } } \ No newline at end of file diff --git a/Ryujinx/Cpu/State/AVec.cs b/ChocolArm64/State/AVec.cs similarity index 100% rename from Ryujinx/Cpu/State/AVec.cs rename to ChocolArm64/State/AVec.cs diff --git a/ChocolArm64/Translation/AILBarrier.cs b/ChocolArm64/Translation/AILBarrier.cs new file mode 100644 index 000000000..25b08de31 --- /dev/null +++ b/ChocolArm64/Translation/AILBarrier.cs @@ -0,0 +1,7 @@ +namespace ChocolArm64.Translation +{ + struct AILBarrier : IAILEmit + { + public void Emit(AILEmitter Context) { } + } +} \ No newline at end of file diff --git a/Ryujinx/Cpu/Translation/AILBlock.cs b/ChocolArm64/Translation/AILBlock.cs similarity index 62% rename from Ryujinx/Cpu/Translation/AILBlock.cs rename to ChocolArm64/Translation/AILBlock.cs index bed195aaf..e580e09c9 100644 --- a/Ryujinx/Cpu/Translation/AILBlock.cs +++ b/ChocolArm64/Translation/AILBlock.cs @@ -4,11 +4,13 @@ namespace ChocolArm64.Translation { class AILBlock : IAILEmit { - public long IntInputs { get; private set; } - public long IntOutputs { get; private set; } + public long IntInputs { get; private set; } + public long IntOutputs { get; private set; } + public long IntAwOutputs { get; private set; } - public long VecInputs { get; private set; } - public long VecOutputs { get; private set; } + public long VecInputs { get; private set; } + public long VecOutputs { get; private set; } + public long VecAwOutputs { get; private set; } public bool HasStateStore { get; private set; } @@ -24,13 +26,22 @@ namespace ChocolArm64.Translation public void Add(IAILEmit ILEmitter) { - if (ILEmitter is AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index)) + if (ILEmitter is AILBarrier) + { + //Those barriers are used to separate the groups of CIL + //opcodes emitted by each ARM instruction. + //We can only consider the new outputs for doing input elimination + //after all the CIL opcodes used by the instruction being emitted. + IntAwOutputs = IntOutputs; + VecAwOutputs = VecOutputs; + } + else if (ILEmitter is AILOpCodeLoad Ld && AILEmitter.IsRegIndex(Ld.Index)) { switch (Ld.IoType) { - case AIoType.Flag: IntInputs |= ((1L << Ld.Index) << 32) & ~IntOutputs; break; - case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntOutputs; break; - case AIoType.Vector: VecInputs |= (1L << Ld.Index) & ~VecOutputs; break; + case AIoType.Flag: IntInputs |= ((1L << Ld.Index) << 32) & ~IntAwOutputs; break; + case AIoType.Int: IntInputs |= (1L << Ld.Index) & ~IntAwOutputs; break; + case AIoType.Vector: VecInputs |= (1L << Ld.Index) & ~VecAwOutputs; break; } } else if (ILEmitter is AILOpCodeStore St) diff --git a/Ryujinx/Cpu/Translation/AILEmitter.cs b/ChocolArm64/Translation/AILEmitter.cs similarity index 95% rename from Ryujinx/Cpu/Translation/AILEmitter.cs rename to ChocolArm64/Translation/AILEmitter.cs index 8f6e1210f..af37a6c75 100644 --- a/Ryujinx/Cpu/Translation/AILEmitter.cs +++ b/ChocolArm64/Translation/AILEmitter.cs @@ -58,11 +58,13 @@ namespace ChocolArm64.Translation this.Root = ILBlocks[Array.IndexOf(Graph, Root)]; } - public ATranslatedSub GetSubroutine() + public AILBlock GetILBlock(int Index) => ILBlocks[Index]; + + public ATranslatedSub GetSubroutine(HashSet Callees) { LocalAlloc = new ALocalAlloc(ILBlocks, Root); - InitSubroutine(); + InitSubroutine(Callees); InitLocals(); foreach (AILBlock ILBlock in ILBlocks) @@ -73,24 +75,7 @@ namespace ChocolArm64.Translation return Subroutine; } - public AILBlock GetILBlock(int Index) => ILBlocks[Index]; - - private void InitLocals() - { - int ParamsStart = ATranslatedSub.FixedArgTypes.Length; - - Locals = new Dictionary(); - - for (int Index = 0; Index < Subroutine.Params.Count; Index++) - { - ARegister Reg = Subroutine.Params[Index]; - - Generator.EmitLdarg(Index + ParamsStart); - Generator.EmitStloc(GetLocalIndex(Reg)); - } - } - - private void InitSubroutine() + private void InitSubroutine(HashSet Callees) { List Params = new List(); @@ -114,9 +99,24 @@ namespace ChocolArm64.Translation Generator = Mthd.GetILGenerator(); - Subroutine = new ATranslatedSub(Mthd, Params); + Subroutine = new ATranslatedSub(Mthd, Params, Callees); } + private void InitLocals() + { + int ParamsStart = ATranslatedSub.FixedArgTypes.Length; + + Locals = new Dictionary(); + + for (int Index = 0; Index < Subroutine.Params.Count; Index++) + { + ARegister Reg = Subroutine.Params[Index]; + + Generator.EmitLdarg(Index + ParamsStart); + Generator.EmitStloc(GetLocalIndex(Reg)); + } + } + private Type[] GetParamTypes(IList Params) { Type[] FixedArgs = ATranslatedSub.FixedArgTypes; diff --git a/Ryujinx/Cpu/Translation/AILEmitterCtx.cs b/ChocolArm64/Translation/AILEmitterCtx.cs similarity index 87% rename from Ryujinx/Cpu/Translation/AILEmitterCtx.cs rename to ChocolArm64/Translation/AILEmitterCtx.cs index cf6445401..466594694 100644 --- a/Ryujinx/Cpu/Translation/AILEmitterCtx.cs +++ b/ChocolArm64/Translation/AILEmitterCtx.cs @@ -12,14 +12,9 @@ namespace ChocolArm64.Translation { private ATranslator Translator; - private Dictionary Labels; + private HashSet Callees; - private AILEmitter Emitter; - - private AILBlock ILBlock; - - private AOpCode LastCmpOp; - private AOpCode LastFlagOp; + private Dictionary Labels; private int BlkIndex; private int OpcIndex; @@ -29,6 +24,13 @@ namespace ChocolArm64.Translation public ABlock CurrBlock => Graph[BlkIndex]; public AOpCode CurrOp => Graph[BlkIndex].OpCodes[OpcIndex]; + private AILEmitter Emitter; + + private AILBlock ILBlock; + + private AOpCode OptOpLastCompare; + private AOpCode OptOpLastFlagSet; + //This is the index of the temporary register, used to store temporary //values needed by some functions, since IL doesn't have a swap instruction. //You can use any value here as long it doesn't conflict with the indices @@ -39,13 +41,32 @@ namespace ChocolArm64.Translation private const int Tmp4Index = -4; private const int Tmp5Index = -5; - public AILEmitterCtx(ATranslator Translator, ABlock[] Graph, ABlock Root) + public AILEmitterCtx( + ATranslator Translator, + ABlock[] Graph, + ABlock Root, + string SubName) { + if (Translator == null) + { + throw new ArgumentNullException(nameof(Translator)); + } + + if (Graph == null) + { + throw new ArgumentNullException(nameof(Graph)); + } + + if (Root == null) + { + throw new ArgumentNullException(nameof(Root)); + } + this.Translator = Translator; this.Graph = Graph; this.Root = Root; - string SubName = $"Sub{Root.Position:X16}"; + Callees = new HashSet(); Labels = new Dictionary(); @@ -55,26 +76,33 @@ namespace ChocolArm64.Translation OpcIndex = -1; - if (!AdvanceOpCode()) + if (Graph.Length == 0 || !AdvanceOpCode()) { throw new ArgumentException(nameof(Graph)); } } - public ATranslatedSub GetSubroutine() => Emitter.GetSubroutine(); + public ATranslatedSub GetSubroutine() + { + return Emitter.GetSubroutine(Callees); + } public bool AdvanceOpCode() { + if (OpcIndex + 1 == CurrBlock.OpCodes.Count && + BlkIndex + 1 == Graph.Length) + { + return false; + } + while (++OpcIndex >= (CurrBlock?.OpCodes.Count ?? 0)) { - if (BlkIndex + 1 >= Graph.Length) - { - return false; - } - BlkIndex++; OpcIndex = -1; + OptOpLastFlagSet = null; + OptOpLastCompare = null; + ILBlock = Emitter.GetILBlock(BlkIndex); } @@ -89,10 +117,19 @@ namespace ChocolArm64.Translation } CurrOp.Emitter(this); + + ILBlock.Add(new AILBarrier()); } public bool TryOptEmitSubroutineCall() { + Callees.Add(((AOpCodeBImm)CurrOp).Imm); + + if (CurrBlock.Next == null) + { + return false; + } + if (!Translator.TryGetCachedSub(CurrOp, out ATranslatedSub Sub)) { return false; @@ -120,7 +157,7 @@ namespace ChocolArm64.Translation public void TryOptMarkCondWithoutCmp() { - LastCmpOp = CurrOp; + OptOpLastCompare = CurrOp; AInstEmitAluHelper.EmitDataLoadOpers(this); @@ -146,14 +183,15 @@ namespace ChocolArm64.Translation { OpCode ILOp; - int IntCond = (int)Cond; + int IntCond = (int)Cond; - if (LastCmpOp != null && LastFlagOp == LastCmpOp && BranchOps.ContainsKey(Cond)) + if (OptOpLastCompare != null && + OptOpLastCompare == OptOpLastFlagSet && BranchOps.ContainsKey(Cond)) { - Ldloc(Tmp3Index, AIoType.Int, LastCmpOp.RegisterSize); - Ldloc(Tmp4Index, AIoType.Int, LastCmpOp.RegisterSize); + Ldloc(Tmp3Index, AIoType.Int, OptOpLastCompare.RegisterSize); + Ldloc(Tmp4Index, AIoType.Int, OptOpLastCompare.RegisterSize); - if (LastCmpOp.Emitter == AInstEmit.Adds) + if (OptOpLastCompare.Emitter == AInstEmit.Adds) { Emit(OpCodes.Neg); } @@ -356,7 +394,7 @@ namespace ChocolArm64.Translation public void EmitLdflg(int Index) => Ldloc(Index, AIoType.Flag); public void EmitStflg(int Index) { - LastFlagOp = CurrOp; + OptOpLastFlagSet = CurrOp; Stloc(Index, AIoType.Flag); } diff --git a/Ryujinx/Cpu/Translation/AILLabel.cs b/ChocolArm64/Translation/AILLabel.cs similarity index 100% rename from Ryujinx/Cpu/Translation/AILLabel.cs rename to ChocolArm64/Translation/AILLabel.cs diff --git a/Ryujinx/Cpu/Translation/AILOpCode.cs b/ChocolArm64/Translation/AILOpCode.cs similarity index 100% rename from Ryujinx/Cpu/Translation/AILOpCode.cs rename to ChocolArm64/Translation/AILOpCode.cs diff --git a/Ryujinx/Cpu/Translation/AILOpCodeBranch.cs b/ChocolArm64/Translation/AILOpCodeBranch.cs similarity index 100% rename from Ryujinx/Cpu/Translation/AILOpCodeBranch.cs rename to ChocolArm64/Translation/AILOpCodeBranch.cs diff --git a/Ryujinx/Cpu/Translation/AILOpCodeCall.cs b/ChocolArm64/Translation/AILOpCodeCall.cs similarity index 100% rename from Ryujinx/Cpu/Translation/AILOpCodeCall.cs rename to ChocolArm64/Translation/AILOpCodeCall.cs diff --git a/Ryujinx/Cpu/Translation/AILOpCodeConst.cs b/ChocolArm64/Translation/AILOpCodeConst.cs similarity index 100% rename from Ryujinx/Cpu/Translation/AILOpCodeConst.cs rename to ChocolArm64/Translation/AILOpCodeConst.cs diff --git a/Ryujinx/Cpu/Translation/AILOpCodeLoad.cs b/ChocolArm64/Translation/AILOpCodeLoad.cs similarity index 95% rename from Ryujinx/Cpu/Translation/AILOpCodeLoad.cs rename to ChocolArm64/Translation/AILOpCodeLoad.cs index 7cb431e2d..d60ce539f 100644 --- a/Ryujinx/Cpu/Translation/AILOpCodeLoad.cs +++ b/ChocolArm64/Translation/AILOpCodeLoad.cs @@ -11,12 +11,10 @@ namespace ChocolArm64.Translation public ARegisterSize RegisterSize { get; private set; } - public AILOpCodeLoad(int Index, AIoType IoType) : this(Index, IoType, ARegisterSize.Int64) { } - - public AILOpCodeLoad(int Index, AIoType IoType, ARegisterSize RegisterSize) + public AILOpCodeLoad(int Index, AIoType IoType, ARegisterSize RegisterSize = 0) { - this.IoType = IoType; this.Index = Index; + this.IoType = IoType; this.RegisterSize = RegisterSize; } diff --git a/Ryujinx/Cpu/Translation/AILOpCodeLog.cs b/ChocolArm64/Translation/AILOpCodeLog.cs similarity index 100% rename from Ryujinx/Cpu/Translation/AILOpCodeLog.cs rename to ChocolArm64/Translation/AILOpCodeLog.cs diff --git a/Ryujinx/Cpu/Translation/AILOpCodeStore.cs b/ChocolArm64/Translation/AILOpCodeStore.cs similarity index 94% rename from Ryujinx/Cpu/Translation/AILOpCodeStore.cs rename to ChocolArm64/Translation/AILOpCodeStore.cs index 34b26fe28..a0feb4377 100644 --- a/Ryujinx/Cpu/Translation/AILOpCodeStore.cs +++ b/ChocolArm64/Translation/AILOpCodeStore.cs @@ -10,13 +10,11 @@ namespace ChocolArm64.Translation public AIoType IoType { get; private set; } public ARegisterSize RegisterSize { get; private set; } - - public AILOpCodeStore(int Index, AIoType IoType) : this(Index, IoType, ARegisterSize.Int64) { } - public AILOpCodeStore(int Index, AIoType IoType, ARegisterSize RegisterSize) + public AILOpCodeStore(int Index, AIoType IoType, ARegisterSize RegisterSize = 0) { - this.IoType = IoType; this.Index = Index; + this.IoType = IoType; this.RegisterSize = RegisterSize; } diff --git a/Ryujinx/Cpu/Translation/AIoType.cs b/ChocolArm64/Translation/AIoType.cs similarity index 100% rename from Ryujinx/Cpu/Translation/AIoType.cs rename to ChocolArm64/Translation/AIoType.cs diff --git a/Ryujinx/Cpu/Translation/ALocalAlloc.cs b/ChocolArm64/Translation/ALocalAlloc.cs similarity index 93% rename from Ryujinx/Cpu/Translation/ALocalAlloc.cs rename to ChocolArm64/Translation/ALocalAlloc.cs index 0661ddc8d..8e9047804 100644 --- a/Ryujinx/Cpu/Translation/ALocalAlloc.cs +++ b/ChocolArm64/Translation/ALocalAlloc.cs @@ -67,14 +67,15 @@ namespace ChocolArm64.Translation public long VecOutputs; } - private const int MaxOptGraphLength = 120; + private const int MaxOptGraphLength = 40; public ALocalAlloc(AILBlock[] Graph, AILBlock Root) { IntPaths = new Dictionary(); VecPaths = new Dictionary(); - if (Graph.Length < MaxOptGraphLength) + if (Graph.Length > 1 && + Graph.Length < MaxOptGraphLength) { InitializeOptimal(Graph, Root); } @@ -149,11 +150,7 @@ namespace ChocolArm64.Translation if (RetTarget) { - BlkIO.Entry = Block; - BlkIO.IntInputs = 0; - BlkIO.VecInputs = 0; - BlkIO.IntOutputs = 0; - BlkIO.VecOutputs = 0; + BlkIO.Entry = Block; } else { @@ -183,10 +180,8 @@ namespace ChocolArm64.Translation { //This is WAY faster than InitializeOptimal, but results in //uneeded loads and stores, so the resulting code will be slower. - long IntInputs = 0; - long IntOutputs = 0; - long VecInputs = 0; - long VecOutputs = 0; + long IntInputs = 0, IntOutputs = 0; + long VecInputs = 0, VecOutputs = 0; foreach (AILBlock Block in Graph) { @@ -200,8 +195,11 @@ namespace ChocolArm64.Translation //in those cases if we attempt to write an output registers that was //not written, we will be just writing zero and messing up the old register value. //So we just need to ensure that all outputs are loaded. - IntInputs |= IntOutputs; - VecInputs |= VecOutputs; + if (Graph.Length > 1) + { + IntInputs |= IntOutputs; + VecInputs |= VecOutputs; + } foreach (AILBlock Block in Graph) { diff --git a/Ryujinx/Cpu/Translation/IAILEmit.cs b/ChocolArm64/Translation/IAILEmit.cs similarity index 100% rename from Ryujinx/Cpu/Translation/IAILEmit.cs rename to ChocolArm64/Translation/IAILEmit.cs diff --git a/Ryujinx/Cpu/Translation/ILGeneratorEx.cs b/ChocolArm64/Translation/ILGeneratorEx.cs similarity index 100% rename from Ryujinx/Cpu/Translation/ILGeneratorEx.cs rename to ChocolArm64/Translation/ILGeneratorEx.cs diff --git a/README.md b/README.md index f39d2f029..841a8258c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Experimental Switch emulator written in C# -Don't expect much from this. Some homebrew apps works, and Tetris shows the intro logos (sometimes) but that's about it for now. +Don't expect much from this. Some homebrew apps work, and Puyo Puyo Tetris shows the intro logo (sometimes) but that's about it for now. Contributions are always welcome. **Building** @@ -10,7 +10,7 @@ Contributions are always welcome. To build this emulator, you need the .NET Core 2.0 (or higher) SDK. https://www.microsoft.com/net/download/ In release builds, memory checks are disabled to improve performances. -Or just drag'n'drop the *.NRO or the game folder on the executable if you have a pre-build version. +Or just drag'n'drop the *.NRO / *.NSO or the game folder on the executable if you have a pre-build version. **Features** @@ -18,34 +18,44 @@ Or just drag'n'drop the *.NRO or the game folder on the executable if you have https://openal.org/downloads/OpenAL11CoreSDK.zip - Keyboard Input is partially supported: - - Arrows. - - Enter > "Start" & Tab > "Select" - - Qwerty: - - A > "A" (QWERTY) / Q > "A" (AZERTY) - - S > "B" - - Z > "X" (QWERTY) / W > "X" (AZERTY) - - X > "Y" - - Key_Up > "Right Stick Up" - - Key_Down > "Right Stick Down" - - Key_Left > "Right Stick Left" - - Key_Right > "Right Stick Right" - - I > "Left Stick Up" - - K > "Left Stick Down" - - J > "Left Stick Left" - - L > "Left Stick Right" - - Tab > "Minus" - - Enter > "Plus" + - Left Joycon: + - Stick Up = W + - Stick Down = S + - Stick Left = A + - Stick Right = D + - Stick Button = F + - DPad Up = Up + - DPad Down = Down + - DPad Left = Left + - DPad Right = Right + - Minus = - + - L = E + - ZL = Q + + - Right Joycon: + - Stick Up = I + - Stick Down = K + - Stick Left = J + - Stick Right = L + - Stick Button = H + - A = Z + - B = X + - X = C + - Y = V + - Plus = + + - R = U + - ZR = O - Config File: `Ryujinx.conf` should be present in executable folder. For more informations [you can go here](CONFIG.md). **Help** -If you have some homebrews that currently doesn't work on it, you can contact us through discord with the compiled NRO/NSO (and source code if possible) and will work to make them work. +If you have some homebrew that currently don't work on the emulator, you can contact us through Discord with the compiled NRO/NSO (and source code if possible) and then we'll make changes to make the requested app / game work. **Contact** -For help, support, suggestion, or if you just want to get in touch with the team, join our Discord server! +For help, support, suggestions, or if you just want to get in touch with the team, join our Discord server! https://discord.gg/VkQYXAZ **Running** @@ -54,10 +64,7 @@ To run this emulator, you need the .NET Core 2.0 (or higher) SDK. Run `dotnet run -c Release -- path\to\homebrew.nro` inside the Ryujinx solution folder to run homebrew apps. Run `dotnet run -c Release -- path\to\game_exefs_and_romfs_folder` to run official games (they need to be decrypted and extracted first!). -Audio is partially supported (glitched) on Linux and macOS, for Windows you need to install the OpenAL Core SDK: -https://openal.org/downloads/OpenAL11CoreSDK.zip +**Latest build** -**Lastest build** - -Those builds are compiled automatically for each commit on the master branch. They may be unstable or not work at all. -To download the lastest automatic build for Windows (64-bits), [Click Here](https://ci.appveyor.com/api/projects/gdkchan/ryujinx/artifacts/ryujinx_lastest_unstable.zip). +These builds are compiled automatically for each commit on the master branch. They may be unstable or not work at all. +To download the latest automatic build for Windows (64-bits), [Click Here](https://ci.appveyor.com/api/projects/gdkchan/ryujinx/artifacts/ryujinx_latest_unstable.zip?pr=false). diff --git a/Ryujinx/Config.cs b/Ryujinx.Core/Config.cs similarity index 73% rename from Ryujinx/Config.cs rename to Ryujinx.Core/Config.cs index e211441fb..c9a76de34 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx.Core/Config.cs @@ -1,26 +1,30 @@ -using System; +using Ryujinx.Core.Input; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -namespace Ryujinx +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 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 JoyCon FakeJoyCon { get; private set; } public static void Read() { - IniParser Parser = new IniParser(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Ryujinx.conf")); + var iniFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + 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")); @@ -28,6 +32,7 @@ namespace Ryujinx 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")); FakeJoyCon = new JoyCon @@ -70,23 +75,24 @@ namespace Ryujinx // https://stackoverflow.com/a/37772571 public class IniParser { - private Dictionary Values; + private readonly Dictionary Values; public IniParser(string Path) { Values = File.ReadLines(Path) - .Where(Line => (!String.IsNullOrWhiteSpace(Line) && !Line.StartsWith("#"))) - .Select(Line => Line.Split(new char[] { '=' }, 2, 0)) + .Where(Line => !string.IsNullOrWhiteSpace(Line) && !Line.StartsWith('#')) + .Select(Line => Line.Split('=', 2)) .ToDictionary(Parts => Parts[0].Trim(), Parts => Parts.Length > 1 ? Parts[1].Trim() : null); } - public string Value(string Name, string Value = null) + /// + /// Gets the setting value for the requested setting . + /// + /// Setting Name + /// Default value of the setting + public string Value(string Name, string defaultValue = null) { - if (Values != null && Values.ContainsKey(Name)) - { - return Values[Name]; - } - return Value; + return Values.TryGetValue(Name, out var value) ? value : defaultValue; } } } diff --git a/Ryujinx.Core/Hid/Hid.cs b/Ryujinx.Core/Hid/Hid.cs new file mode 100644 index 000000000..f25a94375 --- /dev/null +++ b/Ryujinx.Core/Hid/Hid.cs @@ -0,0 +1,264 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Handles; +using System; + +namespace Ryujinx.Core.Input +{ + public class Hid + { + /* + * Reference: + * https://github.com/reswitched/libtransistor/blob/development/lib/hid.c + * https://github.com/reswitched/libtransistor/blob/development/include/libtransistor/hid.h + * https://github.com/switchbrew/libnx/blob/master/nx/source/services/hid.c + * https://github.com/switchbrew/libnx/blob/master/nx/include/switch/services/hid.h + */ + + private const int HidHeaderSize = 0x400; + private const int HidTouchScreenSize = 0x3000; + private const int HidMouseSize = 0x400; + private const int HidKeyboardSize = 0x400; + private const int HidUnkSection1Size = 0x400; + private const int HidUnkSection2Size = 0x400; + private const int HidUnkSection3Size = 0x400; + private const int HidUnkSection4Size = 0x400; + private const int HidUnkSection5Size = 0x200; + private const int HidUnkSection6Size = 0x200; + private const int HidUnkSection7Size = 0x200; + private const int HidUnkSection8Size = 0x800; + private const int HidControllerSerialsSize = 0x4000; + private const int HidControllersSize = 0x32000; + private const int HidUnkSection9Size = 0x800; + + private const int HidTouchHeaderSize = 0x28; + private const int HidTouchEntrySize = 0x298; + + private const int HidTouchEntryHeaderSize = 0x10; + private const int HidTouchEntryTouchSize = 0x28; + + private const int HidControllerSize = 0x5000; + private const int HidControllerHeaderSize = 0x28; + private const int HidControllerLayoutsSize = 0x350; + + private const int HidControllersLayoutHeaderSize = 0x20; + private const int HidControllersInputEntrySize = 0x30; + + private const int HidHeaderOffset = 0; + private const int HidTouchScreenOffset = HidHeaderOffset + HidHeaderSize; + private const int HidMouseOffset = HidTouchScreenOffset + HidTouchScreenSize; + private const int HidKeyboardOffset = HidMouseOffset + HidMouseSize; + private const int HidUnkSection1Offset = HidKeyboardOffset + HidKeyboardSize; + private const int HidUnkSection2Offset = HidUnkSection1Offset + HidUnkSection1Size; + private const int HidUnkSection3Offset = HidUnkSection2Offset + HidUnkSection2Size; + private const int HidUnkSection4Offset = HidUnkSection3Offset + HidUnkSection3Size; + private const int HidUnkSection5Offset = HidUnkSection4Offset + HidUnkSection4Size; + private const int HidUnkSection6Offset = HidUnkSection5Offset + HidUnkSection5Size; + private const int HidUnkSection7Offset = HidUnkSection6Offset + HidUnkSection6Size; + private const int HidUnkSection8Offset = HidUnkSection7Offset + HidUnkSection7Size; + private const int HidControllerSerialsOffset = HidUnkSection8Offset + HidUnkSection8Size; + private const int HidControllersOffset = HidControllerSerialsOffset + HidControllerSerialsSize; + private const int HidUnkSection9Offset = HidControllersOffset + HidControllersSize; + + private const int HidEntryCount = 17; + + private object ShMemLock; + + private (AMemory, long)[] ShMemPositions; + + public Hid() + { + ShMemLock = new object(); + + ShMemPositions = new (AMemory, long)[0]; + } + + internal void ShMemMap(object sender, EventArgs e) + { + HSharedMem SharedMem = (HSharedMem)sender; + + lock (ShMemLock) + { + ShMemPositions = SharedMem.GetVirtualPositions(); + + (AMemory Memory, long Position) ShMem = ShMemPositions[ShMemPositions.Length - 1]; + + Logging.Info($"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!"); + + Init(ShMem.Memory, ShMem.Position); + } + } + + internal void ShMemUnmap(object sender, EventArgs e) + { + HSharedMem SharedMem = (HSharedMem)sender; + + lock (ShMemLock) + { + ShMemPositions = SharedMem.GetVirtualPositions(); + } + } + + private void Init(AMemory Memory, long Position) + { + InitializeJoyconPair( + Memory, + Position, + JoyConColor.Body_Neon_Red, + JoyConColor.Buttons_Neon_Red, + JoyConColor.Body_Neon_Blue, + JoyConColor.Buttons_Neon_Blue); + } + + private void InitializeJoyconPair( + AMemory Memory, + long Position, + JoyConColor LeftColorBody, + JoyConColor LeftColorButtons, + JoyConColor RightColorBody, + JoyConColor RightColorButtons) + { + long BaseControllerOffset = Position + HidControllersOffset + 8 * HidControllerSize; + + HidControllerType Type = + HidControllerType.ControllerType_Handheld | + HidControllerType.ControllerType_JoyconPair; + + bool IsHalf = false; + + HidControllerColorDesc SingleColorDesc = + HidControllerColorDesc.ColorDesc_ColorsNonexistent; + + JoyConColor SingleColorBody = JoyConColor.Black; + JoyConColor SingleColorButtons = JoyConColor.Black; + + HidControllerColorDesc SplitColorDesc = 0; + + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x0, (int)Type); + + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x4, IsHalf ? 1 : 0); + + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x8, (int)SingleColorDesc); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0xc, (int)SingleColorBody); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x10, (int)SingleColorButtons); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x14, (int)SplitColorDesc); + + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x18, (int)LeftColorBody); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x1c, (int)LeftColorButtons); + + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x20, (int)RightColorBody); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x24, (int)RightColorButtons); + } + + public void SetJoyconButton( + HidControllerId ControllerId, + HidControllerLayouts ControllerLayout, + HidControllerButtons Buttons, + HidJoystickPosition LeftStick, + HidJoystickPosition RightStick) + { + lock (ShMemLock) + { + foreach ((AMemory Memory, long Position) in ShMemPositions) + { + long ControllerOffset = Position + HidControllersOffset; + + ControllerOffset += (int)ControllerId * HidControllerSize; + + ControllerOffset += HidControllerHeaderSize; + + ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; + + long LastEntry = Memory.ReadInt64Unchecked(ControllerOffset + 0x10); + + long CurrEntry = (LastEntry + 1) % HidEntryCount; + + long Timestamp = GetTimestamp(); + + Memory.WriteInt64Unchecked(ControllerOffset + 0x0, Timestamp); + Memory.WriteInt64Unchecked(ControllerOffset + 0x8, HidEntryCount); + Memory.WriteInt64Unchecked(ControllerOffset + 0x10, CurrEntry); + Memory.WriteInt64Unchecked(ControllerOffset + 0x18, HidEntryCount - 1); + + ControllerOffset += HidControllersLayoutHeaderSize; + + ControllerOffset += CurrEntry * HidControllersInputEntrySize; + + Memory.WriteInt64Unchecked(ControllerOffset + 0x0, Timestamp); + Memory.WriteInt64Unchecked(ControllerOffset + 0x8, Timestamp); + + Memory.WriteInt64Unchecked(ControllerOffset + 0x10, (uint)Buttons); + + Memory.WriteInt32Unchecked(ControllerOffset + 0x18, LeftStick.DX); + Memory.WriteInt32Unchecked(ControllerOffset + 0x1c, LeftStick.DY); + + Memory.WriteInt64Unchecked(ControllerOffset + 0x20, RightStick.DX); + Memory.WriteInt64Unchecked(ControllerOffset + 0x24, RightStick.DY); + + Memory.WriteInt64Unchecked(ControllerOffset + 0x28, + (uint)HidControllerConnState.Controller_State_Connected | + (uint)HidControllerConnState.Controller_State_Wired); + } + } + } + + public void SetTouchPoints(params HidTouchPoint[] Points) + { + lock (ShMemLock) + { + foreach ((AMemory Memory, long Position) in ShMemPositions) + { + long TouchScreenOffset = Position + HidTouchScreenOffset; + + long LastEntry = Memory.ReadInt64Unchecked(TouchScreenOffset + 0x10); + + long CurrEntry = (LastEntry + 1) % HidEntryCount; + + long Timestamp = GetTimestamp(); + + Memory.WriteInt64Unchecked(TouchScreenOffset + 0x0, Timestamp); + Memory.WriteInt64Unchecked(TouchScreenOffset + 0x8, HidEntryCount); + Memory.WriteInt64Unchecked(TouchScreenOffset + 0x10, CurrEntry); + Memory.WriteInt64Unchecked(TouchScreenOffset + 0x18, HidEntryCount - 1); + Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp); + + long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize; + + long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize; + + long SampleCounter = Memory.ReadInt64Unchecked(LastEntryOffset) + 1; + + TouchEntryOffset += CurrEntry * HidTouchEntrySize; + + Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, SampleCounter); + Memory.WriteInt64Unchecked(TouchEntryOffset + 0x8, Points.Length); + + TouchEntryOffset += HidTouchEntryHeaderSize; + + const int Padding = 0; + + int Index = 0; + + foreach (HidTouchPoint Point in Points) + { + Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, Timestamp); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x8, Padding); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0xc, Index++); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x10, Point.X); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x14, Point.Y); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x18, Point.DiameterX); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x1c, Point.DiameterY); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x20, Point.Angle); + Memory.WriteInt32Unchecked(TouchEntryOffset + 0x24, Padding); + + TouchEntryOffset += HidTouchEntryTouchSize; + } + } + } + } + + private static long GetTimestamp() + { + return (long)((ulong)Environment.TickCount * 19_200); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidControllerButtons.cs b/Ryujinx.Core/Hid/HidControllerButtons.cs new file mode 100644 index 000000000..65eb3f82f --- /dev/null +++ b/Ryujinx.Core/Hid/HidControllerButtons.cs @@ -0,0 +1,35 @@ +using System; + +namespace Ryujinx.Core.Input +{ + [Flags] + public enum HidControllerButtons + { + KEY_A = (1 << 0), + KEY_B = (1 << 1), + KEY_X = (1 << 2), + KEY_Y = (1 << 3), + KEY_LSTICK = (1 << 4), + KEY_RSTICK = (1 << 5), + KEY_L = (1 << 6), + KEY_R = (1 << 7), + KEY_ZL = (1 << 8), + KEY_ZR = (1 << 9), + KEY_PLUS = (1 << 10), + KEY_MINUS = (1 << 11), + KEY_DLEFT = (1 << 12), + KEY_DUP = (1 << 13), + KEY_DRIGHT = (1 << 14), + KEY_DDOWN = (1 << 15), + KEY_LSTICK_LEFT = (1 << 16), + KEY_LSTICK_UP = (1 << 17), + KEY_LSTICK_RIGHT = (1 << 18), + KEY_LSTICK_DOWN = (1 << 19), + KEY_RSTICK_LEFT = (1 << 20), + KEY_RSTICK_UP = (1 << 21), + KEY_RSTICK_RIGHT = (1 << 22), + KEY_RSTICK_DOWN = (1 << 23), + KEY_SL = (1 << 24), + KEY_SR = (1 << 25) + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidControllerColorDesc.cs b/Ryujinx.Core/Hid/HidControllerColorDesc.cs new file mode 100644 index 000000000..fc7fa2178 --- /dev/null +++ b/Ryujinx.Core/Hid/HidControllerColorDesc.cs @@ -0,0 +1,10 @@ +using System; + +namespace Ryujinx.Core.Input +{ + [Flags] + public enum HidControllerColorDesc + { + ColorDesc_ColorsNonexistent = (1 << 1) + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidControllerConnState.cs b/Ryujinx.Core/Hid/HidControllerConnState.cs new file mode 100644 index 000000000..7f47a7f92 --- /dev/null +++ b/Ryujinx.Core/Hid/HidControllerConnState.cs @@ -0,0 +1,11 @@ +using System; + +namespace Ryujinx.Core.Input +{ + [Flags] + public enum HidControllerConnState + { + Controller_State_Connected = (1 << 0), + Controller_State_Wired = (1 << 1) + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidControllerId.cs b/Ryujinx.Core/Hid/HidControllerId.cs new file mode 100644 index 000000000..84b68d27e --- /dev/null +++ b/Ryujinx.Core/Hid/HidControllerId.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.Core.Input +{ + public enum HidControllerId + { + CONTROLLER_PLAYER_1 = 0, + CONTROLLER_PLAYER_2 = 1, + CONTROLLER_PLAYER_3 = 2, + CONTROLLER_PLAYER_4 = 3, + CONTROLLER_PLAYER_5 = 4, + CONTROLLER_PLAYER_6 = 5, + CONTROLLER_PLAYER_7 = 6, + CONTROLLER_PLAYER_8 = 7, + CONTROLLER_HANDHELD = 8, + CONTROLLER_UNKNOWN = 9 + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidControllerLayouts.cs b/Ryujinx.Core/Hid/HidControllerLayouts.cs new file mode 100644 index 000000000..e04c40b2d --- /dev/null +++ b/Ryujinx.Core/Hid/HidControllerLayouts.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Core.Input +{ + public enum HidControllerLayouts + { + Pro_Controller = 0, + Handheld_Joined = 1, + Joined = 2, + Left = 3, + Right = 4, + Main_No_Analog = 5, + Main = 6 + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidControllerType.cs b/Ryujinx.Core/Hid/HidControllerType.cs new file mode 100644 index 000000000..a4eb674c8 --- /dev/null +++ b/Ryujinx.Core/Hid/HidControllerType.cs @@ -0,0 +1,14 @@ +using System; + +namespace Ryujinx.Core.Input +{ + [Flags] + public enum HidControllerType + { + ControllerType_ProController = (1 << 0), + ControllerType_Handheld = (1 << 1), + ControllerType_JoyconPair = (1 << 2), + ControllerType_JoyconLeft = (1 << 3), + ControllerType_JoyconRight = (1 << 4) + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidJoystickPosition.cs b/Ryujinx.Core/Hid/HidJoystickPosition.cs new file mode 100644 index 000000000..61f8189f4 --- /dev/null +++ b/Ryujinx.Core/Hid/HidJoystickPosition.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Core.Input +{ + public struct HidJoystickPosition + { + public int DX; + public int DY; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidTouchPoint.cs b/Ryujinx.Core/Hid/HidTouchPoint.cs new file mode 100644 index 000000000..1207e1d53 --- /dev/null +++ b/Ryujinx.Core/Hid/HidTouchPoint.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.Input +{ + public struct HidTouchPoint + { + public int X; + public int Y; + public int DiameterX; + public int DiameterY; + public int Angle; + } +} \ No newline at end of file diff --git a/Ryujinx/Hid/JoyCon.cs b/Ryujinx.Core/Hid/JoyCon.cs similarity index 63% rename from Ryujinx/Hid/JoyCon.cs rename to Ryujinx.Core/Hid/JoyCon.cs index 9dde2b70a..1aef82fe0 100644 --- a/Ryujinx/Hid/JoyCon.cs +++ b/Ryujinx.Core/Hid/JoyCon.cs @@ -1,24 +1,6 @@ -namespace Ryujinx +//TODO: This is only used by Config, it doesn't belong to Core. +namespace Ryujinx.Core.Input { - public enum JoyConColor //Thanks to CTCaer - { - Body_Grey = 0x828282, - Body_Neon_Blue = 0x0AB9E6, - Body_Neon_Red = 0xFF3C28, - Body_Neon_Yellow = 0xE6FF00, - Body_Neon_Pink = 0xFF3278, - Body_Neon_Green = 0x1EDC00, - Body_Red = 0xE10F00, - - Buttons_Grey = 0x0F0F0F, - Buttons_Neon_Blue = 0x001E1E, - Buttons_Neon_Red = 0x1E0A0A, - Buttons_Neon_Yellow = 0x142800, - Buttons_Neon_Pink = 0x28001E, - Buttons_Neon_Green = 0x002800, - Buttons_Red = 0x280A0A - } - public struct JoyConLeft { public int StickUp; diff --git a/Ryujinx.Core/Hid/JoyConColor.cs b/Ryujinx.Core/Hid/JoyConColor.cs new file mode 100644 index 000000000..21d89fe42 --- /dev/null +++ b/Ryujinx.Core/Hid/JoyConColor.cs @@ -0,0 +1,23 @@ +namespace Ryujinx.Core.Input +{ + public enum JoyConColor //Thanks to CTCaer + { + Black = 0, + + Body_Grey = 0x828282, + Body_Neon_Blue = 0x0AB9E6, + Body_Neon_Red = 0xFF3C28, + Body_Neon_Yellow = 0xE6FF00, + Body_Neon_Pink = 0xFF3278, + Body_Neon_Green = 0x1EDC00, + Body_Red = 0xE10F00, + + Buttons_Grey = 0x0F0F0F, + Buttons_Neon_Blue = 0x001E1E, + Buttons_Neon_Red = 0x1E0A0A, + Buttons_Neon_Yellow = 0x142800, + Buttons_Neon_Pink = 0x28001E, + Buttons_Neon_Green = 0x002800, + Buttons_Red = 0x280A0A + } +} \ No newline at end of file diff --git a/Ryujinx/Loaders/Compression/Lz4.cs b/Ryujinx.Core/Loaders/Compression/Lz4.cs similarity index 97% rename from Ryujinx/Loaders/Compression/Lz4.cs rename to Ryujinx.Core/Loaders/Compression/Lz4.cs index aace200cc..eb1602a09 100644 --- a/Ryujinx/Loaders/Compression/Lz4.cs +++ b/Ryujinx.Core/Loaders/Compression/Lz4.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.Loaders.Compression +namespace Ryujinx.Core.Loaders.Compression { static class Lz4 { diff --git a/Ryujinx/Loaders/ElfDyn.cs b/Ryujinx.Core/Loaders/ElfDyn.cs similarity index 89% rename from Ryujinx/Loaders/ElfDyn.cs rename to Ryujinx.Core/Loaders/ElfDyn.cs index 595d6cfb5..2ed50b3e6 100644 --- a/Ryujinx/Loaders/ElfDyn.cs +++ b/Ryujinx.Core/Loaders/ElfDyn.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Loaders +namespace Ryujinx.Core.Loaders { struct ElfDyn { diff --git a/Ryujinx/Loaders/ElfDynTag.cs b/Ryujinx.Core/Loaders/ElfDynTag.cs similarity index 98% rename from Ryujinx/Loaders/ElfDynTag.cs rename to Ryujinx.Core/Loaders/ElfDynTag.cs index fb6cab3fb..1616c2238 100644 --- a/Ryujinx/Loaders/ElfDynTag.cs +++ b/Ryujinx.Core/Loaders/ElfDynTag.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Loaders +namespace Ryujinx.Core.Loaders { enum ElfDynTag { diff --git a/Ryujinx/Loaders/ElfRel.cs b/Ryujinx.Core/Loaders/ElfRel.cs similarity index 93% rename from Ryujinx/Loaders/ElfRel.cs rename to Ryujinx.Core/Loaders/ElfRel.cs index 8b691d99c..8db274524 100644 --- a/Ryujinx/Loaders/ElfRel.cs +++ b/Ryujinx.Core/Loaders/ElfRel.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Loaders +namespace Ryujinx.Core.Loaders { struct ElfRel { diff --git a/Ryujinx/Loaders/ElfRelType.cs b/Ryujinx.Core/Loaders/ElfRelType.cs similarity index 99% rename from Ryujinx/Loaders/ElfRelType.cs rename to Ryujinx.Core/Loaders/ElfRelType.cs index cc638b19d..a05331388 100644 --- a/Ryujinx/Loaders/ElfRelType.cs +++ b/Ryujinx.Core/Loaders/ElfRelType.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Loaders +namespace Ryujinx.Core.Loaders { enum ElfRelType { diff --git a/Ryujinx/Loaders/ElfSym.cs b/Ryujinx.Core/Loaders/ElfSym.cs similarity index 77% rename from Ryujinx/Loaders/ElfSym.cs rename to Ryujinx.Core/Loaders/ElfSym.cs index c4ed810ce..89e7c61f6 100644 --- a/Ryujinx/Loaders/ElfSym.cs +++ b/Ryujinx.Core/Loaders/ElfSym.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Loaders +namespace Ryujinx.Core.Loaders { struct ElfSym { @@ -16,17 +16,15 @@ namespace Ryujinx.Loaders Binding == ElfSymBinding.STB_GLOBAL || Binding == ElfSymBinding.STB_WEAK; - public int SHIdx { get; private set; } - public long ValueAbs { get; private set; } - public long Value { get; private set; } - public long Size { get; private set; } + public int SHIdx { get; private set; } + public long Value { get; private set; } + public long Size { get; private set; } public ElfSym( string Name, int Info, int Other, int SHIdx, - long ImageBase, long Value, long Size) { @@ -35,7 +33,6 @@ namespace Ryujinx.Loaders this.Binding = (ElfSymBinding)(Info >> 4); this.Visibility = (ElfSymVisibility)Other; this.SHIdx = SHIdx; - this.ValueAbs = Value + ImageBase; this.Value = Value; this.Size = Size; } diff --git a/Ryujinx/Loaders/ElfSymBinding.cs b/Ryujinx.Core/Loaders/ElfSymBinding.cs similarity index 77% rename from Ryujinx/Loaders/ElfSymBinding.cs rename to Ryujinx.Core/Loaders/ElfSymBinding.cs index 8bbc6d4e0..c87894962 100644 --- a/Ryujinx/Loaders/ElfSymBinding.cs +++ b/Ryujinx.Core/Loaders/ElfSymBinding.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Loaders +namespace Ryujinx.Core.Loaders { enum ElfSymBinding { diff --git a/Ryujinx/Loaders/ElfSymType.cs b/Ryujinx.Core/Loaders/ElfSymType.cs similarity index 87% rename from Ryujinx/Loaders/ElfSymType.cs rename to Ryujinx.Core/Loaders/ElfSymType.cs index e504411e1..786395e62 100644 --- a/Ryujinx/Loaders/ElfSymType.cs +++ b/Ryujinx.Core/Loaders/ElfSymType.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Loaders +namespace Ryujinx.Core.Loaders { enum ElfSymType { diff --git a/Ryujinx/Loaders/ElfSymVisibility.cs b/Ryujinx.Core/Loaders/ElfSymVisibility.cs similarity index 82% rename from Ryujinx/Loaders/ElfSymVisibility.cs rename to Ryujinx.Core/Loaders/ElfSymVisibility.cs index a308ef795..e72eb5b83 100644 --- a/Ryujinx/Loaders/ElfSymVisibility.cs +++ b/Ryujinx.Core/Loaders/ElfSymVisibility.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Loaders +namespace Ryujinx.Core.Loaders { enum ElfSymVisibility { diff --git a/Ryujinx/Loaders/Executable.cs b/Ryujinx.Core/Loaders/Executable.cs similarity index 73% rename from Ryujinx/Loaders/Executable.cs rename to Ryujinx.Core/Loaders/Executable.cs index 650436589..943b8e510 100644 --- a/Ryujinx/Loaders/Executable.cs +++ b/Ryujinx.Core/Loaders/Executable.cs @@ -1,21 +1,29 @@ using ChocolArm64.Memory; -using Ryujinx.Loaders.Executables; -using Ryujinx.OsHle; +using Ryujinx.Core.Loaders.Executables; +using Ryujinx.Core.OsHle; using System.Collections.Generic; -namespace Ryujinx.Loaders +namespace Ryujinx.Core.Loaders { class Executable { private AMemory Memory; - private ElfDyn[] Dynamic; + private List Dynamic; + + private Dictionary m_SymbolTable; + + public IReadOnlyDictionary SymbolTable => m_SymbolTable; public long ImageBase { get; private set; } public long ImageEnd { get; private set; } public Executable(IExecutable Exe, AMemory Memory, long ImageBase) { + Dynamic = new List(); + + m_SymbolTable = new Dictionary(); + this.Memory = Memory; this.ImageBase = ImageBase; this.ImageEnd = ImageBase; @@ -26,7 +34,12 @@ namespace Ryujinx.Loaders if (Exe.Mod0Offset == 0) { - MapBss(ImageBase + Exe.DataOffset + Exe.Data.Count, Exe.BssSize); + int BssOffset = Exe.DataOffset + Exe.Data.Length; + int BssSize = Exe.BssSize; + + MapBss(ImageBase + BssOffset, BssSize); + + ImageEnd = ImageBase + BssOffset + BssSize; return; } @@ -45,8 +58,6 @@ namespace Ryujinx.Loaders ImageEnd = BssEndOffset; - List Dynamic = new List(); - while (true) { long TagVal = Memory.ReadInt64(DynamicOffset + 0); @@ -64,28 +75,37 @@ namespace Ryujinx.Loaders Dynamic.Add(new ElfDyn(Tag, Value)); } - this.Dynamic = Dynamic.ToArray(); + long StrTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_STRTAB); + long SymTblAddr = ImageBase + GetFirstValue(ElfDynTag.DT_SYMTAB); + + long SymEntSize = GetFirstValue(ElfDynTag.DT_SYMENT); + + while ((ulong)SymTblAddr < (ulong)StrTblAddr) + { + ElfSym Sym = GetSymbol(SymTblAddr, StrTblAddr); + + m_SymbolTable.TryAdd(Sym.Value, Sym.Name); + + SymTblAddr += SymEntSize; + } } private void WriteData( long Position, - IList Data, + byte[] Data, MemoryType Type, AMemoryPerm Perm) { - Memory.Manager.MapPhys(Position, Data.Count, (int)Type, AMemoryPerm.Write); + Memory.Manager.Map(Position, Data.Length, (int)Type, AMemoryPerm.Write); - for (int Index = 0; Index < Data.Count; Index++) - { - Memory.WriteByte(Position + Index, Data[Index]); - } + AMemoryHelper.WriteBytes(Memory, Position, Data); - Memory.Manager.Reprotect(Position, Data.Count, Perm); + Memory.Manager.Reprotect(Position, Data.Length, Perm); } private void MapBss(long Position, long Size) { - Memory.Manager.MapPhys(Position, Size, (int)MemoryType.Normal, AMemoryPerm.RW); + Memory.Manager.Map(Position, Size, (int)MemoryType.Normal, AMemoryPerm.RW); } private ElfRel GetRelocation(long Position) @@ -130,7 +150,7 @@ namespace Ryujinx.Loaders Name += (char)Chr; } - return new ElfSym(Name, Info, Other, SHIdx, ImageBase, Value, Size); + return new ElfSym(Name, Info, Other, SHIdx, Value, Size); } private long GetFirstValue(ElfDynTag Tag) diff --git a/Ryujinx.Core/Loaders/Executables/IExecutable.cs b/Ryujinx.Core/Loaders/Executables/IExecutable.cs new file mode 100644 index 000000000..09d0aab23 --- /dev/null +++ b/Ryujinx.Core/Loaders/Executables/IExecutable.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Core.Loaders.Executables +{ + public interface IExecutable + { + byte[] Text { get; } + byte[] RO { get; } + byte[] Data { get; } + + int Mod0Offset { get; } + int TextOffset { get; } + int ROOffset { get; } + int DataOffset { get; } + int BssSize { get; } + } +} \ No newline at end of file diff --git a/Ryujinx/Loaders/Executables/Nro.cs b/Ryujinx.Core/Loaders/Executables/Nro.cs similarity index 74% rename from Ryujinx/Loaders/Executables/Nro.cs rename to Ryujinx.Core/Loaders/Executables/Nro.cs index 2217f3310..9f4ef59f5 100644 --- a/Ryujinx/Loaders/Executables/Nro.cs +++ b/Ryujinx.Core/Loaders/Executables/Nro.cs @@ -1,18 +1,12 @@ -using System; -using System.Collections.ObjectModel; using System.IO; -namespace Ryujinx.Loaders.Executables +namespace Ryujinx.Core.Loaders.Executables { class Nro : IExecutable { - private byte[] m_Text; - private byte[] m_RO; - private byte[] m_Data; - - public ReadOnlyCollection Text => Array.AsReadOnly(m_Text); - public ReadOnlyCollection RO => Array.AsReadOnly(m_RO); - public ReadOnlyCollection Data => Array.AsReadOnly(m_Data); + public byte[] Text { get; private set; } + public byte[] RO { get; private set; } + public byte[] Data { get; private set; } public int Mod0Offset { get; private set; } public int TextOffset { get; private set; } @@ -54,9 +48,9 @@ namespace Ryujinx.Loaders.Executables return Reader.ReadBytes(Size); } - m_Text = Read(TextOffset, TextSize); - m_RO = Read(ROOffset, ROSize); - m_Data = Read(DataOffset, DataSize); + Text = Read(TextOffset, TextSize); + RO = Read(ROOffset, ROSize); + Data = Read(DataOffset, DataSize); } } } \ No newline at end of file diff --git a/Ryujinx/Loaders/Executables/Nso.cs b/Ryujinx.Core/Loaders/Executables/Nso.cs similarity index 73% rename from Ryujinx/Loaders/Executables/Nso.cs rename to Ryujinx.Core/Loaders/Executables/Nso.cs index ead1baf15..7341ba622 100644 --- a/Ryujinx/Loaders/Executables/Nso.cs +++ b/Ryujinx.Core/Loaders/Executables/Nso.cs @@ -1,19 +1,14 @@ -using Ryujinx.Loaders.Compression; +using Ryujinx.Core.Loaders.Compression; using System; -using System.Collections.ObjectModel; using System.IO; -namespace Ryujinx.Loaders.Executables +namespace Ryujinx.Core.Loaders.Executables { class Nso : IExecutable { - private byte[] m_Text; - private byte[] m_RO; - private byte[] m_Data; - - public ReadOnlyCollection Text => Array.AsReadOnly(m_Text); - public ReadOnlyCollection RO => Array.AsReadOnly(m_RO); - public ReadOnlyCollection Data => Array.AsReadOnly(m_Data); + public byte[] Text { get; private set; } + public byte[] RO { get; private set; } + public byte[] Data { get; private set; } public int Mod0Offset { get; private set; } public int TextOffset { get; private set; } @@ -57,9 +52,9 @@ namespace Ryujinx.Loaders.Executables byte[] BuildId = Reader.ReadBytes(0x20); - int TextSize = Reader.ReadInt32(); - int ROSize = Reader.ReadInt32(); - int DataSize = Reader.ReadInt32(); + int TextSize = Reader.ReadInt32(); + int ROSize = Reader.ReadInt32(); + int DataSize = Reader.ReadInt32(); Input.Seek(0x24, SeekOrigin.Current); @@ -82,38 +77,38 @@ namespace Ryujinx.Loaders.Executables //Text segment Input.Seek(TextOffset, SeekOrigin.Begin); - m_Text = Reader.ReadBytes(TextSize); + Text = Reader.ReadBytes(TextSize); if (Flags.HasFlag(NsoFlags.IsTextCompressed) || true) { - m_Text = Lz4.Decompress(m_Text, TextDecSize); + Text = Lz4.Decompress(Text, TextDecSize); } //Read-only data segment Input.Seek(ROOffset, SeekOrigin.Begin); - m_RO = Reader.ReadBytes(ROSize); + RO = Reader.ReadBytes(ROSize); if (Flags.HasFlag(NsoFlags.IsROCompressed) || true) { - m_RO = Lz4.Decompress(m_RO, RODecSize); + RO = Lz4.Decompress(RO, RODecSize); } //Data segment Input.Seek(DataOffset, SeekOrigin.Begin); - m_Data = Reader.ReadBytes(DataSize); + Data = Reader.ReadBytes(DataSize); if (Flags.HasFlag(NsoFlags.IsDataCompressed) || true) { - m_Data = Lz4.Decompress(m_Data, DataDecSize); + Data = Lz4.Decompress(Data, DataDecSize); } - using (MemoryStream Text = new MemoryStream(m_Text)) + using (MemoryStream TextMS = new MemoryStream(Text)) { - BinaryReader TextReader = new BinaryReader(Text); + BinaryReader TextReader = new BinaryReader(TextMS); - Text.Seek(4, SeekOrigin.Begin); + TextMS.Seek(4, SeekOrigin.Begin); Mod0Offset = TextReader.ReadInt32(); } diff --git a/Ryujinx.Core/Logging.cs b/Ryujinx.Core/Logging.cs new file mode 100644 index 000000000..89064ccbc --- /dev/null +++ b/Ryujinx.Core/Logging.cs @@ -0,0 +1,212 @@ +using Ryujinx.Core.OsHle.Ipc; +using System; +using System.Diagnostics; +using System.IO; +using System.Text; + +namespace Ryujinx.Core +{ + public static class Logging + { + private static Stopwatch ExecutionTime; + + 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; + + static Logging() + { + if (File.Exists(LogFileName)) File.Delete(LogFileName); + + ExecutionTime = new Stopwatch(); + + ExecutionTime.Start(); + } + + public static string GetExecutionTime() + { + return ExecutionTime.ElapsedMilliseconds.ToString().PadLeft(8, '0') + "ms"; + } + + private static string WhoCalledMe() + { + return new StackTrace().GetFrame(2).GetMethod().Name; + } + + private static void LogFile(string Message) + { + if (EnableLogFile) + { + using (StreamWriter Writer = File.AppendText(LogFileName)) + { + Writer.WriteLine(Message); + } + } + } + + public static void Info(string Message) + { + if (EnableInfo) + { + string Text = $"{GetExecutionTime()} | INFO > {Message}"; + + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(Text.PadLeft(Text.Length + 1, ' ')); + Console.ResetColor(); + + LogFile(Text); + } + } + + public static void Trace(string Message) + { + if (EnableTrace) + { + string Text = $"{GetExecutionTime()} | TRACE > {WhoCalledMe()} - {Message}"; + + Console.ForegroundColor = ConsoleColor.DarkGray; + Console.WriteLine(Text.PadLeft(Text.Length + 1, ' ')); + Console.ResetColor(); + + LogFile(Text); + } + } + + public static void Debug(string Message) + { + if (EnableDebug) + { + string Text = $"{GetExecutionTime()} | DEBUG > {WhoCalledMe()} - {Message}"; + + Console.ForegroundColor = ConsoleColor.Gray; + Console.WriteLine(Text.PadLeft(Text.Length + 1, ' ')); + Console.ResetColor(); + + LogFile(Text); + } + } + + public static void Warn(string Message) + { + if (EnableWarn) + { + string Text = $"{GetExecutionTime()} | WARN > {WhoCalledMe()} - {Message}"; + + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine(Text.PadLeft(Text.Length + 1, ' ')); + Console.ResetColor(); + + LogFile(Text); + } + } + + public static void Error(string Message) + { + if (EnableError) + { + string Text = $"{GetExecutionTime()} | ERROR > {WhoCalledMe()} - {Message}"; + + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(Text.PadLeft(Text.Length + 1, ' ')); + Console.ResetColor(); + + LogFile(Text); + } + } + + public static void Fatal(string Message) + { + if (EnableFatal) + { + string Text = $"{GetExecutionTime()} | FATAL > {WhoCalledMe()} - {Message}"; + + Console.ForegroundColor = ConsoleColor.Magenta; + Console.WriteLine(Text.PadLeft(Text.Length + 1, ' ')); + Console.ResetColor(); + + LogFile(Text); + } + } + + public static void Ipc(byte[] Data, long CmdPtr, bool Domain) + { + if (EnableIpc) + { + Console.ForegroundColor = ConsoleColor.Cyan; + Console.WriteLine(IpcLog.Message(Data, CmdPtr, Domain)); + Console.ResetColor(); + } + } + + //https://www.codeproject.com/Articles/36747/Quick-and-Dirty-HexDump-of-a-Byte-Array + public static string HexDump(byte[] bytes, int bytesPerLine = 16) + { + if (bytes == null) return ""; + int bytesLength = bytes.Length; + + char[] HexChars = "0123456789ABCDEF".ToCharArray(); + + int firstHexColumn = + 8 // 8 characters for the address + + 3; // 3 spaces + + int firstCharColumn = firstHexColumn + + bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space + + (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th + + 2; // 2 spaces + + int lineLength = firstCharColumn + + bytesPerLine // - characters to show the ascii value + + Environment.NewLine.Length; // Carriage return and line feed (should normally be 2) + + char[] line = (new String(' ', lineLength - Environment.NewLine.Length) + Environment.NewLine).ToCharArray(); + int expectedLines = (bytesLength + bytesPerLine - 1) / bytesPerLine; + StringBuilder result = new StringBuilder(expectedLines * lineLength); + + for (int i = 0; i < bytesLength; i += bytesPerLine) + { + line[0] = HexChars[(i >> 28) & 0xF]; + line[1] = HexChars[(i >> 24) & 0xF]; + line[2] = HexChars[(i >> 20) & 0xF]; + line[3] = HexChars[(i >> 16) & 0xF]; + line[4] = HexChars[(i >> 12) & 0xF]; + line[5] = HexChars[(i >> 8) & 0xF]; + line[6] = HexChars[(i >> 4) & 0xF]; + line[7] = HexChars[(i >> 0) & 0xF]; + + int hexColumn = firstHexColumn; + int charColumn = firstCharColumn; + + for (int j = 0; j < bytesPerLine; j++) + { + if (j > 0 && (j & 7) == 0) hexColumn++; + if (i + j >= bytesLength) + { + line[hexColumn] = ' '; + line[hexColumn + 1] = ' '; + line[charColumn] = ' '; + } + else + { + byte b = bytes[i + j]; + line[hexColumn] = HexChars[(b >> 4) & 0xF]; + line[hexColumn + 1] = HexChars[b & 0xF]; + line[charColumn] = (b < 32 ? '·' : (char)b); + } + hexColumn += 3; + charColumn++; + } + + result.Append(line); + } + return result.ToString(); + } + } +} diff --git a/Ryujinx/OsHle/CondVar.cs b/Ryujinx.Core/OsHle/CondVar.cs similarity index 92% rename from Ryujinx/OsHle/CondVar.cs rename to Ryujinx.Core/OsHle/CondVar.cs index 91ea37bdb..f5fe3d292 100644 --- a/Ryujinx/OsHle/CondVar.cs +++ b/Ryujinx.Core/OsHle/CondVar.cs @@ -1,8 +1,8 @@ -using Ryujinx.OsHle.Handles; +using Ryujinx.Core.OsHle.Handles; using System.Collections.Generic; using System.Threading; -namespace Ryujinx.OsHle +namespace Ryujinx.Core.OsHle { class CondVar { @@ -24,7 +24,7 @@ namespace Ryujinx.OsHle WaitingThreads = new List(); } - public void WaitForSignal(HThread Thread) + public bool WaitForSignal(HThread Thread) { int Count = Process.Memory.ReadInt32(CondVarAddress); @@ -41,12 +41,14 @@ namespace Ryujinx.OsHle } else { - Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000)); + bool Result = Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000)); lock (WaitingThreads) { WaitingThreads.Remove(Thread); } + + return Result; } } @@ -60,6 +62,8 @@ namespace Ryujinx.OsHle } ReleaseCondVarValue(); + + return true; } public void SetSignal(HThread Thread, int Count) diff --git a/Ryujinx.Core/OsHle/ErrorCode.cs b/Ryujinx.Core/OsHle/ErrorCode.cs new file mode 100644 index 000000000..4210b2300 --- /dev/null +++ b/Ryujinx.Core/OsHle/ErrorCode.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Core.OsHle +{ + static class ErrorCode + { + public static uint MakeError(ErrorModule Module, int Code) + { + return (uint)Module | ((uint)Code << 9); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/ErrorModule.cs b/Ryujinx.Core/OsHle/ErrorModule.cs new file mode 100644 index 000000000..f23e8d270 --- /dev/null +++ b/Ryujinx.Core/OsHle/ErrorModule.cs @@ -0,0 +1,63 @@ +namespace Ryujinx.Core.OsHle +{ + enum ErrorModule + { + Kernel = 1, + Fs = 2, + Nvidia_TransferMemory = 3, + Ncm = 5, + Dd = 6, + Lr = 8, + Loader = 9, + IPC_Command_Interface = 10, + IPC = 11, + Pm = 15, + Ns = 16, + Htc = 18, + Sm = 21, + RO_Userland = 22, + SdMmc = 24, + Spl = 26, + Ethc = 100, + I2C = 101, + Settings = 105, + Nifm = 110, + Display = 114, + Ntc = 116, + Fdm = 117, + Pcie = 120, + Friends = 121, + SSL = 123, + Account = 124, + Mii = 126, + Am = 128, + Play_Report = 129, + Pcv = 133, + Omm = 134, + Nim = 137, + Psc = 138, + Usb = 140, + Nsd = 141, + Btm = 143, + Erpt = 147, + Apm = 148, + Audio = 153, + Npns = 154, + Arp = 157, + Boot = 158, + Nfc = 161, + Userland_Assert = 162, + Userland_Crash = 168, + Hid = 203, + Capture = 206, + Libnx = 345, + Homebrew_ABI = 346, + Homebrew_Loader = 347, + libnx_Nvidia_Errors = 348, + Tc = 651, + General_Web_Applet = 800, + Wifi_Web_Auth_Applet = 809, + Whitelisted_Applet = 810, + ShopN = 811 + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Exceptions/GuestBrokeExecutionException.cs b/Ryujinx.Core/OsHle/Exceptions/GuestBrokeExecutionException.cs similarity index 85% rename from Ryujinx/OsHle/Exceptions/GuestBrokeExecutionException.cs rename to Ryujinx.Core/OsHle/Exceptions/GuestBrokeExecutionException.cs index 1369c57b2..db4929c5b 100644 --- a/Ryujinx/OsHle/Exceptions/GuestBrokeExecutionException.cs +++ b/Ryujinx.Core/OsHle/Exceptions/GuestBrokeExecutionException.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.OsHle.Exceptions +namespace Ryujinx.Core.OsHle.Exceptions { public class GuestBrokeExecutionException : Exception { diff --git a/Ryujinx/OsHle/Exceptions/UndefinedInstructionException.cs b/Ryujinx.Core/OsHle/Exceptions/UndefinedInstructionException.cs similarity index 90% rename from Ryujinx/OsHle/Exceptions/UndefinedInstructionException.cs rename to Ryujinx.Core/OsHle/Exceptions/UndefinedInstructionException.cs index 86033a82a..20cf83862 100644 --- a/Ryujinx/OsHle/Exceptions/UndefinedInstructionException.cs +++ b/Ryujinx.Core/OsHle/Exceptions/UndefinedInstructionException.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.OsHle.Exceptions +namespace Ryujinx.Core.OsHle.Exceptions { public class UndefinedInstructionException : Exception { diff --git a/Ryujinx/OsHle/FileDesc.cs b/Ryujinx.Core/OsHle/FileDesc.cs similarity index 85% rename from Ryujinx/OsHle/FileDesc.cs rename to Ryujinx.Core/OsHle/FileDesc.cs index 2a21f5007..4be83bb07 100644 --- a/Ryujinx/OsHle/FileDesc.cs +++ b/Ryujinx.Core/OsHle/FileDesc.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.OsHle +namespace Ryujinx.Core.OsHle { class FileDesc { diff --git a/Ryujinx.Core/OsHle/Handles/HDomain.cs b/Ryujinx.Core/OsHle/Handles/HDomain.cs new file mode 100644 index 000000000..26c604554 --- /dev/null +++ b/Ryujinx.Core/OsHle/Handles/HDomain.cs @@ -0,0 +1,48 @@ +using System; + +namespace Ryujinx.Core.OsHle.Handles +{ + class HDomain : HSession, IDisposable + { + private IdDictionary Objects; + + public HDomain(HSession Session) : base(Session) + { + Objects = new IdDictionary(); + } + + public int Add(object Obj) + { + return Objects.Add(Obj); + } + + public bool Delete(int Id) + { + return Objects.Delete(Id); + } + + public object GetObject(int Id) + { + return Objects.GetData(Id); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + foreach (object Obj in Objects) + { + if (Obj != this && Obj is IDisposable DisposableObj) + { + DisposableObj.Dispose(); + } + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Handles/HEvent.cs b/Ryujinx.Core/OsHle/Handles/HEvent.cs new file mode 100644 index 000000000..4e881ca24 --- /dev/null +++ b/Ryujinx.Core/OsHle/Handles/HEvent.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Core.OsHle.Handles +{ + class HEvent + { + + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Handles/HSession.cs b/Ryujinx.Core/OsHle/Handles/HSession.cs similarity index 57% rename from Ryujinx/OsHle/Handles/HSession.cs rename to Ryujinx.Core/OsHle/Handles/HSession.cs index 6b9016591..f30e91f98 100644 --- a/Ryujinx/OsHle/Handles/HSession.cs +++ b/Ryujinx.Core/OsHle/Handles/HSession.cs @@ -1,21 +1,23 @@ -namespace Ryujinx.OsHle.Handles +using Ryujinx.Core.OsHle.IpcServices; + +namespace Ryujinx.Core.OsHle.Handles { class HSession { - public string ServiceName { get; private set; } + public IIpcService Service { get; private set; } public bool IsInitialized { get; private set; } public int State { get; set; } - public HSession(string ServiceName) + public HSession(IIpcService Service) { - this.ServiceName = ServiceName; + this.Service = Service; } public HSession(HSession Session) { - ServiceName = Session.ServiceName; + Service = Session.Service; IsInitialized = Session.IsInitialized; } diff --git a/Ryujinx/OsHle/Handles/HSessionObj.cs b/Ryujinx.Core/OsHle/Handles/HSessionObj.cs similarity index 94% rename from Ryujinx/OsHle/Handles/HSessionObj.cs rename to Ryujinx.Core/OsHle/Handles/HSessionObj.cs index a871a11f4..ed0530f74 100644 --- a/Ryujinx/OsHle/Handles/HSessionObj.cs +++ b/Ryujinx.Core/OsHle/Handles/HSessionObj.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.OsHle.Handles +namespace Ryujinx.Core.OsHle.Handles { class HSessionObj : HSession, IDisposable { diff --git a/Ryujinx.Core/OsHle/Handles/HSharedMem.cs b/Ryujinx.Core/OsHle/Handles/HSharedMem.cs new file mode 100644 index 000000000..b6bdc8987 --- /dev/null +++ b/Ryujinx.Core/OsHle/Handles/HSharedMem.cs @@ -0,0 +1,44 @@ +using ChocolArm64.Memory; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.Handles +{ + class HSharedMem + { + private List<(AMemory, long)> Positions; + + public EventHandler MemoryMapped; + public EventHandler MemoryUnmapped; + + public HSharedMem() + { + Positions = new List<(AMemory, long)>(); + } + + public void AddVirtualPosition(AMemory Memory, long Position) + { + lock (Positions) + { + Positions.Add((Memory, Position)); + + MemoryMapped?.Invoke(this, EventArgs.Empty); + } + } + + public void RemoveVirtualPosition(AMemory Memory, long Position) + { + lock (Positions) + { + Positions.Remove((Memory, Position)); + + MemoryUnmapped?.Invoke(this, EventArgs.Empty); + } + } + + public (AMemory, long)[] GetVirtualPositions() + { + return Positions.ToArray(); + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Handles/HThread.cs b/Ryujinx.Core/OsHle/Handles/HThread.cs similarity index 79% rename from Ryujinx/OsHle/Handles/HThread.cs rename to Ryujinx.Core/OsHle/Handles/HThread.cs index fef2faf2c..c631cedc6 100644 --- a/Ryujinx/OsHle/Handles/HThread.cs +++ b/Ryujinx.Core/OsHle/Handles/HThread.cs @@ -1,13 +1,13 @@ using ChocolArm64; -namespace Ryujinx.OsHle.Handles +namespace Ryujinx.Core.OsHle.Handles { - class HThread + public class HThread { public AThread Thread { get; private set; } public int ProcessorId { get; private set; } - public int Priority { get; private set; } + public int Priority { get; set; } public int ThreadId => Thread.ThreadId; diff --git a/Ryujinx/OsHle/Handles/HTransferMem.cs b/Ryujinx.Core/OsHle/Handles/HTransferMem.cs similarity index 93% rename from Ryujinx/OsHle/Handles/HTransferMem.cs rename to Ryujinx.Core/OsHle/Handles/HTransferMem.cs index b24e14129..701fc451c 100644 --- a/Ryujinx/OsHle/Handles/HTransferMem.cs +++ b/Ryujinx.Core/OsHle/Handles/HTransferMem.cs @@ -1,6 +1,6 @@ using ChocolArm64.Memory; -namespace Ryujinx.OsHle.Handles +namespace Ryujinx.Core.OsHle.Handles { class HTransferMem { diff --git a/Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs b/Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs new file mode 100644 index 000000000..1156e035f --- /dev/null +++ b/Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs @@ -0,0 +1,63 @@ +using System; + +namespace Ryujinx.Core.OsHle.Handles +{ + class KProcessHandleTable : IDisposable + { + private IdDictionary Handles; + + public KProcessHandleTable() + { + Handles = new IdDictionary(); + } + + public int OpenHandle(object Obj) + { + return Handles.Add(Obj); + } + + public T GetData(int Handle) + { + return Handles.GetData(Handle); + } + + public bool ReplaceData(int Id, object Data) + { + 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() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + foreach (object Obj in Handles) + { + if (Obj is IDisposable DisposableObj) + { + DisposableObj.Dispose(); + } + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs similarity index 96% rename from Ryujinx/OsHle/Handles/KProcessScheduler.cs rename to Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs index 9044987fd..b11160781 100644 --- a/Ryujinx/OsHle/Handles/KProcessScheduler.cs +++ b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs @@ -3,9 +3,9 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; -namespace Ryujinx.OsHle.Handles +namespace Ryujinx.Core.OsHle.Handles { - class KProcessScheduler : IDisposable + public class KProcessScheduler : IDisposable { private class SchedulerThread : IDisposable { @@ -183,7 +183,7 @@ namespace Ryujinx.OsHle.Handles TryResumingExecution(SchedThread); } - public void WaitForSignal(HThread Thread, int Timeout = -1) + public bool WaitForSignal(HThread Thread, int Timeout = -1) { SchedulerThread SchedThread; @@ -206,22 +206,26 @@ namespace Ryujinx.OsHle.Handles { Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!"); - return; + return false; } } + bool Result; + if (Timeout >= 0) { Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms."); - SchedThread.WaitEvent.WaitOne(Timeout); + Result = SchedThread.WaitEvent.WaitOne(Timeout); } else { - SchedThread.WaitEvent.WaitOne(); + Result = SchedThread.WaitEvent.WaitOne(); } TryResumingExecution(SchedThread); + + return Result; } private void TryResumingExecution(SchedulerThread SchedThread) diff --git a/Ryujinx.Core/OsHle/Homebrew.cs b/Ryujinx.Core/OsHle/Homebrew.cs new file mode 100644 index 000000000..2a717ca73 --- /dev/null +++ b/Ryujinx.Core/OsHle/Homebrew.cs @@ -0,0 +1,69 @@ +using ChocolArm64.Memory; + +namespace Ryujinx.Core.OsHle +{ + static class Homebrew + { + //http://switchbrew.org/index.php?title=Homebrew_ABI + public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle) + { + Memory.Manager.Map(Position, AMemoryMgr.PageSize, (int)MemoryType.Normal, AMemoryPerm.RW); + + //MainThreadHandle + WriteConfigEntry(Memory, ref Position, 1, 0, MainThreadHandle); + + //NextLoadPath + WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400); + + //AppletType + WriteConfigEntry(Memory, ref Position, 7); + + //EndOfList + WriteConfigEntry(Memory, ref Position, 0); + } + + private static void WriteConfigEntry( + AMemory Memory, + ref long Position, + int Key, + int Flags = 0, + long Value0 = 0, + long Value1 = 0) + { + Memory.WriteInt32(Position + 0x00, Key); + Memory.WriteInt32(Position + 0x04, Flags); + Memory.WriteInt64(Position + 0x08, Value0); + Memory.WriteInt64(Position + 0x10, Value1); + + Position += 0x18; + } + + public static string ReadHbAbiNextLoadPath(AMemory Memory, long Position) + { + string FileName = null; + + while (true) + { + long Key = Memory.ReadInt64(Position); + + if (Key == 2) + { + long Value0 = Memory.ReadInt64(Position + 0x08); + long Value1 = Memory.ReadInt64(Position + 0x10); + + FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, (int)(Value1 - Value0)); + + break; + } + else if (Key == 0) + { + break; + } + + Position += 0x18; + } + + return FileName; + } + } +} diff --git a/Ryujinx.Core/OsHle/Horizon.cs b/Ryujinx.Core/OsHle/Horizon.cs new file mode 100644 index 000000000..c3f8cd8b0 --- /dev/null +++ b/Ryujinx.Core/OsHle/Horizon.cs @@ -0,0 +1,177 @@ +using Ryujinx.Core.Loaders.Executables; +using Ryujinx.Core.OsHle.Handles; +using System; +using System.Collections.Concurrent; +using System.IO; + +namespace Ryujinx.Core.OsHle +{ + public class Horizon : IDisposable + { + internal const int HidSize = 0x40000; + internal const int FontSize = 0x50; + + internal ConcurrentDictionary Mutexes { get; private set; } + internal ConcurrentDictionary CondVars { get; private set; } + + private ConcurrentDictionary Processes; + + internal HSharedMem HidSharedMem; + internal HSharedMem FontSharedMem; + + private Switch Ns; + + public Horizon(Switch Ns) + { + this.Ns = Ns; + + Mutexes = new ConcurrentDictionary(); + CondVars = new ConcurrentDictionary(); + + Processes = new ConcurrentDictionary(); + + HidSharedMem = new HSharedMem(); + FontSharedMem = new HSharedMem(); + } + + public void LoadCart(string ExeFsDir, string RomFsFile = null) + { + if (RomFsFile != null) + { + Ns.VFs.LoadRomFs(RomFsFile); + } + + Process MainProcess = MakeProcess(); + + void LoadNso(string FileName) + { + foreach (string File in Directory.GetFiles(ExeFsDir, FileName)) + { + if (Path.GetExtension(File) != string.Empty) + { + continue; + } + + Logging.Info($"Loading {Path.GetFileNameWithoutExtension(File)}..."); + + using (FileStream Input = new FileStream(File, FileMode.Open)) + { + Nso Program = new Nso(Input); + + MainProcess.LoadProgram(Program); + } + } + } + + LoadNso("rtld"); + + MainProcess.SetEmptyArgs(); + + LoadNso("main"); + LoadNso("subsdk*"); + LoadNso("sdk"); + + MainProcess.Run(); + } + + public void LoadProgram(string FileName) + { + bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro"; + + Process MainProcess = MakeProcess(); + + using (FileStream Input = new FileStream(FileName, FileMode.Open)) + { + MainProcess.LoadProgram(IsNro + ? (IExecutable)new Nro(Input) + : (IExecutable)new Nso(Input)); + } + + MainProcess.SetEmptyArgs(); + MainProcess.Run(IsNro); + } + + private Process MakeProcess() + { + Process Process; + + lock (Processes) + { + int ProcessId = 0; + + while (Processes.ContainsKey(ProcessId)) + { + ProcessId++; + } + + Process = new Process(Ns, ProcessId); + + Processes.TryAdd(ProcessId, Process); + } + + return Process; + } + + 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}"); + + if (NextNro == string.Empty) + { + NextNro = "sdmc:/hbmenu.nro"; + } + + NextNro = NextNro.Replace("sdmc:", string.Empty); + + NextNro = Ns.VFs.GetFullPath(Ns.VFs.GetSdCardPath(), NextNro); + + if (File.Exists(NextNro)) + { + //TODO: Those dictionaries shouldn't even exist, + //the Mutex and CondVar helper classes should be static. + Mutexes.Clear(); + CondVars.Clear(); + + LoadProgram(NextNro); + } + } + + if (Processes.TryRemove(ProcessId, out Process)) + { + Process.StopAllThreadsAsync(); + Process.Dispose(); + + if (Processes.Count == 0) + { + Ns.OnFinish(EventArgs.Empty); + } + } + } + + internal bool TryGetProcess(int ProcessId, out Process Process) + { + return Processes.TryGetValue(ProcessId, out Process); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + foreach (Process Process in Processes.Values) + { + Process.StopAllThreadsAsync(); + Process.Dispose(); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Utilities/IdPoolWithObj.cs b/Ryujinx.Core/OsHle/IdDictionary.cs similarity index 50% rename from Ryujinx/OsHle/Utilities/IdPoolWithObj.cs rename to Ryujinx.Core/OsHle/IdDictionary.cs index 621466a82..0b9092461 100644 --- a/Ryujinx/OsHle/Utilities/IdPoolWithObj.cs +++ b/Ryujinx.Core/OsHle/IdDictionary.cs @@ -3,31 +3,40 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -namespace Ryujinx.OsHle.Utilities +namespace Ryujinx.Core.OsHle { - class IdPoolWithObj : IEnumerable> + class IdDictionary : IEnumerable { - private IdPool Ids; - private ConcurrentDictionary Objs; - public IdPoolWithObj() - { - Ids = new IdPool(); + private int FreeIdHint = 1; + public IdDictionary() + { Objs = new ConcurrentDictionary(); } - public int GenerateId(object Data) + public int Add(object Data) { - int Id = Ids.GenerateId(); - - if (Id == -1 || !Objs.TryAdd(Id, Data)) + if (Objs.TryAdd(FreeIdHint, Data)) { - throw new InvalidOperationException(); + return FreeIdHint++; } - return Id; + return AddSlow(Data); + } + + private int AddSlow(object Data) + { + for (int Id = 1; Id < int.MaxValue; Id++) + { + if (Objs.TryAdd(Id, Data)) + { + return Id; + } + } + + throw new InvalidOperationException(); } public bool ReplaceData(int Id, object Data) @@ -42,6 +51,16 @@ namespace Ryujinx.OsHle.Utilities return false; } + public object GetData(int Id) + { + if (Objs.TryGetValue(Id, out object Data)) + { + return Data; + } + + return null; + } + public T GetData(int Id) { if (Objs.TryGetValue(Id, out object Data) && Data is T) @@ -52,7 +71,7 @@ namespace Ryujinx.OsHle.Utilities return default(T); } - public void Delete(int Id) + public bool Delete(int Id) { if (Objs.TryRemove(Id, out object Obj)) { @@ -61,18 +80,22 @@ namespace Ryujinx.OsHle.Utilities DisposableObj.Dispose(); } - Ids.DeleteId(Id); + FreeIdHint = Id; + + return true; } + + return false; } - IEnumerator> IEnumerable>.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() { - return Objs.GetEnumerator(); + return Objs.Values.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { - return Objs.GetEnumerator(); + return Objs.Values.GetEnumerator(); } } } \ No newline at end of file diff --git a/Ryujinx/OsHle/Ipc/IpcBuffDesc.cs b/Ryujinx.Core/OsHle/Ipc/IpcBuffDesc.cs similarity index 95% rename from Ryujinx/OsHle/Ipc/IpcBuffDesc.cs rename to Ryujinx.Core/OsHle/Ipc/IpcBuffDesc.cs index 41b1efe03..01bb15392 100644 --- a/Ryujinx/OsHle/Ipc/IpcBuffDesc.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcBuffDesc.cs @@ -1,6 +1,6 @@ using System.IO; -namespace Ryujinx.OsHle.Ipc +namespace Ryujinx.Core.OsHle.Ipc { struct IpcBuffDesc { diff --git a/Ryujinx/OsHle/Ipc/IpcDomCmd.cs b/Ryujinx.Core/OsHle/Ipc/IpcDomCmd.cs similarity index 70% rename from Ryujinx/OsHle/Ipc/IpcDomCmd.cs rename to Ryujinx.Core/OsHle/Ipc/IpcDomCmd.cs index 035671859..1ef0c4082 100644 --- a/Ryujinx/OsHle/Ipc/IpcDomCmd.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcDomCmd.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.OsHle.Ipc +namespace Ryujinx.Core.OsHle.Ipc { enum IpcDomCmd { diff --git a/Ryujinx/OsHle/Ipc/IpcHandleDesc.cs b/Ryujinx.Core/OsHle/Ipc/IpcHandleDesc.cs similarity index 98% rename from Ryujinx/OsHle/Ipc/IpcHandleDesc.cs rename to Ryujinx.Core/OsHle/Ipc/IpcHandleDesc.cs index fa5d7e1de..572c0e649 100644 --- a/Ryujinx/OsHle/Ipc/IpcHandleDesc.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcHandleDesc.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace Ryujinx.OsHle.Ipc +namespace Ryujinx.Core.OsHle.Ipc { class IpcHandleDesc { diff --git a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs new file mode 100644 index 000000000..f2179a962 --- /dev/null +++ b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs @@ -0,0 +1,215 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.IpcServices; +using System; +using System.IO; + +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, + IpcMessage Request, + int ThreadId, + long CmdPtr, + int HndId) + { + IpcMessage Response = new IpcMessage(Request.IsDomain && Request.Type == IpcMessageType.Request); + + using (MemoryStream Raw = new MemoryStream(Request.RawData)) + { + BinaryReader ReqReader = new BinaryReader(Raw); + + if (Request.Type == IpcMessageType.Request) + { + string ServiceName = Session.Service.GetType().Name; + + ServiceProcessRequest ProcReq = null; + + bool IgnoreNullPR = false; + + string DbgServiceName = string.Empty; + + if (Session is HDomain Dom) + { + if (Request.DomCmd == IpcDomCmd.SendMsg) + { + long Magic = ReqReader.ReadInt64(); + int CmdId = (int)ReqReader.ReadInt64(); + + object Obj = Dom.GetObject(Request.DomObjId); + + if (Obj is HDomain) + { + Session.Service.Commands.TryGetValue(CmdId, out ProcReq); + + DbgServiceName = $"{ProcReq?.Method.Name ?? CmdId.ToString()}"; + } + else if (Obj != null) + { + ((IIpcService)Obj).Commands.TryGetValue(CmdId, out ProcReq); + + DbgServiceName = $"{Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}"; + } + } + else if (Request.DomCmd == IpcDomCmd.DeleteObj) + { + Dom.Delete(Request.DomObjId); + + Response = FillResponse(Response, 0); + + IgnoreNullPR = true; + } + } + else + { + long Magic = ReqReader.ReadInt64(); + int CmdId = (int)ReqReader.ReadInt64(); + + if (Session is HSessionObj) + { + object Obj = ((HSessionObj)Session).Obj; + + ((IIpcService)Obj).Commands.TryGetValue(CmdId, out ProcReq); + + DbgServiceName = $"{Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}"; + } + else + { + Session.Service.Commands.TryGetValue(CmdId, out ProcReq); + + DbgServiceName = $"{ProcReq?.Method.Name ?? CmdId.ToString()}"; + } + } + + DbgServiceName = $"Tid {ThreadId} {ServiceName} {DbgServiceName}"; + + Logging.Debug($"IpcMessage: {DbgServiceName}"); + + if (ProcReq != null) + { + using (MemoryStream ResMS = new MemoryStream()) + { + BinaryWriter ResWriter = new BinaryWriter(ResMS); + + ServiceCtx Context = new ServiceCtx( + Ns, + Process, + Memory, + Session, + Request, + Response, + ReqReader, + ResWriter); + + long Result = ProcReq(Context); + + Response = FillResponse(Response, Result, ResMS.ToArray()); + } + } + else if (!IgnoreNullPR) + { + throw new NotImplementedException(DbgServiceName); + } + } + else if (Request.Type == IpcMessageType.Control) + { + long Magic = ReqReader.ReadInt64(); + long CmdId = ReqReader.ReadInt64(); + + switch (CmdId) + { + case 0: + { + HDomain Dom = new HDomain(Session); + + Process.HandleTable.ReplaceData(HndId, Dom); + + Request = FillResponse(Response, 0, Dom.Add(Dom)); + + break; + } + + case 3: + { + Request = FillResponse(Response, 0, 0x500); + + break; + } + + //TODO: Whats the difference between IpcDuplicateSession/Ex? + case 2: + case 4: + { + int Unknown = ReqReader.ReadInt32(); + + int Handle = Process.HandleTable.OpenHandle(Session); + + Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); + + Request = FillResponse(Response, 0); + + break; + } + + default: throw new NotImplementedException(CmdId.ToString()); + } + } + else if (Request.Type == IpcMessageType.CloseSession) + { + //TODO + } + else + { + throw new NotImplementedException(Request.Type.ToString()); + } + + AMemoryHelper.WriteBytes(Memory, CmdPtr, Response.GetBytes(CmdPtr)); + } + } + + private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values) + { + using (MemoryStream MS = new MemoryStream()) + { + BinaryWriter Writer = new BinaryWriter(MS); + + foreach (int Value in Values) + { + Writer.Write(Value); + } + + return FillResponse(Response, Result, MS.ToArray()); + } + } + + private static IpcMessage FillResponse(IpcMessage Response, long Result, byte[] Data = null) + { + Response.Type = IpcMessageType.Response; + + using (MemoryStream MS = new MemoryStream()) + { + BinaryWriter Writer = new BinaryWriter(MS); + + Writer.Write(SfcoMagic); + Writer.Write(Result); + + if (Data != null) + { + Writer.Write(Data); + } + + Response.RawData = MS.ToArray(); + } + + return Response; + } + } +} diff --git a/Ryujinx.Core/OsHle/Ipc/IpcLog.cs b/Ryujinx.Core/OsHle/Ipc/IpcLog.cs new file mode 100644 index 000000000..dfec7ccfd --- /dev/null +++ b/Ryujinx.Core/OsHle/Ipc/IpcLog.cs @@ -0,0 +1,179 @@ +using System; +using System.IO; + +namespace Ryujinx.Core.OsHle.Ipc +{ + public static class IpcLog + { + public static string Message(byte[] Data, long CmdPtr, bool Domain) + { + string IpcMessage = ""; + + using (MemoryStream MS = new MemoryStream(Data)) + { + BinaryReader Reader = new BinaryReader(MS); + + int Word0 = Reader.ReadInt32(); + int Word1 = Reader.ReadInt32(); + + int Type = (Word0 & 0xffff); + + int PtrBuffCount = (Word0 >> 16) & 0xf; + int SendBuffCount = (Word0 >> 20) & 0xf; + int RecvBuffCount = (Word0 >> 24) & 0xf; + int XchgBuffCount = (Word0 >> 28) & 0xf; + + int RawDataSize = (Word1 >> 0) & 0x3ff; + int RecvListFlags = (Word1 >> 10) & 0xf; + bool HndDescEnable = ((Word1 >> 31) & 0x1) != 0; + + IpcMessage += Environment.NewLine + $" {Logging.GetExecutionTime()} | IpcMessage >" + Environment.NewLine + + $" Type: {Enum.GetName(typeof(IpcMessageType), Type)}" + Environment.NewLine + + + $" PtrBuffCount: {PtrBuffCount.ToString()}" + Environment.NewLine + + $" SendBuffCount: {SendBuffCount.ToString()}" + Environment.NewLine + + $" RecvBuffCount: {RecvBuffCount.ToString()}" + Environment.NewLine + + $" XchgBuffCount: {XchgBuffCount.ToString()}" + Environment.NewLine + + + $" RawDataSize: {RawDataSize.ToString()}" + Environment.NewLine + + $" RecvListFlags: {RecvListFlags.ToString()}" + Environment.NewLine + + $" HndDescEnable: {HndDescEnable.ToString()}" + Environment.NewLine; + + if (HndDescEnable) + { + int Word = Reader.ReadInt32(); + + bool HasPId = (Word & 1) != 0; + + int[] ToCopy = new int[(Word >> 1) & 0xf]; + int[] ToMove = new int[(Word >> 5) & 0xf]; + + long PId = HasPId ? Reader.ReadInt64() : 0; + + for (int Index = 0; Index < ToCopy.Length; Index++) + { + ToCopy[Index] = Reader.ReadInt32(); + } + + for (int Index = 0; Index < ToMove.Length; Index++) + { + ToMove[Index] = Reader.ReadInt32(); + } + + IpcMessage += Environment.NewLine + " HndDesc:" + Environment.NewLine + + $" PId: {PId.ToString()}" + Environment.NewLine + + $" ToCopy.Length: {ToCopy.Length.ToString()}" + Environment.NewLine + + $" ToMove.Length: {ToMove.Length.ToString()}" + Environment.NewLine; + } + + for (int Index = 0; Index < PtrBuffCount; Index++) + { + long IpcPtrBuffDescWord0 = Reader.ReadUInt32(); + long IpcPtrBuffDescWord1 = Reader.ReadUInt32(); + + long Position = IpcPtrBuffDescWord1; + Position |= (IpcPtrBuffDescWord0 << 20) & 0x0f00000000; + Position |= (IpcPtrBuffDescWord0 << 30) & 0x7000000000; + + int IpcPtrBuffDescIndex = ((int)IpcPtrBuffDescWord0 >> 0) & 0x03f; + IpcPtrBuffDescIndex |= ((int)IpcPtrBuffDescWord0 >> 3) & 0x1c0; + + short Size = (short)(IpcPtrBuffDescWord0 >> 16); + + IpcMessage += Environment.NewLine + $" PtrBuff[{Index}]:" + Environment.NewLine + + $" Position: {Position.ToString()}" + Environment.NewLine + + $" IpcPtrBuffDescIndex: {IpcPtrBuffDescIndex.ToString()}" + Environment.NewLine + + $" Size: {Size.ToString()}" + Environment.NewLine; + } + + ReadIpcBuffValues(Reader, SendBuffCount, IpcMessage, "SendBuff"); + ReadIpcBuffValues(Reader, RecvBuffCount, IpcMessage, "RecvBuff"); + ReadIpcBuffValues(Reader, XchgBuffCount, IpcMessage, "XchgBuff"); + + RawDataSize *= 4; + + long RecvListPos = Reader.BaseStream.Position + RawDataSize; + long Pad0 = 0; + + if ((Reader.BaseStream.Position + CmdPtr & 0xf) != 0) + { + Pad0 = 0x10 - (Reader.BaseStream.Position + CmdPtr & 0xf); + } + + Reader.BaseStream.Seek(Pad0, SeekOrigin.Current); + + int RecvListCount = RecvListFlags - 2; + + if (RecvListCount == 0) + { + RecvListCount = 1; + } + else if (RecvListCount < 0) + { + RecvListCount = 0; + } + + if (Domain && (IpcMessageType)Type == IpcMessageType.Request) + { + int DomWord0 = Reader.ReadInt32(); + + int DomCmd = (DomWord0 & 0xff); + + RawDataSize = (DomWord0 >> 16) & 0xffff; + + int DomObjId = Reader.ReadInt32(); + + Reader.ReadInt64(); //Padding + + IpcMessage += Environment.NewLine + $" Domain:" + Environment.NewLine + + $" DomCmd: {Enum.GetName(typeof(IpcDomCmd), DomCmd)}" + Environment.NewLine + + $" DomObjId: {DomObjId.ToString()}" + Environment.NewLine; + } + + byte[] RawData = Reader.ReadBytes(RawDataSize); + + IpcMessage += Environment.NewLine + $" RawData:" + Environment.NewLine + Logging.HexDump(RawData); + + Reader.BaseStream.Seek(RecvListPos, SeekOrigin.Begin); + + for (int Index = 0; Index < RecvListCount; Index++) + { + long RecvListBuffValue = Reader.ReadInt64(); + long RecvListBuffPosition = RecvListBuffValue & 0xffffffffffff; + long RecvListBuffSize = (short)(RecvListBuffValue >> 48); + + IpcMessage += Environment.NewLine + $" RecvList[{Index}]:" + Environment.NewLine + + $" Value: {RecvListBuffValue.ToString()}" + Environment.NewLine + + $" Position: {RecvListBuffPosition.ToString()}" + Environment.NewLine + + $" Size: {RecvListBuffSize.ToString()}" + Environment.NewLine; + } + } + + return IpcMessage; + } + + private static void ReadIpcBuffValues(BinaryReader Reader, int Count, string IpcMessage, string BufferName) + { + for (int Index = 0; Index < Count; Index++) + { + long Word0 = Reader.ReadUInt32(); + long Word1 = Reader.ReadUInt32(); + long Word2 = Reader.ReadUInt32(); + + long Position = Word1; + Position |= (Word2 << 4) & 0x0f00000000; + Position |= (Word2 << 34) & 0x7000000000; + + long Size = Word0; + Size |= (Word2 << 8) & 0xf00000000; + + int Flags = (int)Word2 & 3; + + IpcMessage += Environment.NewLine + $" {BufferName}[{Index}]:" + Environment.NewLine + + $" Position: {Position.ToString()}" + Environment.NewLine + + $" Flags: {Flags.ToString()}" + Environment.NewLine + + $" Size: {Size.ToString()}" + Environment.NewLine; + } + } + } +} diff --git a/Ryujinx/OsHle/Ipc/IpcMessage.cs b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs similarity index 92% rename from Ryujinx/OsHle/Ipc/IpcMessage.cs rename to Ryujinx.Core/OsHle/Ipc/IpcMessage.cs index 407fd65f9..ebb3dbca0 100644 --- a/Ryujinx/OsHle/Ipc/IpcMessage.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.IO; -namespace Ryujinx.OsHle.Ipc +namespace Ryujinx.Core.OsHle.Ipc { class IpcMessage { @@ -41,6 +41,8 @@ namespace Ryujinx.OsHle.Ipc 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); @@ -108,7 +110,7 @@ namespace Ryujinx.OsHle.Ipc RecvListCount = 0; } - if (Domain) + if (Domain && Type == IpcMessageType.Request) { int DomWord0 = Reader.ReadInt32(); @@ -215,17 +217,27 @@ namespace Ryujinx.OsHle.Ipc public long GetSendBuffPtr() { - if (SendBuff.Count > 0 && SendBuff[0].Position != 0) + if (SendBuff.Count > 0 && SendBuff[0].Size != 0) { return SendBuff[0].Position; } - if (PtrBuff.Count > 0 && PtrBuff[0].Position != 0) + if (PtrBuff.Count > 0 && PtrBuff[0].Size != 0) { return PtrBuff[0].Position; } + if (ReceiveBuff.Count > 0 && ReceiveBuff[0].Size != 0) + { + return ReceiveBuff[0].Position; + } + + if (RecvListBuff.Count > 0 && RecvListBuff[0].Size != 0) + { + return RecvListBuff[0].Position; + } + return -1; } } -} \ No newline at end of file +} diff --git a/Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs b/Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs new file mode 100644 index 000000000..560af41e7 --- /dev/null +++ b/Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Core.OsHle.Ipc +{ + enum IpcMessageType + { + Response = 0, + CloseSession = 2, + Request = 4, + Control = 5 + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Ipc/IpcPtrBuffDesc.cs b/Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs similarity index 95% rename from Ryujinx/OsHle/Ipc/IpcPtrBuffDesc.cs rename to Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs index 414d71f45..d39f78db8 100644 --- a/Ryujinx/OsHle/Ipc/IpcPtrBuffDesc.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcPtrBuffDesc.cs @@ -1,6 +1,6 @@ using System.IO; -namespace Ryujinx.OsHle.Ipc +namespace Ryujinx.Core.OsHle.Ipc { struct IpcPtrBuffDesc { diff --git a/Ryujinx/OsHle/Ipc/IpcRecvListBuffDesc.cs b/Ryujinx.Core/OsHle/Ipc/IpcRecvListBuffDesc.cs similarity index 92% rename from Ryujinx/OsHle/Ipc/IpcRecvListBuffDesc.cs rename to Ryujinx.Core/OsHle/Ipc/IpcRecvListBuffDesc.cs index 8180b8dd3..c647208f6 100644 --- a/Ryujinx/OsHle/Ipc/IpcRecvListBuffDesc.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcRecvListBuffDesc.cs @@ -1,6 +1,6 @@ using System.IO; -namespace Ryujinx.OsHle.Ipc +namespace Ryujinx.Core.OsHle.Ipc { struct IpcRecvListBuffDesc { diff --git a/Ryujinx/OsHle/Ipc/ServiceProcessRequest.cs b/Ryujinx.Core/OsHle/Ipc/ServiceProcessRequest.cs similarity index 65% rename from Ryujinx/OsHle/Ipc/ServiceProcessRequest.cs rename to Ryujinx.Core/OsHle/Ipc/ServiceProcessRequest.cs index 838a6aea8..ea7580744 100644 --- a/Ryujinx/OsHle/Ipc/ServiceProcessRequest.cs +++ b/Ryujinx.Core/OsHle/Ipc/ServiceProcessRequest.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.OsHle.Ipc +namespace Ryujinx.Core.OsHle.Ipc { delegate long ServiceProcessRequest(ServiceCtx Context); } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/KernelErr.cs b/Ryujinx.Core/OsHle/KernelErr.cs new file mode 100644 index 000000000..19983af19 --- /dev/null +++ b/Ryujinx.Core/OsHle/KernelErr.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.OsHle +{ + static class KernelErr + { + public const int InvalidMemRange = 110; + public const int InvalidHandle = 114; + public const int Timeout = 117; + public const int InvalidInfo = 120; + public const int InvalidIpcReq = 123; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/MemoryRegions.cs b/Ryujinx.Core/OsHle/MemoryRegions.cs new file mode 100644 index 000000000..75b97b1f2 --- /dev/null +++ b/Ryujinx.Core/OsHle/MemoryRegions.cs @@ -0,0 +1,29 @@ +using ChocolArm64.Memory; + +namespace Ryujinx.Core.OsHle +{ + static class MemoryRegions + { + public const long AddrSpaceStart = 0x08000000; + + public const long MapRegionAddress = 0x10000000; + public const long MapRegionSize = 0x20000000; + + public const long HeapRegionAddress = MapRegionAddress + MapRegionSize; + public const long HeapRegionSize = TlsPagesAddress - HeapRegionAddress; + + public const long MainStackSize = 0x100000; + + public const long MainStackAddress = AMemoryMgr.AddrSize - MainStackSize; + + public const long TlsPagesSize = 0x4000; + + public const long TlsPagesAddress = MainStackAddress - TlsPagesSize; + + public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize; + + public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart; + + public const long AddrSpaceSize = AMemoryMgr.AddrSize - AddrSpaceStart; + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/MemoryType.cs b/Ryujinx.Core/OsHle/MemoryType.cs similarity index 96% rename from Ryujinx/OsHle/MemoryType.cs rename to Ryujinx.Core/OsHle/MemoryType.cs index b1ac330ae..d3b43dd55 100644 --- a/Ryujinx/OsHle/MemoryType.cs +++ b/Ryujinx.Core/OsHle/MemoryType.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.OsHle +namespace Ryujinx.Core.OsHle { enum MemoryType { diff --git a/Ryujinx/OsHle/Mutex.cs b/Ryujinx.Core/OsHle/Mutex.cs similarity index 98% rename from Ryujinx/OsHle/Mutex.cs rename to Ryujinx.Core/OsHle/Mutex.cs index 43862d7b2..c619e1217 100644 --- a/Ryujinx/OsHle/Mutex.cs +++ b/Ryujinx.Core/OsHle/Mutex.cs @@ -1,8 +1,8 @@ -using Ryujinx.OsHle.Handles; +using Ryujinx.Core.OsHle.Handles; using System.Collections.Concurrent; using System.Threading; -namespace Ryujinx.OsHle +namespace Ryujinx.Core.OsHle { class Mutex { diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs new file mode 100644 index 000000000..a8719e1c2 --- /dev/null +++ b/Ryujinx.Core/OsHle/Process.cs @@ -0,0 +1,355 @@ +using ChocolArm64; +using ChocolArm64.Events; +using ChocolArm64.Memory; +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 System; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle +{ + class Process : IDisposable + { + private const int TlsSize = 0x200; + private const int TotalTlsSlots = 32; + + private Switch Ns; + + public bool NeedsHbAbi { get; private set; } + + public long HbAbiDataPosition { get; private set; } + + public int ProcessId { get; private set; } + + private ATranslator Translator; + + public AMemory Memory { get; private set; } + + public ServiceMgr Services { get; private set; } + + public KProcessScheduler Scheduler { get; private set; } + + public KProcessHandleTable HandleTable { get; private set; } + + private SvcHandler SvcHandler; + + private ConcurrentDictionary TlsSlots; + + private ConcurrentDictionary ThreadsByTpidr; + + private List Executables; + + private HThread MainThread; + + private long ImageBase; + + private bool ShouldDispose; + + private bool Disposed; + + public Process(Switch Ns, int ProcessId) + { + this.Ns = Ns; + this.ProcessId = ProcessId; + + Memory = new AMemory(); + + Services = new ServiceMgr(); + + HandleTable = new KProcessHandleTable(); + + Scheduler = new KProcessScheduler(); + + SvcHandler = new SvcHandler(Ns, this); + + TlsSlots = new ConcurrentDictionary(); + + ThreadsByTpidr = new ConcurrentDictionary(); + + Executables = new List(); + + ImageBase = MemoryRegions.AddrSpaceStart; + + MapRWMemRegion( + MemoryRegions.TlsPagesAddress, + MemoryRegions.TlsPagesSize, + MemoryType.ThreadLocal); + } + + public void LoadProgram(IExecutable Program) + { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Process)); + } + + Logging.Info($"Image base at 0x{ImageBase:x16}."); + + Executable Executable = new Executable(Program, Memory, ImageBase); + + Executables.Add(Executable); + + ImageBase = AMemoryHelper.PageRoundUp(Executable.ImageEnd); + } + + public void SetEmptyArgs() + { + //TODO: This should be part of Run. + ImageBase += AMemoryMgr.PageSize; + } + + public bool Run(bool NeedsHbAbi = false) + { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Process)); + } + + this.NeedsHbAbi = NeedsHbAbi; + + if (Executables.Count == 0) + { + return false; + } + + MapRWMemRegion( + MemoryRegions.MainStackAddress, + MemoryRegions.MainStackSize, + MemoryType.Normal); + + long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize; + + int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 0, 0); + + if (Handle == -1) + { + return false; + } + + MainThread = HandleTable.GetData(Handle); + + if (NeedsHbAbi) + { + HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd); + + Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle); + + MainThread.Thread.ThreadState.X0 = (ulong)HbAbiDataPosition; + MainThread.Thread.ThreadState.X1 = ulong.MaxValue; + } + + Scheduler.StartThread(MainThread); + + return true; + } + + private void MapRWMemRegion(long Position, long Size, MemoryType Type) + { + Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW); + } + + public void StopAllThreadsAsync() + { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Process)); + } + + if (MainThread != null) + { + MainThread.Thread.StopExecution(); + } + + foreach (AThread Thread in TlsSlots.Values) + { + Thread.StopExecution(); + } + } + + public int MakeThread( + long EntryPoint, + long StackTop, + long ArgsPtr, + int Priority, + int ProcessorId) + { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Process)); + } + + AThread Thread = new AThread(GetTranslator(), Memory, EntryPoint); + + HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority); + + int Handle = HandleTable.OpenHandle(ThreadHnd); + + int ThreadId = GetFreeTlsSlot(Thread); + + 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.Tpidr = Tpidr; + Thread.ThreadState.X0 = (ulong)ArgsPtr; + Thread.ThreadState.X1 = (ulong)Handle; + Thread.ThreadState.X31 = (ulong)StackTop; + + Thread.WorkFinished += ThreadFinished; + + ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, ThreadHnd); + + return Handle; + } + + private void BreakHandler(object sender, AInstExceptionEventArgs e) + { + throw new GuestBrokeExecutionException(); + } + + private void UndefinedHandler(object sender, AInstUndefinedEventArgs e) + { + throw new UndefinedInstructionException(e.Position, e.RawOpCode); + } + + private ATranslator GetTranslator() + { + if (Translator == null) + { + Dictionary SymbolTable = new Dictionary(); + + foreach (Executable Exe in Executables) + { + foreach (KeyValuePair KV in Exe.SymbolTable) + { + SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value); + } + } + + Translator = new ATranslator(SymbolTable); + + Translator.CpuTrace += CpuTraceHandler; + } + + return Translator; + } + + private void CpuTraceHandler(object sender, ACpuTraceEventArgs e) + { + string NsoName = string.Empty; + + for (int Index = Executables.Count - 1; Index >= 0; Index--) + { + if (e.Position >= Executables[Index].ImageBase) + { + NsoName = $"{(e.Position - Executables[Index].ImageBase):x16}"; + + break; + } + } + + Logging.Trace($"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}"); + } + + public void EnableCpuTracing() + { + Translator.EnableCpuTrace = true; + } + + public void DisableCpuTracing() + { + Translator.EnableCpuTrace = false; + } + + private int GetFreeTlsSlot(AThread Thread) + { + for (int Index = 1; Index < TotalTlsSlots; Index++) + { + if (TlsSlots.TryAdd(Index, Thread)) + { + return Index; + } + } + + throw new InvalidOperationException(); + } + + private void ThreadFinished(object sender, EventArgs e) + { + if (sender is AThread Thread) + { + Logging.Info($"Thread {Thread.ThreadId} exiting..."); + + TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _); + } + + if (TlsSlots.Count == 0) + { + if (ShouldDispose) + { + Dispose(); + } + + Logging.Info($"No threads running, now exiting Process {ProcessId}..."); + + Ns.Os.ExitProcess(ProcessId); + } + } + + private int GetTlsSlot(long Position) + { + return (int)((Position - MemoryRegions.TlsPagesAddress) / TlsSize); + } + + public HThread GetThread(long Tpidr) + { + if (!ThreadsByTpidr.TryGetValue(Tpidr, out HThread Thread)) + { + Logging.Error($"Thread with TPIDR 0x{Tpidr:x16} not found!"); + } + + return Thread; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing && !Disposed) + { + //If there is still some thread running, disposing the objects is not + //safe as the thread may try to access those resources. Instead, we set + //the flag to have the Process disposed when all threads finishes. + //Note: This may not happen if the guest code gets stuck on a infinite loop. + if (TlsSlots.Count > 0) + { + ShouldDispose = true; + + Logging.Info($"Process {ProcessId} waiting all threads terminate..."); + + return; + } + + Disposed = true; + + Services.Dispose(); + HandleTable.Dispose(); + Scheduler.Dispose(); + SvcHandler.Dispose(); + Memory.Dispose(); + + Logging.Info($"Process {ProcessId} exiting..."); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/ServiceCtx.cs b/Ryujinx.Core/OsHle/ServiceCtx.cs similarity index 82% rename from Ryujinx/OsHle/ServiceCtx.cs rename to Ryujinx.Core/OsHle/ServiceCtx.cs index 501d8d0f0..60c378d5a 100644 --- a/Ryujinx/OsHle/ServiceCtx.cs +++ b/Ryujinx.Core/OsHle/ServiceCtx.cs @@ -1,13 +1,14 @@ using ChocolArm64.Memory; -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Ipc; using System.IO; -namespace Ryujinx.OsHle +namespace Ryujinx.Core.OsHle { class ServiceCtx { 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 IpcMessage Request { get; private set; } @@ -17,6 +18,7 @@ namespace Ryujinx.OsHle public ServiceCtx( Switch Ns, + Process Process, AMemory Memory, HSession Session, IpcMessage Request, @@ -25,6 +27,7 @@ namespace Ryujinx.OsHle BinaryWriter ResponseData) { this.Ns = Ns; + this.Process = Process; this.Memory = Memory; this.Session = Session; this.Request = Request; diff --git a/Ryujinx.Core/OsHle/ServiceMgr.cs b/Ryujinx.Core/OsHle/ServiceMgr.cs new file mode 100644 index 000000000..f59647afe --- /dev/null +++ b/Ryujinx.Core/OsHle/ServiceMgr.cs @@ -0,0 +1,110 @@ +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 Services; + + public ServiceMgr() + { + Services = new Dictionary(); + } + + public IIpcService GetService(string Name) + { + lock (Services) + { + if (!Services.TryGetValue(Name, out IIpcService Service)) + { + switch (Name) + { + case "acc:u0": Service = new ServiceAcc(); break; + case "aoc:u": Service = new ServiceNs(); break; + case "apm": Service = new ServiceApm(); break; + case "apm:p": Service = new ServiceApm(); break; + case "appletOE": Service = new ServiceAppletOE(); break; + case "audout:u": Service = new ServiceAudOut(); break; + case "audren:u": Service = new ServiceAudRen(); break; + case "bsd:s": Service = new ServiceBsd(); break; + case "bsd:u": Service = new ServiceBsd(); break; + case "friend:a": Service = new ServiceFriend(); break; + case "fsp-srv": Service = new ServiceFspSrv(); break; + case "hid": Service = new ServiceHid(); break; + case "lm": Service = new ServiceLm(); break; + case "nifm:u": Service = new ServiceNifm(); break; + case "nvdrv": Service = new ServiceNvDrv(); break; + case "nvdrv:a": Service = new ServiceNvDrv(); break; + case "pctl:a": Service = new ServicePctl(); break; + case "pl:u": Service = new ServicePl(); break; + case "set": Service = new ServiceSet(); break; + case "set:sys": Service = new ServiceSetSys(); break; + case "sfdnsres": Service = new ServiceSfdnsres(); break; + case "sm:": Service = new ServiceSm(); break; + case "ssl": Service = new ServiceSsl(); break; + case "time:s": Service = new ServiceTime(); break; + case "time:u": Service = new ServiceTime(); break; + case "vi:m": Service = new ServiceVi(); break; + case "vi:s": Service = new ServiceVi(); break; + case "vi:u": Service = new ServiceVi(); break; + } + + if (Service == null) + { + throw new NotImplementedException(Name); + } + + Services.Add(Name, Service); + } + + return Service; + } + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + lock (Services) + { + foreach (IIpcService Service in Services.Values) + { + if (Service is IDisposable DisposableSrv) + { + DisposableSrv.Dispose(); + } + } + + Services.Clear(); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Acc/IManagerForApplication.cs b/Ryujinx.Core/OsHle/Services/Acc/IManagerForApplication.cs similarity index 85% rename from Ryujinx/OsHle/Objects/Acc/IManagerForApplication.cs rename to Ryujinx.Core/OsHle/Services/Acc/IManagerForApplication.cs index 404ee7da6..ab491eac7 100644 --- a/Ryujinx/OsHle/Objects/Acc/IManagerForApplication.cs +++ b/Ryujinx.Core/OsHle/Services/Acc/IManagerForApplication.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Acc +namespace Ryujinx.Core.OsHle.IpcServices.Acc { - class IManagerForApplication : IIpcInterface + class IManagerForApplication : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx/OsHle/Objects/Acc/IProfile.cs b/Ryujinx.Core/OsHle/Services/Acc/IProfile.cs similarity index 88% rename from Ryujinx/OsHle/Objects/Acc/IProfile.cs rename to Ryujinx.Core/OsHle/Services/Acc/IProfile.cs index c84c7ae28..77fe2b48c 100644 --- a/Ryujinx/OsHle/Objects/Acc/IProfile.cs +++ b/Ryujinx.Core/OsHle/Services/Acc/IProfile.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Acc +namespace Ryujinx.Core.OsHle.IpcServices.Acc { - class IProfile : IIpcInterface + class IProfile : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx.Core/OsHle/Services/Acc/ServiceAcc.cs b/Ryujinx.Core/OsHle/Services/Acc/ServiceAcc.cs new file mode 100644 index 000000000..8844bb5d1 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Acc/ServiceAcc.cs @@ -0,0 +1,49 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Acc +{ + class ServiceAcc : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceAcc() + { + m_Commands = new Dictionary() + { + { 3, ListOpenUsers }, + { 5, GetProfile }, + { 100, InitializeApplicationInfo }, + { 101, GetBaasAccountManagerForApplication } + }; + } + + public long ListOpenUsers(ServiceCtx Context) + { + return 0; + } + + public long GetProfile(ServiceCtx Context) + { + MakeObject(Context, new IProfile()); + + return 0; + } + + public long InitializeApplicationInfo(ServiceCtx Context) + { + return 0; + } + + public long GetBaasAccountManagerForApplication(ServiceCtx Context) + { + MakeObject(Context, new IManagerForApplication()); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Am/IApplicationFunctions.cs b/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs similarity index 79% rename from Ryujinx/OsHle/Objects/Am/IApplicationFunctions.cs rename to Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs index 138d9084f..c989cdd44 100644 --- a/Ryujinx/OsHle/Objects/Am/IApplicationFunctions.cs +++ b/Ryujinx.Core/OsHle/Services/Am/IApplicationFunctions.cs @@ -1,12 +1,12 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; using System.IO; -using static Ryujinx.OsHle.Objects.ObjHelper; +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; -namespace Ryujinx.OsHle.Objects.Am +namespace Ryujinx.Core.OsHle.IpcServices.Am { - class IApplicationFunctions : IIpcInterface + class IApplicationFunctions : IIpcService { private Dictionary m_Commands; @@ -19,6 +19,7 @@ namespace Ryujinx.OsHle.Objects.Am { 1, PopLaunchParameter }, { 20, EnsureSaveData }, { 21, GetDesiredLanguage }, + { 22, SetTerminateResult }, { 40, NotifyRunning } }; } @@ -52,6 +53,18 @@ namespace Ryujinx.OsHle.Objects.Am return 0; } + public long SetTerminateResult(ServiceCtx Context) + { + int ErrorCode = Context.RequestData.ReadInt32(); + + int Module = ErrorCode & 0xFF; + int Description = (ErrorCode >> 9) & 0xFFF; + + Logging.Info($"({(ErrorModule)Module}){2000 + Module}-{Description}"); + + return 0; + } + public long NotifyRunning(ServiceCtx Context) { Context.ResponseData.Write(1); diff --git a/Ryujinx/OsHle/Objects/Am/IApplicationProxy.cs b/Ryujinx.Core/OsHle/Services/Am/IApplicationProxy.cs similarity index 92% rename from Ryujinx/OsHle/Objects/Am/IApplicationProxy.cs rename to Ryujinx.Core/OsHle/Services/Am/IApplicationProxy.cs index 3d692d6bc..5417d7f04 100644 --- a/Ryujinx/OsHle/Objects/Am/IApplicationProxy.cs +++ b/Ryujinx.Core/OsHle/Services/Am/IApplicationProxy.cs @@ -1,11 +1,11 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -using static Ryujinx.OsHle.Objects.ObjHelper; +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; -namespace Ryujinx.OsHle.Objects.Am +namespace Ryujinx.Core.OsHle.IpcServices.Am { - class IApplicationProxy : IIpcInterface + class IApplicationProxy : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx/OsHle/Objects/Am/IAudioController.cs b/Ryujinx.Core/OsHle/Services/Am/IAudioController.cs similarity index 76% rename from Ryujinx/OsHle/Objects/Am/IAudioController.cs rename to Ryujinx.Core/OsHle/Services/Am/IAudioController.cs index 0ca49f892..1212f1e24 100644 --- a/Ryujinx/OsHle/Objects/Am/IAudioController.cs +++ b/Ryujinx.Core/OsHle/Services/Am/IAudioController.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Am +namespace Ryujinx.Core.OsHle.IpcServices.Am { - class IAudioController : IIpcInterface + class IAudioController : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx/OsHle/Objects/Am/ICommonStateGetter.cs b/Ryujinx.Core/OsHle/Services/Am/ICommonStateGetter.cs similarity index 93% rename from Ryujinx/OsHle/Objects/Am/ICommonStateGetter.cs rename to Ryujinx.Core/OsHle/Services/Am/ICommonStateGetter.cs index 5a3af8e11..2999bbbae 100644 --- a/Ryujinx/OsHle/Objects/Am/ICommonStateGetter.cs +++ b/Ryujinx.Core/OsHle/Services/Am/ICommonStateGetter.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Am +namespace Ryujinx.Core.OsHle.IpcServices.Am { - class ICommonStateGetter : IIpcInterface + class ICommonStateGetter : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx/OsHle/Objects/Am/IDebugFunctions.cs b/Ryujinx.Core/OsHle/Services/Am/IDebugFunctions.cs similarity index 76% rename from Ryujinx/OsHle/Objects/Am/IDebugFunctions.cs rename to Ryujinx.Core/OsHle/Services/Am/IDebugFunctions.cs index dc57e8e6e..944e58d81 100644 --- a/Ryujinx/OsHle/Objects/Am/IDebugFunctions.cs +++ b/Ryujinx.Core/OsHle/Services/Am/IDebugFunctions.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Am +namespace Ryujinx.Core.OsHle.IpcServices.Am { - class IDebugFunctions : IIpcInterface + class IDebugFunctions : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx/OsHle/Objects/Am/IDisplayController.cs b/Ryujinx.Core/OsHle/Services/Am/IDisplayController.cs similarity index 76% rename from Ryujinx/OsHle/Objects/Am/IDisplayController.cs rename to Ryujinx.Core/OsHle/Services/Am/IDisplayController.cs index 886120866..979e842a6 100644 --- a/Ryujinx/OsHle/Objects/Am/IDisplayController.cs +++ b/Ryujinx.Core/OsHle/Services/Am/IDisplayController.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Am +namespace Ryujinx.Core.OsHle.IpcServices.Am { - class IDisplayController : IIpcInterface + class IDisplayController : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx/OsHle/Objects/Am/ILibraryAppletCreator.cs b/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletCreator.cs similarity index 76% rename from Ryujinx/OsHle/Objects/Am/ILibraryAppletCreator.cs rename to Ryujinx.Core/OsHle/Services/Am/ILibraryAppletCreator.cs index 91fae3dd0..9f5b5e69c 100644 --- a/Ryujinx/OsHle/Objects/Am/ILibraryAppletCreator.cs +++ b/Ryujinx.Core/OsHle/Services/Am/ILibraryAppletCreator.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Am +namespace Ryujinx.Core.OsHle.IpcServices.Am { - class ILibraryAppletCreator : IIpcInterface + class ILibraryAppletCreator : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx/OsHle/Objects/Am/ISelfController.cs b/Ryujinx.Core/OsHle/Services/Am/ISelfController.cs similarity index 66% rename from Ryujinx/OsHle/Objects/Am/ISelfController.cs rename to Ryujinx.Core/OsHle/Services/Am/ISelfController.cs index c46396c55..403e4072d 100644 --- a/Ryujinx/OsHle/Objects/Am/ISelfController.cs +++ b/Ryujinx.Core/OsHle/Services/Am/ISelfController.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Am +namespace Ryujinx.Core.OsHle.IpcServices.Am { - class ISelfController : IIpcInterface + class ISelfController : IIpcService { private Dictionary m_Commands; @@ -13,13 +13,28 @@ namespace Ryujinx.OsHle.Objects.Am { m_Commands = new Dictionary() { + { 1, Exit }, + { 10, SetScreenShotPermission }, { 11, SetOperationModeChangedNotification }, { 12, SetPerformanceModeChangedNotification }, { 13, SetFocusHandlingMode }, + { 14, SetRestartMessageEnabled }, { 16, SetOutOfFocusSuspendingEnabled } }; } + public long Exit(ServiceCtx Context) + { + return 0; + } + + public long SetScreenShotPermission(ServiceCtx Context) + { + bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; + + return 0; + } + public long SetOperationModeChangedNotification(ServiceCtx Context) { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; @@ -43,6 +58,13 @@ namespace Ryujinx.OsHle.Objects.Am return 0; } + public long SetRestartMessageEnabled(ServiceCtx Context) + { + bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; + + return 0; + } + public long SetOutOfFocusSuspendingEnabled(ServiceCtx Context) { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; diff --git a/Ryujinx/OsHle/Objects/Am/IStorage.cs b/Ryujinx.Core/OsHle/Services/Am/IStorage.cs similarity index 79% rename from Ryujinx/OsHle/Objects/Am/IStorage.cs rename to Ryujinx.Core/OsHle/Services/Am/IStorage.cs index d5a7ee610..375b960b1 100644 --- a/Ryujinx/OsHle/Objects/Am/IStorage.cs +++ b/Ryujinx.Core/OsHle/Services/Am/IStorage.cs @@ -1,11 +1,11 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -using static Ryujinx.OsHle.Objects.ObjHelper; +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; -namespace Ryujinx.OsHle.Objects.Am +namespace Ryujinx.Core.OsHle.IpcServices.Am { - class IStorage : IIpcInterface + class IStorage : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx/OsHle/Objects/Am/IStorageAccessor.cs b/Ryujinx.Core/OsHle/Services/Am/IStorageAccessor.cs similarity index 92% rename from Ryujinx/OsHle/Objects/Am/IStorageAccessor.cs rename to Ryujinx.Core/OsHle/Services/Am/IStorageAccessor.cs index bcf0fc01c..6d83e6f94 100644 --- a/Ryujinx/OsHle/Objects/Am/IStorageAccessor.cs +++ b/Ryujinx.Core/OsHle/Services/Am/IStorageAccessor.cs @@ -1,11 +1,11 @@ using ChocolArm64.Memory; -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Am +namespace Ryujinx.Core.OsHle.IpcServices.Am { - class IStorageAccessor : IIpcInterface + class IStorageAccessor : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx/OsHle/Objects/Am/IWindowController.cs b/Ryujinx.Core/OsHle/Services/Am/IWindowController.cs similarity index 86% rename from Ryujinx/OsHle/Objects/Am/IWindowController.cs rename to Ryujinx.Core/OsHle/Services/Am/IWindowController.cs index 1796644e3..ddc73bced 100644 --- a/Ryujinx/OsHle/Objects/Am/IWindowController.cs +++ b/Ryujinx.Core/OsHle/Services/Am/IWindowController.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Am +namespace Ryujinx.Core.OsHle.IpcServices.Am { - class IWindowController : IIpcInterface + class IWindowController : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx.Core/OsHle/Services/Am/ServiceAppletOE.cs b/Ryujinx.Core/OsHle/Services/Am/ServiceAppletOE.cs new file mode 100644 index 000000000..b60c93dd5 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Am/ServiceAppletOE.cs @@ -0,0 +1,29 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Am +{ + class ServiceAppletOE : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceAppletOE() + { + m_Commands = new Dictionary() + { + { 0, OpenApplicationProxy } + }; + } + + public long OpenApplicationProxy(ServiceCtx Context) + { + MakeObject(Context, new IApplicationProxy()); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Apm/ISession.cs b/Ryujinx.Core/OsHle/Services/Apm/ISession.cs similarity index 85% rename from Ryujinx/OsHle/Objects/Apm/ISession.cs rename to Ryujinx.Core/OsHle/Services/Apm/ISession.cs index f3965b484..500f7596c 100644 --- a/Ryujinx/OsHle/Objects/Apm/ISession.cs +++ b/Ryujinx.Core/OsHle/Services/Apm/ISession.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Apm +namespace Ryujinx.Core.OsHle.IpcServices.Apm { - class ISession : IIpcInterface + class ISession : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx.Core/OsHle/Services/Apm/ServiceApm.cs b/Ryujinx.Core/OsHle/Services/Apm/ServiceApm.cs new file mode 100644 index 000000000..d6c0400ac --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Apm/ServiceApm.cs @@ -0,0 +1,29 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Apm +{ + class ServiceApm : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceApm() + { + m_Commands = new Dictionary() + { + { 0, OpenSession } + }; + } + + public long OpenSession(ServiceCtx Context) + { + MakeObject(Context, new ISession()); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Aud/IAudioOut.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs similarity index 64% rename from Ryujinx/OsHle/Objects/Aud/IAudioOut.cs rename to Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs index 9e55e30e8..2312920f6 100644 --- a/Ryujinx/OsHle/Objects/Aud/IAudioOut.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs @@ -1,15 +1,15 @@ -using ChocolArm64.Memory; -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Ipc; +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Ipc; using OpenTK.Audio; using OpenTK.Audio.OpenAL; using System; using System.Collections.Generic; using System.IO; -namespace Ryujinx.OsHle.Objects.Aud +namespace Ryujinx.Core.OsHle.IpcServices.Aud { - class IAudioOut : IIpcInterface + class IAudioOut : IIpcService, IDisposable { private Dictionary m_Commands; @@ -39,7 +39,7 @@ namespace Ryujinx.OsHle.Objects.Aud //IAudioOut private AudioOutState State = AudioOutState.Stopped; - private Queue KeysQueue = new Queue(); + private Queue BufferIdQueue = new Queue(); //OpenAL private bool OpenALInstalled = true; @@ -71,7 +71,7 @@ namespace Ryujinx.OsHle.Objects.Aud OpenALInstalled = false; } - if (OpenALInstalled) AL.Listener(ALListenerf.Gain, (float)8.0); //Add more gain to it + if (OpenALInstalled) AL.Listener(ALListenerf.Gain, 8.0f); //Add more gain to it } return 0; @@ -88,6 +88,7 @@ namespace Ryujinx.OsHle.Objects.Aud AL.SourceStop(Source); AL.DeleteSource(Source); + AL.DeleteBuffers(1, ref Buffer); } State = AudioOutState.Stopped; } @@ -99,9 +100,8 @@ namespace Ryujinx.OsHle.Objects.Aud { long BufferId = Context.RequestData.ReadInt64(); - KeysQueue.Enqueue(BufferId); - byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, sizeof(long) * 5); + using (MemoryStream MS = new MemoryStream(AudioOutBuffer)) { BinaryReader Reader = new BinaryReader(MS); @@ -111,18 +111,26 @@ namespace Ryujinx.OsHle.Objects.Aud long SizeDataInSampleBuffer = Reader.ReadInt64(); long OffsetDataInSampleBuffer = Reader.ReadInt64(); - byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerSampleBuffer + OffsetDataInSampleBuffer, (int)SizeDataInSampleBuffer); - - if (OpenALInstalled) + if (SizeDataInSampleBuffer > 0) { - if (AudioCtx == null) //Needed to call the instance of AudioContext() - return 0; + BufferIdQueue.Enqueue(BufferId); - Buffer = AL.GenBuffer(); - AL.BufferData(Buffer, ALFormat.Stereo16, AudioSampleBuffer, AudioSampleBuffer.Length, 48000); + byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerSampleBuffer + OffsetDataInSampleBuffer, (int)SizeDataInSampleBuffer); - Source = AL.GenSource(); - AL.SourceQueueBuffer(Source, Buffer); + if (OpenALInstalled) + { + if (AudioCtx == null) //Needed to call the instance of AudioContext() + return 0; + + EnsureAudioFinalized(); + + Source = AL.GenSource(); + Buffer = AL.GenBuffer(); + + AL.BufferData(Buffer, ALFormat.Stereo16, AudioSampleBuffer, AudioSampleBuffer.Length, 48000); + AL.SourceQueueBuffer(Source, Buffer); + AL.SourcePlay(Source); + } } } @@ -131,7 +139,7 @@ namespace Ryujinx.OsHle.Objects.Aud public long RegisterBufferEvent(ServiceCtx Context) { - int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent()); + int Handle = Context.Process.HandleTable.OpenHandle(new HEvent()); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -140,25 +148,19 @@ namespace Ryujinx.OsHle.Objects.Aud public long GetReleasedAudioOutBuffer(ServiceCtx Context) { - long TempKey = 0; + int ReleasedBuffersCount = 0; - if (KeysQueue.Count > 0) TempKey = KeysQueue.Dequeue(); - - AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, BitConverter.GetBytes(TempKey)); - - int ReleasedBuffersCount = 1; - Context.ResponseData.Write(ReleasedBuffersCount); - - if (OpenALInstalled) + for(int i = 0; i < BufferIdQueue.Count; i++) { - if (AudioCtx == null) //Needed to call the instance of AudioContext() - return 0; + long BufferId = BufferIdQueue.Dequeue(); - AL.SourcePlay(Source); - int[] FreeBuffers = AL.SourceUnqueueBuffers(Source, 1); - AL.DeleteBuffers(FreeBuffers); + AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position + (8 * i), BitConverter.GetBytes(BufferId)); + + ReleasedBuffersCount++; } + Context.ResponseData.Write(ReleasedBuffersCount); + return 0; } @@ -176,5 +178,33 @@ namespace Ryujinx.OsHle.Objects.Aud { return 0; } + + private void EnsureAudioFinalized() + { + if (Source != 0 || + Buffer != 0) + { + AL.SourceStop(Source); + AL.SourceUnqueueBuffer(Buffer); + AL.DeleteSource(Source); + AL.DeleteBuffers(1, ref Buffer); + + Source = 0; + Buffer = 0; + } + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + EnsureAudioFinalized(); + } + } } } diff --git a/Ryujinx/OsHle/Objects/Aud/IAudioRenderer.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs similarity index 90% rename from Ryujinx/OsHle/Objects/Aud/IAudioRenderer.cs rename to Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs index a953d82a0..4d29371fb 100644 --- a/Ryujinx/OsHle/Objects/Aud/IAudioRenderer.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs @@ -1,10 +1,10 @@ -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Aud +namespace Ryujinx.Core.OsHle.IpcServices.Aud { - class IAudioRenderer : IIpcInterface + class IAudioRenderer : IIpcService { private Dictionary m_Commands; @@ -56,7 +56,7 @@ namespace Ryujinx.OsHle.Objects.Aud public long QuerySystemEvent(ServiceCtx Context) { - int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent()); + int Handle = Context.Process.HandleTable.OpenHandle(new HEvent()); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudOut.cs b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudOut.cs new file mode 100644 index 000000000..eb923562b --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudOut.cs @@ -0,0 +1,57 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; +using System.Text; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Aud +{ + class ServiceAudOut : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceAudOut() + { + m_Commands = new Dictionary() + { + { 0, ListAudioOuts }, + { 1, OpenAudioOut }, + }; + } + + public long ListAudioOuts(ServiceCtx Context) + { + long Position = Context.Request.ReceiveBuff[0].Position; + + AMemoryHelper.WriteBytes(Context.Memory, Position, Encoding.ASCII.GetBytes("iface")); + + Context.ResponseData.Write(1); + + return 0; + } + + public long OpenAudioOut(ServiceCtx Context) + { + MakeObject(Context, new IAudioOut()); + + Context.ResponseData.Write(48000); //Sample Rate + Context.ResponseData.Write(2); //Channel Count + Context.ResponseData.Write(2); //PCM Format + /* + 0 - Invalid + 1 - INT8 + 2 - INT16 + 3 - INT24 + 4 - INT32 + 5 - PCM Float + 6 - ADPCM + */ + Context.ResponseData.Write(0); //Unknown + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs new file mode 100644 index 000000000..15bda04cc --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs @@ -0,0 +1,59 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Aud +{ + class ServiceAudRen : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceAudRen() + { + m_Commands = new Dictionary() + { + { 0, OpenAudioRenderer }, + { 1, GetAudioRendererWorkBufferSize }, + { 2, GetAudioRenderersProcessMasterVolume } + }; + } + + public long OpenAudioRenderer(ServiceCtx Context) + { + MakeObject(Context, new IAudioRenderer()); + + return 0; + } + + public long GetAudioRendererWorkBufferSize(ServiceCtx Context) + { + int SampleRate = Context.RequestData.ReadInt32(); + int Unknown4 = Context.RequestData.ReadInt32(); + int Unknown8 = Context.RequestData.ReadInt32(); + int UnknownC = Context.RequestData.ReadInt32(); + int Unknown10 = Context.RequestData.ReadInt32(); + int Unknown14 = Context.RequestData.ReadInt32(); + int Unknown18 = Context.RequestData.ReadInt32(); + int Unknown1c = Context.RequestData.ReadInt32(); + int Unknown20 = Context.RequestData.ReadInt32(); + int Unknown24 = Context.RequestData.ReadInt32(); + int Unknown28 = Context.RequestData.ReadInt32(); + int Unknown2c = Context.RequestData.ReadInt32(); + int Rev1Magic = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(0x400L); + + return 0; + } + + public long GetAudioRenderersProcessMasterVolume(ServiceCtx Context) + { + Context.ResponseData.Write(0); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs b/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs new file mode 100644 index 000000000..da1e51e17 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs @@ -0,0 +1,492 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Ipc; +using Ryujinx.Core.OsHle.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; + +namespace Ryujinx.Core.OsHle.IpcServices.Bsd +{ + + //bsd_errno == (SocketException.ErrorCode - 10000) + //https://github.com/freebsd/freebsd/blob/master/sys/sys/errno.h + public enum BsdError + { + ENOTSOCK = 38, /* Socket operation on non-socket */ + EDESTADDRREQ = 39, /* Destination address required */ + EMSGSIZE = 40, /* Message too long */ + EPROTOTYPE = 41, /* Protocol wrong type for socket */ + ENOPROTOOPT = 42, /* Protocol not available */ + EPROTONOSUPPORT = 43, /* Protocol not supported */ + ESOCKTNOSUPPORT = 44, /* Socket type not supported */ + EOPNOTSUPP = 45, /* Operation not supported */ + EPFNOSUPPORT = 46, /* Protocol family not supported */ + EAFNOSUPPORT = 47, /* Address family not supported by protocol family */ + EADDRINUSE = 48, /* Address already in use */ + EADDRNOTAVAIL = 49, /* Can't assign requested address */ + ENETDOWN = 50, /* Network is down */ + ENETUNREACH = 51, /* Network is unreachable */ + ENETRESET = 52, /* Network dropped connection on reset */ + ECONNABORTED = 53, /* Software caused connection abort */ + ECONNRESET = 54, /* Connection reset by peer */ + ENOBUFS = 55, /* No buffer space available */ + EISCONN = 56, /* Socket is already connected */ + ENOTCONN = 57, /* Socket is not connected */ + ESHUTDOWN = 58, /* Can't send after socket shutdown */ + ETOOMANYREFS = 59, /* Too many references: can't splice */ + ETIMEDOUT = 60, /* Operation timed out */ + ECONNREFUSED = 61 /* Connection refused */ + } + + class SocketBsd + { + public int Family; + public int Type; + public int Protocol; + public IPAddress IpAddress; + public IPEndPoint RemoteEP; + public Socket Handle; + } + + class ServiceBsd : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + private List Sockets = new List(); + + public ServiceBsd() + { + m_Commands = new Dictionary() + { + { 0, Initialize }, + { 1, StartMonitoring }, + { 2, Socket }, + { 6, Poll }, + { 8, Recv }, + { 10, Send }, + { 11, SendTo }, + { 12, Accept }, + { 13, Bind }, + { 14, Connect }, + { 18, Listen }, + { 21, SetSockOpt }, + { 26, Close } + }; + } + + //(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno + public long Initialize(ServiceCtx Context) + { + /* + typedef struct { + u32 version; // Observed 1 on 2.0 LibAppletWeb, 2 on 3.0. + u32 tcp_tx_buf_size; // Size of the TCP transfer (send) buffer (initial or fixed). + u32 tcp_rx_buf_size; // Size of the TCP recieve buffer (initial or fixed). + u32 tcp_tx_buf_max_size; // Maximum size of the TCP transfer (send) buffer. If it is 0, the size of the buffer is fixed to its initial value. + u32 tcp_rx_buf_max_size; // Maximum size of the TCP receive buffer. If it is 0, the size of the buffer is fixed to its initial value. + u32 udp_tx_buf_size; // Size of the UDP transfer (send) buffer (typically 0x2400 bytes). + u32 udp_rx_buf_size; // Size of the UDP receive buffer (typically 0xA500 bytes). + u32 sb_efficiency; // Number of buffers for each socket (standard values range from 1 to 8). + } BsdBufferConfig; + */ + + long Pid = Context.RequestData.ReadInt64(); + long TransferMemorySize = Context.RequestData.ReadInt64(); + + // Two other args are unknown! + + Context.ResponseData.Write(0); + + //Todo: Stub + + return 0; + } + + //(u64, pid) + public long StartMonitoring(ServiceCtx Context) + { + //Todo: Stub + + return 0; + } + + //(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) + public long Socket(ServiceCtx Context) + { + SocketBsd NewBSDSocket = new SocketBsd + { + Family = Context.RequestData.ReadInt32(), + Type = Context.RequestData.ReadInt32(), + Protocol = Context.RequestData.ReadInt32() + }; + + Sockets.Add(NewBSDSocket); + + Sockets[Sockets.Count - 1].Handle = new Socket((AddressFamily)Sockets[Sockets.Count - 1].Family, + (SocketType)Sockets[Sockets.Count - 1].Type, + (ProtocolType)Sockets[Sockets.Count - 1].Protocol); + + Context.ResponseData.Write(Sockets.Count - 1); + Context.ResponseData.Write(0); + + return 0; + } + + //(u32, u32, buffer) -> (i32 ret, u32 bsd_errno, buffer) + public long Poll(ServiceCtx Context) + { + int PollCount = Context.RequestData.ReadInt32(); + int TimeOut = Context.RequestData.ReadInt32(); + + //https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h + //https://msdn.microsoft.com/fr-fr/library/system.net.sockets.socket.poll(v=vs.110).aspx + //https://github.com/switchbrew/libnx/blob/e0457c4534b3c37426d83e1a620f82cb28c3b528/nx/source/services/bsd.c#L343 + //https://github.com/TuxSH/ftpd/blob/switch_pr/source/ftp.c#L1634 + //https://linux.die.net/man/2/poll + + byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.SendBuff[0].Position, + (int)Context.Request.SendBuff[0].Size); + int SocketId = Get32(SentBuffer, 0); + short RequestedEvents = (short)Get16(SentBuffer, 4); + short ReturnedEvents = (short)Get16(SentBuffer, 6); + + //Todo: Stub - Need to implemented the Type-22 buffer. + + Context.ResponseData.Write(1); + Context.ResponseData.Write(0); + + return 0; + } + + //(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, buffer message) + public long Recv(ServiceCtx Context) + { + try + { + int SocketId = Context.RequestData.ReadInt32(); + int SocketFlags = Context.RequestData.ReadInt32(); + byte[] ReceivedBuffer = new byte[Context.Request.ReceiveBuff[0].Size]; + int ReadedBytes = Sockets[SocketId].Handle.Receive(ReceivedBuffer); + + //Logging.Debug("Received Buffer:" + Environment.NewLine + Logging.HexDump(ReceivedBuffer)); + + AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, ReceivedBuffer); + + Context.ResponseData.Write(ReadedBytes); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket, u32 flags, buffer) -> (i32 ret, u32 bsd_errno) + public long Send(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + int SocketFlags = Context.RequestData.ReadInt32(); + byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.SendBuff[0].Position, + (int)Context.Request.SendBuff[0].Size); + + try + { + //Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer)); + + int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer); + + Context.ResponseData.Write(BytesSent); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket, u32 flags, buffer, buffer) -> (i32 ret, u32 bsd_errno) + public long SendTo(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + int SocketFlags = Context.RequestData.ReadInt32(); + byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.SendBuff[0].Position, + (int)Context.Request.SendBuff[0].Size); + byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.SendBuff[1].Position, + (int)Context.Request.SendBuff[1].Size); + + if (!Sockets[SocketId].Handle.Connected) + { + try + { + ParseAddrBuffer(SocketId, AddressBuffer); + + Sockets[SocketId].Handle.Connect(Sockets[SocketId].RemoteEP); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + } + + try + { + //Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer)); + + int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer); + + Context.ResponseData.Write(BytesSent); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer addr) + public long Accept(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + long AddrBufferPtr = Context.Request.ReceiveBuff[0].Position; + + Socket HandleAccept = null; + + var TimeOut = Task.Factory.StartNew(() => + { + try + { + HandleAccept = Sockets[SocketId].Handle.Accept(); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + }); + + TimeOut.Wait(10000); + + if (HandleAccept != null) + { + SocketBsd NewBSDSocket = new SocketBsd + { + IpAddress = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint).Address, + RemoteEP = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint), + Handle = HandleAccept + }; + + Sockets.Add(NewBSDSocket); + + using (MemoryStream MS = new MemoryStream()) + { + BinaryWriter Writer = new BinaryWriter(MS); + + Writer.Write((byte)0); + Writer.Write((byte)Sockets[Sockets.Count - 1].Handle.AddressFamily); + Writer.Write((Int16)((IPEndPoint)Sockets[Sockets.Count - 1].Handle.LocalEndPoint).Port); + + string[] IpAdress = Sockets[Sockets.Count - 1].IpAddress.ToString().Split('.'); + Writer.Write(byte.Parse(IpAdress[0])); + Writer.Write(byte.Parse(IpAdress[1])); + Writer.Write(byte.Parse(IpAdress[2])); + Writer.Write(byte.Parse(IpAdress[3])); + + AMemoryHelper.WriteBytes(Context.Memory, AddrBufferPtr, MS.ToArray()); + + Context.ResponseData.Write(Sockets.Count - 1); + Context.ResponseData.Write(0); + Context.ResponseData.Write(MS.Length); + } + } + else + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write((int)BsdError.ETIMEDOUT); + } + + return 0; + } + + //(u32 socket, buffer) -> (i32 ret, u32 bsd_errno) + public long Bind(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + + byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.SendBuff[0].Position, + (int)Context.Request.SendBuff[0].Size); + + try + { + ParseAddrBuffer(SocketId, AddressBuffer); + + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket, buffer) -> (i32 ret, u32 bsd_errno) + public long Connect(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + + byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.SendBuff[0].Position, + (int)Context.Request.SendBuff[0].Size); + + try + { + ParseAddrBuffer(SocketId, AddressBuffer); + + Sockets[SocketId].Handle.Connect(Sockets[SocketId].RemoteEP); + + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket, u32 backlog) -> (i32 ret, u32 bsd_errno) + public long Listen(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + int BackLog = Context.RequestData.ReadInt32(); + + try + { + Sockets[SocketId].Handle.Bind(Sockets[SocketId].RemoteEP); + Sockets[SocketId].Handle.Listen(BackLog); + + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket, u32 level, u32 option_name, buffer) -> (i32 ret, u32 bsd_errno) + public long SetSockOpt(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + int SocketLevel = Context.RequestData.ReadInt32(); + int SocketOptionName = Context.RequestData.ReadInt32(); + + byte[] SocketOptionValue = AMemoryHelper.ReadBytes(Context.Memory, + Context.Request.PtrBuff[0].Position, + Context.Request.PtrBuff[0].Size); + + try + { + Sockets[SocketId].Handle.SetSocketOption((SocketOptionLevel)SocketLevel, + (SocketOptionName)SocketOptionName, + Get32(SocketOptionValue, 0)); + + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + //(u32 socket) -> (i32 ret, u32 bsd_errno) + public long Close(ServiceCtx Context) + { + int SocketId = Context.RequestData.ReadInt32(); + + try + { + Sockets[SocketId].Handle.Close(); + Sockets[SocketId] = null; + + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + } + catch (SocketException Ex) + { + Context.ResponseData.Write(-1); + Context.ResponseData.Write(Ex.ErrorCode - 10000); + } + + return 0; + } + + public void ParseAddrBuffer(int SocketId, byte[] AddrBuffer) + { + using (MemoryStream MS = new MemoryStream(AddrBuffer)) + { + BinaryReader Reader = new BinaryReader(MS); + + int Size = Reader.ReadByte(); + int Family = Reader.ReadByte(); + int Port = EndianSwap.Swap16(Reader.ReadInt16()); + string IpAddress = Reader.ReadByte().ToString() + + "." + Reader.ReadByte().ToString() + + "." + Reader.ReadByte().ToString() + + "." + Reader.ReadByte().ToString(); + + Logging.Debug($"Try to connect to {IpAddress}:{Port}"); + + Sockets[SocketId].IpAddress = IPAddress.Parse(IpAddress); + Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port); + } + } + + private int Get16(byte[] Data, int Address) + { + return + Data[Address + 0] << 0 | + Data[Address + 1] << 8; + } + + private int Get32(byte[] Data, int Address) + { + return + Data[Address + 0] << 0 | + Data[Address + 1] << 8 | + Data[Address + 2] << 16 | + Data[Address + 3] << 24; + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Friend/IFriendService.cs b/Ryujinx.Core/OsHle/Services/Friend/IFriendService.cs similarity index 76% rename from Ryujinx/OsHle/Objects/Friend/IFriendService.cs rename to Ryujinx.Core/OsHle/Services/Friend/IFriendService.cs index 41084f8da..e3e03da85 100644 --- a/Ryujinx/OsHle/Objects/Friend/IFriendService.cs +++ b/Ryujinx.Core/OsHle/Services/Friend/IFriendService.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Friend +namespace Ryujinx.Core.OsHle.IpcServices.Friend { - class IFriendService : IIpcInterface + class IFriendService : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx.Core/OsHle/Services/Friend/ServiceFriend.cs b/Ryujinx.Core/OsHle/Services/Friend/ServiceFriend.cs new file mode 100644 index 000000000..674877f67 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Friend/ServiceFriend.cs @@ -0,0 +1,29 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Friend +{ + class ServiceFriend : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceFriend() + { + m_Commands = new Dictionary() + { + { 0, CreateFriendService } + }; + } + + public static long CreateFriendService(ServiceCtx Context) + { + MakeObject(Context, new IFriendService()); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/FsErr.cs b/Ryujinx.Core/OsHle/Services/FspSrv/FsErr.cs new file mode 100644 index 000000000..656d529f2 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/FspSrv/FsErr.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Core.OsHle.IpcServices.FspSrv +{ + static class FsErr + { + public const int PathDoesNotExist = 1; + public const int PathAlreadyExists = 2; + public const int PathAlreadyInUse = 7; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/IDirectory.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IDirectory.cs new file mode 100644 index 000000000..54dbec746 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/FspSrv/IDirectory.cs @@ -0,0 +1,117 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Ipc; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Ryujinx.Core.OsHle.IpcServices.FspSrv +{ + class IDirectory : IIpcService, IDisposable + { + private const int DirectoryEntrySize = 0x310; + + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + private List DirectoryEntries; + + private int CurrentItemIndex; + + public event EventHandler Disposed; + + public string HostPath { get; private set; } + + public IDirectory(string HostPath, int Flags) + { + m_Commands = new Dictionary() + { + { 0, Read }, + { 1, GetEntryCount } + }; + + this.HostPath = HostPath; + + DirectoryEntries = new List(); + + if ((Flags & 1) != 0) + { + DirectoryEntries.AddRange(Directory.GetDirectories(HostPath)); + } + + if ((Flags & 2) != 0) + { + DirectoryEntries.AddRange(Directory.GetFiles(HostPath)); + } + + CurrentItemIndex = 0; + } + + public long Read(ServiceCtx Context) + { + long BufferPosition = Context.Request.ReceiveBuff[0].Position; + long BufferLen = Context.Request.ReceiveBuff[0].Size; + + int MaxReadCount = (int)(BufferLen / DirectoryEntrySize); + + int Count = Math.Min(DirectoryEntries.Count - CurrentItemIndex, MaxReadCount); + + for (int Index = 0; Index < Count; Index++) + { + long Position = BufferPosition + Index * DirectoryEntrySize; + + WriteDirectoryEntry(Context, Position, DirectoryEntries[CurrentItemIndex++]); + } + + Context.ResponseData.Write((long)Count); + + return 0; + } + + private void WriteDirectoryEntry(ServiceCtx Context, long Position, string FullPath) + { + for (int Offset = 0; Offset < 0x300; Offset += 8) + { + Context.Memory.WriteInt64(Position + Offset, 0); + } + + byte[] NameBuffer = Encoding.UTF8.GetBytes(Path.GetFileName(FullPath)); + + AMemoryHelper.WriteBytes(Context.Memory, Position, NameBuffer); + + int Type = 0; + long Size = 0; + + if (File.Exists(FullPath)) + { + Type = 1; + Size = new FileInfo(FullPath).Length; + } + + Context.Memory.WriteInt32(Position + 0x300, 0); //Padding? + Context.Memory.WriteInt32(Position + 0x304, Type); + Context.Memory.WriteInt64(Position + 0x308, Size); + } + + public long GetEntryCount(ServiceCtx Context) + { + Context.ResponseData.Write((long)DirectoryEntries.Count); + + return 0; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Disposed?.Invoke(this, EventArgs.Empty); + } + } + } +} diff --git a/Ryujinx/OsHle/Objects/FspSrv/IFile.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IFile.cs similarity index 61% rename from Ryujinx/OsHle/Objects/FspSrv/IFile.cs rename to Ryujinx.Core/OsHle/Services/FspSrv/IFile.cs index 1c6cc2e29..ac2100f29 100644 --- a/Ryujinx/OsHle/Objects/FspSrv/IFile.cs +++ b/Ryujinx.Core/OsHle/Services/FspSrv/IFile.cs @@ -1,12 +1,12 @@ using ChocolArm64.Memory; -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System; using System.Collections.Generic; using System.IO; -namespace Ryujinx.OsHle.Objects.FspSrv +namespace Ryujinx.Core.OsHle.IpcServices.FspSrv { - class IFile : IIpcInterface, IDisposable + class IFile : IIpcService, IDisposable { private Dictionary m_Commands; @@ -14,15 +14,23 @@ namespace Ryujinx.OsHle.Objects.FspSrv private Stream BaseStream; - public IFile(Stream BaseStream) + public event EventHandler Disposed; + + public string HostPath { get; private set; } + + public IFile(Stream BaseStream, string HostPath) { m_Commands = new Dictionary() { - { 0, Read }, - { 1, Write } + { 0, Read }, + { 1, Write }, + { 2, Flush }, + { 3, SetSize }, + { 4, GetSize } }; this.BaseStream = BaseStream; + this.HostPath = HostPath; } public long Read(ServiceCtx Context) @@ -35,14 +43,13 @@ namespace Ryujinx.OsHle.Objects.FspSrv byte[] Data = new byte[Size]; + BaseStream.Seek(Offset, SeekOrigin.Begin); + int ReadSize = BaseStream.Read(Data, 0, (int)Size); AMemoryHelper.WriteBytes(Context.Memory, Position, Data); - //TODO: Use ReadSize, we need to return the size that was REALLY read from the file. - //This is a workaround because we are doing something wrong and the game expects to read - //data from a file that doesn't yet exists -- and breaks if it can't read anything. - Context.ResponseData.Write((long)Size); + Context.ResponseData.Write((long)ReadSize); return 0; } @@ -63,6 +70,29 @@ namespace Ryujinx.OsHle.Objects.FspSrv return 0; } + public long Flush(ServiceCtx Context) + { + BaseStream.Flush(); + + return 0; + } + + public long SetSize(ServiceCtx Context) + { + long Size = Context.RequestData.ReadInt64(); + + BaseStream.SetLength(Size); + + return 0; + } + + public long GetSize(ServiceCtx Context) + { + Context.ResponseData.Write(BaseStream.Length); + + return 0; + } + public void Dispose() { Dispose(true); @@ -73,6 +103,8 @@ namespace Ryujinx.OsHle.Objects.FspSrv if (disposing && BaseStream != null) { BaseStream.Dispose(); + + Disposed?.Invoke(this, EventArgs.Empty); } } } diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs new file mode 100644 index 000000000..62bcb8e8b --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs @@ -0,0 +1,400 @@ +using Ryujinx.Core.OsHle.Ipc; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +using static Ryujinx.Core.OsHle.ErrorCode; +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.FspSrv +{ + class IFileSystem : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + private HashSet OpenPaths; + + private string Path; + + public IFileSystem(string Path) + { + m_Commands = new Dictionary() + { + { 0, CreateFile }, + { 1, DeleteFile }, + { 2, CreateDirectory }, + { 3, DeleteDirectory }, + { 4, DeleteDirectoryRecursively }, + { 5, RenameFile }, + { 6, RenameDirectory }, + { 7, GetEntryType }, + { 8, OpenFile }, + { 9, OpenDirectory }, + { 10, Commit }, + { 11, GetFreeSpaceSize }, + { 12, GetTotalSpaceSize }, + //{ 13, CleanDirectoryRecursively }, + //{ 14, GetFileTimeStampRaw } + }; + + OpenPaths = new HashSet(); + + this.Path = Path; + } + + public long CreateFile(ServiceCtx Context) + { + long Position = Context.Request.PtrBuff[0].Position; + + string Name = ReadUtf8String(Context); + + long Mode = Context.RequestData.ReadInt64(); + int Size = Context.RequestData.ReadInt32(); + + string FileName = Context.Ns.VFs.GetFullPath(Path, Name); + + if (FileName == null) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + if (File.Exists(FileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); + } + + if (IsPathAlreadyInUse(FileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + using (FileStream NewFile = File.Create(FileName)) + { + NewFile.SetLength(Size); + } + + return 0; + } + + public long DeleteFile(ServiceCtx Context) + { + long Position = Context.Request.PtrBuff[0].Position; + + string Name = ReadUtf8String(Context); + + string FileName = Context.Ns.VFs.GetFullPath(Path, Name); + + if (!File.Exists(FileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + if (IsPathAlreadyInUse(FileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + File.Delete(FileName); + + return 0; + } + + public long CreateDirectory(ServiceCtx Context) + { + long Position = Context.Request.PtrBuff[0].Position; + + string Name = ReadUtf8String(Context); + + string DirName = Context.Ns.VFs.GetFullPath(Path, Name); + + if (DirName == null) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + if (Directory.Exists(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); + } + + if (IsPathAlreadyInUse(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + Directory.CreateDirectory(DirName); + + return 0; + } + + public long DeleteDirectory(ServiceCtx Context) + { + return DeleteDirectory(Context, false); + } + + public long DeleteDirectoryRecursively(ServiceCtx Context) + { + return DeleteDirectory(Context, true); + } + + private long DeleteDirectory(ServiceCtx Context, bool Recursive) + { + long Position = Context.Request.PtrBuff[0].Position; + + string Name = ReadUtf8String(Context); + + string DirName = Context.Ns.VFs.GetFullPath(Path, Name); + + if (!Directory.Exists(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + if (IsPathAlreadyInUse(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + Directory.Delete(DirName, Recursive); + + return 0; + } + + public long RenameFile(ServiceCtx Context) + { + string OldName = ReadUtf8String(Context, 0); + string NewName = ReadUtf8String(Context, 1); + + string OldFileName = Context.Ns.VFs.GetFullPath(Path, OldName); + string NewFileName = Context.Ns.VFs.GetFullPath(Path, NewName); + + if (!File.Exists(OldFileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + if (File.Exists(NewFileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); + } + + if (IsPathAlreadyInUse(OldFileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + File.Move(OldFileName, NewFileName); + + return 0; + } + + public long RenameDirectory(ServiceCtx Context) + { + string OldName = ReadUtf8String(Context, 0); + string NewName = ReadUtf8String(Context, 1); + + string OldDirName = Context.Ns.VFs.GetFullPath(Path, OldName); + string NewDirName = Context.Ns.VFs.GetFullPath(Path, NewName); + + if (!Directory.Exists(OldDirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + if (Directory.Exists(NewDirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyExists); + } + + if (IsPathAlreadyInUse(OldDirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + Directory.Move(OldDirName, NewDirName); + + return 0; + } + + public long GetEntryType(ServiceCtx Context) + { + long Position = Context.Request.PtrBuff[0].Position; + + string Name = ReadUtf8String(Context); + + string FileName = Context.Ns.VFs.GetFullPath(Path, Name); + + if (File.Exists(FileName)) + { + Context.ResponseData.Write(1); + } + else if (Directory.Exists(FileName)) + { + Context.ResponseData.Write(0); + } + else + { + Context.ResponseData.Write(0); + + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + return 0; + } + + public long OpenFile(ServiceCtx Context) + { + long Position = Context.Request.PtrBuff[0].Position; + + int FilterFlags = Context.RequestData.ReadInt32(); + + string Name = ReadUtf8String(Context); + + string FileName = Context.Ns.VFs.GetFullPath(Path, Name); + + if (!File.Exists(FileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + if (IsPathAlreadyInUse(FileName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + FileStream Stream = new FileStream(FileName, FileMode.Open); + + IFile FileInterface = new IFile(Stream, FileName); + + FileInterface.Disposed += RemoveFileInUse; + + lock (OpenPaths) + { + OpenPaths.Add(FileName); + } + + MakeObject(Context, FileInterface); + + return 0; + } + + public long OpenDirectory(ServiceCtx Context) + { + long Position = Context.Request.PtrBuff[0].Position; + + int FilterFlags = Context.RequestData.ReadInt32(); + + string Name = ReadUtf8String(Context); + + string DirName = Context.Ns.VFs.GetFullPath(Path, Name); + + if (!Directory.Exists(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); + } + + if (IsPathAlreadyInUse(DirName)) + { + return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); + } + + IDirectory DirInterface = new IDirectory(DirName, FilterFlags); + + DirInterface.Disposed += RemoveDirectoryInUse; + + lock (OpenPaths) + { + OpenPaths.Add(DirName); + } + + MakeObject(Context, DirInterface); + + return 0; + } + + public long Commit(ServiceCtx Context) + { + return 0; + } + + public long GetFreeSpaceSize(ServiceCtx Context) + { + long Position = Context.Request.PtrBuff[0].Position; + + string Name = ReadUtf8String(Context); + + Context.ResponseData.Write(Context.Ns.VFs.GetDrive().AvailableFreeSpace); + + return 0; + } + + public long GetTotalSpaceSize(ServiceCtx Context) + { + long Position = Context.Request.PtrBuff[0].Position; + + string Name = ReadUtf8String(Context); + + Context.ResponseData.Write(Context.Ns.VFs.GetDrive().TotalSize); + + return 0; + } + + private bool IsPathAlreadyInUse(string Path) + { + lock (OpenPaths) + { + return OpenPaths.Contains(Path); + } + } + + private void RemoveFileInUse(object sender, EventArgs e) + { + IFile FileInterface = (IFile)sender; + + lock (OpenPaths) + { + FileInterface.Disposed -= RemoveFileInUse; + + OpenPaths.Remove(FileInterface.HostPath); + } + } + + private void RemoveDirectoryInUse(object sender, EventArgs e) + { + IDirectory DirInterface = (IDirectory)sender; + + lock (OpenPaths) + { + DirInterface.Disposed -= RemoveDirectoryInUse; + + OpenPaths.Remove(DirInterface.HostPath); + } + } + + private string ReadUtf8String(ServiceCtx Context, int Index = 0) + { + long Position = Context.Request.PtrBuff[Index].Position; + long Size = Context.Request.PtrBuff[Index].Size; + + using (MemoryStream MS = new MemoryStream()) + { + while (Size-- > 0) + { + byte Value = Context.Memory.ReadByte(Position++); + + if (Value == 0) + { + break; + } + + MS.WriteByte(Value); + } + + return Encoding.UTF8.GetString(MS.ToArray()); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/FspSrv/IStorage.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IStorage.cs similarity index 92% rename from Ryujinx/OsHle/Objects/FspSrv/IStorage.cs rename to Ryujinx.Core/OsHle/Services/FspSrv/IStorage.cs index 40fb87cac..297461a04 100644 --- a/Ryujinx/OsHle/Objects/FspSrv/IStorage.cs +++ b/Ryujinx.Core/OsHle/Services/FspSrv/IStorage.cs @@ -1,11 +1,11 @@ using ChocolArm64.Memory; -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; using System.IO; -namespace Ryujinx.OsHle.Objects.FspSrv +namespace Ryujinx.Core.OsHle.IpcServices.FspSrv { - class IStorage : IIpcInterface + class IStorage : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/ServiceFspSrv.cs b/Ryujinx.Core/OsHle/Services/FspSrv/ServiceFspSrv.cs new file mode 100644 index 000000000..991f40272 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/FspSrv/ServiceFspSrv.cs @@ -0,0 +1,67 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.FspSrv +{ + class ServiceFspSrv : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceFspSrv() + { + m_Commands = new Dictionary() + { + { 1, Initialize }, + { 18, MountSdCard }, + { 51, MountSaveData }, + { 200, OpenDataStorageByCurrentProcess }, + { 203, OpenRomStorage }, + { 1005, GetGlobalAccessLogMode } + }; + } + + public long Initialize(ServiceCtx Context) + { + return 0; + } + + public long MountSdCard(ServiceCtx Context) + { + MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetSdCardPath())); + + return 0; + } + + public long MountSaveData(ServiceCtx Context) + { + MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetGameSavesPath())); + + return 0; + } + + public long OpenDataStorageByCurrentProcess(ServiceCtx Context) + { + MakeObject(Context, new IStorage(Context.Ns.VFs.RomFs)); + + return 0; + } + + public long OpenRomStorage(ServiceCtx Context) + { + MakeObject(Context, new IStorage(Context.Ns.VFs.RomFs)); + + return 0; + } + + public long GetGlobalAccessLogMode(ServiceCtx Context) + { + Context.ResponseData.Write(0); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Hid/IActiveVibrationDeviceList.cs b/Ryujinx.Core/OsHle/Services/Hid/IActiveVibrationDeviceList.cs new file mode 100644 index 000000000..f6596f429 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Hid/IActiveVibrationDeviceList.cs @@ -0,0 +1,27 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Hid +{ + class IActiveApplicationDeviceList : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public IActiveApplicationDeviceList() + { + m_Commands = new Dictionary() + { + { 0, ActivateVibrationDevice } + }; + } + + public long ActivateVibrationDevice(ServiceCtx Context) + { + int VibrationDeviceHandle = Context.RequestData.ReadInt32(); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Hid/IAppletResource.cs b/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs similarity index 51% rename from Ryujinx/OsHle/Objects/Hid/IAppletResource.cs rename to Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs index ac7ccf564..ef437c022 100644 --- a/Ryujinx/OsHle/Objects/Hid/IAppletResource.cs +++ b/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs @@ -1,30 +1,32 @@ -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Hid +namespace Ryujinx.Core.OsHle.IpcServices.Hid { - class IAppletResource : IIpcInterface + class IAppletResource : IIpcService { private Dictionary m_Commands; public IReadOnlyDictionary Commands => m_Commands; - public HSharedMem Handle; + private HSharedMem HidSharedMem; - public IAppletResource(HSharedMem Handle) + public IAppletResource(HSharedMem HidSharedMem) { m_Commands = new Dictionary() { { 0, GetSharedMemoryHandle } }; - this.Handle = Handle; + this.HidSharedMem = HidSharedMem; } - public static long GetSharedMemoryHandle(ServiceCtx Context) + public long GetSharedMemoryHandle(ServiceCtx Context) { - Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.HidHandle); + int Handle = Context.Process.HandleTable.OpenHandle(HidSharedMem); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); return 0; } diff --git a/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs b/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs new file mode 100644 index 000000000..b1f930725 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs @@ -0,0 +1,111 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Hid +{ + class ServiceHid : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceHid() + { + m_Commands = new Dictionary() + { + { 0, CreateAppletResource }, + { 11, ActivateTouchScreen }, + { 100, SetSupportedNpadStyleSet }, + { 101, GetSupportedNpadStyleSet }, + { 102, SetSupportedNpadIdType }, + { 103, ActivateNpad }, + { 120, SetNpadJoyHoldType }, + { 121, GetNpadJoyHoldType }, + { 200, GetVibrationDeviceInfo }, + { 203, CreateActiveVibrationDeviceList }, + { 206, SendVibrationValues } + }; + } + + public long CreateAppletResource(ServiceCtx Context) + { + MakeObject(Context, new IAppletResource(Context.Ns.Os.HidSharedMem)); + + return 0; + } + + public long ActivateTouchScreen(ServiceCtx Context) + { + long Unknown = Context.RequestData.ReadInt64(); + + return 0; + } + + public long GetSupportedNpadStyleSet(ServiceCtx Context) + { + Context.ResponseData.Write(0); + + return 0; + } + + public long SetSupportedNpadStyleSet(ServiceCtx Context) + { + long Unknown0 = Context.RequestData.ReadInt64(); + long Unknown8 = Context.RequestData.ReadInt64(); + + return 0; + } + + public long SetSupportedNpadIdType(ServiceCtx Context) + { + long Unknown = Context.RequestData.ReadInt64(); + + return 0; + } + + public long ActivateNpad(ServiceCtx Context) + { + long Unknown = Context.RequestData.ReadInt64(); + + return 0; + } + + public long SetNpadJoyHoldType(ServiceCtx Context) + { + long Unknown0 = Context.RequestData.ReadInt64(); + long Unknown8 = Context.RequestData.ReadInt64(); + + return 0; + } + + public long GetNpadJoyHoldType(ServiceCtx Context) + { + Context.ResponseData.Write(0L); + + return 0; + } + + public long GetVibrationDeviceInfo(ServiceCtx Context) + { + int VibrationDeviceHandle = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(0L); //VibrationDeviceInfoForIpc + + return 0; + } + + public long CreateActiveVibrationDeviceList(ServiceCtx Context) + { + MakeObject(Context, new IActiveApplicationDeviceList()); + + return 0; + } + + public long SendVibrationValues(ServiceCtx Context) + { + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/IIpcInterface.cs b/Ryujinx.Core/OsHle/Services/IIpcService.cs similarity index 56% rename from Ryujinx/OsHle/Objects/IIpcInterface.cs rename to Ryujinx.Core/OsHle/Services/IIpcService.cs index af0594cc1..eebcdfbe5 100644 --- a/Ryujinx/OsHle/Objects/IIpcInterface.cs +++ b/Ryujinx.Core/OsHle/Services/IIpcService.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects +namespace Ryujinx.Core.OsHle.IpcServices { - interface IIpcInterface + interface IIpcService { IReadOnlyDictionary Commands { get; } } diff --git a/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs b/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs new file mode 100644 index 000000000..5ee097b6f --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Lm/ILogger.cs @@ -0,0 +1,143 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Ipc; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Ryujinx.Core.OsHle.IpcServices.Lm +{ + class ILogger : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ILogger() + { + m_Commands = new Dictionary() + { + { 0, Log } + }; + } + + enum Flags + { + Padding, + IsHead, + IsTail + } + + enum Severity + { + Trace, + Info, + Warning, + Error, + Critical + } + + enum Field + { + Padding, + Skip, + Message, + Line, + Filename, + Function, + Module, + Thread + } + + public long Log(ServiceCtx Context) + { + long BufferPosition = Context.Request.PtrBuff[0].Position; + long BufferLen = Context.Request.PtrBuff[0].Size; + + byte[] LogBuffer = AMemoryHelper.ReadBytes(Context.Memory, BufferPosition, (int)BufferLen); + + MemoryStream LogMessage = new MemoryStream(LogBuffer); + BinaryReader bReader = new BinaryReader(LogMessage); + + //Header reading. + long Pid = bReader.ReadInt64(); + long ThreadCxt = bReader.ReadInt64(); + int Infos = bReader.ReadInt32(); + int PayloadLen = bReader.ReadInt32(); + + int iFlags = Infos & 0xFFFF; + int iSeverity = (Infos >> 17) & 0x7F; + int iVerbosity = (Infos >> 25) & 0x7F; + + //ToDo: For now we don't care about Head or Tail Log. + bool IsHeadLog = Convert.ToBoolean(iFlags & (int)Flags.IsHead); + bool IsTailLog = Convert.ToBoolean(iFlags & (int)Flags.IsTail); + + string LogString = "nn::diag::detail::LogImpl()" + Environment.NewLine + Environment.NewLine + + "Header:" + Environment.NewLine + + $" Pid: {Pid}" + Environment.NewLine + + $" ThreadContext: {ThreadCxt}" + Environment.NewLine + + $" Flags: {IsHeadLog}/{IsTailLog}" + Environment.NewLine + + $" Severity: {Enum.GetName(typeof(Severity), iSeverity)}" + Environment.NewLine + + $" Verbosity: {iVerbosity}"; + + LogString += Environment.NewLine + Environment.NewLine + "Message:" + Environment.NewLine; + + string StrMessage = "", StrLine = "", StrFilename = "", StrFunction = "", + StrModule = "", StrThread = ""; + + do + { + byte FieldType = bReader.ReadByte(); + byte FieldSize = bReader.ReadByte(); + + if ((Field)FieldType != Field.Skip || FieldSize != 0) + { + byte[] Message = bReader.ReadBytes(FieldSize); + switch ((Field)FieldType) + { + case Field.Message: + StrMessage = Encoding.UTF8.GetString(Message); + break; + + case Field.Line: + StrLine = BitConverter.ToInt32(Message, 0).ToString(); + break; + + case Field.Filename: + StrFilename = Encoding.UTF8.GetString(Message); + break; + + case Field.Function: + StrFunction = Encoding.UTF8.GetString(Message); + break; + + case Field.Module: + StrModule = Encoding.UTF8.GetString(Message); + break; + + case Field.Thread: + StrThread = Encoding.UTF8.GetString(Message); + break; + } + } + + } + while (LogMessage.Position != PayloadLen + 0x18); // 0x18 - Size of Header LogMessage. + + LogString += StrModule + " > " + StrThread + ": " + StrFilename + "@" + StrFunction + "(" + StrLine + ") '" + StrMessage + "'" + Environment.NewLine; + + switch((Severity)iSeverity) + { + case Severity.Trace: Logging.Trace(LogString); break; + case Severity.Info: Logging.Info(LogString); break; + case Severity.Warning: Logging.Warn(LogString); break; + case Severity.Error: Logging.Error(LogString); break; + case Severity.Critical: Logging.Fatal(LogString); break; + } + + return 0; + } + } +} + \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Lm/ServiceLm.cs b/Ryujinx.Core/OsHle/Services/Lm/ServiceLm.cs new file mode 100644 index 000000000..ca3fe35e8 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Lm/ServiceLm.cs @@ -0,0 +1,31 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Lm +{ + class ServiceLm : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceLm() + { + m_Commands = new Dictionary() + { + { 0, Initialize } + }; + } + + public long Initialize(ServiceCtx Context) + { + Context.Session.Initialize(); + + MakeObject(Context, new ILogger()); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nifm/IGeneralService.cs b/Ryujinx.Core/OsHle/Services/Nifm/IGeneralService.cs new file mode 100644 index 000000000..c31ee36b2 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nifm/IGeneralService.cs @@ -0,0 +1,34 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Nifm +{ + class IGeneralService : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public IGeneralService() + { + m_Commands = new Dictionary() + { + { 4, CreateRequest } + }; + } + + //CreateRequest(i32) + public long CreateRequest(ServiceCtx Context) + { + int Unknown = Context.RequestData.ReadInt32(); + + MakeObject(Context, new IRequest()); + + //Todo: Stub + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs b/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs new file mode 100644 index 000000000..6110e5fbe --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs @@ -0,0 +1,49 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Nifm +{ + class IRequest : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public IRequest() + { + m_Commands = new Dictionary() + { + { 0, GetRequestState }, + { 1, GetResult }, + { 2, GetSystemEventReadableHandles } + }; + } + + // -> i32 + public long GetRequestState(ServiceCtx Context) + { + Context.ResponseData.Write(0); + + //Todo: Stub + + return 0; + } + + public long GetResult(ServiceCtx Context) + { + //Todo: Stub + + return 0; + } + + //GetSystemEventReadableHandles() -> (KObject, KObject) + public long GetSystemEventReadableHandles(ServiceCtx Context) + { + Context.Response.HandleDesc = IpcHandleDesc.MakeMove(0xbadcafe); + + //Todo: Stub + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nifm/ServiceNifm.cs b/Ryujinx.Core/OsHle/Services/Nifm/ServiceNifm.cs new file mode 100644 index 000000000..7e183389b --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nifm/ServiceNifm.cs @@ -0,0 +1,29 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Nifm +{ + class ServiceNifm : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceNifm() + { + m_Commands = new Dictionary() + { + { 4, CreateGeneralServiceOld } + }; + } + + public long CreateGeneralServiceOld(ServiceCtx Context) + { + MakeObject(Context, new IGeneralService()); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Time/ITimeZoneService.cs b/Ryujinx.Core/OsHle/Services/Ns/ServiceNs.cs similarity index 65% rename from Ryujinx/OsHle/Objects/Time/ITimeZoneService.cs rename to Ryujinx.Core/OsHle/Services/Ns/ServiceNs.cs index 29e7ec927..720baa6ec 100644 --- a/Ryujinx/OsHle/Objects/Time/ITimeZoneService.cs +++ b/Ryujinx.Core/OsHle/Services/Ns/ServiceNs.cs @@ -1,19 +1,19 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Time +namespace Ryujinx.Core.OsHle.IpcServices.Ns { - class ITimeZoneService : IIpcInterface + class ServiceNs : IIpcService { private Dictionary m_Commands; public IReadOnlyDictionary Commands => m_Commands; - public ITimeZoneService() + public ServiceNs() { m_Commands = new Dictionary() { - //... + //{ 1, Function } }; } } diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvFd.cs b/Ryujinx.Core/OsHle/Services/Nv/NvFd.cs new file mode 100644 index 000000000..dbce74adf --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvFd.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Core.OsHle.IpcServices.NvServices +{ + class NvFd + { + public string Name { get; private set; } + + public NvFd(string Name) + { + this.Name = Name; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs new file mode 100644 index 000000000..ca844f9f2 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nv/NvMap.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Core.OsHle.IpcServices.NvServices +{ + class NvMap + { + public int Handle; + public int Id; + public int Size; + public int Align; + public int Kind; + public long Address; + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceNvDrv.cs b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs similarity index 54% rename from Ryujinx/OsHle/Services/ServiceNvDrv.cs rename to Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs index 405eace2b..67ad44919 100644 --- a/Ryujinx/OsHle/Services/ServiceNvDrv.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs @@ -1,54 +1,82 @@ using ChocolArm64.Memory; -using Ryujinx.Gpu; -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Ipc; -using Ryujinx.OsHle.Utilities; +using Ryujinx.Core.OsHle.Ipc; +using Ryujinx.Core.OsHle.Utilities; +using Ryujinx.Graphics.Gpu; using System; using System.Collections.Generic; -namespace Ryujinx.OsHle.Services +namespace Ryujinx.Core.OsHle.IpcServices.NvServices { - static partial class Service + class ServiceNvDrv : IIpcService { - private delegate long ServiceProcessRequest(ServiceCtx Context); + private delegate long ServiceProcessIoctl(ServiceCtx Context); - private static Dictionary<(string, int), ServiceProcessRequest> IoctlCmds = - new Dictionary<(string, int), ServiceProcessRequest>() + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + private Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds; + + private IdDictionary Fds; + + private IdDictionary NvMaps; + private IdDictionary NvMapsById; + + public ServiceNvDrv() { - { ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel }, - { ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace }, - { ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx }, - { ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions }, - { ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx }, - { ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig }, - { ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait }, - { ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize }, - { ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo }, - { ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics }, - { ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks }, - { ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask }, - { ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData }, - { ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap }, - { ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo }, - { ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx }, - { ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind }, - { ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier }, - { ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority }, - { ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 }, - { ("/dev/nvmap", 0x0101), NvMapIocCreate }, - { ("/dev/nvmap", 0x0103), NvMapIocFromId }, - { ("/dev/nvmap", 0x0104), NvMapIocAlloc }, - { ("/dev/nvmap", 0x0109), NvMapIocParam }, - { ("/dev/nvmap", 0x010e), NvMapIocGetId }, - }; + m_Commands = new Dictionary() + { + { 0, Open }, + { 1, Ioctl }, + { 2, Close }, + { 3, Initialize }, + { 4, QueryEvent }, + { 8, SetClientPid }, + }; - public static long NvDrvOpen(ServiceCtx Context) + IoctlCmds = new Dictionary<(string, int), ServiceProcessIoctl>() + { + { ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel }, + { ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace }, + { ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx }, + { ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions }, + { ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx }, + { ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig }, + { ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait }, + { ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize }, + { ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo }, + { ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics }, + { ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks }, + { ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask }, + { ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData }, + { ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap }, + { ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo }, + { ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx }, + { ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind }, + { ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier }, + { ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority }, + { ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 }, + { ("/dev/nvmap", 0x0101), NvMapIocCreate }, + { ("/dev/nvmap", 0x0103), NvMapIocFromId }, + { ("/dev/nvmap", 0x0104), NvMapIocAlloc }, + { ("/dev/nvmap", 0x0105), NvMapIocFree }, + { ("/dev/nvmap", 0x0109), NvMapIocParam }, + { ("/dev/nvmap", 0x010e), NvMapIocGetId }, + }; + + Fds = new IdDictionary(); + + NvMaps = new IdDictionary(); + NvMapsById = new IdDictionary(); + } + + public long Open(ServiceCtx Context) { long NamePtr = Context.Request.SendBuff[0].Position; string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr); - int Fd = Context.Ns.Os.Fds.GenerateId(new FileDesc(Name)); + int Fd = Fds.Add(new NvFd(Name)); Context.ResponseData.Write(Fd); Context.ResponseData.Write(0); @@ -56,18 +84,18 @@ namespace Ryujinx.OsHle.Services return 0; } - public static long NvDrvIoctl(ServiceCtx Context) + public long Ioctl(ServiceCtx Context) { int Fd = Context.RequestData.ReadInt32(); int Cmd = Context.RequestData.ReadInt32() & 0xffff; - FileDesc FdData = Context.Ns.Os.Fds.GetData(Fd); + NvFd FdData = Fds.GetData(Fd); - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); Context.ResponseData.Write(0); - if (IoctlCmds.TryGetValue((FdData.Name, Cmd), out ServiceProcessRequest ProcReq)) + if (IoctlCmds.TryGetValue((FdData.Name, Cmd), out ServiceProcessIoctl ProcReq)) { return ProcReq(Context); } @@ -77,18 +105,18 @@ namespace Ryujinx.OsHle.Services } } - public static long NvDrvClose(ServiceCtx Context) + public long Close(ServiceCtx Context) { int Fd = Context.RequestData.ReadInt32(); - Context.Ns.Os.Fds.Delete(Fd); + Fds.Delete(Fd); Context.ResponseData.Write(0); return 0; } - public static long NvDrvInitialize(ServiceCtx Context) + public long Initialize(ServiceCtx Context) { long TransferMemSize = Context.RequestData.ReadInt64(); int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0]; @@ -98,7 +126,7 @@ namespace Ryujinx.OsHle.Services return 0; } - public static long NvDrvQueryEvent(ServiceCtx Context) + public long QueryEvent(ServiceCtx Context) { int Fd = Context.RequestData.ReadInt32(); int EventId = Context.RequestData.ReadInt32(); @@ -110,7 +138,7 @@ namespace Ryujinx.OsHle.Services return 0; } - public static long NvDrvSetClientPid(ServiceCtx Context) + public long SetClientPid(ServiceCtx Context) { long Pid = Context.RequestData.ReadInt64(); @@ -119,18 +147,18 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvGpuAsIoctlBindChannel(ServiceCtx Context) + private long NvGpuAsIoctlBindChannel(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); int Fd = Context.Memory.ReadInt32(Position); return 0; } - private static long NvGpuAsIoctlAllocSpace(ServiceCtx Context) + private long NvGpuAsIoctlAllocSpace(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -142,11 +170,11 @@ namespace Ryujinx.OsHle.Services if ((Flags & 1) != 0) { - Align = Context.Ns.Gpu.MemoryMgr.Reserve(Align, (long)Pages * PageSize, 1); + Align = Context.Ns.Gpu.ReserveMemory(Align, (long)Pages * PageSize, 1); } else { - Align = Context.Ns.Gpu.MemoryMgr.Reserve((long)Pages * PageSize, Align); + Align = Context.Ns.Gpu.ReserveMemory((long)Pages * PageSize, Align); } Context.Memory.WriteInt64(Position + 0x10, Align); @@ -154,9 +182,9 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvGpuAsIoctlMapBufferEx(ServiceCtx Context) + private long NvGpuAsIoctlMapBufferEx(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -168,18 +196,29 @@ namespace Ryujinx.OsHle.Services long MapSize = Reader.ReadInt64(); long Offset = Reader.ReadInt64(); - HNvMap NvMap = Context.Ns.Os.Handles.GetData(Handle); - - if (NvMap != null) + if (Handle == 0) { - if ((Flags & 1) != 0) - { - Offset = Context.Ns.Gpu.MemoryMgr.Map(NvMap.Address, Offset, NvMap.Size); - } - else - { - Offset = Context.Ns.Gpu.MemoryMgr.Map(NvMap.Address, NvMap.Size); - } + //Handle 0 is valid here, but it refers to something else. + //TODO: Figure out what, for now just return success. + return 0; + } + + NvMap Map = NvMaps.GetData(Handle); + + if (Map == null) + { + Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); + + return -1; //TODO: Corrent error code. + } + + if ((Flags & 1) != 0) + { + Offset = Context.Ns.Gpu.MapMemory(Map.Address, Offset, Map.Size); + } + else + { + Offset = Context.Ns.Gpu.MapMemory(Map.Address, Map.Size); } Context.Memory.WriteInt64(Position + 0x20, Offset); @@ -187,9 +226,9 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvGpuAsIoctlGetVaRegions(ServiceCtx Context) + private long NvGpuAsIoctlGetVaRegions(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position); @@ -217,9 +256,9 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvGpuAsIoctlInitializeEx(ServiceCtx Context) + private long NvGpuAsIoctlInitializeEx(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -234,9 +273,9 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvHostIoctlCtrlGetConfig(ServiceCtx Context) + private long NvHostIoctlCtrlGetConfig(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position + 0x82); @@ -249,9 +288,9 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvHostIoctlCtrlEventWait(ServiceCtx Context) + private long NvHostIoctlCtrlEventWait(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -265,18 +304,18 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context) + private long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); Context.Memory.WriteInt32(Position, 1); return 0; } - private static long NvGpuIoctlZcullGetInfo(ServiceCtx Context) + private long NvGpuIoctlZcullGetInfo(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemWriter Writer = new MemWriter(Context.Memory, Position); @@ -294,9 +333,9 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvGpuIoctlGetCharacteristics(ServiceCtx Context) + private long NvGpuIoctlGetCharacteristics(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position); @@ -356,9 +395,9 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvGpuIoctlGetTpcMasks(ServiceCtx Context) + private long NvGpuIoctlGetTpcMasks(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -370,9 +409,9 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context) + private long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); Context.Memory.WriteInt32(Position + 0, 7); Context.Memory.WriteInt32(Position + 4, 1); @@ -380,25 +419,25 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvMapIoctlChannelSetUserData(ServiceCtx Context) + private long NvMapIoctlChannelSetUserData(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); return 0; } - private static long NvMapIoctlChannelSetNvMap(ServiceCtx Context) + private long NvMapIoctlChannelSetNvMap(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); int Fd = Context.Memory.ReadInt32(Position); return 0; } - private static long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context) + private long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position + 0x10); @@ -417,7 +456,7 @@ namespace Ryujinx.OsHle.Services int Size = (int)(GpFifoHdr >> 40) & 0x7ffffc; - long CpuAddr = Context.Ns.Gpu.MemoryMgr.GetCpuAddr(GpuAddr); + long CpuAddr = Context.Ns.Gpu.GetCpuAddr(GpuAddr); if (CpuAddr != -1) { @@ -425,7 +464,7 @@ namespace Ryujinx.OsHle.Services NsGpuPBEntry[] PushBuffer = NsGpuPBEntry.DecodePushBuffer(Data); - Context.Ns.Gpu.PGraph.ProcessPushBuffer(PushBuffer, Context.Memory); + Context.Ns.Gpu.ProcessPushBuffer(PushBuffer, Context.Memory); } } @@ -435,9 +474,9 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context) + private long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); int ClassNum = Context.Memory.ReadInt32(Position + 0); int Flags = Context.Memory.ReadInt32(Position + 4); @@ -447,9 +486,9 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvMapIoctlChannelZcullBind(ServiceCtx Context) + private long NvMapIoctlChannelZcullBind(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -460,9 +499,9 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context) + private long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); @@ -474,18 +513,18 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvMapIoctlChannelSetPriority(ServiceCtx Context) + private long NvMapIoctlChannelSetPriority(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); int Priority = Context.Memory.ReadInt32(Position); return 0; } - private static long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context) + private long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context) { - long Position = Context.Request.PtrBuff[0].Position; + long Position = Context.Request.GetSendBuffPtr(); MemReader Reader = new MemReader(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position + 0xc); @@ -503,45 +542,46 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvMapIocCreate(ServiceCtx Context) + private long NvMapIocCreate(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); int Size = Context.Memory.ReadInt32(Position); - int Id = Context.Ns.Os.NvMapIds.GenerateId(); + NvMap Map = new NvMap() { Size = Size }; - int Handle = Context.Ns.Os.Handles.GenerateId(new HNvMap(Id, Size)); + Map.Handle = NvMaps.Add(Map); - Context.Memory.WriteInt32(Position + 4, Handle); + Map.Id = NvMapsById.Add(Map); + + Context.Memory.WriteInt32(Position + 4, Map.Handle); + + Logging.Info($"NvMap {Map.Id} created with size {Size:x8}!"); return 0; } - private static long NvMapIocFromId(ServiceCtx Context) + private long NvMapIocFromId(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); int Id = Context.Memory.ReadInt32(Position); - int Handle = -1; + NvMap Map = NvMapsById.GetData(Id); - foreach (KeyValuePair KV in Context.Ns.Os.Handles) + if (Map == null) { - if (KV.Value is HNvMap NvMap && NvMap.Id == Id) - { - Handle = KV.Key; - - break; - } + Logging.Warn($"Trying to use invalid NvMap Id {Id}!"); + + return -1; //TODO: Corrent error code. } - Context.Memory.WriteInt32(Position + 4, Handle); + Context.Memory.WriteInt32(Position + 4, Map.Handle); return 0; } - private static long NvMapIocAlloc(ServiceCtx Context) + private long NvMapIocAlloc(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -554,21 +594,49 @@ namespace Ryujinx.OsHle.Services byte Kind = (byte)Reader.ReadInt64(); long Addr = Reader.ReadInt64(); - HNvMap NvMap = Context.Ns.Os.Handles.GetData(Handle); + NvMap Map = NvMaps.GetData(Handle); - if (NvMap != null) + if (Map == null) { - NvMap.Address = Addr; - NvMap.Align = Align; - NvMap.Kind = Kind; + Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); + + return -1; //TODO: Corrent error code. } - Logging.Debug($"NvMapIocAlloc at {NvMap.Address:x16}"); + Map.Address = Addr; + Map.Align = Align; + Map.Kind = Kind; return 0; } - private static long NvMapIocParam(ServiceCtx Context) + private long NvMapIocFree(ServiceCtx Context) + { + long Position = Context.Request.GetSendBuffPtr(); + + MemReader Reader = new MemReader(Context.Memory, Position); + MemWriter Writer = new MemWriter(Context.Memory, Position + 8); + + int Handle = Reader.ReadInt32(); + int Padding = Reader.ReadInt32(); + + NvMap Map = NvMaps.GetData(Handle); + + if (Map == null) + { + Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); + + return -1; //TODO: Corrent error code. + } + + Writer.WriteInt64(0); + Writer.WriteInt32(Map.Size); + Writer.WriteInt32(0); + + return 0; + } + + private long NvMapIocParam(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); @@ -577,16 +645,23 @@ namespace Ryujinx.OsHle.Services int Handle = Reader.ReadInt32(); int Param = Reader.ReadInt32(); - HNvMap NvMap = Context.Ns.Os.Handles.GetData(Handle); + NvMap Map = NvMaps.GetData(Handle); + + if (Map == null) + { + Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); + + return -1; //TODO: Corrent error code. + } int Response = 0; - + switch (Param) { - case 1: Response = NvMap.Size; break; - case 2: Response = NvMap.Align; break; - case 4: Response = 0x40000000; break; - case 5: Response = NvMap.Kind; break; + case 1: Response = Map.Size; break; + case 2: Response = Map.Align; break; + case 4: Response = 0x40000000; break; + case 5: Response = Map.Kind; break; } Context.Memory.WriteInt32(Position + 8, Response); @@ -594,17 +669,29 @@ namespace Ryujinx.OsHle.Services return 0; } - private static long NvMapIocGetId(ServiceCtx Context) + private long NvMapIocGetId(ServiceCtx Context) { long Position = Context.Request.GetSendBuffPtr(); int Handle = Context.Memory.ReadInt32(Position + 4); - HNvMap NvMap = Context.Ns.Os.Handles.GetData(Handle); + NvMap Map = NvMaps.GetData(Handle); - Context.Memory.WriteInt32(Position, NvMap.Id); + if (Map == null) + { + Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!"); + + return -1; //TODO: Corrent error code. + } + + Context.Memory.WriteInt32(Position, Map.Id); return 0; } + + public NvMap GetNvMap(int Handle) + { + return NvMaps.GetData(Handle); + } } } \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/ObjHelper.cs b/Ryujinx.Core/OsHle/Services/ObjHelper.cs similarity index 62% rename from Ryujinx/OsHle/Objects/ObjHelper.cs rename to Ryujinx.Core/OsHle/Services/ObjHelper.cs index bab222032..89d986aeb 100644 --- a/Ryujinx/OsHle/Objects/ObjHelper.cs +++ b/Ryujinx.Core/OsHle/Services/ObjHelper.cs @@ -1,7 +1,7 @@ -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Ipc; -namespace Ryujinx.OsHle.Objects +namespace Ryujinx.Core.OsHle.IpcServices { static class ObjHelper { @@ -9,13 +9,13 @@ namespace Ryujinx.OsHle.Objects { if (Context.Session is HDomain Dom) { - Context.Response.ResponseObjIds.Add(Dom.GenerateObjectId(Obj)); + Context.Response.ResponseObjIds.Add(Dom.Add(Obj)); } else { HSessionObj HndData = new HSessionObj(Context.Session, Obj); - int VHandle = Context.Ns.Os.Handles.GenerateId(HndData); + int VHandle = Context.Process.HandleTable.OpenHandle(HndData); Context.Response.HandleDesc = IpcHandleDesc.MakeMove(VHandle); } diff --git a/Ryujinx/OsHle/Objects/Am/IParentalControlService.cs b/Ryujinx.Core/OsHle/Services/Pctl/IParentalControlService.cs similarity index 75% rename from Ryujinx/OsHle/Objects/Am/IParentalControlService.cs rename to Ryujinx.Core/OsHle/Services/Pctl/IParentalControlService.cs index c462ff07f..4eb92d31d 100644 --- a/Ryujinx/OsHle/Objects/Am/IParentalControlService.cs +++ b/Ryujinx.Core/OsHle/Services/Pctl/IParentalControlService.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Am +namespace Ryujinx.Core.OsHle.IpcServices.Pctl { - class IParentalControlService : IIpcInterface + class IParentalControlService : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx.Core/OsHle/Services/Pctl/ServicePctl.cs b/Ryujinx.Core/OsHle/Services/Pctl/ServicePctl.cs new file mode 100644 index 000000000..2d5e22a46 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Pctl/ServicePctl.cs @@ -0,0 +1,29 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Pctl +{ + class ServicePctl : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServicePctl() + { + m_Commands = new Dictionary() + { + { 0, CreateService } + }; + } + + public static long CreateService(ServiceCtx Context) + { + MakeObject(Context, new IParentalControlService()); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs b/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs new file mode 100644 index 000000000..bb795f3f4 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs @@ -0,0 +1,53 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Pl +{ + class ServicePl : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServicePl() + { + m_Commands = new Dictionary() + { + { 1, GetLoadState }, + { 2, GetFontSize }, + { 3, GetSharedMemoryAddressOffset }, + { 4, GetSharedMemoryNativeHandle } + }; + } + + public long GetLoadState(ServiceCtx Context) + { + Context.ResponseData.Write(1); //Loaded + + return 0; + } + + public long GetFontSize(ServiceCtx Context) + { + Context.ResponseData.Write(Horizon.FontSize); + + return 0; + } + + public long GetSharedMemoryAddressOffset(ServiceCtx Context) + { + Context.ResponseData.Write(0); + + return 0; + } + + public long GetSharedMemoryNativeHandle(ServiceCtx Context) + { + int Handle = Context.Process.HandleTable.OpenHandle(Context.Ns.Os.FontSharedMem); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Set/ServiceSet.cs b/Ryujinx.Core/OsHle/Services/Set/ServiceSet.cs new file mode 100644 index 000000000..c60e1712a --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Set/ServiceSet.cs @@ -0,0 +1,45 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Ipc; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Set +{ + class ServiceSet : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceSet() + { + m_Commands = new Dictionary() + { + { 1, GetAvailableLanguageCodes } + }; + } + + private const int LangCodesCount = 13; + + public static long GetAvailableLanguageCodes(ServiceCtx Context) + { + int PtrBuffSize = Context.RequestData.ReadInt32(); + + if (Context.Request.RecvListBuff.Count > 0) + { + long Position = Context.Request.RecvListBuff[0].Position; + short Size = Context.Request.RecvListBuff[0].Size; + + //This should return an array of ints with values matching the LanguageCode enum. + foreach (long value in new long[] { 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L }) + { + AMemoryHelper.WriteBytes(Context.Memory, Position += 8, BitConverter.GetBytes(value)); + } + } + + Context.ResponseData.Write(LangCodesCount); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Set/ServiceSetSys.cs b/Ryujinx.Core/OsHle/Services/Set/ServiceSetSys.cs new file mode 100644 index 000000000..dee6573d3 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Set/ServiceSetSys.cs @@ -0,0 +1,33 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Set +{ + class ServiceSetSys : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceSetSys() + { + m_Commands = new Dictionary() + { + { 23, GetColorSetId }, + { 24, SetColorSetId } + }; + } + + public static long GetColorSetId(ServiceCtx Context) + { + Context.ResponseData.Write((int)Context.Ns.Settings.ThemeColor); + + return 0; + } + + public static long SetColorSetId(ServiceCtx Context) + { + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Sfdnsres/ServiceSfdnsres.cs b/Ryujinx.Core/OsHle/Services/Sfdnsres/ServiceSfdnsres.cs new file mode 100644 index 000000000..f110ae736 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Sfdnsres/ServiceSfdnsres.cs @@ -0,0 +1,20 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Sfdnsres +{ + class ServiceSfdnsres : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceSfdnsres() + { + m_Commands = new Dictionary() + { + //{ 0, Function } + }; + } + } +} diff --git a/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs b/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs new file mode 100644 index 000000000..cb745e373 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs @@ -0,0 +1,67 @@ +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Sm +{ + class ServiceSm : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceSm() + { + m_Commands = new Dictionary() + { + { 0, Initialize }, + { 1, GetService } + }; + } + + private const int SmNotInitialized = 0x415; + + public long Initialize(ServiceCtx Context) + { + Context.Session.Initialize(); + + return 0; + } + + public long GetService(ServiceCtx Context) + { + //Only for kernel version > 3.0.0. + if (!Context.Session.IsInitialized) + { + //return SmNotInitialized; + } + + string Name = string.Empty; + + for (int Index = 0; Index < 8 && + Context.RequestData.BaseStream.Position < + Context.RequestData.BaseStream.Length; Index++) + { + byte Chr = Context.RequestData.ReadByte(); + + if (Chr >= 0x20 && Chr < 0x7f) + { + Name += (char)Chr; + } + } + + if (Name == string.Empty) + { + return 0; + } + + HSession Session = new HSession(Context.Process.Services.GetService(Name)); + + int Handle = Context.Process.HandleTable.OpenHandle(Session); + + Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Ssl/ServiceSsl.cs b/Ryujinx.Core/OsHle/Services/Ssl/ServiceSsl.cs new file mode 100644 index 000000000..23934b140 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Ssl/ServiceSsl.cs @@ -0,0 +1,20 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Ssl +{ + class ServiceSsl : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceSsl() + { + m_Commands = new Dictionary() + { + //{ 0, Function } + }; + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Time/ISteadyClock.cs b/Ryujinx.Core/OsHle/Services/Time/ISteadyClock.cs similarity index 76% rename from Ryujinx/OsHle/Objects/Time/ISteadyClock.cs rename to Ryujinx.Core/OsHle/Services/Time/ISteadyClock.cs index a5139bab0..d20e4378a 100644 --- a/Ryujinx/OsHle/Objects/Time/ISteadyClock.cs +++ b/Ryujinx.Core/OsHle/Services/Time/ISteadyClock.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Time +namespace Ryujinx.Core.OsHle.IpcServices.Time { - class ISteadyClock : IIpcInterface + class ISteadyClock : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx/OsHle/Objects/Time/ISystemClock.cs b/Ryujinx.Core/OsHle/Services/Time/ISystemClock.cs similarity index 90% rename from Ryujinx/OsHle/Objects/Time/ISystemClock.cs rename to Ryujinx.Core/OsHle/Services/Time/ISystemClock.cs index 1d9bae3bb..4d4493dad 100644 --- a/Ryujinx/OsHle/Objects/Time/ISystemClock.cs +++ b/Ryujinx.Core/OsHle/Services/Time/ISystemClock.cs @@ -1,10 +1,10 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Time +namespace Ryujinx.Core.OsHle.IpcServices.Time { - class ISystemClock : IIpcInterface + class ISystemClock : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx.Core/OsHle/Services/Time/ITimeZoneService.cs b/Ryujinx.Core/OsHle/Services/Time/ITimeZoneService.cs new file mode 100644 index 000000000..d220824c5 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Time/ITimeZoneService.cs @@ -0,0 +1,69 @@ +using Ryujinx.Core.OsHle.Ipc; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Time +{ + class ITimeZoneService : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + private static DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local); + + public ITimeZoneService() + { + m_Commands = new Dictionary() + { + { 101, ToCalendarTimeWithMyRule } + }; + } + + //(nn::time::PosixTime)-> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo) + public long ToCalendarTimeWithMyRule(ServiceCtx Context) + { + long PosixTime = Context.RequestData.ReadInt64(); + + Epoch = Epoch.AddSeconds(PosixTime).ToLocalTime(); + + /* + struct CalendarTime { + u16_le year; + u8 month; // Starts at 1 + u8 day; // Starts at 1 + u8 hour; + u8 minute; + u8 second; + INSERT_PADDING_BYTES(1); + }; + */ + Context.ResponseData.Write((short)Epoch.Year); + Context.ResponseData.Write((byte)Epoch.Month); + Context.ResponseData.Write((byte)Epoch.Day); + Context.ResponseData.Write((byte)Epoch.Hour); + Context.ResponseData.Write((byte)Epoch.Minute); + Context.ResponseData.Write((byte)Epoch.Second); + Context.ResponseData.Write((byte)0); + + /* Thanks to TuxSH + struct CalendarAdditionalInfo { + u32 tm_wday; //day of week [0,6] (Sunday = 0) + s32 tm_yday; //day of year [0,365] + struct timezone { + char[8] tz_name; + bool isDaylightSavingTime; + s32 utcOffsetSeconds; + }; + }; + */ + Context.ResponseData.Write((int)Epoch.DayOfWeek); + Context.ResponseData.Write(Epoch.DayOfYear); + Context.ResponseData.Write(new byte[8]); + Context.ResponseData.Write(Convert.ToByte(Epoch.IsDaylightSavingTime())); + Context.ResponseData.Write(0); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Time/ServiceTime.cs b/Ryujinx.Core/OsHle/Services/Time/ServiceTime.cs new file mode 100644 index 000000000..43f28bb80 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Time/ServiceTime.cs @@ -0,0 +1,62 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Time +{ + class ServiceTime : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceTime() + { + m_Commands = new Dictionary() + { + { 0, GetStandardUserSystemClock }, + { 1, GetStandardNetworkSystemClock }, + { 2, GetStandardSteadyClock }, + { 3, GetTimeZoneService }, + { 4, GetStandardLocalSystemClock } + }; + } + + public long GetStandardUserSystemClock(ServiceCtx Context) + { + MakeObject(Context, new ISystemClock(SystemClockType.User)); + + return 0; + } + + public long GetStandardNetworkSystemClock(ServiceCtx Context) + { + MakeObject(Context, new ISystemClock(SystemClockType.Network)); + + return 0; + } + + public long GetStandardSteadyClock(ServiceCtx Context) + { + MakeObject(Context, new ISteadyClock()); + + return 0; + } + + public long GetTimeZoneService(ServiceCtx Context) + { + MakeObject(Context, new ITimeZoneService()); + + return 0; + } + + public long GetStandardLocalSystemClock(ServiceCtx Context) + { + MakeObject(Context, new ISystemClock(SystemClockType.Local)); + + return 0; + } + + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Time/SystemClockType.cs b/Ryujinx.Core/OsHle/Services/Time/SystemClockType.cs similarity index 64% rename from Ryujinx/OsHle/Objects/Time/SystemClockType.cs rename to Ryujinx.Core/OsHle/Services/Time/SystemClockType.cs index ad9675aab..2314942a3 100644 --- a/Ryujinx/OsHle/Objects/Time/SystemClockType.cs +++ b/Ryujinx.Core/OsHle/Services/Time/SystemClockType.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.OsHle.Objects.Time +namespace Ryujinx.Core.OsHle.IpcServices.Time { enum SystemClockType { diff --git a/Ryujinx/OsHle/Display.cs b/Ryujinx.Core/OsHle/Services/Vi/Display.cs similarity index 79% rename from Ryujinx/OsHle/Display.cs rename to Ryujinx.Core/OsHle/Services/Vi/Display.cs index f62430fa8..ceadc3931 100644 --- a/Ryujinx/OsHle/Display.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/Display.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.OsHle +namespace Ryujinx.Core.OsHle.IpcServices.Vi { class Display { diff --git a/Ryujinx.Core/OsHle/Services/Vi/GbpBuffer.cs b/Ryujinx.Core/OsHle/Services/Vi/GbpBuffer.cs new file mode 100644 index 000000000..5fe585d06 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Vi/GbpBuffer.cs @@ -0,0 +1,60 @@ +using System.IO; + +namespace Ryujinx.Core.OsHle.IpcServices.Android +{ + struct GbpBuffer + { + public int Magic { get; private set; } + public int Width { get; private set; } + public int Height { get; private set; } + public int Stride { get; private set; } + public int Format { get; private set; } + public int Usage { get; private set; } + + public int Pid { get; private set; } + public int RefCount { get; private set; } + + public int FdsCount { get; private set; } + public int IntsCount { get; private set; } + + public byte[] RawData { get; private set; } + + public int Size => RawData.Length + 10 * 4; + + public GbpBuffer(BinaryReader Reader) + { + Magic = Reader.ReadInt32(); + Width = Reader.ReadInt32(); + Height = Reader.ReadInt32(); + Stride = Reader.ReadInt32(); + Format = Reader.ReadInt32(); + Usage = Reader.ReadInt32(); + + Pid = Reader.ReadInt32(); + RefCount = Reader.ReadInt32(); + + FdsCount = Reader.ReadInt32(); + IntsCount = Reader.ReadInt32(); + + RawData = Reader.ReadBytes((FdsCount + IntsCount) * 4); + } + + public void Write(BinaryWriter Writer) + { + Writer.Write(Magic); + Writer.Write(Width); + Writer.Write(Height); + Writer.Write(Stride); + Writer.Write(Format); + Writer.Write(Usage); + + Writer.Write(Pid); + Writer.Write(RefCount); + + Writer.Write(FdsCount); + Writer.Write(IntsCount); + + Writer.Write(RawData); + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Vi/IApplicationDisplayService.cs b/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs similarity index 78% rename from Ryujinx/OsHle/Objects/Vi/IApplicationDisplayService.cs rename to Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs index 4fa359280..0ff1f9099 100644 --- a/Ryujinx/OsHle/Objects/Vi/IApplicationDisplayService.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs @@ -1,20 +1,22 @@ using ChocolArm64.Memory; -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; using System.IO; -using static Ryujinx.OsHle.Objects.Android.Parcel; -using static Ryujinx.OsHle.Objects.ObjHelper; +using static Ryujinx.Core.OsHle.IpcServices.Android.Parcel; +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; -namespace Ryujinx.OsHle.Objects.Vi +namespace Ryujinx.Core.OsHle.IpcServices.Vi { - class IApplicationDisplayService : IIpcInterface + class IApplicationDisplayService : IIpcService { private Dictionary m_Commands; public IReadOnlyDictionary Commands => m_Commands; + private IdDictionary Displays; + public IApplicationDisplayService() { m_Commands = new Dictionary() @@ -24,16 +26,21 @@ namespace Ryujinx.OsHle.Objects.Vi { 102, GetManagerDisplayService }, { 103, GetIndirectDisplayTransactionService }, { 1010, OpenDisplay }, + { 1020, CloseDisplay }, { 2020, OpenLayer }, + { 2021, CloseLayer }, { 2030, CreateStrayLayer }, + { 2031, DestroyStrayLayer }, { 2101, SetLayerScalingMode }, { 5202, GetDisplayVSyncEvent } }; + + Displays = new IdDictionary(); } public long GetRelayService(ServiceCtx Context) { - MakeObject(Context, new IHOSBinderDriver()); + MakeObject(Context, new IHOSBinderDriver(Context.Ns.Gpu.Renderer)); return 0; } @@ -54,7 +61,7 @@ namespace Ryujinx.OsHle.Objects.Vi public long GetIndirectDisplayTransactionService(ServiceCtx Context) { - MakeObject(Context, new IHOSBinderDriver()); + MakeObject(Context, new IHOSBinderDriver(Context.Ns.Gpu.Renderer)); return 0; } @@ -63,13 +70,22 @@ namespace Ryujinx.OsHle.Objects.Vi { string Name = GetDisplayName(Context); - long DisplayId = Context.Ns.Os.Displays.GenerateId(new Display(Name)); + long DisplayId = Displays.Add(new Display(Name)); Context.ResponseData.Write(DisplayId); return 0; } + public long CloseDisplay(ServiceCtx Context) + { + int DisplayId = Context.RequestData.ReadInt32(); + + Displays.Delete(DisplayId); + + return 0; + } + public long OpenLayer(ServiceCtx Context) { long LayerId = Context.RequestData.ReadInt64(); @@ -86,6 +102,13 @@ namespace Ryujinx.OsHle.Objects.Vi return 0; } + public long CloseLayer(ServiceCtx Context) + { + long LayerId = Context.RequestData.ReadInt64(); + + return 0; + } + public long CreateStrayLayer(ServiceCtx Context) { long LayerFlags = Context.RequestData.ReadInt64(); @@ -93,7 +116,7 @@ namespace Ryujinx.OsHle.Objects.Vi long ParcelPtr = Context.Request.ReceiveBuff[0].Position; - Display Disp = Context.Ns.Os.Displays.GetData((int)DisplayId); + Display Disp = Displays.GetData((int)DisplayId); byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr); @@ -105,6 +128,11 @@ namespace Ryujinx.OsHle.Objects.Vi return 0; } + public long DestroyStrayLayer(ServiceCtx Context) + { + return 0; + } + public long SetLayerScalingMode(ServiceCtx Context) { int ScalingMode = Context.RequestData.ReadInt32(); @@ -117,7 +145,7 @@ namespace Ryujinx.OsHle.Objects.Vi { string Name = GetDisplayName(Context); - int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent()); + int Handle = Context.Process.HandleTable.OpenHandle(new HEvent()); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs b/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs new file mode 100644 index 000000000..b24a773bf --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs @@ -0,0 +1,77 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Ipc; +using Ryujinx.Core.OsHle.IpcServices.Android; +using Ryujinx.Graphics.Gal; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Vi +{ + class IHOSBinderDriver : IIpcService, IDisposable + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + private NvFlinger Flinger; + + public IHOSBinderDriver(IGalRenderer Renderer) + { + m_Commands = new Dictionary() + { + { 0, TransactParcel }, + { 1, AdjustRefcount }, + { 2, GetNativeHandle } + }; + + Flinger = new NvFlinger(Renderer); + } + + public long TransactParcel(ServiceCtx Context) + { + int Id = Context.RequestData.ReadInt32(); + int Code = Context.RequestData.ReadInt32(); + + long DataPos = Context.Request.SendBuff[0].Position; + long DataSize = Context.Request.SendBuff[0].Size; + + byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, (int)DataSize); + + Data = Parcel.GetParcelData(Data); + + return Flinger.ProcessParcelRequest(Context, Data, Code); + } + + public long AdjustRefcount(ServiceCtx Context) + { + int Id = Context.RequestData.ReadInt32(); + int AddVal = Context.RequestData.ReadInt32(); + int Type = Context.RequestData.ReadInt32(); + + return 0; + } + + public long GetNativeHandle(ServiceCtx Context) + { + int Id = Context.RequestData.ReadInt32(); + uint Unk = Context.RequestData.ReadUInt32(); + + Context.Response.HandleDesc = IpcHandleDesc.MakeMove(0xbadcafe); + + return 0; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Flinger.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Vi/IManagerDisplayService.cs b/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs similarity index 63% rename from Ryujinx/OsHle/Objects/Vi/IManagerDisplayService.cs rename to Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs index c2bbf43b2..69dbff47a 100644 --- a/Ryujinx/OsHle/Objects/Vi/IManagerDisplayService.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Vi +namespace Ryujinx.Core.OsHle.IpcServices.Vi { - class IManagerDisplayService : IIpcInterface + class IManagerDisplayService : IIpcService { private Dictionary m_Commands; @@ -13,8 +13,9 @@ namespace Ryujinx.OsHle.Objects.Vi { m_Commands = new Dictionary() { - { 2010, CreateManagedLayer }, - { 6000, AddToLayerStack } + { 2010, CreateManagedLayer }, + { 2011, DestroyManagedLayer }, + { 6000, AddToLayerStack } }; } @@ -25,6 +26,11 @@ namespace Ryujinx.OsHle.Objects.Vi return 0; } + public long DestroyManagedLayer(ServiceCtx Context) + { + return 0; + } + public static long AddToLayerStack(ServiceCtx Context) { return 0; diff --git a/Ryujinx/OsHle/Objects/Vi/ISystemDisplayService.cs b/Ryujinx.Core/OsHle/Services/Vi/ISystemDisplayService.cs similarity index 80% rename from Ryujinx/OsHle/Objects/Vi/ISystemDisplayService.cs rename to Ryujinx.Core/OsHle/Services/Vi/ISystemDisplayService.cs index 39d197864..d87fcbf6e 100644 --- a/Ryujinx/OsHle/Objects/Vi/ISystemDisplayService.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/ISystemDisplayService.cs @@ -1,9 +1,9 @@ -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; -namespace Ryujinx.OsHle.Objects.Vi +namespace Ryujinx.Core.OsHle.IpcServices.Vi { - class ISystemDisplayService : IIpcInterface + class ISystemDisplayService : IIpcService { private Dictionary m_Commands; diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs new file mode 100644 index 000000000..5309dcabb --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -0,0 +1,497 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.IpcServices.NvServices; +using Ryujinx.Graphics.Gal; +using System; +using System.IO; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +using static Ryujinx.Core.OsHle.IpcServices.Android.Parcel; + +namespace Ryujinx.Core.OsHle.IpcServices.Android +{ + class NvFlinger : IDisposable + { + private delegate long ServiceProcessParcel(ServiceCtx Context, BinaryReader ParcelReader); + + private Dictionary<(string, int), ServiceProcessParcel> Commands; + + private const int BufferQueueCount = 0x40; + private const int BufferQueueMask = BufferQueueCount - 1; + + [Flags] + private enum HalTransform + { + FlipX = 1 << 0, + FlipY = 1 << 1, + Rotate90 = 1 << 2 + } + + private enum BufferState + { + Free, + Dequeued, + Queued, + Acquired + } + + private struct Rect + { + public int Top; + public int Left; + public int Right; + public int Bottom; + } + + private struct BufferEntry + { + public BufferState State; + + public HalTransform Transform; + + public Rect Crop; + + public GbpBuffer Data; + } + + private IGalRenderer Renderer; + + private BufferEntry[] BufferQueue; + + private ManualResetEvent WaitBufferFree; + + private object RenderQueueLock; + + private int RenderQueueCount; + + private bool NvFlingerDisposed; + + private bool KeepRunning; + + public NvFlinger(IGalRenderer Renderer) + { + Commands = new Dictionary<(string, int), ServiceProcessParcel>() + { + { ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x4), GbpDetachBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery }, + { ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect }, + { ("android.gui.IGraphicBufferProducer", 0xb), GbpDisconnect }, + { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer } + }; + + this.Renderer = Renderer; + + BufferQueue = new BufferEntry[0x40]; + + WaitBufferFree = new ManualResetEvent(false); + + RenderQueueLock = new object(); + + KeepRunning = true; + } + + public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code) + { + using (MemoryStream MS = new MemoryStream(ParcelData)) + { + BinaryReader Reader = new BinaryReader(MS); + + MS.Seek(4, SeekOrigin.Current); + + int StrSize = Reader.ReadInt32(); + + string InterfaceName = Encoding.Unicode.GetString(Reader.ReadBytes(StrSize * 2)); + + long Remainder = MS.Position & 0xf; + + if (Remainder != 0) + { + MS.Seek(0x10 - Remainder, SeekOrigin.Current); + } + + MS.Seek(0x50, SeekOrigin.Begin); + + if (Commands.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq)) + { + Logging.Debug($"{InterfaceName} {ProcReq.Method.Name}"); + + return ProcReq(Context, Reader); + } + else + { + throw new NotImplementedException($"{InterfaceName} {Code}"); + } + } + } + + private long GbpRequestBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + int Slot = ParcelReader.ReadInt32(); + + using (MemoryStream MS = new MemoryStream()) + { + BinaryWriter Writer = new BinaryWriter(MS); + + BufferEntry Entry = BufferQueue[Slot]; + + int BufferCount = 1; //? + long BufferSize = Entry.Data.Size; + + Writer.Write(BufferCount); + Writer.Write(BufferSize); + + Entry.Data.Write(Writer); + + Writer.Write(0); + + return MakeReplyParcel(Context, MS.ToArray()); + } + } + + private long GbpDequeueBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + //TODO: Errors. + int Format = ParcelReader.ReadInt32(); + int Width = ParcelReader.ReadInt32(); + int Height = ParcelReader.ReadInt32(); + int GetTimestamps = ParcelReader.ReadInt32(); + int Usage = ParcelReader.ReadInt32(); + + int Slot = GetFreeSlotBlocking(Width, Height); + + return MakeReplyParcel(Context, Slot, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + } + + private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + Context.Ns.Statistics.RecordGameFrameTime(); + + //TODO: Errors. + int Slot = ParcelReader.ReadInt32(); + int Unknown4 = ParcelReader.ReadInt32(); + int Unknown8 = ParcelReader.ReadInt32(); + int Unknownc = ParcelReader.ReadInt32(); + int Timestamp = ParcelReader.ReadInt32(); + int IsAutoTimestamp = ParcelReader.ReadInt32(); + int CropTop = ParcelReader.ReadInt32(); + int CropLeft = ParcelReader.ReadInt32(); + int CropRight = ParcelReader.ReadInt32(); + int CropBottom = ParcelReader.ReadInt32(); + int ScalingMode = ParcelReader.ReadInt32(); + int Transform = ParcelReader.ReadInt32(); + int StickyTransform = ParcelReader.ReadInt32(); + int Unknown34 = ParcelReader.ReadInt32(); + int Unknown38 = ParcelReader.ReadInt32(); + int IsFenceValid = ParcelReader.ReadInt32(); + int Fence0Id = ParcelReader.ReadInt32(); + int Fence0Value = ParcelReader.ReadInt32(); + int Fence1Id = ParcelReader.ReadInt32(); + int Fence1Value = ParcelReader.ReadInt32(); + + BufferQueue[Slot].Transform = (HalTransform)Transform; + + BufferQueue[Slot].Crop.Top = CropTop; + BufferQueue[Slot].Crop.Left = CropLeft; + BufferQueue[Slot].Crop.Right = CropRight; + BufferQueue[Slot].Crop.Bottom = CropBottom; + + BufferQueue[Slot].State = BufferState.Queued; + + SendFrameBuffer(Context, Slot); + + return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); + } + + private long GbpDetachBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + return MakeReplyParcel(Context, 0); + } + + private long GbpCancelBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + //TODO: Errors. + int Slot = ParcelReader.ReadInt32(); + + BufferQueue[Slot].State = BufferState.Free; + + return MakeReplyParcel(Context, 0); + } + + private long GbpQuery(ServiceCtx Context, BinaryReader ParcelReader) + { + return MakeReplyParcel(Context, 0, 0); + } + + private long GbpConnect(ServiceCtx Context, BinaryReader ParcelReader) + { + return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); + } + + private long GbpDisconnect(ServiceCtx Context, BinaryReader ParcelReader) + { + return MakeReplyParcel(Context, 0); + } + + private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + int Slot = ParcelReader.ReadInt32(); + + int BufferCount = ParcelReader.ReadInt32(); + long BufferSize = ParcelReader.ReadInt64(); + + BufferQueue[Slot].State = BufferState.Free; + + BufferQueue[Slot].Data = new GbpBuffer(ParcelReader); + + return MakeReplyParcel(Context, 0); + } + + private long MakeReplyParcel(ServiceCtx Context, params int[] Ints) + { + using (MemoryStream MS = new MemoryStream()) + { + BinaryWriter Writer = new BinaryWriter(MS); + + foreach (int Int in Ints) + { + Writer.Write(Int); + } + + return MakeReplyParcel(Context, MS.ToArray()); + } + } + + private long MakeReplyParcel(ServiceCtx Context, byte[] Data) + { + long ReplyPos = Context.Request.ReceiveBuff[0].Position; + long ReplySize = Context.Request.ReceiveBuff[0].Size; + + byte[] Reply = MakeParcel(Data, new byte[0]); + + AMemoryHelper.WriteBytes(Context.Memory, ReplyPos, Reply); + + return 0; + } + + private unsafe void SendFrameBuffer(ServiceCtx Context, int Slot) + { + int FbWidth = BufferQueue[Slot].Data.Width; + int FbHeight = BufferQueue[Slot].Data.Height; + + long FbSize = (uint)FbWidth * FbHeight * 4; + + NvMap NvMap = GetNvMap(Context, Slot); + + if ((ulong)(NvMap.Address + FbSize) > AMemoryMgr.AddrSize) + { + Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!"); + + BufferQueue[Slot].State = BufferState.Free; + + WaitBufferFree.Set(); + + return; + } + + BufferQueue[Slot].State = BufferState.Acquired; + + Rect Crop = BufferQueue[Slot].Crop; + + int RealWidth = FbWidth; + int RealHeight = FbHeight; + + float XSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX) ? -1 : 1; + float YSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY) ? -1 : 1; + + float ScaleX = 1; + float ScaleY = 1; + + float OffsX = 0; + float OffsY = 0; + + if (Crop.Right != 0 && + Crop.Bottom != 0) + { + //Who knows if this is right, I was never good with math... + RealWidth = Crop.Right - Crop.Left; + RealHeight = Crop.Bottom - Crop.Top; + + if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90)) + { + ScaleY = (float)FbHeight / RealHeight; + ScaleX = (float)FbWidth / RealWidth; + + OffsY = ((-(float)Crop.Left / Crop.Right) + ScaleX - 1) * -XSign; + OffsX = ((-(float)Crop.Top / Crop.Bottom) + ScaleY - 1) * -YSign; + } + else + { + ScaleX = (float)FbWidth / RealWidth; + ScaleY = (float)FbHeight / RealHeight; + + OffsX = ((-(float)Crop.Left / Crop.Right) + ScaleX - 1) * XSign; + OffsY = ((-(float)Crop.Top / Crop.Bottom) + ScaleY - 1) * -YSign; + } + } + + ScaleX *= XSign; + ScaleY *= YSign; + + float Rotate = 0; + + if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90)) + { + Rotate = -MathF.PI * 0.5f; + } + + lock (RenderQueueLock) + { + if (NvFlingerDisposed) + { + return; + } + + Interlocked.Increment(ref RenderQueueCount); + } + + byte* Fb = (byte*)Context.Memory.Ram + NvMap.Address; + + Context.Ns.Gpu.Renderer.QueueAction(delegate() + { + Context.Ns.Gpu.Renderer.SetFrameBuffer( + Fb, + FbWidth, + FbHeight, + ScaleX, + ScaleY, + OffsX, + OffsY, + Rotate); + + BufferQueue[Slot].State = BufferState.Free; + + Interlocked.Decrement(ref RenderQueueCount); + + lock (WaitBufferFree) + { + WaitBufferFree.Set(); + } + }); + } + + private NvMap GetNvMap(ServiceCtx Context, int Slot) + { + int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c); + + if (!BitConverter.IsLittleEndian) + { + byte[] RawValue = BitConverter.GetBytes(NvMapHandle); + + Array.Reverse(RawValue); + + NvMapHandle = BitConverter.ToInt32(RawValue, 0); + } + + ServiceNvDrv NvDrv = (ServiceNvDrv)Context.Process.Services.GetService("nvdrv"); + + return NvDrv.GetNvMap(NvMapHandle); + } + + private int GetFreeSlotBlocking(int Width, int Height) + { + int Slot; + + do + { + lock (WaitBufferFree) + { + if ((Slot = GetFreeSlot(Width, Height)) != -1) + { + break; + } + + Logging.Debug("Waiting for a free BufferQueue slot..."); + + if (!KeepRunning) + { + break; + } + + WaitBufferFree.Reset(); + } + + WaitBufferFree.WaitOne(); + } + while (KeepRunning); + + Logging.Debug($"Found free BufferQueue slot {Slot}!"); + + return Slot; + } + + private int GetFreeSlot(int Width, int Height) + { + lock (BufferQueue) + { + for (int Slot = 0; Slot < BufferQueue.Length; Slot++) + { + if (BufferQueue[Slot].State != BufferState.Free) + { + continue; + } + + GbpBuffer Data = BufferQueue[Slot].Data; + + if (Data.Width == Width && + Data.Height == Height) + { + BufferQueue[Slot].State = BufferState.Dequeued; + + return Slot; + } + } + } + + return -1; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing && !NvFlingerDisposed) + { + lock (RenderQueueLock) + { + NvFlingerDisposed = true; + } + + //Ensure that all pending actions was sent before + //we can safely assume that the class was disposed. + while (RenderQueueCount > 0) + { + Thread.Yield(); + } + + Renderer.ResetFrameBuffer(); + + lock (WaitBufferFree) + { + KeepRunning = false; + + WaitBufferFree.Set(); + } + + WaitBufferFree.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Parcel.cs b/Ryujinx.Core/OsHle/Services/Vi/Parcel.cs similarity index 96% rename from Ryujinx/OsHle/Objects/Parcel.cs rename to Ryujinx.Core/OsHle/Services/Vi/Parcel.cs index 0d322bab8..3404c227e 100644 --- a/Ryujinx/OsHle/Objects/Parcel.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/Parcel.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace Ryujinx.OsHle.Objects.Android +namespace Ryujinx.Core.OsHle.IpcServices.Android { static class Parcel { diff --git a/Ryujinx.Core/OsHle/Services/Vi/ServiceVi.cs b/Ryujinx.Core/OsHle/Services/Vi/ServiceVi.cs new file mode 100644 index 000000000..2c3dd2a3c --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Vi/ServiceVi.cs @@ -0,0 +1,31 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Vi +{ + class ServiceVi : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceVi() + { + m_Commands = new Dictionary() + { + { 2, GetDisplayService } + }; + } + + public long GetDisplayService(ServiceCtx Context) + { + int Unknown = Context.RequestData.ReadInt32(); + + MakeObject(Context, new IApplicationDisplayService()); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Svc/SvcHandler.cs b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs similarity index 68% rename from Ryujinx/OsHle/Svc/SvcHandler.cs rename to Ryujinx.Core/OsHle/Svc/SvcHandler.cs index 3ab89d445..3ce56a3cd 100644 --- a/Ryujinx/OsHle/Svc/SvcHandler.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs @@ -1,11 +1,13 @@ +using ChocolArm64.Events; using ChocolArm64.Memory; using ChocolArm64.State; +using Ryujinx.Core.OsHle.Handles; using System; using System.Collections.Generic; -namespace Ryujinx.OsHle.Svc +namespace Ryujinx.Core.OsHle.Svc { - partial class SvcHandler + partial class SvcHandler : IDisposable { private delegate void SvcFunc(AThreadState ThreadState); @@ -15,6 +17,10 @@ namespace Ryujinx.OsHle.Svc private Process Process; private AMemory Memory; + private HashSet<(HSharedMem, long)> MappedSharedMems; + + private ulong CurrentHeapSize; + private static Random Rng; public SvcHandler(Switch Ns, Process Process) @@ -24,12 +30,17 @@ namespace Ryujinx.OsHle.Svc { 0x01, SvcSetHeapSize }, { 0x03, SvcSetMemoryAttribute }, { 0x04, SvcMapMemory }, + { 0x05, SvcUnmapMemory }, { 0x06, SvcQueryMemory }, { 0x07, SvcExitProcess }, { 0x08, SvcCreateThread }, { 0x09, SvcStartThread }, + { 0x0a, SvcExitThread }, { 0x0b, SvcSleepThread }, { 0x0c, SvcGetThreadPriority }, + { 0x0d, SvcSetThreadPriority }, + { 0x0f, SvcSetThreadCoreMask }, + { 0x12, SvcClearEvent }, { 0x13, SvcMapSharedMemory }, { 0x14, SvcUnmapSharedMemory }, { 0x15, SvcCreateTransferMemory }, @@ -44,6 +55,7 @@ namespace Ryujinx.OsHle.Svc { 0x1f, SvcConnectToNamedPort }, { 0x21, SvcSendSyncRequest }, { 0x22, SvcSendSyncRequestWithUserBuffer }, + { 0x25, SvcGetThreadId }, { 0x26, SvcBreak }, { 0x27, SvcOutputDebugString }, { 0x29, SvcGetInfo } @@ -52,6 +64,8 @@ namespace Ryujinx.OsHle.Svc this.Ns = Ns; this.Process = Process; this.Memory = Process.Memory; + + MappedSharedMems = new HashSet<(HSharedMem, long)>(); } static SvcHandler() @@ -59,7 +73,7 @@ namespace Ryujinx.OsHle.Svc Rng = new Random(); } - public void SvcCall(object sender, AInstExceptEventArgs e) + public void SvcCall(object sender, AInstExceptionEventArgs e) { AThreadState ThreadState = (AThreadState)sender; @@ -76,5 +90,26 @@ namespace Ryujinx.OsHle.Svc throw new NotImplementedException(e.Id.ToString("x4")); } } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + lock (MappedSharedMems) + { + foreach ((HSharedMem SharedMem, long Position) in MappedSharedMems) + { + SharedMem.RemoveVirtualPosition(Memory, Position); + } + + MappedSharedMems.Clear(); + } + } + } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs new file mode 100644 index 000000000..80f24d2bd --- /dev/null +++ b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs @@ -0,0 +1,264 @@ +using ChocolArm64.Memory; +using ChocolArm64.State; +using Ryujinx.Core.OsHle.Handles; + +using static Ryujinx.Core.OsHle.ErrorCode; + +namespace Ryujinx.Core.OsHle.Svc +{ + partial class SvcHandler + { + private void SvcSetHeapSize(AThreadState ThreadState) + { + uint Size = (uint)ThreadState.X1; + + long Position = MemoryRegions.HeapRegionAddress; + + if (Size > CurrentHeapSize) + { + Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW); + } + else + { + Memory.Manager.Unmap(Position + Size, (long)CurrentHeapSize - Size); + } + + CurrentHeapSize = Size; + + ThreadState.X0 = 0; + ThreadState.X1 = (ulong)Position; + } + + private void SvcSetMemoryAttribute(AThreadState ThreadState) + { + long Position = (long)ThreadState.X0; + long Size = (long)ThreadState.X1; + int State0 = (int)ThreadState.X2; + int State1 = (int)ThreadState.X3; + + if ((State0 == 0 && State1 == 0) || + (State0 == 8 && State1 == 0)) + { + Memory.Manager.ClearAttrBit(Position, Size, 3); + } + else if (State0 == 8 && State1 == 8) + { + Memory.Manager.SetAttrBit(Position, Size, 3); + } + + ThreadState.X0 = 0; + } + + private void SvcMapMemory(AThreadState ThreadState) + { + long Dst = (long)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + if (!IsValidPosition(Src)) + { + Logging.Warn($"Tried to map Memory at invalid src address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + if (!IsValidMapPosition(Dst)) + { + Logging.Warn($"Tried to map Memory at invalid dst address {Dst:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src); + + Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm); + + Memory.Manager.Reprotect(Src, Size, AMemoryPerm.None); + + Memory.Manager.SetAttrBit(Src, Size, 0); + + ThreadState.X0 = 0; + } + + private void SvcUnmapMemory(AThreadState ThreadState) + { + long Dst = (long)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + if (!IsValidPosition(Src)) + { + Logging.Warn($"Tried to unmap Memory at invalid src address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + if (!IsValidMapPosition(Dst)) + { + Logging.Warn($"Tried to unmap Memory at invalid dst address {Dst:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst); + + Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory); + + Memory.Manager.Reprotect(Src, Size, DstInfo.Perm); + + Memory.Manager.ClearAttrBit(Src, Size, 0); + + ThreadState.X0 = 0; + } + + private void SvcQueryMemory(AThreadState ThreadState) + { + long InfoPtr = (long)ThreadState.X0; + long Position = (long)ThreadState.X2; + + AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); + + if (MapInfo == null) + { + long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; + + long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1; + + MapInfo = new AMemoryMapInfo(AddrSpaceEnd, ReservedSize, (int)MemoryType.Reserved, 0, AMemoryPerm.None); + } + + Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position); + Memory.WriteInt64(InfoPtr + 0x08, MapInfo.Size); + Memory.WriteInt32(InfoPtr + 0x10, MapInfo.Type); + Memory.WriteInt32(InfoPtr + 0x14, MapInfo.Attr); + Memory.WriteInt32(InfoPtr + 0x18, (int)MapInfo.Perm); + Memory.WriteInt32(InfoPtr + 0x1c, 0); + Memory.WriteInt32(InfoPtr + 0x20, 0); + Memory.WriteInt32(InfoPtr + 0x24, 0); + //TODO: X1. + + ThreadState.X0 = 0; + ThreadState.X1 = 0; + } + + private void SvcMapSharedMemory(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + int Perm = (int)ThreadState.X3; + + if (!IsValidPosition(Src)) + { + Logging.Warn($"Tried to map SharedMemory at invalid address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + HSharedMem SharedMem = Process.HandleTable.GetData(Handle); + + if (SharedMem != null) + { + Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, AMemoryPerm.Write); + + AMemoryHelper.FillWithZeros(Memory, Src, (int)Size); + + Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); + + lock (MappedSharedMems) + { + MappedSharedMems.Add((SharedMem, Src)); + } + + SharedMem.AddVirtualPosition(Memory, Src); + + ThreadState.X0 = 0; + } + + //TODO: Error codes. + } + + private void SvcUnmapSharedMemory(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + if (!IsValidPosition(Src)) + { + Logging.Warn($"Tried to unmap SharedMemory at invalid address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + HSharedMem SharedMem = Process.HandleTable.GetData(Handle); + + if (SharedMem != null) + { + Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory); + + SharedMem.RemoveVirtualPosition(Memory, Src); + + lock (MappedSharedMems) + { + MappedSharedMems.Remove((SharedMem, Src)); + } + + ThreadState.X0 = 0; + } + + //TODO: Error codes. + } + + private void SvcCreateTransferMemory(AThreadState ThreadState) + { + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + int Perm = (int)ThreadState.X3; + + if (!IsValidPosition(Src)) + { + Logging.Warn($"Tried to create TransferMemory at invalid address {Src:x16}!"); + + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); + + return; + } + + AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src); + + Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); + + HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size); + + ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem); + + ThreadState.X0 = 0; + ThreadState.X1 = Handle; + } + + private static bool IsValidPosition(long Position) + { + return Position >= MemoryRegions.AddrSpaceStart && + Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; + } + + private static bool IsValidMapPosition(long Position) + { + return Position >= MemoryRegions.MapRegionAddress && + Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize; + } + } +} \ No newline at end of file diff --git a/Ryujinx/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs similarity index 55% rename from Ryujinx/OsHle/Svc/SvcSystem.cs rename to Ryujinx.Core/OsHle/Svc/SvcSystem.cs index 0570ccb09..671a32d36 100644 --- a/Ryujinx/OsHle/Svc/SvcSystem.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs @@ -1,24 +1,42 @@ using ChocolArm64.Memory; using ChocolArm64.State; -using Ryujinx.OsHle.Exceptions; -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Ipc; +using Ryujinx.Core.OsHle.Exceptions; +using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.Ipc; using System; using System.Threading; -namespace Ryujinx.OsHle.Svc +using static Ryujinx.Core.OsHle.ErrorCode; + +namespace Ryujinx.Core.OsHle.Svc { partial class SvcHandler { - private void SvcExitProcess(AThreadState ThreadState) => Ns.Os.ExitProcess(ThreadState.ProcessId); + private const int AllowedCpuIdBitmask = 0b1111; + + private const bool EnableProcessDebugging = false; + + private void SvcExitProcess(AThreadState ThreadState) + { + Ns.Os.ExitProcess(ThreadState.ProcessId); + } + + private void SvcClearEvent(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + + //TODO: Implement events. + + ThreadState.X0 = 0; + } private void SvcCloseHandle(AThreadState ThreadState) { int Handle = (int)ThreadState.X0; - Ns.Os.CloseHandle(Handle); + Process.HandleTable.CloseHandle(Handle); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcResetSignal(AThreadState ThreadState) @@ -27,7 +45,7 @@ namespace Ryujinx.OsHle.Svc //TODO: Implement events. - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcWaitSynchronization(AThreadState ThreadState) @@ -43,12 +61,12 @@ namespace Ryujinx.OsHle.Svc Process.Scheduler.Suspend(CurrThread.ProcessorId); Process.Scheduler.Resume(CurrThread); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcGetSystemTick(AThreadState ThreadState) { - ThreadState.X0 = (ulong)ThreadState.CntpctEl0; + ThreadState.X0 = ThreadState.CntpctEl0; } private void SvcConnectToNamedPort(AThreadState ThreadState) @@ -61,10 +79,12 @@ namespace Ryujinx.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(Name); + HSession Session = new HSession(Process.Services.GetService(Name)); - ThreadState.X1 = (ulong)Ns.Os.Handles.GenerateId(Session); - ThreadState.X0 = (int)SvcResult.Success; + ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session); + + ThreadState.X0 = 0; + ThreadState.X1 = Handle; } private void SvcSendSyncRequest(AThreadState ThreadState) @@ -100,21 +120,29 @@ namespace Ryujinx.OsHle.Svc byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size); - HSession Session = Ns.Os.Handles.GetData(Handle); + HSession Session = Process.HandleTable.GetData(Handle); IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr, Session is HDomain); if (Session != null) { - IpcHandler.IpcCall(Ns, Memory, Session, Cmd, CmdPtr, Handle); + IpcHandler.IpcCall( + Ns, + Process, + Memory, + Session, + Cmd, + ThreadState.ThreadId, + CmdPtr, + Handle); byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } else { - ThreadState.X0 = (int)SvcResult.ErrBadIpcReq; + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidIpcReq); } Thread.Yield(); @@ -140,7 +168,7 @@ namespace Ryujinx.OsHle.Svc Logging.Info($"SvcOutputDebugString: {Str}"); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcGetInfo(AThreadState ThreadState) @@ -154,74 +182,69 @@ namespace Ryujinx.OsHle.Svc if (InfoType == 18 || InfoType == 19) { - ThreadState.X0 = (int)SvcResult.ErrBadInfo; + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo); return; } switch (InfoType) { - case 2: ThreadState.X1 = GetMapRegionBaseAddr(); break; - case 3: ThreadState.X1 = GetMapRegionSize(); break; - case 4: ThreadState.X1 = GetHeapRegionBaseAddr(); break; - case 5: ThreadState.X1 = GetHeapRegionSize(); break; - case 6: ThreadState.X1 = GetTotalMem(); break; - case 7: ThreadState.X1 = GetUsedMem(); break; - case 11: ThreadState.X1 = GetRnd64(); break; - case 12: ThreadState.X1 = GetAddrSpaceBaseAddr(); break; - case 13: ThreadState.X1 = GetAddrSpaceSize(); break; - case 14: ThreadState.X1 = GetMapRegionBaseAddr(); break; - case 15: ThreadState.X1 = GetMapRegionSize(); break; + case 0: + ThreadState.X1 = AllowedCpuIdBitmask; + break; + + case 2: + ThreadState.X1 = MemoryRegions.MapRegionAddress; + break; + + case 3: + ThreadState.X1 = MemoryRegions.MapRegionSize; + break; + + case 4: + ThreadState.X1 = MemoryRegions.HeapRegionAddress; + break; + + case 5: + ThreadState.X1 = MemoryRegions.HeapRegionSize; + break; + + case 6: + ThreadState.X1 = MemoryRegions.TotalMemoryAvailable; + break; + + case 7: + ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize; + break; + + case 8: + ThreadState.X1 = EnableProcessDebugging ? 1 : 0; + break; + + case 11: + ThreadState.X1 = (ulong)Rng.Next() + ((ulong)Rng.Next() << 32); + break; + + case 12: + ThreadState.X1 = MemoryRegions.AddrSpaceStart; + break; + + case 13: + ThreadState.X1 = MemoryRegions.AddrSpaceSize; + break; + + case 14: + ThreadState.X1 = MemoryRegions.MapRegionAddress; + break; + + case 15: + ThreadState.X1 = MemoryRegions.MapRegionSize; + break; default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}"); } - ThreadState.X0 = (int)SvcResult.Success; - } - - private ulong GetTotalMem() - { - return (ulong)Memory.Manager.GetTotalMemorySize(); - } - - private ulong GetUsedMem() - { - return (ulong)Memory.Manager.GetUsedMemorySize(); - } - - private ulong GetRnd64() - { - return (ulong)Rng.Next() + ((ulong)Rng.Next() << 32); - } - - private ulong GetAddrSpaceBaseAddr() - { - return 0x08000000; - } - - private ulong GetAddrSpaceSize() - { - return AMemoryMgr.AddrSize - GetAddrSpaceBaseAddr(); - } - - private ulong GetMapRegionBaseAddr() - { - return 0x80000000; - } - - private ulong GetMapRegionSize() - { - return 0x40000000; - } - - private ulong GetHeapRegionBaseAddr() - { - return GetMapRegionBaseAddr() + GetMapRegionSize(); - } - - private ulong GetHeapRegionSize() - { - return 0x40000000; + ThreadState.X0 = 0; } } -} \ No newline at end of file +} diff --git a/Ryujinx/OsHle/Svc/SvcThread.cs b/Ryujinx.Core/OsHle/Svc/SvcThread.cs similarity index 57% rename from Ryujinx/OsHle/Svc/SvcThread.cs rename to Ryujinx.Core/OsHle/Svc/SvcThread.cs index cc2bbb1eb..231ee2a23 100644 --- a/Ryujinx/OsHle/Svc/SvcThread.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcThread.cs @@ -1,7 +1,7 @@ using ChocolArm64.State; -using Ryujinx.OsHle.Handles; +using Ryujinx.Core.OsHle.Handles; -namespace Ryujinx.OsHle.Svc +namespace Ryujinx.Core.OsHle.Svc { partial class SvcHandler { @@ -28,7 +28,7 @@ namespace Ryujinx.OsHle.Svc Priority, ProcessorId); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; ThreadState.X1 = (ulong)Handle; } @@ -39,18 +39,25 @@ namespace Ryujinx.OsHle.Svc { int Handle = (int)ThreadState.X0; - HThread Thread = Ns.Os.Handles.GetData(Handle); + HThread Thread = Process.HandleTable.GetData(Handle); if (Thread != null) { Process.Scheduler.StartThread(Thread); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } //TODO: Error codes. } + private void SvcExitThread(AThreadState ThreadState) + { + HThread CurrThread = Process.GetThread(ThreadState.Tpidr); + + CurrThread.Thread.StopExecution(); + } + private void SvcSleepThread(AThreadState ThreadState) { ulong NanoSecs = ThreadState.X0; @@ -71,12 +78,51 @@ namespace Ryujinx.OsHle.Svc { int Handle = (int)ThreadState.X1; - HThread Thread = Ns.Os.Handles.GetData(Handle); + HThread Thread = Process.HandleTable.GetData(Handle); if (Thread != null) { + ThreadState.X0 = 0; ThreadState.X1 = (ulong)Thread.Priority; - ThreadState.X0 = (int)SvcResult.Success; + } + + //TODO: Error codes. + } + + private void SvcSetThreadPriority(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X1; + int Prio = (int)ThreadState.X0; + + HThread Thread = Process.HandleTable.GetData(Handle); + + if (Thread != null) + { + Thread.Priority = Prio; + + ThreadState.X0 = 0; + } + + //TODO: Error codes. + } + + private void SvcSetThreadCoreMask(AThreadState ThreadState) + { + ThreadState.X0 = 0; + + //TODO: Error codes. + } + + private void SvcGetThreadId(AThreadState ThreadState) + { + int Handle = (int)ThreadState.X0; + + HThread Thread = Process.HandleTable.GetData(Handle); + + if (Thread != null) + { + ThreadState.X0 = 0; + ThreadState.X1 = (ulong)Thread.ThreadId; } //TODO: Error codes. diff --git a/Ryujinx/OsHle/Svc/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs similarity index 76% rename from Ryujinx/OsHle/Svc/SvcThreadSync.cs rename to Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs index f342f51d5..38356073e 100644 --- a/Ryujinx/OsHle/Svc/SvcThreadSync.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs @@ -1,7 +1,9 @@ using ChocolArm64.State; -using Ryujinx.OsHle.Handles; +using Ryujinx.Core.OsHle.Handles; -namespace Ryujinx.OsHle.Svc +using static Ryujinx.Core.OsHle.ErrorCode; + +namespace Ryujinx.Core.OsHle.Svc { partial class SvcHandler { @@ -11,7 +13,7 @@ namespace Ryujinx.OsHle.Svc long MutexAddress = (long)ThreadState.X1; int RequestingThreadHandle = (int)ThreadState.X2; - HThread RequestingThread = Ns.Os.Handles.GetData(RequestingThreadHandle); + HThread RequestingThread = Process.HandleTable.GetData(RequestingThreadHandle); Mutex M = new Mutex(Process, MutexAddress, OwnerThreadHandle); @@ -19,7 +21,7 @@ namespace Ryujinx.OsHle.Svc M.WaitForLock(RequestingThread, RequestingThreadHandle); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcArbitrateUnlock(AThreadState ThreadState) @@ -31,7 +33,7 @@ namespace Ryujinx.OsHle.Svc M.Unlock(); } - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState) @@ -41,7 +43,7 @@ namespace Ryujinx.OsHle.Svc int ThreadHandle = (int)ThreadState.X2; long Timeout = (long)ThreadState.X3; - HThread Thread = Ns.Os.Handles.GetData(ThreadHandle); + HThread Thread = Process.HandleTable.GetData(ThreadHandle); Mutex M = new Mutex(Process, MutexAddress, ThreadHandle); @@ -53,11 +55,16 @@ namespace Ryujinx.OsHle.Svc Cv = Ns.Os.CondVars.GetOrAdd(CondVarAddress, Cv); - Cv.WaitForSignal(Thread); + if (!Cv.WaitForSignal(Thread)) + { + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout); + + return; + } M.WaitForLock(Thread, ThreadHandle); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcSignalProcessWideKey(AThreadState ThreadState) @@ -72,7 +79,7 @@ namespace Ryujinx.OsHle.Svc Cv.SetSignal(CurrThread, Count); } - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Utilities/EndianSwap.cs b/Ryujinx.Core/OsHle/Utilities/EndianSwap.cs new file mode 100644 index 000000000..6758f1f2f --- /dev/null +++ b/Ryujinx.Core/OsHle/Utilities/EndianSwap.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Core.OsHle.Utilities +{ + static class EndianSwap + { + public static short Swap16(short Value) => (short)(((Value >> 8) & 0xff) | (Value << 8)); + } +} diff --git a/Ryujinx/OsHle/Utilities/MemReader.cs b/Ryujinx.Core/OsHle/Utilities/MemReader.cs similarity index 95% rename from Ryujinx/OsHle/Utilities/MemReader.cs rename to Ryujinx.Core/OsHle/Utilities/MemReader.cs index 9868293a0..fe92f68fd 100644 --- a/Ryujinx/OsHle/Utilities/MemReader.cs +++ b/Ryujinx.Core/OsHle/Utilities/MemReader.cs @@ -1,6 +1,6 @@ using ChocolArm64.Memory; -namespace Ryujinx.OsHle.Utilities +namespace Ryujinx.Core.OsHle.Utilities { class MemReader { diff --git a/Ryujinx/OsHle/Utilities/MemWriter.cs b/Ryujinx.Core/OsHle/Utilities/MemWriter.cs similarity index 94% rename from Ryujinx/OsHle/Utilities/MemWriter.cs rename to Ryujinx.Core/OsHle/Utilities/MemWriter.cs index 041b0a970..21b6a3b65 100644 --- a/Ryujinx/OsHle/Utilities/MemWriter.cs +++ b/Ryujinx.Core/OsHle/Utilities/MemWriter.cs @@ -1,6 +1,6 @@ using ChocolArm64.Memory; -namespace Ryujinx.OsHle.Utilities +namespace Ryujinx.Core.OsHle.Utilities { class MemWriter { diff --git a/Ryujinx.Core/PerformanceStatistics.cs b/Ryujinx.Core/PerformanceStatistics.cs new file mode 100644 index 000000000..e5d930f93 --- /dev/null +++ b/Ryujinx.Core/PerformanceStatistics.cs @@ -0,0 +1,84 @@ +using System.Diagnostics; +using System.Timers; + +namespace Ryujinx.Core +{ + public class PerformanceStatistics + { + Stopwatch ExecutionTime = new Stopwatch(); + Timer ResetTimer = new Timer(1000); + + long CurrentGameFrameEnded; + long CurrentSystemFrameEnded; + long CurrentSystemFrameStart; + long LastGameFrameEnded; + long LastSystemFrameEnded; + + double AccumulatedGameFrameTime; + double AccumulatedSystemFrameTime; + double CurrentGameFrameTime; + double CurrentSystemFrameTime; + double PreviousGameFrameTime; + double PreviousSystemFrameTime; + public double GameFrameRate { get; private set; } + public double SystemFrameRate { get; private set; } + public long SystemFramesRendered; + public long GameFramesRendered; + public long ElapsedMilliseconds => ExecutionTime.ElapsedMilliseconds; + public long ElapsedMicroseconds => (long) + (((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000); + public long ElapsedNanoseconds => (long) + (((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000000); + + public PerformanceStatistics() + { + ExecutionTime.Start(); + ResetTimer.Elapsed += ResetTimerElapsed; + ResetTimer.AutoReset = true; + ResetTimer.Start(); + } + + private void ResetTimerElapsed(object sender, ElapsedEventArgs e) + { + ResetStatistics(); + } + + public void StartSystemFrame() + { + PreviousSystemFrameTime = CurrentSystemFrameTime; + LastSystemFrameEnded = CurrentSystemFrameEnded; + CurrentSystemFrameStart = ElapsedMicroseconds; + } + + public void EndSystemFrame() + { + CurrentSystemFrameEnded = ElapsedMicroseconds; + CurrentSystemFrameTime = CurrentSystemFrameEnded - CurrentSystemFrameStart; + AccumulatedSystemFrameTime += CurrentSystemFrameTime; + SystemFramesRendered++; + } + + public void RecordGameFrameTime() + { + CurrentGameFrameEnded = ElapsedMicroseconds; + CurrentGameFrameTime = CurrentGameFrameEnded - LastGameFrameEnded; + PreviousGameFrameTime = CurrentGameFrameTime; + LastGameFrameEnded = CurrentGameFrameEnded; + AccumulatedGameFrameTime += CurrentGameFrameTime; + GameFramesRendered++; + } + + public void ResetStatistics() + { + GameFrameRate = 1000 / ((AccumulatedGameFrameTime / GameFramesRendered) / 1000); + GameFrameRate = double.IsNaN(GameFrameRate) ? 0 : GameFrameRate; + SystemFrameRate = 1000 / ((AccumulatedSystemFrameTime / SystemFramesRendered) / 1000); + SystemFrameRate = double.IsNaN(SystemFrameRate) ? 0 : SystemFrameRate; + + GameFramesRendered = 0; + SystemFramesRendered = 0; + AccumulatedGameFrameTime = 0; + AccumulatedSystemFrameTime = 0; + } + } +} diff --git a/Ryujinx.Core/Ryujinx.Core.csproj b/Ryujinx.Core/Ryujinx.Core.csproj new file mode 100644 index 000000000..7d5ad7185 --- /dev/null +++ b/Ryujinx.Core/Ryujinx.Core.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.0 + + + + true + + + + true + + + + + + + + diff --git a/Ryujinx.Core/Settings/ColorSet.cs b/Ryujinx.Core/Settings/ColorSet.cs new file mode 100644 index 000000000..43483363f --- /dev/null +++ b/Ryujinx.Core/Settings/ColorSet.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Core.Settings +{ + public enum ColorSet + { + BasicWhite = 0, + BasicBlack = 1 + } +} diff --git a/Ryujinx.Core/Settings/SetSys.cs b/Ryujinx.Core/Settings/SetSys.cs new file mode 100644 index 000000000..d8b6eb6ef --- /dev/null +++ b/Ryujinx.Core/Settings/SetSys.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Core.Settings +{ + public class SetSys + { + public ColorSet ThemeColor; + } +} diff --git a/Ryujinx.Core/Switch.cs b/Ryujinx.Core/Switch.cs new file mode 100644 index 000000000..487f3bdb9 --- /dev/null +++ b/Ryujinx.Core/Switch.cs @@ -0,0 +1,69 @@ +using Ryujinx.Core.Input; +using Ryujinx.Core.OsHle; +using Ryujinx.Core.Settings; +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Gpu; +using System; + +namespace Ryujinx.Core +{ + public class Switch : IDisposable + { + internal NsGpu Gpu { get; private set; } + internal Horizon Os { get; private set; } + internal VirtualFs VFs { get; private set; } + + public Hid Hid { get; private set; } + public SetSys Settings { get; private set; } + public PerformanceStatistics Statistics { get; private set; } + + public event EventHandler Finish; + + public Switch(IGalRenderer Renderer) + { + Gpu = new NsGpu(Renderer); + + VFs = new VirtualFs(); + + Hid = new Hid(); + + Statistics = new PerformanceStatistics(); + + Os = new Horizon(this); + + Os.HidSharedMem.MemoryMapped += Hid.ShMemMap; + Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap; + + Settings = new SetSys(); + } + + public void LoadCart(string ExeFsDir, string RomFsFile = null) + { + Os.LoadCart(ExeFsDir, RomFsFile); + } + + public void LoadProgram(string FileName) + { + Os.LoadProgram(FileName); + } + + internal virtual void OnFinish(EventArgs e) + { + Finish?.Invoke(this, e); + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + Os.Dispose(); + VFs.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/VirtualFs.cs b/Ryujinx.Core/VirtualFs.cs similarity index 63% rename from Ryujinx/VirtualFs.cs rename to Ryujinx.Core/VirtualFs.cs index e5579bcf1..c0858e0ee 100644 --- a/Ryujinx/VirtualFs.cs +++ b/Ryujinx.Core/VirtualFs.cs @@ -1,13 +1,13 @@ using System; using System.IO; -namespace Ryujinx +namespace Ryujinx.Core { class VirtualFs : IDisposable { - private const string BasePath = "Fs"; - private const string SavesPath = "Saves"; - private const string SdCardPath = "SdCard"; + private const string BasePath = "RyuFs"; + private const string NandPath = "nand"; + private const string SdCardPath = "sdmc"; public Stream RomFs { get; private set; } @@ -18,10 +18,18 @@ namespace Ryujinx public string GetFullPath(string BasePath, string FileName) { - if (FileName.StartsWith('/')) + if (FileName.StartsWith("//")) + { + FileName = FileName.Substring(2); + } + else if (FileName.StartsWith('/')) { FileName = FileName.Substring(1); } + else + { + return null; + } string FullPath = Path.GetFullPath(Path.Combine(BasePath, FileName)); @@ -35,7 +43,7 @@ namespace Ryujinx public string GetSdCardPath() => MakeDirAndGetFullPath(SdCardPath); - public string GetGameSavesPath() => MakeDirAndGetFullPath(SavesPath); + public string GetGameSavesPath() => MakeDirAndGetFullPath(NandPath); private string MakeDirAndGetFullPath(string Dir) { @@ -49,9 +57,16 @@ namespace Ryujinx return FullPath; } + public DriveInfo GetDrive() + { + return new DriveInfo(Path.GetPathRoot(GetBasePath())); + } + public string GetBasePath() { - return Path.Combine(Directory.GetCurrentDirectory(), BasePath); + string AppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + + return Path.Combine(AppDataPath, BasePath); } public void Dispose() @@ -61,9 +76,9 @@ namespace Ryujinx protected virtual void Dispose(bool disposing) { - if (disposing && RomFs != null) + if (disposing) { - RomFs.Dispose(); + RomFs?.Dispose(); } } } diff --git a/Ryujinx.Graphics/Gal/EmbeddedResource.cs b/Ryujinx.Graphics/Gal/EmbeddedResource.cs new file mode 100644 index 000000000..45b77da71 --- /dev/null +++ b/Ryujinx.Graphics/Gal/EmbeddedResource.cs @@ -0,0 +1,20 @@ +using System.IO; +using System.Reflection; + +namespace Ryujinx.Graphics.Gal +{ + static class EmbeddedResource + { + public static string GetString(string Name) + { + Assembly Asm = typeof(EmbeddedResource).Assembly; + + using (Stream ResStream = Asm.GetManifestResourceStream(Name)) + { + StreamReader Reader = new StreamReader(ResStream); + + return Reader.ReadToEnd(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx/Gal/GalPrimitiveType.cs b/Ryujinx.Graphics/Gal/GalPrimitiveType.cs similarity index 95% rename from Ryujinx/Gal/GalPrimitiveType.cs rename to Ryujinx.Graphics/Gal/GalPrimitiveType.cs index 7b6d99a0b..ce084149d 100644 --- a/Ryujinx/Gal/GalPrimitiveType.cs +++ b/Ryujinx.Graphics/Gal/GalPrimitiveType.cs @@ -1,4 +1,4 @@ -namespace Gal +namespace Ryujinx.Graphics.Gal { public enum GalPrimitiveType { diff --git a/Ryujinx/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs similarity index 96% rename from Ryujinx/Gal/GalVertexAttrib.cs rename to Ryujinx.Graphics/Gal/GalVertexAttrib.cs index bbc326337..dc38c5934 100644 --- a/Ryujinx/Gal/GalVertexAttrib.cs +++ b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs @@ -1,4 +1,4 @@ -namespace Gal +namespace Ryujinx.Graphics.Gal { public struct GalVertexAttrib { diff --git a/Ryujinx/Gal/GalVertexAttribSize.cs b/Ryujinx.Graphics/Gal/GalVertexAttribSize.cs similarity index 93% rename from Ryujinx/Gal/GalVertexAttribSize.cs rename to Ryujinx.Graphics/Gal/GalVertexAttribSize.cs index 11f0470c2..d3ce60ace 100644 --- a/Ryujinx/Gal/GalVertexAttribSize.cs +++ b/Ryujinx.Graphics/Gal/GalVertexAttribSize.cs @@ -1,4 +1,4 @@ -namespace Gal +namespace Ryujinx.Graphics.Gal { public enum GalVertexAttribSize { diff --git a/Ryujinx/Gal/GalVertexAttribType.cs b/Ryujinx.Graphics/Gal/GalVertexAttribType.cs similarity index 86% rename from Ryujinx/Gal/GalVertexAttribType.cs rename to Ryujinx.Graphics/Gal/GalVertexAttribType.cs index c0ed59fb2..358836fda 100644 --- a/Ryujinx/Gal/GalVertexAttribType.cs +++ b/Ryujinx.Graphics/Gal/GalVertexAttribType.cs @@ -1,4 +1,4 @@ -namespace Gal +namespace Ryujinx.Graphics.Gal { public enum GalVertexAttribType { diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs new file mode 100644 index 000000000..aa4eac4e1 --- /dev/null +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -0,0 +1,30 @@ +using System; + +namespace Ryujinx.Graphics.Gal +{ + public unsafe interface IGalRenderer + { + void QueueAction(Action ActionMthd); + void RunActions(); + + void InitializeFrameBuffer(); + void ResetFrameBuffer(); + void Render(); + void SetWindowSize(int Width, int Height); + void SetFrameBuffer( + byte* Fb, + int Width, + int Height, + float ScaleX, + float ScaleY, + float OffsX, + float OffsY, + float Rotate); + + void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs); + + void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height); + + void BindTexture(int Index); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl b/Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl new file mode 100644 index 000000000..74e33bd7c --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/FbFragShader.glsl @@ -0,0 +1,13 @@ +#version 330 core + +precision highp float; + +uniform sampler2D tex; + +in vec2 tex_coord; + +out vec4 out_frag_color; + +void main(void) { + out_frag_color = texture(tex, tex_coord); +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl b/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl new file mode 100644 index 000000000..35d560c09 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl @@ -0,0 +1,28 @@ +#version 330 core + +precision highp float; + +uniform mat2 transform; +uniform vec2 window_size; +uniform vec2 offset; + +layout(location = 0) in vec2 in_position; +layout(location = 1) in vec2 in_tex_coord; + +out vec2 tex_coord; + +// Have a fixed aspect ratio, fit the image within the available space. +vec2 get_scale_ratio(void) { + vec2 native_size = vec2(1280, 720); + vec2 ratio = vec2( + (window_size.y * native_size.x) / (native_size.y * window_size.x), + (window_size.x * native_size.y) / (native_size.x * window_size.y) + ); + return min(ratio, 1); +} + +void main(void) { + tex_coord = in_tex_coord; + vec2 t_pos = (transform * in_position) + offset; + gl_Position = vec4(t_pos * get_scale_ratio(), 0, 1); +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs new file mode 100644 index 000000000..b761811f6 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs @@ -0,0 +1,250 @@ +using OpenTK; +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + unsafe class FrameBuffer + { + public int WindowWidth { get; set; } + public int WindowHeight { get; set; } + + private int VtxShaderHandle; + private int FragShaderHandle; + private int PrgShaderHandle; + + private int TexHandle; + private int TexWidth; + private int TexHeight; + + private int VaoHandle; + private int VboHandle; + + private int[] Pixels; + + private byte* FbPtr; + + private object FbPtrLock; + + public FrameBuffer(int Width, int Height) + { + if (Width < 0) + { + throw new ArgumentOutOfRangeException(nameof(Width)); + } + + if (Height < 0) + { + throw new ArgumentOutOfRangeException(nameof(Height)); + } + + FbPtrLock = new object(); + + TexWidth = Width; + TexHeight = Height; + + WindowWidth = Width; + WindowHeight = Height; + + SetupShaders(); + SetupTexture(); + SetupVertex(); + } + + private void SetupShaders() + { + VtxShaderHandle = GL.CreateShader(ShaderType.VertexShader); + FragShaderHandle = GL.CreateShader(ShaderType.FragmentShader); + + string VtxShaderSource = EmbeddedResource.GetString("GlFbVtxShader"); + string FragShaderSource = EmbeddedResource.GetString("GlFbFragShader"); + + GL.ShaderSource(VtxShaderHandle, VtxShaderSource); + GL.ShaderSource(FragShaderHandle, FragShaderSource); + GL.CompileShader(VtxShaderHandle); + GL.CompileShader(FragShaderHandle); + + PrgShaderHandle = GL.CreateProgram(); + + GL.AttachShader(PrgShaderHandle, VtxShaderHandle); + GL.AttachShader(PrgShaderHandle, FragShaderHandle); + GL.LinkProgram(PrgShaderHandle); + GL.UseProgram(PrgShaderHandle); + + int TexUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "tex"); + + GL.Uniform1(TexUniformLocation, 0); + + int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size"); + + GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f)); + } + + private void SetupTexture() + { + Pixels = new int[TexWidth * TexHeight]; + + if (TexHandle == 0) + { + TexHandle = GL.GenTexture(); + } + + GL.BindTexture(TextureTarget.Texture2D, TexHandle); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); + GL.TexImage2D(TextureTarget.Texture2D, + 0, + PixelInternalFormat.Rgba, + TexWidth, + TexHeight, + 0, + PixelFormat.Rgba, + PixelType.UnsignedByte, + IntPtr.Zero); + } + + private void SetupVertex() + { + VaoHandle = GL.GenVertexArray(); + VboHandle = GL.GenBuffer(); + + float[] Buffer = new float[] + { + -1, 1, 0, 0, + 1, 1, 1, 0, + -1, -1, 0, 1, + 1, -1, 1, 1 + }; + + IntPtr Length = new IntPtr(Buffer.Length * 4); + + GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); + GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); + GL.BindBuffer(BufferTarget.ArrayBuffer, 0); + + GL.BindVertexArray(VaoHandle); + + GL.EnableVertexAttribArray(0); + + GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); + + GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 16, 0); + + GL.EnableVertexAttribArray(1); + + GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); + + GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8); + + GL.BindVertexArray(0); + } + + public unsafe void Set(byte* Fb, int Width, int Height, Matrix2 Transform, Vector2 Offs) + { + if (Fb == null) + { + throw new ArgumentNullException(nameof(Fb)); + } + + if (Width < 0) + { + throw new ArgumentOutOfRangeException(nameof(Width)); + } + + if (Height < 0) + { + throw new ArgumentOutOfRangeException(nameof(Height)); + } + + lock (FbPtrLock) + { + FbPtr = Fb; + } + + if (Width != TexWidth || + Height != TexHeight) + { + TexWidth = Width; + TexHeight = Height; + + SetupTexture(); + } + + GL.UseProgram(PrgShaderHandle); + + int TransformUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "transform"); + + GL.UniformMatrix2(TransformUniformLocation, false, ref Transform); + + int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size"); + + GL.Uniform2(WindowSizeUniformLocation, new Vector2(WindowWidth, WindowHeight)); + + int OffsetUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "offset"); + + GL.Uniform2(OffsetUniformLocation, Offs); + } + + public void Reset() + { + lock (FbPtrLock) + { + FbPtr = null; + } + } + + public void Render() + { + lock (FbPtrLock) + { + if (FbPtr == null) + { + return; + } + + for (int Y = 0; Y < TexHeight; Y++) + for (int X = 0; X < TexWidth; X++) + { + Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y))); + } + } + + GL.BindTexture(TextureTarget.Texture2D, TexHandle); + GL.TexSubImage2D(TextureTarget.Texture2D, + 0, + 0, + 0, + TexWidth, + TexHeight, + PixelFormat.Rgba, + PixelType.UnsignedByte, + Pixels); + + GL.ActiveTexture(TextureUnit.Texture0); + + GL.BindVertexArray(VaoHandle); + + GL.UseProgram(PrgShaderHandle); + + GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4); + } + + private int GetSwizzleOffset(int X, int Y) + { + int Pos; + + Pos = (Y & 0x7f) >> 4; + Pos += (X >> 4) << 3; + Pos += (Y >> 7) * ((TexWidth >> 4) << 3); + Pos *= 1024; + Pos += ((Y & 0xf) >> 3) << 9; + Pos += ((X & 0xf) >> 3) << 8; + Pos += ((Y & 0x7) >> 1) << 6; + Pos += ((X & 0x7) >> 2) << 5; + Pos += ((Y & 0x1) >> 0) << 4; + Pos += ((X & 0x3) >> 0) << 2; + + return Pos; + } + } +} \ No newline at end of file diff --git a/Ryujinx/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs similarity index 86% rename from Ryujinx/Gal/OpenGL/OpenGLRenderer.cs rename to Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs index 72ad6f706..002e54dad 100644 --- a/Ryujinx/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -1,8 +1,10 @@ +using OpenTK; using OpenTK.Graphics.OpenGL; using System; +using System.Collections.Concurrent; using System.Collections.Generic; -namespace Gal.OpenGL +namespace Ryujinx.Graphics.Gal.OpenGL { public class OpenGLRenderer : IGalRenderer { @@ -23,9 +25,9 @@ namespace Gal.OpenGL private Texture[] Textures; - private Queue ActionsQueue; + private ConcurrentQueue ActionsQueue; - public long FrameBufferPtr { get; set; } + private FrameBuffer FbRenderer; public OpenGLRenderer() { @@ -33,7 +35,17 @@ namespace Gal.OpenGL Textures = new Texture[8]; - ActionsQueue = new Queue(); + ActionsQueue = new ConcurrentQueue(); + } + + public void InitializeFrameBuffer() + { + FbRenderer = new FrameBuffer(1280, 720); + } + + public void ResetFrameBuffer() + { + FbRenderer.Reset(); } public void QueueAction(Action ActionMthd) @@ -43,14 +55,18 @@ namespace Gal.OpenGL public void RunActions() { - while (ActionsQueue.Count > 0) + int Count = ActionsQueue.Count; + + while (Count-- > 0 && ActionsQueue.TryDequeue(out Action RenderAction)) { - ActionsQueue.Dequeue()(); + RenderAction(); } } public void Render() { + FbRenderer.Render(); + for (int Index = 0; Index < VertexBuffers.Count; Index++) { VertexBuffer Vb = VertexBuffers[Index]; @@ -62,7 +78,32 @@ namespace Gal.OpenGL GL.DrawArrays(PrimitiveType.TriangleStrip, 0, Vb.PrimCount); } } - + } + + public void SetWindowSize(int Width, int Height) + { + FbRenderer.WindowWidth = Width; + FbRenderer.WindowHeight = Height; + } + + public unsafe void SetFrameBuffer( + byte* Fb, + int Width, + int Height, + float ScaleX, + float ScaleY, + float OffsX, + float OffsY, + float Rotate) + { + Matrix2 Transform; + + Transform = Matrix2.CreateScale(ScaleX, ScaleY); + Transform *= Matrix2.CreateRotation(Rotate); + + Vector2 Offs = new Vector2(OffsX, OffsY); + + FbRenderer.Set(Fb, Width, Height, Transform, Offs); } public void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs) diff --git a/Ryujinx/Gpu/BCn.cs b/Ryujinx.Graphics/Gpu/BCn.cs similarity index 99% rename from Ryujinx/Gpu/BCn.cs rename to Ryujinx.Graphics/Gpu/BCn.cs index bf782d167..b1caf4675 100644 --- a/Ryujinx/Gpu/BCn.cs +++ b/Ryujinx.Graphics/Gpu/BCn.cs @@ -1,7 +1,7 @@ using System; using System.Drawing; -namespace Ryujinx.Gpu +namespace Ryujinx.Graphics.Gpu { static class BCn { diff --git a/Ryujinx.Graphics/Gpu/NsGpu.cs b/Ryujinx.Graphics/Gpu/NsGpu.cs new file mode 100644 index 000000000..133d0af25 --- /dev/null +++ b/Ryujinx.Graphics/Gpu/NsGpu.cs @@ -0,0 +1,53 @@ +using ChocolArm64.Memory; +using Ryujinx.Graphics.Gal; + +namespace Ryujinx.Graphics.Gpu +{ + public class NsGpu + { + public IGalRenderer Renderer { get; private set; } + + internal NsGpuMemoryMgr MemoryMgr { get; private set; } + + internal NsGpuPGraph PGraph { get; private set; } + + public NsGpu(IGalRenderer Renderer) + { + this.Renderer = Renderer; + + MemoryMgr = new NsGpuMemoryMgr(); + + PGraph = new NsGpuPGraph(this); + } + + public long GetCpuAddr(long Position) + { + return MemoryMgr.GetCpuAddr(Position); + } + + public long MapMemory(long CpuAddr, long Size) + { + return MemoryMgr.Map(CpuAddr, Size); + } + + public long MapMemory(long CpuAddr, long GpuAddr, long Size) + { + return MemoryMgr.Map(CpuAddr, GpuAddr, Size); + } + + public void ProcessPushBuffer(NsGpuPBEntry[] PushBuffer, AMemory Memory) + { + PGraph.ProcessPushBuffer(PushBuffer, Memory); + } + + public long ReserveMemory(long Size, long Align) + { + return MemoryMgr.Reserve(Size, Align); + } + + public long ReserveMemory(long GpuAddr, long Size, long Align) + { + return MemoryMgr.Reserve(GpuAddr, Size, Align); + } + } +} \ No newline at end of file diff --git a/Ryujinx/Gpu/NsGpuEngine.cs b/Ryujinx.Graphics/Gpu/NsGpuEngine.cs similarity index 87% rename from Ryujinx/Gpu/NsGpuEngine.cs rename to Ryujinx.Graphics/Gpu/NsGpuEngine.cs index bf1045696..118e2b72a 100644 --- a/Ryujinx/Gpu/NsGpuEngine.cs +++ b/Ryujinx.Graphics/Gpu/NsGpuEngine.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Gpu +namespace Ryujinx.Graphics.Gpu { enum NsGpuEngine { diff --git a/Ryujinx/Gpu/NsGpuMemoryMgr.cs b/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs similarity index 98% rename from Ryujinx/Gpu/NsGpuMemoryMgr.cs rename to Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs index 563a5c099..7f50640d5 100644 --- a/Ryujinx/Gpu/NsGpuMemoryMgr.cs +++ b/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Gpu +namespace Ryujinx.Graphics.Gpu { class NsGpuMemoryMgr { @@ -16,7 +16,7 @@ namespace Ryujinx.Gpu private const int PTLvl1Mask = PTLvl1Size - 1; private const int PageMask = PageSize - 1; - private const int PTLvl0Bit = PTPageBits + PTLvl0Bits; + private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; private const int PTLvl1Bit = PTPageBits; private const long PteUnmapped = -1; diff --git a/Ryujinx/Gpu/NsGpuPBEntry.cs b/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs similarity index 97% rename from Ryujinx/Gpu/NsGpuPBEntry.cs rename to Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs index 226a7f61d..8063651aa 100644 --- a/Ryujinx/Gpu/NsGpuPBEntry.cs +++ b/Ryujinx.Graphics/Gpu/NsGpuPBEntry.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; -namespace Ryujinx.Gpu +namespace Ryujinx.Graphics.Gpu { - struct NsGpuPBEntry + public struct NsGpuPBEntry { public NsGpuRegister Register { get; private set; } diff --git a/Ryujinx/Gpu/NsGpuPGraph.cs b/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs similarity index 99% rename from Ryujinx/Gpu/NsGpuPGraph.cs rename to Ryujinx.Graphics/Gpu/NsGpuPGraph.cs index e40b6283e..eb893f74c 100644 --- a/Ryujinx/Gpu/NsGpuPGraph.cs +++ b/Ryujinx.Graphics/Gpu/NsGpuPGraph.cs @@ -1,8 +1,8 @@ using ChocolArm64.Memory; -using Gal; +using Ryujinx.Graphics.Gal; using System.Collections.Generic; -namespace Ryujinx.Gpu +namespace Ryujinx.Graphics.Gpu { class NsGpuPGraph { diff --git a/Ryujinx/Gpu/NsGpuRegister.cs b/Ryujinx.Graphics/Gpu/NsGpuRegister.cs similarity index 98% rename from Ryujinx/Gpu/NsGpuRegister.cs rename to Ryujinx.Graphics/Gpu/NsGpuRegister.cs index 740ca9feb..319e2c01f 100644 --- a/Ryujinx/Gpu/NsGpuRegister.cs +++ b/Ryujinx.Graphics/Gpu/NsGpuRegister.cs @@ -1,6 +1,6 @@ -namespace Ryujinx.Gpu +namespace Ryujinx.Graphics.Gpu { - enum NsGpuRegister + public enum NsGpuRegister { BindChannel = 0, diff --git a/Ryujinx/Gpu/NsGpuTexture.cs b/Ryujinx.Graphics/Gpu/NsGpuTexture.cs similarity index 79% rename from Ryujinx/Gpu/NsGpuTexture.cs rename to Ryujinx.Graphics/Gpu/NsGpuTexture.cs index 26500c04a..aac422005 100644 --- a/Ryujinx/Gpu/NsGpuTexture.cs +++ b/Ryujinx.Graphics/Gpu/NsGpuTexture.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Gpu +namespace Ryujinx.Graphics.Gpu { struct NsGpuTexture { diff --git a/Ryujinx/Gpu/NsGpuTextureFormat.cs b/Ryujinx.Graphics/Gpu/NsGpuTextureFormat.cs similarity index 76% rename from Ryujinx/Gpu/NsGpuTextureFormat.cs rename to Ryujinx.Graphics/Gpu/NsGpuTextureFormat.cs index 9bb122812..2993840be 100644 --- a/Ryujinx/Gpu/NsGpuTextureFormat.cs +++ b/Ryujinx.Graphics/Gpu/NsGpuTextureFormat.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Gpu +namespace Ryujinx.Graphics.Gpu { enum NsGpuTextureFormat { diff --git a/Ryujinx/Gpu/SwizzleAddr.cs b/Ryujinx.Graphics/Gpu/SwizzleAddr.cs similarity index 99% rename from Ryujinx/Gpu/SwizzleAddr.cs rename to Ryujinx.Graphics/Gpu/SwizzleAddr.cs index 5ad35a538..08e61eb58 100644 --- a/Ryujinx/Gpu/SwizzleAddr.cs +++ b/Ryujinx.Graphics/Gpu/SwizzleAddr.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.Gpu +namespace Ryujinx.Graphics.Gpu { class SwizzleAddr { diff --git a/Ryujinx.Graphics/Ryujinx.Graphics.csproj b/Ryujinx.Graphics/Ryujinx.Graphics.csproj new file mode 100644 index 000000000..01bf41f83 --- /dev/null +++ b/Ryujinx.Graphics/Ryujinx.Graphics.csproj @@ -0,0 +1,32 @@ + + + + netcoreapp2.0 + + + + true + + + + true + + + + + + + + + + + + + GlFbVtxShader + + + GlFbFragShader + + + + diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index 0f1e64134..b5bbdbc17 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -1,73 +1,103 @@ -using ChocolArm64; +using ChocolArm64; using ChocolArm64.Memory; using ChocolArm64.State; using NUnit.Framework; -using System; -using System.Runtime.InteropServices; using System.Threading; namespace Ryujinx.Tests.Cpu { [TestFixture] - public partial class CpuTest + public class CpuTest { - IntPtr Ram; - AMemoryAlloc Allocator; - AMemory Memory; + protected long Position { get; private set; } + private long Size; + + private long EntryPoint; + + private AMemory Memory; + private AThread Thread; [SetUp] public void Setup() { - Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize); - Allocator = new AMemoryAlloc(); - Memory = new AMemory(Ram, Allocator); - Memory.Manager.MapPhys(0x1000, 0x1000, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute); + Position = 0x0; + Size = 0x1000; + + EntryPoint = Position; + + ATranslator Translator = new ATranslator(); + Memory = new AMemory(); + Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute); + Thread = new AThread(Translator, Memory, EntryPoint); } [TearDown] public void Teardown() { - Marshal.FreeHGlobal(Ram); + Memory.Dispose(); + Thread = null; + Memory = null; } - private void Execute(AThread Thread) + protected void Reset() { - AutoResetEvent Wait = new AutoResetEvent(false); - Thread.ThreadState.Break += (sender, e) => Thread.StopExecution(); - Thread.WorkFinished += (sender, e) => Wait.Set(); - - Wait.Reset(); - Thread.Execute(); - Wait.WaitOne(); + Teardown(); + Setup(); } - private AThreadState SingleOpcode(uint Opcode, - ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, - AVec V0 = new AVec(), AVec V1 = new AVec(), AVec V2 = new AVec()) + protected void Opcode(uint Opcode) { - Memory.WriteUInt32(0x1000, Opcode); - Memory.WriteUInt32(0x1004, 0xD4200000); // BRK #0 - Memory.WriteUInt32(0x1008, 0xD65F03C0); // RET + Thread.Memory.WriteUInt32Unchecked(Position, Opcode); + Position += 4; + } - AThread Thread = new AThread(Memory, ThreadPriority.Normal, 0x1000); + protected void SetThreadState(ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X31 = 0, + AVec V0 = default(AVec), AVec V1 = default(AVec), AVec V2 = default(AVec), + bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, int Fpcr = 0x0) + { Thread.ThreadState.X0 = X0; Thread.ThreadState.X1 = X1; Thread.ThreadState.X2 = X2; + Thread.ThreadState.X31 = X31; Thread.ThreadState.V0 = V0; Thread.ThreadState.V1 = V1; Thread.ThreadState.V2 = V2; - Execute(Thread); + Thread.ThreadState.Overflow = Overflow; + Thread.ThreadState.Carry = Carry; + Thread.ThreadState.Zero = Zero; + Thread.ThreadState.Negative = Negative; + Thread.ThreadState.Fpcr = Fpcr; + } + + protected void ExecuteOpcodes() + { + using (ManualResetEvent Wait = new ManualResetEvent(false)) + { + Thread.ThreadState.Break += (sender, e) => Thread.StopExecution(); + Thread.WorkFinished += (sender, e) => Wait.Set(); + + Thread.Execute(); + Wait.WaitOne(); + } + } + + protected AThreadState GetThreadState() + { return Thread.ThreadState; } - [Test] - public void SanityCheck() + protected AThreadState SingleOpcode(uint Opcode, + ulong X0 = 0, ulong X1 = 0, ulong X2 = 0, ulong X31 = 0, + AVec V0 = default(AVec), AVec V1 = default(AVec), AVec V2 = default(AVec), + bool Overflow = false, bool Carry = false, bool Zero = false, bool Negative = false, int Fpcr = 0x0) { - uint Opcode = 0xD503201F; // NOP - Assert.AreEqual(SingleOpcode(Opcode, X0: 0).X0, 0); - Assert.AreEqual(SingleOpcode(Opcode, X0: 1).X0, 1); - Assert.AreEqual(SingleOpcode(Opcode, X0: 2).X0, 2); - Assert.AreEqual(SingleOpcode(Opcode, X0: 42).X0, 42); + this.Opcode(Opcode); + this.Opcode(0xD4200000); // BRK #0 + this.Opcode(0xD65F03C0); // RET + SetThreadState(X0, X1, X2, X31, V0, V1, V2, Overflow, Carry, Zero, Negative, Fpcr); + ExecuteOpcodes(); + + return GetThreadState(); } } } diff --git a/Ryujinx.Tests/Cpu/CpuTestAlu.cs b/Ryujinx.Tests/Cpu/CpuTestAlu.cs index a05c0b199..8116fc7c7 100644 --- a/Ryujinx.Tests/Cpu/CpuTestAlu.cs +++ b/Ryujinx.Tests/Cpu/CpuTestAlu.cs @@ -1,11 +1,42 @@ -using ChocolArm64.State; +using ChocolArm64.State; using NUnit.Framework; namespace Ryujinx.Tests.Cpu { - [TestFixture] - public partial class CpuTest + public class CpuTestAlu : CpuTest { + [TestCase(0x9A020020u, 2u, 3u, true, 6u)] + [TestCase(0x9A020020u, 2u, 3u, false, 5u)] + [TestCase(0x1A020020u, 2u, 3u, true, 6u)] + [TestCase(0x1A020020u, 2u, 3u, false, 5u)] + [TestCase(0x1A020020u, 0xFFFFFFFFu, 0x2u, false, 0x1u)] + public void Adc(uint Opcode, uint A, uint B, bool CarryState, uint Result) + { + // ADC (X0/W0), (X1/W1), (X2/W2) + AThreadState ThreadState = SingleOpcode(Opcode, X1: A, X2: B, Carry: CarryState); + Assert.AreEqual(Result, ThreadState.X0); + } + + [TestCase(0x3A020020u, 2u, 3u, false, false, false, false, 5u)] + [TestCase(0x3A020020u, 2u, 3u, true, false, false, false, 6u)] + [TestCase(0xBA020020u, 2u, 3u, false, false, false, false, 5u)] + [TestCase(0xBA020020u, 2u, 3u, true, false, false, false, 6u)] + [TestCase(0x3A020020u, 0xFFFFFFFEu, 0x1u, true, false, true, true, 0x0u)] + [TestCase(0x3A020020u, 0xFFFFFFFFu, 0xFFFFFFFFu, true, true, false, true, 0xFFFFFFFFu)] + public void Adcs(uint Opcode, uint A, uint B, bool CarryState, bool Negative, bool Zero, bool Carry, uint Result) + { + //ADCS (X0/W0), (X1, W1), (X2/W2) + AThreadState ThreadState = SingleOpcode(Opcode, X1: A, X2: B, Carry: CarryState); + Assert.Multiple(() => + { + Assert.IsFalse(ThreadState.Overflow); + Assert.AreEqual(Negative, ThreadState.Negative); + Assert.AreEqual(Zero, ThreadState.Zero); + Assert.AreEqual(Carry, ThreadState.Carry); + Assert.AreEqual(Result, ThreadState.X0); + }); + } + [Test] public void Add() { @@ -14,36 +45,82 @@ namespace Ryujinx.Tests.Cpu Assert.AreEqual(3, ThreadState.X0); } - [Test] - public void Ands() + [TestCase(2u, false, false)] + [TestCase(5u, false, false)] + [TestCase(7u, false, false)] + [TestCase(0xFFFFFFFFu, false, true )] + [TestCase(0xFFFFFFFBu, true, true )] + public void Adds(uint A, bool Zero, bool Carry) + { + //ADDS WZR, WSP, #5 + AThreadState ThreadState = SingleOpcode(0x310017FF, X31: A); + Assert.Multiple(() => + { + Assert.IsFalse(ThreadState.Negative); + Assert.IsFalse(ThreadState.Overflow); + Assert.AreEqual(Zero, ThreadState.Zero); + Assert.AreEqual(Carry, ThreadState.Carry); + Assert.AreEqual(A, ThreadState.X31); + }); + } + + [TestCase(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFul, true, false)] + [TestCase(0xFFFFFFFFu, 0x00000000u, 0x00000000ul, false, true)] + [TestCase(0x12345678u, 0x7324A993u, 0x12240010ul, false, false)] + public void Ands(uint A, uint B, ulong Result, bool Negative, bool Zero) { // ANDS W0, W1, W2 uint Opcode = 0x6A020020; - var tests = new[] + AThreadState ThreadState = SingleOpcode(Opcode, X1: A, X2: B); + Assert.Multiple(() => { - new { W1 = 0xFFFFFFFFul, W2 = 0xFFFFFFFFul, Result = 0xFFFFFFFFul, Negative = true, Zero = false }, - new { W1 = 0xFFFFFFFFul, W2 = 0x00000000ul, Result = 0x00000000ul, Negative = false, Zero = true }, - new { W1 = 0x12345678ul, W2 = 0x7324A993ul, Result = 0x12240010ul, Negative = false, Zero = false }, - }; - - foreach (var test in tests) - { - AThreadState ThreadState = SingleOpcode(Opcode, X1: test.W1, X2: test.W2); - Assert.AreEqual(test.Result, ThreadState.X0); - Assert.AreEqual(test.Negative, ThreadState.Negative); - Assert.AreEqual(test.Zero, ThreadState.Zero); - } + Assert.AreEqual(Result, ThreadState.X0); + Assert.AreEqual(Negative, ThreadState.Negative); + Assert.AreEqual(Zero, ThreadState.Zero); + }); } - [Test] - public void OrrBitmasks() + [TestCase(0x0000FF44u, 0x00000004u, 0x00000FF4u)] + [TestCase(0x00000000u, 0x00000004u, 0x00000000u)] + [TestCase(0x0000FF44u, 0x00000008u, 0x000000FFu)] + [TestCase(0xFFFFFFFFu, 0x00000004u, 0xFFFFFFFFu)] + [TestCase(0xFFFFFFFFu, 0x00000008u, 0xFFFFFFFFu)] + [TestCase(0xFFFFFFFFu, 0x00000020u, 0xFFFFFFFFu)] + [TestCase(0x0FFFFFFFu, 0x0000001Cu, 0x00000000u)] + [TestCase(0x80000000u, 0x0000001Fu, 0xFFFFFFFFu)] + [TestCase(0xCAFE0000u, 0x00000020u, 0xCAFE0000u)] + public void Asrv32(uint A, uint ShiftValue, uint Result) { - // ORR W0, WZR, #0x01010101 - Assert.AreEqual(0x01010101, SingleOpcode(0x3200C3E0).X0); - // ORR W1, WZR, #0x00F000F0 - Assert.AreEqual(0x00F000F0, SingleOpcode(0x320C8FE1).X1); - // ORR W2, WZR, #1 - Assert.AreEqual(0x00000001, SingleOpcode(0x320003E2).X2); + // ASRV W0, W1, W2 + AThreadState ThreadState = SingleOpcode(0x1AC22820, X1: A, X2: ShiftValue); + Assert.AreEqual(Result, ThreadState.X0); + } + + [TestCase(0x000000000000FF44ul, 0x00000004u, 0x0000000000000FF4ul)] + [TestCase(0x0000000000000000ul, 0x00000004u, 0x0000000000000000ul)] + [TestCase(0x000000000000FF44ul, 0x00000008u, 0x00000000000000FFul)] + [TestCase(0x00000000FFFFFFFFul, 0x00000004u, 0x000000000FFFFFFFul)] + [TestCase(0x00000000FFFFFFFFul, 0x00000008u, 0x0000000000FFFFFFul)] + [TestCase(0x00000000FFFFFFFFul, 0x00000020u, 0x0000000000000000ul)] + [TestCase(0x000000000FFFFFFFul, 0x0000001Cu, 0x0000000000000000ul)] + [TestCase(0x000CC4488FFFFFFFul, 0x0000001Cu, 0x0000000000CC4488ul)] + [TestCase(0xFFFFFFFFFFFFFFFFul, 0x0000001Cu, 0xFFFFFFFFFFFFFFFFul)] + [TestCase(0x8000000000000000ul, 0x0000003Fu, 0xFFFFFFFFFFFFFFFFul)] + [TestCase(0xCAFE000000000000ul, 0x00000040u, 0xCAFE000000000000ul)] + public void Asrv64(ulong A, uint ShiftValue, ulong Result) + { + // ASRV X0, X1, X2 + AThreadState ThreadState = SingleOpcode(0x9AC22820, X1: A, X2: ShiftValue); + Assert.AreEqual(Result, ThreadState.X0); + } + + [TestCase(0x01010101u, 0x3200C3E2u)] + [TestCase(0x00F000F0u, 0x320C8FE2u)] + [TestCase(0x00000001u, 0x320003E2u)] + public void OrrBitmasks(uint Bitmask, uint Opcode) + { + // ORR W2, WZR, #Bitmask + Assert.AreEqual(Bitmask, SingleOpcode(Opcode).X2); } [Test] @@ -61,5 +138,25 @@ namespace Ryujinx.Tests.Cpu AThreadState ThreadState = SingleOpcode(0x5AC00821, X1: 0x12345678); Assert.AreEqual(0x78563412, ThreadState.X1); } + + [TestCase(0x7A020020u, 4u, 2u, false, false, false, true, 1u)] + [TestCase(0x7A020020u, 4u, 2u, true, false, false, true, 2u)] + [TestCase(0xFA020020u, 4u, 2u, false, false, false, true, 1u)] + [TestCase(0xFA020020u, 4u, 2u, true, false, false, true, 2u)] + [TestCase(0x7A020020u, 4u, 4u, false, true, false, false, 0xFFFFFFFFu)] + [TestCase(0x7A020020u, 4u, 4u, true, false, true, true, 0x0u)] + public void Sbcs(uint Opcode, uint A, uint B, bool CarryState, bool Negative, bool Zero, bool Carry, uint Result) + { + //SBCS (X0/W0), (X1, W1), (X2/W2) + AThreadState ThreadState = SingleOpcode(Opcode, X1: A, X2: B, Carry: CarryState); + Assert.Multiple(() => + { + Assert.IsFalse(ThreadState.Overflow); + Assert.AreEqual(Negative, ThreadState.Negative); + Assert.AreEqual(Zero, ThreadState.Zero); + Assert.AreEqual(Carry, ThreadState.Carry); + Assert.AreEqual(Result, ThreadState.X0); + }); + } } } diff --git a/Ryujinx.Tests/Cpu/CpuTestMisc.cs b/Ryujinx.Tests/Cpu/CpuTestMisc.cs new file mode 100644 index 000000000..3a995fe26 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestMisc.cs @@ -0,0 +1,279 @@ +using ChocolArm64.State; +using NUnit.Framework; + +namespace Ryujinx.Tests.Cpu +{ + public class CpuTestMisc : CpuTest + { + [TestCase(0ul)] + [TestCase(1ul)] + [TestCase(2ul)] + [TestCase(42ul)] + public void SanityCheck(ulong A) + { + // NOP + uint Opcode = 0xD503201F; + AThreadState ThreadState = SingleOpcode(Opcode, X0: A); + Assert.AreEqual(A, ThreadState.X0); + } + + [TestCase(0xFFFFFFFDu)] // Roots. + [TestCase(0x00000005u)] + public void Misc1(uint A) + { + // ((A + 3) * (A - 5)) / ((A + 5) * (A - 3)) = 0 + + /* + ADD W2, W0, 3 + SUB W1, W0, #5 + MUL W2, W2, W1 + ADD W1, W0, 5 + SUB W0, W0, #3 + MUL W0, W1, W0 + SDIV W0, W2, W0 + BRK #0 + RET + */ + + SetThreadState(X0: A); + Opcode(0x11000C02); + Opcode(0x51001401); + Opcode(0x1B017C42); + Opcode(0x11001401); + Opcode(0x51000C00); + Opcode(0x1B007C20); + Opcode(0x1AC00C40); + Opcode(0xD4200000); + Opcode(0xD65F03C0); + ExecuteOpcodes(); + Assert.AreEqual(0, GetThreadState().X0); + } + + [TestCase(-20f, -5f)] // 18 integer solutions. + [TestCase(-12f, -6f)] + [TestCase(-12f, 3f)] + [TestCase(-8f, -8f)] + [TestCase(-6f, -12f)] + [TestCase(-5f, -20f)] + [TestCase(-4f, 2f)] + [TestCase(-3f, 12f)] + [TestCase(-2f, 4f)] + [TestCase(2f, -4f)] + [TestCase(3f, -12f)] + [TestCase(4f, -2f)] + [TestCase(5f, 20f)] + [TestCase(6f, 12f)] + [TestCase(8f, 8f)] + [TestCase(12f, -3f)] + [TestCase(12f, 6f)] + [TestCase(20f, 5f)] + public void Misc2(float A, float B) + { + // 1 / ((1 / A + 1 / B) ^ 2) = 16 + + /* + FMOV S2, 1.0e+0 + FDIV S0, S2, S0 + FDIV S1, S2, S1 + FADD S0, S0, S1 + FDIV S0, S2, S0 + FMUL S0, S0, S0 + BRK #0 + RET + */ + + SetThreadState(V0: new AVec { S0 = A }, V1: new AVec { S0 = B }); + Opcode(0x1E2E1002); + Opcode(0x1E201840); + Opcode(0x1E211841); + Opcode(0x1E212800); + Opcode(0x1E201840); + Opcode(0x1E200800); + Opcode(0xD4200000); + Opcode(0xD65F03C0); + ExecuteOpcodes(); + Assert.AreEqual(16f, GetThreadState().V0.S0); + } + + [TestCase(-20d, -5d)] // 18 integer solutions. + [TestCase(-12d, -6d)] + [TestCase(-12d, 3d)] + [TestCase(-8d, -8d)] + [TestCase(-6d, -12d)] + [TestCase(-5d, -20d)] + [TestCase(-4d, 2d)] + [TestCase(-3d, 12d)] + [TestCase(-2d, 4d)] + [TestCase(2d, -4d)] + [TestCase(3d, -12d)] + [TestCase(4d, -2d)] + [TestCase(5d, 20d)] + [TestCase(6d, 12d)] + [TestCase(8d, 8d)] + [TestCase(12d, -3d)] + [TestCase(12d, 6d)] + [TestCase(20d, 5d)] + public void Misc3(double A, double B) + { + // 1 / ((1 / A + 1 / B) ^ 2) = 16 + + /* + FMOV D2, 1.0e+0 + FDIV D0, D2, D0 + FDIV D1, D2, D1 + FADD D0, D0, D1 + FDIV D0, D2, D0 + FMUL D0, D0, D0 + BRK #0 + RET + */ + + SetThreadState(V0: new AVec { D0 = A }, V1: new AVec { D0 = B }); + Opcode(0x1E6E1002); + Opcode(0x1E601840); + Opcode(0x1E611841); + Opcode(0x1E612800); + Opcode(0x1E601840); + Opcode(0x1E600800); + Opcode(0xD4200000); + Opcode(0xD65F03C0); + ExecuteOpcodes(); + Assert.AreEqual(16d, GetThreadState().V0.D0); + } + + [Test] + public void MiscR() + { + ulong Result = 5; + + /* + 0x0000000000000000: MOV X0, #2 + 0x0000000000000004: MOV X1, #3 + 0x0000000000000008: ADD X0, X0, X1 + 0x000000000000000C: BRK #0 + 0x0000000000000010: RET + */ + + Opcode(0xD2800040); + Opcode(0xD2800061); + Opcode(0x8B010000); + Opcode(0xD4200000); + Opcode(0xD65F03C0); + ExecuteOpcodes(); + Assert.AreEqual(Result, GetThreadState().X0); + + Reset(); + + /* + 0x0000000000000000: MOV X0, #3 + 0x0000000000000004: MOV X1, #2 + 0x0000000000000008: ADD X0, X0, X1 + 0x000000000000000C: BRK #0 + 0x0000000000000010: RET + */ + + Opcode(0xD2800060); + Opcode(0xD2800041); + Opcode(0x8B010000); + Opcode(0xD4200000); + Opcode(0xD65F03C0); + ExecuteOpcodes(); + Assert.AreEqual(Result, GetThreadState().X0); + } + + [Test, Explicit] + public void Misc5() + { + /* + 0x0000000000000000: SUBS X0, X0, #1 + 0x0000000000000004: B.NE #0 + 0x0000000000000008: BRK #0 + 0x000000000000000C: RET + */ + + SetThreadState(X0: 0x100000000); + Opcode(0xF1000400); + Opcode(0x54FFFFE1); + Opcode(0xD4200000); + Opcode(0xD65F03C0); + ExecuteOpcodes(); + Assert.Multiple(() => + { + Assert.AreEqual(0, GetThreadState().X0); + Assert.IsTrue(GetThreadState().Zero); + }); + } + + [Test] + public void MiscF([Range(0, 92, 1)] int A) + { + ulong F_n(uint n) + { + ulong a = 0, b = 1, c; + + if (n == 0) + { + return a; + } + + for (uint i = 2; i <= n; i++) + { + c = a + b; + a = b; + b = c; + } + + return b; + } + + /* + 0x0000000000000000: MOV W4, W0 + 0x0000000000000004: CBZ W0, #0x3C + 0x0000000000000008: CMP W0, #1 + 0x000000000000000C: B.LS #0x48 + 0x0000000000000010: MOVZ W2, #0x2 + 0x0000000000000014: MOVZ X1, #0x1 + 0x0000000000000018: MOVZ X3, #0 + 0x000000000000001C: ADD X0, X3, X1 + 0x0000000000000020: ADD W2, W2, #1 + 0x0000000000000024: MOV X3, X1 + 0x0000000000000028: MOV X1, X0 + 0x000000000000002C: CMP W4, W2 + 0x0000000000000030: B.HS #0x1C + 0x0000000000000034: BRK #0 + 0x0000000000000038: RET + 0x000000000000003C: MOVZ X0, #0 + 0x0000000000000040: BRK #0 + 0x0000000000000044: RET + 0x0000000000000048: MOVZ X0, #0x1 + 0x000000000000004C: BRK #0 + 0x0000000000000050: RET + */ + + SetThreadState(X0: (uint)A); + Opcode(0x2A0003E4); + Opcode(0x340001C0); + Opcode(0x7100041F); + Opcode(0x540001E9); + Opcode(0x52800042); + Opcode(0xD2800021); + Opcode(0xD2800003); + Opcode(0x8B010060); + Opcode(0x11000442); + Opcode(0xAA0103E3); + Opcode(0xAA0003E1); + Opcode(0x6B02009F); + Opcode(0x54FFFF62); + Opcode(0xD4200000); + Opcode(0xD65F03C0); + Opcode(0xD2800000); + Opcode(0xD4200000); + Opcode(0xD65F03C0); + Opcode(0xD2800020); + Opcode(0xD4200000); + Opcode(0xD65F03C0); + ExecuteOpcodes(); + Assert.AreEqual(F_n((uint)A), GetThreadState().X0); + } + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTestScalar.cs b/Ryujinx.Tests/Cpu/CpuTestScalar.cs index 26549b872..a178be272 100644 --- a/Ryujinx.Tests/Cpu/CpuTestScalar.cs +++ b/Ryujinx.Tests/Cpu/CpuTestScalar.cs @@ -3,8 +3,7 @@ using NUnit.Framework; namespace Ryujinx.Tests.Cpu { - [TestFixture] - public partial class CpuTest + public class CpuTestScalar : CpuTest { [TestCase(0x00000000u, 0x80000000u, 0x00000000u)] [TestCase(0x80000000u, 0x00000000u, 0x00000000u)] @@ -15,10 +14,9 @@ namespace Ryujinx.Tests.Cpu [TestCase(0x7F7FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu)] [TestCase(0x7FC00000u, 0x3F800000u, 0x7FC00000u)] [TestCase(0x3F800000u, 0x7FC00000u, 0x7FC00000u)] - // NaN tests - //[TestCase(0x7F800001u, 0x7FC00042u, 0x7FC00001u)] - //[TestCase(0x7FC00042u, 0x7F800001u, 0x7FC00001u)] - //[TestCase(0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Au)] + [TestCase(0x7F800001u, 0x7FC00042u, 0x7FC00001u)] + [TestCase(0x7FC00042u, 0x7F800001u, 0x7FC00001u)] + [TestCase(0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Au)] public void Fmax_S(uint A, uint B, uint Result) { // FMAX S0, S1, S2 diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs new file mode 100644 index 000000000..56aaef488 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs @@ -0,0 +1,78 @@ +using ChocolArm64.State; +using NUnit.Framework; + +namespace Ryujinx.Tests.Cpu +{ + public class CpuTestSimdArithmetic : CpuTest + { + [TestCase(0x3FE66666u, 'N', false, 0x40000000u)] + [TestCase(0x3F99999Au, 'N', false, 0x3F800000u)] + [TestCase(0x404CCCCDu, 'P', false, 0x40800000u)] + [TestCase(0x40733333u, 'P', false, 0x40800000u)] + [TestCase(0x404CCCCDu, 'M', false, 0x40400000u)] + [TestCase(0x40733333u, 'M', false, 0x40400000u)] + [TestCase(0x3F99999Au, 'Z', false, 0x3F800000u)] + [TestCase(0x3FE66666u, 'Z', false, 0x3F800000u)] + [TestCase(0x00000000u, 'N', false, 0x00000000u)] + [TestCase(0x00000000u, 'P', false, 0x00000000u)] + [TestCase(0x00000000u, 'M', false, 0x00000000u)] + [TestCase(0x00000000u, 'Z', false, 0x00000000u)] + [TestCase(0x80000000u, 'N', false, 0x80000000u)] + [TestCase(0x80000000u, 'P', false, 0x80000000u)] + [TestCase(0x80000000u, 'M', false, 0x80000000u)] + [TestCase(0x80000000u, 'Z', false, 0x80000000u)] + [TestCase(0x7F800000u, 'N', false, 0x7F800000u)] + [TestCase(0x7F800000u, 'P', false, 0x7F800000u)] + [TestCase(0x7F800000u, 'M', false, 0x7F800000u)] + [TestCase(0x7F800000u, 'Z', false, 0x7F800000u)] + [TestCase(0xFF800000u, 'N', false, 0xFF800000u)] + [TestCase(0xFF800000u, 'P', false, 0xFF800000u)] + [TestCase(0xFF800000u, 'M', false, 0xFF800000u)] + [TestCase(0xFF800000u, 'Z', false, 0xFF800000u)] + [TestCase(0xFF800001u, 'N', false, 0xFFC00001u)] + [TestCase(0xFF800001u, 'P', false, 0xFFC00001u)] + [TestCase(0xFF800001u, 'M', false, 0xFFC00001u)] + [TestCase(0xFF800001u, 'Z', false, 0xFFC00001u)] + [TestCase(0xFF800001u, 'N', true, 0x7FC00000u)] + [TestCase(0xFF800001u, 'P', true, 0x7FC00000u)] + [TestCase(0xFF800001u, 'M', true, 0x7FC00000u)] + [TestCase(0xFF800001u, 'Z', true, 0x7FC00000u)] + [TestCase(0x7FC00002u, 'N', false, 0x7FC00002u)] + [TestCase(0x7FC00002u, 'P', false, 0x7FC00002u)] + [TestCase(0x7FC00002u, 'M', false, 0x7FC00002u)] + [TestCase(0x7FC00002u, 'Z', false, 0x7FC00002u)] + [TestCase(0x7FC00002u, 'N', true, 0x7FC00000u)] + [TestCase(0x7FC00002u, 'P', true, 0x7FC00000u)] + [TestCase(0x7FC00002u, 'M', true, 0x7FC00000u)] + [TestCase(0x7FC00002u, 'Z', true, 0x7FC00000u)] + public void Frintx_S(uint A, char RoundType, bool DefaultNaN, uint Result) + { + int FpcrTemp = 0x0; + switch(RoundType) + { + case 'N': + FpcrTemp = 0x0; + break; + + case 'P': + FpcrTemp = 0x400000; + break; + + case 'M': + FpcrTemp = 0x800000; + break; + + case 'Z': + FpcrTemp = 0xC00000; + break; + } + if(DefaultNaN) + { + FpcrTemp |= 1 << 25; + } + AVec V1 = new AVec { X0 = A }; + AThreadState ThreadState = SingleOpcode(0x1E274020, V1: V1, Fpcr: FpcrTemp); + Assert.AreEqual(Result, ThreadState.V0.X0); + } + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs b/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs new file mode 100644 index 000000000..372689d08 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdMove.cs @@ -0,0 +1,44 @@ +using ChocolArm64.State; +using NUnit.Framework; + +namespace Ryujinx.Tests.Cpu +{ + public class CpuTestSimdMove : CpuTest + { + [TestCase(0u, 0u, 0x2313221221112010ul, 0x0000000000000000ul)] + [TestCase(1u, 0u, 0x2313221221112010ul, 0x2717261625152414ul)] + [TestCase(0u, 1u, 0x2322131221201110ul, 0x0000000000000000ul)] + [TestCase(1u, 1u, 0x2322131221201110ul, 0x2726171625241514ul)] + [TestCase(0u, 2u, 0x2322212013121110ul, 0x0000000000000000ul)] + [TestCase(1u, 2u, 0x2322212013121110ul, 0x2726252417161514ul)] + [TestCase(1u, 3u, 0x1716151413121110ul, 0x2726252423222120ul)] + public void Zip1_V(uint Q, uint size, ulong Result_0, ulong Result_1) + { + // ZIP1 V0., V1., V2. + uint Opcode = 0x0E023820 | (Q << 30) | (size << 22); + AVec V1 = new AVec { X0 = 0x1716151413121110, X1 = 0x1F1E1D1C1B1A1918 }; + AVec V2 = new AVec { X0 = 0x2726252423222120, X1 = 0x2F2E2D2C2B2A2928 }; + AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Assert.AreEqual(Result_0, ThreadState.V0.X0); + Assert.AreEqual(Result_1, ThreadState.V0.X1); + } + + [TestCase(0u, 0u, 0x2717261625152414ul, 0x0000000000000000ul)] + [TestCase(1u, 0u, 0x2B1B2A1A29192818ul, 0x2F1F2E1E2D1D2C1Cul)] + [TestCase(0u, 1u, 0x2726171625241514ul, 0x0000000000000000ul)] + [TestCase(1u, 1u, 0x2B2A1B1A29281918ul, 0x2F2E1F1E2D2C1D1Cul)] + [TestCase(0u, 2u, 0x2726252417161514ul, 0x0000000000000000ul)] + [TestCase(1u, 2u, 0x2B2A29281B1A1918ul, 0x2F2E2D2C1F1E1D1Cul)] + [TestCase(1u, 3u, 0x1F1E1D1C1B1A1918ul, 0x2F2E2D2C2B2A2928ul)] + public void Zip2_V(uint Q, uint size, ulong Result_0, ulong Result_1) + { + // ZIP2 V0., V1., V2. + uint Opcode = 0x0E027820 | (Q << 30) | (size << 22); + AVec V1 = new AVec { X0 = 0x1716151413121110, X1 = 0x1F1E1D1C1B1A1918 }; + AVec V2 = new AVec { X0 = 0x2726252423222120, X1 = 0x2F2E2D2C2B2A2928 }; + AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); + Assert.AreEqual(Result_0, ThreadState.V0.X0); + Assert.AreEqual(Result_1, ThreadState.V0.X1); + } + } +} diff --git a/Ryujinx.Tests/Ryujinx.Tests.csproj b/Ryujinx.Tests/Ryujinx.Tests.csproj index f8b430e62..ae4ea6c33 100644 --- a/Ryujinx.Tests/Ryujinx.Tests.csproj +++ b/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -12,6 +12,6 @@ - + diff --git a/Ryujinx.sln b/Ryujinx.sln index 777539885..34a5f4887 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -1,11 +1,17 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26730.8 +VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx", "Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx", "Ryujinx\Ryujinx.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Tests", "Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Core", "Ryujinx.Core\Ryujinx.Core.csproj", "{CB92CFF9-1D62-4D4F-9E88-8130EF61E351}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChocolArm64", "ChocolArm64\ChocolArm64.csproj", "{2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics", "Ryujinx.Graphics\Ryujinx.Graphics.csproj", "{EAAE36AF-7781-4578-A7E0-F0EFD2025569}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +27,18 @@ Global {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.Build.0 = Debug|Any CPU {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.ActiveCfg = Release|Any CPU {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.Build.0 = Release|Any CPU + {CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|Any CPU.Build.0 = Release|Any CPU + {2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}.Release|Any CPU.Build.0 = Release|Any CPU + {EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Ryujinx/Cpu/AOptimizations.cs b/Ryujinx/Cpu/AOptimizations.cs deleted file mode 100644 index cbfd1ce51..000000000 --- a/Ryujinx/Cpu/AOptimizations.cs +++ /dev/null @@ -1,4 +0,0 @@ -public static class AOptimizations -{ - -} \ No newline at end of file diff --git a/Ryujinx/Cpu/ATranslatedSub.cs b/Ryujinx/Cpu/ATranslatedSub.cs deleted file mode 100644 index 71a6793a2..000000000 --- a/Ryujinx/Cpu/ATranslatedSub.cs +++ /dev/null @@ -1,104 +0,0 @@ -using ChocolArm64.Memory; -using ChocolArm64.State; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Reflection.Emit; - -namespace ChocolArm64 -{ - class ATranslatedSub - { - private delegate long AA64Subroutine(AThreadState Register, AMemory Memory); - - private AA64Subroutine ExecDelegate; - - private bool HasDelegate; - - public static Type[] FixedArgTypes { get; private set; } - - public static int StateArgIdx { get; private set; } - public static int MemoryArgIdx { get; private set; } - - public DynamicMethod Method { get; private set; } - - public HashSet SubCalls { get; private set; } - - public List Params { get; private set; } - - public bool NeedsReJit { get; private set; } - - public ATranslatedSub() - { - SubCalls = new HashSet(); - } - - public ATranslatedSub(DynamicMethod Method, List Params) : this() - { - if (Params == null) - { - throw new ArgumentNullException(nameof(Params)); - } - - this.Method = Method; - this.Params = Params; - } - - static ATranslatedSub() - { - MethodInfo MthdInfo = typeof(AA64Subroutine).GetMethod("Invoke"); - - ParameterInfo[] Params = MthdInfo.GetParameters(); - - FixedArgTypes = new Type[Params.Length]; - - for (int Index = 0; Index < Params.Length; Index++) - { - Type ParamType = Params[Index].ParameterType; - - FixedArgTypes[Index] = ParamType; - - if (ParamType == typeof(AThreadState)) - { - StateArgIdx = Index; - } - else if (ParamType == typeof(AMemory)) - { - MemoryArgIdx = Index; - } - } - } - - public long Execute(AThreadState ThreadState, AMemory Memory) - { - if (!HasDelegate) - { - string Name = $"{Method.Name}_Dispatch"; - - DynamicMethod Mthd = new DynamicMethod(Name, typeof(long), FixedArgTypes); - - ILGenerator Generator = Mthd.GetILGenerator(); - - Generator.EmitLdargSeq(FixedArgTypes.Length); - - foreach (ARegister Reg in Params) - { - Generator.EmitLdarg(StateArgIdx); - - Generator.Emit(OpCodes.Ldfld, Reg.GetField()); - } - - Generator.Emit(OpCodes.Call, Method); - Generator.Emit(OpCodes.Ret); - - ExecDelegate = (AA64Subroutine)Mthd.CreateDelegate(typeof(AA64Subroutine)); - - HasDelegate = true; - } - - return ExecDelegate(ThreadState, Memory); - } - - public void MarkForReJit() => NeedsReJit = true; - } -} \ No newline at end of file diff --git a/Ryujinx/Cpu/ATranslator.cs b/Ryujinx/Cpu/ATranslator.cs deleted file mode 100644 index 96bbc89ea..000000000 --- a/Ryujinx/Cpu/ATranslator.cs +++ /dev/null @@ -1,106 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Instruction; -using ChocolArm64.Translation; -using System.Collections.Generic; -using System.Reflection.Emit; - -namespace ChocolArm64 -{ - class ATranslator - { - public AThread Thread { get; private set; } - - private Dictionary CachedSubs; - - private bool KeepRunning; - - public ATranslator(AThread Parent) - { - this.Thread = Parent; - - CachedSubs = new Dictionary(); - - KeepRunning = true; - } - - public void StopExecution() => KeepRunning = false; - - public void ExecuteSubroutine(long Position) - { - do - { - if (CachedSubs.TryGetValue(Position, out ATranslatedSub Sub) && !Sub.NeedsReJit) - { - Position = Sub.Execute(Thread.ThreadState, Thread.Memory); - } - else - { - Position = TranslateSubroutine(Position).Execute(Thread.ThreadState, Thread.Memory); - } - } - while (Position != 0 && KeepRunning); - } - - public bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub) - { - if (OpCode.Emitter != AInstEmit.Bl) - { - Sub = null; - - return false; - } - - return TryGetCachedSub(((AOpCodeBImmAl)OpCode).Imm, out Sub); - } - - public bool TryGetCachedSub(long Position, out ATranslatedSub Sub) - { - return CachedSubs.TryGetValue(Position, out Sub); - } - - public bool HasCachedSub(long Position) - { - return CachedSubs.ContainsKey(Position); - } - - private ATranslatedSub TranslateSubroutine(long Position) - { - (ABlock[] Graph, ABlock Root) Cfg = ADecoder.DecodeSubroutine(this, Position); - - AILEmitterCtx Context = new AILEmitterCtx( - this, - Cfg.Graph, - Cfg.Root); - - if (Context.CurrBlock.Position != Position) - { - Context.Emit(OpCodes.Br, Context.GetLabel(Position)); - } - - do - { - Context.EmitOpCode(); - } - while (Context.AdvanceOpCode()); - - //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 (TS.SubCalls.Contains(Position)) - { - TS.MarkForReJit(); - } - } - - ATranslatedSub Subroutine = Context.GetSubroutine(); - - if (!CachedSubs.TryAdd(Position, Subroutine)) - { - CachedSubs[Position] = Subroutine; - } - - return Subroutine; - } - } -} \ No newline at end of file diff --git a/Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs b/Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs deleted file mode 100644 index d5a0051b5..000000000 --- a/Ryujinx/Cpu/Instruction/AInstEmitMemoryHelper.cs +++ /dev/null @@ -1,138 +0,0 @@ -using ChocolArm64.Decoder; -using ChocolArm64.Memory; -using ChocolArm64.Translation; -using System; -using System.Reflection.Emit; - -namespace ChocolArm64.Instruction -{ - static class AInstEmitMemoryHelper - { - private enum Extension - { - Zx, - Sx32, - Sx64 - } - - public static void EmitReadZxCall(AILEmitterCtx Context, int Size) - { - EmitReadCall(Context, Extension.Zx, Size); - } - - public static void EmitReadSx32Call(AILEmitterCtx Context, int Size) - { - EmitReadCall(Context, Extension.Sx32, Size); - } - - public static void EmitReadSx64Call(AILEmitterCtx Context, int Size) - { - EmitReadCall(Context, Extension.Sx64, Size); - } - - private static void EmitReadCall(AILEmitterCtx Context, Extension Ext, int Size) - { - bool IsSimd = GetIsSimd(Context); - - string Name = null; - - if (Size < 0 || Size > (IsSimd ? 4 : 3)) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - if (IsSimd) - { - switch (Size) - { - case 0: Name = nameof(AMemory.ReadVector8); break; - case 1: Name = nameof(AMemory.ReadVector16); break; - case 2: Name = nameof(AMemory.ReadVector32); break; - case 3: Name = nameof(AMemory.ReadVector64); break; - case 4: Name = nameof(AMemory.ReadVector128); break; - } - } - else - { - switch (Size) - { - case 0: Name = nameof(AMemory.ReadByte); break; - case 1: Name = nameof(AMemory.ReadUInt16); break; - case 2: Name = nameof(AMemory.ReadUInt32); break; - case 3: Name = nameof(AMemory.ReadUInt64); break; - } - } - - Context.EmitCall(typeof(AMemory), Name); - - if (!IsSimd) - { - if (Ext == Extension.Sx32 || - Ext == Extension.Sx64) - { - switch (Size) - { - case 0: Context.Emit(OpCodes.Conv_I1); break; - case 1: Context.Emit(OpCodes.Conv_I2); break; - case 2: Context.Emit(OpCodes.Conv_I4); break; - } - } - - if (Size < 3) - { - Context.Emit(Ext == Extension.Sx64 - ? OpCodes.Conv_I8 - : OpCodes.Conv_U8); - } - } - } - - public static void EmitWriteCall(AILEmitterCtx Context, int Size) - { - bool IsSimd = GetIsSimd(Context); - - string Name = null; - - if (Size < 0 || Size > (IsSimd ? 4 : 3)) - { - throw new ArgumentOutOfRangeException(nameof(Size)); - } - - if (Size < 3 && !IsSimd) - { - Context.Emit(OpCodes.Conv_I4); - } - - if (IsSimd) - { - switch (Size) - { - case 0: Name = nameof(AMemory.WriteVector8); break; - case 1: Name = nameof(AMemory.WriteVector16); break; - case 2: Name = nameof(AMemory.WriteVector32); break; - case 3: Name = nameof(AMemory.WriteVector64); break; - case 4: Name = nameof(AMemory.WriteVector128); break; - } - } - else - { - switch (Size) - { - case 0: Name = nameof(AMemory.WriteByte); break; - case 1: Name = nameof(AMemory.WriteUInt16); break; - case 2: Name = nameof(AMemory.WriteUInt32); break; - case 3: Name = nameof(AMemory.WriteUInt64); break; - } - } - - Context.EmitCall(typeof(AMemory), Name); - } - - private static bool GetIsSimd(AILEmitterCtx Context) - { - return Context.CurrOp is IAOpCodeSimd && - !(Context.CurrOp is AOpCodeSimdMemMs || - Context.CurrOp is AOpCodeSimdMemSs); - } - } -} \ No newline at end of file diff --git a/Ryujinx/Cpu/Memory/AMemoryAlloc.cs b/Ryujinx/Cpu/Memory/AMemoryAlloc.cs deleted file mode 100644 index b11e77931..000000000 --- a/Ryujinx/Cpu/Memory/AMemoryAlloc.cs +++ /dev/null @@ -1,35 +0,0 @@ -using ChocolArm64.Exceptions; - -namespace ChocolArm64.Memory -{ - public class AMemoryAlloc - { - private long PhysPos; - - public long Alloc(long Size) - { - long Position = PhysPos; - - Size = AMemoryHelper.PageRoundUp(Size); - - PhysPos += Size; - - if (PhysPos > AMemoryMgr.RamSize || PhysPos < 0) - { - throw new VmmOutOfMemoryException(Size); - } - - return Position; - } - - public void Free(long Position) - { - //TODO - } - - public long GetFreeMem() - { - return AMemoryMgr.RamSize - PhysPos; - } - } -} \ No newline at end of file diff --git a/Ryujinx/Cpu/State/AInstExceptEventArgs.cs b/Ryujinx/Cpu/State/AInstExceptEventArgs.cs deleted file mode 100644 index f2ee039b6..000000000 --- a/Ryujinx/Cpu/State/AInstExceptEventArgs.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace ChocolArm64.State -{ - public class AInstExceptEventArgs : EventArgs - { - public int Id { get; private set; } - - public AInstExceptEventArgs(int Id) - { - this.Id = Id; - } - } -} \ No newline at end of file diff --git a/Ryujinx/Gal/IGalRenderer.cs b/Ryujinx/Gal/IGalRenderer.cs deleted file mode 100644 index 306d0d511..000000000 --- a/Ryujinx/Gal/IGalRenderer.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Gal -{ - public interface IGalRenderer - { - long FrameBufferPtr { get; set; } - - void QueueAction(Action ActionMthd); - void RunActions(); - - void Render(); - void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs); - void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height); - void BindTexture(int Index); - } -} \ No newline at end of file diff --git a/Ryujinx/Gpu/NsGpu.cs b/Ryujinx/Gpu/NsGpu.cs deleted file mode 100644 index 6aa7332cd..000000000 --- a/Ryujinx/Gpu/NsGpu.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Gal; - -namespace Ryujinx.Gpu -{ - class NsGpu - { - public IGalRenderer Renderer { get; private set; } - - public NsGpuMemoryMgr MemoryMgr { get; private set; } - - public NsGpuPGraph PGraph { get; private set; } - - public NsGpu(IGalRenderer Renderer) - { - this.Renderer = Renderer; - - MemoryMgr = new NsGpuMemoryMgr(); - - PGraph = new NsGpuPGraph(this); - } - } -} \ No newline at end of file diff --git a/Ryujinx/Hid.cs b/Ryujinx/Hid.cs deleted file mode 100644 index c344ec587..000000000 --- a/Ryujinx/Hid.cs +++ /dev/null @@ -1,185 +0,0 @@ -using Ryujinx.OsHle; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx -{ - public class Hid - { - /* - Thanks to: - https://github.com/reswitched/libtransistor/blob/development/lib/hid.c - https://github.com/reswitched/libtransistor/blob/development/include/libtransistor/hid.h - https://github.com/switchbrew/libnx/blob/master/nx/source/services/hid.c - https://github.com/switchbrew/libnx/blob/master/nx/include/switch/services/hid.h - - struct HidSharedMemory - { - header[0x400]; - touchscreen[0x3000]; - mouse[0x400]; - keyboard[0x400]; - unkSection1[0x400]; - unkSection2[0x400]; - unkSection3[0x400]; - unkSection4[0x400]; - unkSection5[0x200]; - unkSection6[0x200]; - unkSection7[0x200]; - unkSection8[0x800]; - controllerSerials[0x4000]; - controllers[0x5000 * 10]; - unkSection9[0x4600]; - } - */ - - private const int Hid_Num_Entries = 16; - private Switch Ns; - private long SharedMemOffset; - - public Hid(Switch Ns) - { - this.Ns = Ns; - } - - public void Init(long HidOffset) - { - unsafe - { - if (HidOffset == 0 || HidOffset + Horizon.HidSize > uint.MaxValue) - { - return; - } - - SharedMemOffset = HidOffset; - - uint InnerOffset = (uint)Marshal.SizeOf(typeof(HidSharedMemHeader)); - - IntPtr HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); - - HidTouchScreen TouchScreen = new HidTouchScreen(); - TouchScreen.Header.TimestampTicks = (ulong)Environment.TickCount; - TouchScreen.Header.NumEntries = (ulong)Hid_Num_Entries; - TouchScreen.Header.LatestEntry = 0; - TouchScreen.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1; - TouchScreen.Header.Timestamp = (ulong)Environment.TickCount; - - //TODO: Write this structure when the input is implemented - //Marshal.StructureToPtr(TouchScreen, HidPtr, false); - - InnerOffset += (uint)Marshal.SizeOf(typeof(HidTouchScreen)); - HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); - - HidMouse Mouse = new HidMouse(); - Mouse.Header.TimestampTicks = (ulong)Environment.TickCount; - Mouse.Header.NumEntries = (ulong)Hid_Num_Entries; - Mouse.Header.LatestEntry = 0; - Mouse.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1; - - //TODO: Write this structure when the input is implemented - //Marshal.StructureToPtr(Mouse, HidPtr, false); - - InnerOffset += (uint)Marshal.SizeOf(typeof(HidMouse)); - HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); - - HidKeyboard Keyboard = new HidKeyboard(); - Keyboard.Header.TimestampTicks = (ulong)Environment.TickCount; - Keyboard.Header.NumEntries = (ulong)Hid_Num_Entries; - Keyboard.Header.LatestEntry = 0; - Keyboard.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1; - - //TODO: Write this structure when the input is implemented - //Marshal.StructureToPtr(Keyboard, HidPtr, false); - - InnerOffset += (uint)Marshal.SizeOf(typeof(HidKeyboard)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection1)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection2)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection3)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection4)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection5)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection6)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection7)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection8)) + - (uint)Marshal.SizeOf(typeof(HidControllerSerials)); - - //Increase the loop to initialize more controller. - for (int i = 8; i < Enum.GetNames(typeof(HidControllerID)).Length - 1; i++) - { - HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset + (uint)(Marshal.SizeOf(typeof(HidController)) * i)); - - HidController Controller = new HidController(); - Controller.Header.Type = (uint)(HidControllerType.ControllerType_Handheld | HidControllerType.ControllerType_JoyconPair); - Controller.Header.IsHalf = 0; - Controller.Header.SingleColorsDescriptor = (uint)(HidControllerColorDescription.ColorDesc_ColorsNonexistent); - Controller.Header.SingleColorBody = 0; - Controller.Header.SingleColorButtons = 0; - Controller.Header.SplitColorsDescriptor = 0; - Controller.Header.LeftColorBody = (uint)JoyConColor.Body_Neon_Red; - Controller.Header.LeftColorButtons = (uint)JoyConColor.Buttons_Neon_Red; - Controller.Header.RightColorBody = (uint)JoyConColor.Body_Neon_Blue; - Controller.Header.RightColorButtons = (uint)JoyConColor.Buttons_Neon_Blue; - - Controller.Layouts = new HidControllerLayout[Enum.GetNames(typeof(HidControllerLayouts)).Length]; - Controller.Layouts[(int)HidControllerLayouts.Main] = new HidControllerLayout(); - Controller.Layouts[(int)HidControllerLayouts.Main].Header.LatestEntry = (ulong)Hid_Num_Entries; - - Marshal.StructureToPtr(Controller, HidPtr, false); - } - - Logging.Info("HID Initialized!"); - } - } - - public void SendControllerButtons(HidControllerID ControllerId, - HidControllerLayouts Layout, - HidControllerKeys Buttons, - JoystickPosition LeftJoystick, - JoystickPosition RightJoystick) - { - uint InnerOffset = (uint)Marshal.SizeOf(typeof(HidSharedMemHeader)) + - (uint)Marshal.SizeOf(typeof(HidTouchScreen)) + - (uint)Marshal.SizeOf(typeof(HidMouse)) + - (uint)Marshal.SizeOf(typeof(HidKeyboard)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection1)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection2)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection3)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection4)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection5)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection6)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection7)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection8)) + - (uint)Marshal.SizeOf(typeof(HidControllerSerials)) + - ((uint)(Marshal.SizeOf(typeof(HidController)) * (int)ControllerId)) + - (uint)Marshal.SizeOf(typeof(HidControllerHeader)) + - (uint)Layout * (uint)Marshal.SizeOf(typeof(HidControllerLayout)); - - IntPtr HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); - - HidControllerLayoutHeader OldControllerHeaderLayout = (HidControllerLayoutHeader)Marshal.PtrToStructure(HidPtr, typeof(HidControllerLayoutHeader)); - - HidControllerLayoutHeader ControllerLayoutHeader = new HidControllerLayoutHeader - { - TimestampTicks = (ulong)Environment.TickCount, - NumEntries = (ulong)Hid_Num_Entries, - MaxEntryIndex = (ulong)Hid_Num_Entries - 1, - LatestEntry = (OldControllerHeaderLayout.LatestEntry < (ulong)Hid_Num_Entries ? OldControllerHeaderLayout.LatestEntry + 1 : 0) - }; - - Marshal.StructureToPtr(ControllerLayoutHeader, HidPtr, false); - - InnerOffset += (uint)Marshal.SizeOf(typeof(HidControllerLayoutHeader)) + (uint)((uint)(ControllerLayoutHeader.LatestEntry) * Marshal.SizeOf(typeof(HidControllerInputEntry))); - HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); - - HidControllerInputEntry ControllerInputEntry = new HidControllerInputEntry(); - ControllerInputEntry.Timestamp = (ulong)Environment.TickCount; - ControllerInputEntry.Timestamp_2 = (ulong)Environment.TickCount; - ControllerInputEntry.Buttons = (ulong)Buttons; - ControllerInputEntry.Joysticks = new JoystickPosition[(int)HidControllerJoystick.Joystick_Num_Sticks]; - ControllerInputEntry.Joysticks[(int)HidControllerJoystick.Joystick_Left] = LeftJoystick; - ControllerInputEntry.Joysticks[(int)HidControllerJoystick.Joystick_Right] = RightJoystick; - ControllerInputEntry.ConnectionState = (ulong)(HidControllerConnectionState.Controller_State_Connected | HidControllerConnectionState.Controller_State_Wired); - - Marshal.StructureToPtr(ControllerInputEntry, HidPtr, false); - } - } -} diff --git a/Ryujinx/Hid/HidController.cs b/Ryujinx/Hid/HidController.cs deleted file mode 100644 index 433d7b9ba..000000000 --- a/Ryujinx/Hid/HidController.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx -{ - public enum HidControllerKeys - { - KEY_A = (1 << 0), - KEY_B = (1 << 1), - KEY_X = (1 << 2), - KEY_Y = (1 << 3), - KEY_LSTICK = (1 << 4), - KEY_RSTICK = (1 << 5), - KEY_L = (1 << 6), - KEY_R = (1 << 7), - KEY_ZL = (1 << 8), - KEY_ZR = (1 << 9), - KEY_PLUS = (1 << 10), - KEY_MINUS = (1 << 11), - KEY_DLEFT = (1 << 12), - KEY_DUP = (1 << 13), - KEY_DRIGHT = (1 << 14), - KEY_DDOWN = (1 << 15), - KEY_LSTICK_LEFT = (1 << 16), - KEY_LSTICK_UP = (1 << 17), - KEY_LSTICK_RIGHT = (1 << 18), - KEY_LSTICK_DOWN = (1 << 19), - KEY_RSTICK_LEFT = (1 << 20), - KEY_RSTICK_UP = (1 << 21), - KEY_RSTICK_RIGHT = (1 << 22), - KEY_RSTICK_DOWN = (1 << 23), - KEY_SL = (1 << 24), - KEY_SR = (1 << 25), - - // Pseudo-key for at least one finger on the touch screen - KEY_TOUCH = (1 << 26), - - // Buttons by orientation (for single Joy-Con), also works with Joy-Con pairs, Pro Controller - KEY_JOYCON_RIGHT = (1 << 0), - KEY_JOYCON_DOWN = (1 << 1), - KEY_JOYCON_UP = (1 << 2), - KEY_JOYCON_LEFT = (1 << 3), - - // Generic catch-all directions, also works for single Joy-Con - KEY_UP = KEY_DUP | KEY_LSTICK_UP | KEY_RSTICK_UP, - KEY_DOWN = KEY_DDOWN | KEY_LSTICK_DOWN | KEY_RSTICK_DOWN, - KEY_LEFT = KEY_DLEFT | KEY_LSTICK_LEFT | KEY_RSTICK_LEFT, - KEY_RIGHT = KEY_DRIGHT | KEY_LSTICK_RIGHT | KEY_RSTICK_RIGHT, - } - - public enum HidControllerID - { - CONTROLLER_PLAYER_1 = 0, - CONTROLLER_PLAYER_2 = 1, - CONTROLLER_PLAYER_3 = 2, - CONTROLLER_PLAYER_4 = 3, - CONTROLLER_PLAYER_5 = 4, - CONTROLLER_PLAYER_6 = 5, - CONTROLLER_PLAYER_7 = 6, - CONTROLLER_PLAYER_8 = 7, - CONTROLLER_HANDHELD = 8, - CONTROLLER_UNKNOWN = 9 - } - - public enum HidControllerJoystick - { - Joystick_Left = 0, - Joystick_Right = 1, - Joystick_Num_Sticks = 2 - } - - public enum HidControllerLayouts - { - Pro_Controller, - Handheld_Joined, - Joined, - Left, - Right, - Main_No_Analog, - Main - } - - public enum HidControllerConnectionState - { - Controller_State_Connected = (1 << 0), - Controller_State_Wired = (1 << 1) - } - - public enum HidControllerType - { - ControllerType_ProController = (1 << 0), - ControllerType_Handheld = (1 << 1), - ControllerType_JoyconPair = (1 << 2), - ControllerType_JoyconLeft = (1 << 3), - ControllerType_JoyconRight = (1 << 4) - } - - public enum HidControllerColorDescription - { - ColorDesc_ColorsNonexistent = (1 << 1), - } - - [StructLayout(LayoutKind.Sequential, Size = 0x8)] - public struct JoystickPosition - { - public int DX; - public int DY; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x20)] - public struct HidControllerMAC - { - public ulong Timestamp; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public byte[] MAC; - public ulong Unknown; - public ulong Timestamp_2; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x28)] - public struct HidControllerHeader - { - public uint Type; - public uint IsHalf; - public uint SingleColorsDescriptor; - public uint SingleColorBody; - public uint SingleColorButtons; - public uint SplitColorsDescriptor; - public uint LeftColorBody; - public uint LeftColorButtons; - public uint RightColorBody; - public uint RightColorButtons; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x20)] - public struct HidControllerLayoutHeader - { - public ulong TimestampTicks; - public ulong NumEntries; - public ulong LatestEntry; - public ulong MaxEntryIndex; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x30)] - public struct HidControllerInputEntry - { - public ulong Timestamp; - public ulong Timestamp_2; - public ulong Buttons; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)HidControllerJoystick.Joystick_Num_Sticks)] - public JoystickPosition[] Joysticks; - public ulong ConnectionState; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x350)] - public struct HidControllerLayout - { - public HidControllerLayoutHeader Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public HidControllerInputEntry[] Entries; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x5000)] - public struct HidController - { - public HidControllerHeader Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] - public HidControllerLayout[] Layouts; - /* - pro_controller - handheld_joined - joined - left - right - main_no_analog - main - */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x2A70)] - public byte[] Unknown_1; - public HidControllerMAC MacLeft; - public HidControllerMAC MacRight; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xDF8)] - public byte[] Unknown_2; - } -} diff --git a/Ryujinx/Hid/HidKeyboard.cs b/Ryujinx/Hid/HidKeyboard.cs deleted file mode 100644 index 2ee51bfa1..000000000 --- a/Ryujinx/Hid/HidKeyboard.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx -{ - [StructLayout(LayoutKind.Sequential, Size = 0x20)] - public struct HidKeyboardHeader - { - public ulong TimestampTicks; - public ulong NumEntries; - public ulong LatestEntry; - public ulong MaxEntryIndex; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x38)] - public struct HidKeyboardEntry - { - public ulong Timestamp; - public ulong Timestamp_2; - public ulong Modifier; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public uint[] Keys; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidKeyboard - { - public HidKeyboardHeader Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public HidKeyboardEntry[] Entries; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x28)] - public byte[] Padding; - } -} diff --git a/Ryujinx/Hid/HidMouse.cs b/Ryujinx/Hid/HidMouse.cs deleted file mode 100644 index db01e649c..000000000 --- a/Ryujinx/Hid/HidMouse.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx -{ - [StructLayout(LayoutKind.Sequential, Size = 0x20)] - public struct HidMouseHeader - { - public ulong TimestampTicks; - public ulong NumEntries; - public ulong LatestEntry; - public ulong MaxEntryIndex; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x30)] - public struct HidMouseEntry - { - public ulong Timestamp; - public ulong Timestamp_2; - public uint X; - public uint Y; - public uint VelocityX; - public uint VelocityY; - public uint ScrollVelocityX; - public uint ScrollVelocityY; - public ulong Buttons; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidMouse - { - public HidMouseHeader Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public HidMouseEntry[] Entries; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xB0)] - public byte[] Padding; - } -} diff --git a/Ryujinx/Hid/HidTouchScreen.cs b/Ryujinx/Hid/HidTouchScreen.cs deleted file mode 100644 index 7fb022893..000000000 --- a/Ryujinx/Hid/HidTouchScreen.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx -{ - [StructLayout(LayoutKind.Sequential, Size = 0x28)] - public struct HidTouchScreenHeader - { - public ulong TimestampTicks; - public ulong NumEntries; - public ulong LatestEntry; - public ulong MaxEntryIndex; - public ulong Timestamp; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x10)] - public struct HidTouchScreenEntryHeader - { - public ulong Timestamp; - public ulong NumTouches; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x28)] - public struct HidTouchScreenEntryTouch - { - public ulong Timestamp; - public uint Padding; - public uint TouchIndex; - public uint X; - public uint Y; - public uint DiameterX; - public uint DiameterY; - public uint Angle; - public uint Padding_2; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x298)] - public struct HidTouchScreenEntry - { - public HidTouchScreenEntryHeader Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public HidTouchScreenEntryTouch[] Touches; - public ulong Unknown; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x3000)] - public struct HidTouchScreen - { - public HidTouchScreenHeader Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public HidTouchScreenEntry[] Entries; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3C0)] - public byte[] Padding; - } -} diff --git a/Ryujinx/Hid/HidUnknown.cs b/Ryujinx/Hid/HidUnknown.cs deleted file mode 100644 index ef2172d5b..000000000 --- a/Ryujinx/Hid/HidUnknown.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx -{ - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidSharedMemHeader - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidUnknownSection1 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidUnknownSection2 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidUnknownSection3 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidUnknownSection4 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x200)] - public struct HidUnknownSection5 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x200)] - public struct HidUnknownSection6 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x200)] - public struct HidUnknownSection7 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x800)] - public struct HidUnknownSection8 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x800)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x4000)] - public struct HidControllerSerials - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x4600)] - public struct HidUnknownSection9 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4600)] - public byte[] Padding; - } -} diff --git a/Ryujinx/Loaders/Executables/IExecutable.cs b/Ryujinx/Loaders/Executables/IExecutable.cs deleted file mode 100644 index 3e42a834a..000000000 --- a/Ryujinx/Loaders/Executables/IExecutable.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.ObjectModel; - -namespace Ryujinx.Loaders.Executables -{ - interface IExecutable - { - ReadOnlyCollection Text { get; } - ReadOnlyCollection RO { get; } - ReadOnlyCollection Data { get; } - - int Mod0Offset { get; } - int TextOffset { get; } - int ROOffset { get; } - int DataOffset { get; } - int BssSize { get; } - } -} \ No newline at end of file diff --git a/Ryujinx/Logging.cs b/Ryujinx/Logging.cs deleted file mode 100644 index 308f068f8..000000000 --- a/Ryujinx/Logging.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; - -namespace Ryujinx -{ - public static class Logging - { - private static Stopwatch ExecutionTime = new Stopwatch(); - private static string LogFileName = "Ryujinx.log"; - - public static bool EnableInfo = Config.LoggingEnableInfo; - public static bool EnableTrace = Config.LoggingEnableTrace; - public static bool EnableDebug = Config.LoggingEnableDebug; - public static bool EnableWarn = Config.LoggingEnableWarn; - public static bool EnableError = Config.LoggingEnableError; - public static bool EnableFatal = Config.LoggingEnableFatal; - public static bool EnableLogFile = Config.LoggingEnableLogFile; - - static Logging() - { - ExecutionTime.Start(); - - if (File.Exists(LogFileName)) File.Delete(LogFileName); - } - - public static string GetExecutionTime() - { - return ExecutionTime.ElapsedMilliseconds.ToString().PadLeft(8, '0') + "ms"; - } - - private static string WhoCalledMe() - { - return new StackTrace().GetFrame(2).GetMethod().Name; - } - - private static void LogFile(string Message) - { - if (EnableLogFile) - { - using (StreamWriter Writer = File.AppendText(LogFileName)) - { - Writer.WriteLine(Message); - } - } - } - - public static void Info(string Message) - { - if (EnableInfo) - { - string Text = $"{GetExecutionTime()} | INFO > {Message}"; - - Console.ForegroundColor = ConsoleColor.White; - Console.WriteLine(Text.PadLeft(Text.Length + 1, ' ')); - Console.ResetColor(); - - LogFile(Text); - } - } - - public static void Trace(string Message) - { - if (EnableTrace) - { - string Text = $"{GetExecutionTime()} | TRACE > {WhoCalledMe()} - {Message}"; - - Console.ForegroundColor = ConsoleColor.DarkGray; - Console.WriteLine(Text.PadLeft(Text.Length + 1, ' ')); - Console.ResetColor(); - - LogFile(Text); - } - } - - public static void Debug(string Message) - { - if (EnableDebug) - { - string Text = $"{GetExecutionTime()} | DEBUG > {WhoCalledMe()} - {Message}"; - - Console.ForegroundColor = ConsoleColor.Gray; - Console.WriteLine(Text.PadLeft(Text.Length + 1, ' ')); - Console.ResetColor(); - - LogFile(Text); - } - } - - public static void Warn(string Message) - { - if (EnableWarn) - { - string Text = $"{GetExecutionTime()} | WARN > {WhoCalledMe()} - {Message}"; - - Console.ForegroundColor = ConsoleColor.Yellow; - Console.WriteLine(Text.PadLeft(Text.Length + 1, ' ')); - Console.ResetColor(); - - LogFile(Text); - } - } - - public static void Error(string Message) - { - if (EnableError) - { - string Text = $"{GetExecutionTime()} | ERROR > {WhoCalledMe()} - {Message}"; - - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(Text.PadLeft(Text.Length + 1, ' ')); - Console.ResetColor(); - - LogFile(Text); - } - } - - public static void Fatal(string Message) - { - if (EnableFatal) - { - string Text = $"{GetExecutionTime()} | FATAL > {WhoCalledMe()} - {Message}"; - - Console.ForegroundColor = ConsoleColor.Magenta; - Console.WriteLine(Text.PadLeft(Text.Length + 1, ' ')); - Console.ResetColor(); - - LogFile(Text); - } - } - } -} diff --git a/Ryujinx/OsHle/Handles/HDomain.cs b/Ryujinx/OsHle/Handles/HDomain.cs deleted file mode 100644 index e781602a1..000000000 --- a/Ryujinx/OsHle/Handles/HDomain.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Ryujinx.OsHle.Utilities; -using System; -using System.Collections.Generic; - -namespace Ryujinx.OsHle.Handles -{ - class HDomain : HSession - { - private Dictionary Objects; - - private IdPool ObjIds; - - public HDomain(HSession Session) : base(Session) - { - Objects = new Dictionary(); - - ObjIds = new IdPool(); - } - - public int GenerateObjectId(object Obj) - { - int Id = ObjIds.GenerateId(); - - if (Id == -1) - { - throw new InvalidOperationException(); - } - - Objects.Add(Id, Obj); - - return Id; - } - - public void DeleteObject(int Id) - { - if (Objects.TryGetValue(Id, out object Obj)) - { - if (Obj is IDisposable DisposableObj) - { - DisposableObj.Dispose(); - } - - ObjIds.DeleteId(Id); - Objects.Remove(Id); - } - } - - public object GetObject(int Id) - { - if (Objects.TryGetValue(Id, out object Obj)) - { - return Obj; - } - - return null; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Handles/HEvent.cs b/Ryujinx/OsHle/Handles/HEvent.cs deleted file mode 100644 index d9d0ff4c2..000000000 --- a/Ryujinx/OsHle/Handles/HEvent.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Ryujinx.OsHle.Handles -{ - class HEvent - { - - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Handles/HNvMap.cs b/Ryujinx/OsHle/Handles/HNvMap.cs deleted file mode 100644 index 3e15eda38..000000000 --- a/Ryujinx/OsHle/Handles/HNvMap.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.OsHle.Handles -{ - class HNvMap - { - public int Id { get; private set; } - public int Size { get; private set; } - - public int Align { get; set; } - public int Kind { get; set; } - public long Address { get; set; } - - public HNvMap(int Id, int Size) - { - this.Id = Id; - this.Size = Size; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Handles/HSharedMem.cs b/Ryujinx/OsHle/Handles/HSharedMem.cs deleted file mode 100644 index a493276a2..000000000 --- a/Ryujinx/OsHle/Handles/HSharedMem.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Ryujinx.OsHle.Handles -{ - class HSharedMem - { - private List Positions; - - public int PositionsCount => Positions.Count; - - public EventHandler MemoryMapped; - public EventHandler MemoryUnmapped; - - public HSharedMem(long PhysPos) - { - Positions = new List(); - } - - public void AddVirtualPosition(long Position) - { - lock (Positions) - { - Positions.Add(Position); - - if (MemoryMapped != null) - { - MemoryMapped(this, EventArgs.Empty); - } - } - } - - public void RemoveVirtualPosition(long Position) - { - lock (Positions) - { - Positions.Remove(Position); - - if (MemoryUnmapped != null) - { - MemoryUnmapped(this, EventArgs.Empty); - } - } - } - - public long GetVirtualPosition(int Index) - { - lock (Positions) - { - if (Index < 0 || Index >= Positions.Count) - { - throw new ArgumentOutOfRangeException(nameof(Index)); - } - - return Positions[Index]; - } - } - - public bool TryGetLastVirtualPosition(out long Position) - { - lock (Positions) - { - if (Positions.Count > 0) - { - Position = Positions[Positions.Count - 1]; - - return true; - } - - Position = 0; - - return false; - } - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Horizon.cs b/Ryujinx/OsHle/Horizon.cs deleted file mode 100644 index e72c62dc0..000000000 --- a/Ryujinx/OsHle/Horizon.cs +++ /dev/null @@ -1,202 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.Loaders.Executables; -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Utilities; -using System.Collections.Concurrent; -using System.IO; -using System; - -namespace Ryujinx.OsHle -{ - class Horizon - { - internal const int HidSize = 0x40000; - internal const int FontSize = 0x50; - - internal int HidHandle { get; private set; } - internal int FontHandle { get; private set; } - - public long HidOffset { get; private set; } - public long FontOffset { get; private set; } - - internal IdPool IdGen { get; private set; } - internal IdPool NvMapIds { get; private set; } - - internal IdPoolWithObj Handles { get; private set; } - internal IdPoolWithObj Fds { get; private set; } - internal IdPoolWithObj Displays { get; private set; } - - public ConcurrentDictionary Mutexes { get; private set; } - public ConcurrentDictionary CondVars { get; private set; } - - private ConcurrentDictionary Processes; - - private HSharedMem HidSharedMem; - - private AMemoryAlloc Allocator; - - private Switch Ns; - - public Horizon(Switch Ns) - { - this.Ns = Ns; - - IdGen = new IdPool(); - NvMapIds = new IdPool(); - - Handles = new IdPoolWithObj(); - Fds = new IdPoolWithObj(); - Displays = new IdPoolWithObj(); - - Mutexes = new ConcurrentDictionary(); - CondVars = new ConcurrentDictionary(); - - Processes = new ConcurrentDictionary(); - - Allocator = new AMemoryAlloc(); - - HidOffset = Allocator.Alloc(HidSize); - FontOffset = Allocator.Alloc(FontSize); - - HidSharedMem = new HSharedMem(HidOffset); - - HidSharedMem.MemoryMapped += HidInit; - - HidHandle = Handles.GenerateId(HidSharedMem); - - FontHandle = Handles.GenerateId(new HSharedMem(FontOffset)); - } - - public void LoadCart(string ExeFsDir, string RomFsFile = null) - { - if (RomFsFile != null) - { - Ns.VFs.LoadRomFs(RomFsFile); - } - - int ProcessId = IdGen.GenerateId(); - - Process MainProcess = new Process(Ns, Allocator, ProcessId); - - void LoadNso(string FileName) - { - foreach (string File in Directory.GetFiles(ExeFsDir, FileName)) - { - if (Path.GetExtension(File) != string.Empty) - { - continue; - } - - Logging.Info($"Loading {Path.GetFileNameWithoutExtension(File)}..."); - - using (FileStream Input = new FileStream(File, FileMode.Open)) - { - Nso Program = new Nso(Input); - - MainProcess.LoadProgram(Program); - } - } - } - - LoadNso("rtld"); - - MainProcess.SetEmptyArgs(); - - LoadNso("main"); - LoadNso("subsdk*"); - LoadNso("sdk"); - - MainProcess.InitializeHeap(); - MainProcess.Run(); - - Processes.TryAdd(ProcessId, MainProcess); - } - - public void LoadProgram(string FileName) - { - int ProcessId = IdGen.GenerateId(); - - Process MainProcess = new Process(Ns, Allocator, ProcessId); - - using (FileStream Input = new FileStream(FileName, FileMode.Open)) - { - if (Path.GetExtension(FileName).ToLower() == ".nro") - { - MainProcess.LoadProgram(new Nro(Input)); - } - else - { - MainProcess.LoadProgram(new Nso(Input)); - } - } - - MainProcess.SetEmptyArgs(); - MainProcess.InitializeHeap(); - MainProcess.Run(); - - Processes.TryAdd(ProcessId, MainProcess); - } - - public void FinalizeAllProcesses() - { - foreach (Process Process in Processes.Values) - { - Process.StopAllThreads(); - Process.Dispose(); - } - } - - internal bool ExitProcess(int ProcessId) - { - bool Success = Processes.TryRemove(ProcessId, out Process Process); - - if (Success) - { - Process.StopAllThreads(); - } - - if (Processes.Count == 0) - { - Ns.OnFinish(EventArgs.Empty); - } - - return Success; - } - - internal bool TryGetProcess(int ProcessId, out Process Process) - { - if (!Processes.TryGetValue(ProcessId, out Process)) - { - return false; - } - - return true; - } - - internal void CloseHandle(int Handle) - { - object HndData = Handles.GetData(Handle); - - if (HndData is HTransferMem TransferMem) - { - TransferMem.Memory.Manager.Reprotect( - TransferMem.Position, - TransferMem.Size, - TransferMem.Perm); - } - - Handles.Delete(Handle); - } - - private void HidInit(object sender, EventArgs e) - { - HSharedMem SharedMem = (HSharedMem)sender; - - if (SharedMem.TryGetLastVirtualPosition(out long Position)) - { - Logging.Info($"HID shared memory successfully mapped to {Position:x16}!"); - Ns.Hid.Init(Position); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Ipc/IpcHandler.cs b/Ryujinx/OsHle/Ipc/IpcHandler.cs deleted file mode 100644 index f12789145..000000000 --- a/Ryujinx/OsHle/Ipc/IpcHandler.cs +++ /dev/null @@ -1,277 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Objects; -using Ryujinx.OsHle.Services; -using System; -using System.Collections.Generic; -using System.IO; - -namespace Ryujinx.OsHle.Ipc -{ - static class IpcHandler - { - private static Dictionary<(string, int), ServiceProcessRequest> ServiceCmds = - new Dictionary<(string, int), ServiceProcessRequest>() - { - { ( "acc:u0", 3), Service.AccU0ListOpenUsers }, - { ( "acc:u0", 5), Service.AccU0GetProfile }, - { ( "acc:u0", 100), Service.AccU0InitializeApplicationInfo }, - { ( "acc:u0", 101), Service.AccU0GetBaasAccountManagerForApplication }, - { ( "apm", 0), Service.ApmOpenSession }, - { ( "apm:p", 0), Service.ApmOpenSession }, - { ( "appletOE", 0), Service.AppletOpenApplicationProxy }, - { ( "audout:u", 0), Service.AudOutListAudioOuts }, - { ( "audout:u", 1), Service.AudOutOpenAudioOut }, - { ( "audren:u", 0), Service.AudRenOpenAudioRenderer }, - { ( "audren:u", 1), Service.AudRenGetAudioRendererWorkBufferSize }, - { ( "friend:a", 0), Service.FriendCreateFriendService }, - { ( "fsp-srv", 1), Service.FspSrvInitialize }, - { ( "fsp-srv", 18), Service.FspSrvMountSdCard }, - { ( "fsp-srv", 51), Service.FspSrvMountSaveData }, - { ( "fsp-srv", 200), Service.FspSrvOpenDataStorageByCurrentProcess }, - { ( "fsp-srv", 203), Service.FspSrvOpenRomStorage }, - { ( "fsp-srv", 1005), Service.FspSrvGetGlobalAccessLogMode }, - { ( "hid", 0), Service.HidCreateAppletResource }, - { ( "hid", 11), Service.HidActivateTouchScreen }, - { ( "hid", 100), Service.HidSetSupportedNpadStyleSet }, - { ( "hid", 102), Service.HidSetSupportedNpadIdType }, - { ( "hid", 103), Service.HidActivateNpad }, - { ( "hid", 120), Service.HidSetNpadJoyHoldType }, - { ( "lm", 0), Service.LmInitialize }, - { ( "nvdrv", 0), Service.NvDrvOpen }, - { ( "nvdrv", 1), Service.NvDrvIoctl }, - { ( "nvdrv", 2), Service.NvDrvClose }, - { ( "nvdrv", 3), Service.NvDrvInitialize }, - { ( "nvdrv", 4), Service.NvDrvQueryEvent }, - { ( "nvdrv", 8), Service.NvDrvSetClientPid }, - { ( "nvdrv:a", 0), Service.NvDrvOpen }, - { ( "nvdrv:a", 1), Service.NvDrvIoctl }, - { ( "nvdrv:a", 2), Service.NvDrvClose }, - { ( "nvdrv:a", 3), Service.NvDrvInitialize }, - { ( "nvdrv:a", 4), Service.NvDrvQueryEvent }, - { ( "nvdrv:a", 8), Service.NvDrvSetClientPid }, - { ( "pctl:a", 0), Service.PctlCreateService }, - { ( "pl:u", 1), Service.PlGetLoadState }, - { ( "pl:u", 2), Service.PlGetFontSize }, - { ( "pl:u", 3), Service.PlGetSharedMemoryAddressOffset }, - { ( "pl:u", 4), Service.PlGetSharedMemoryNativeHandle }, - { ( "set", 1), Service.SetGetAvailableLanguageCodes }, - { ( "sm:", 0), Service.SmInitialize }, - { ( "sm:", 1), Service.SmGetService }, - { ( "time:u", 0), Service.TimeGetStandardUserSystemClock }, - { ( "time:u", 1), Service.TimeGetStandardNetworkSystemClock }, - { ( "time:u", 2), Service.TimeGetStandardSteadyClock }, - { ( "time:u", 3), Service.TimeGetTimeZoneService }, - { ( "time:u", 4), Service.TimeGetStandardLocalSystemClock }, - { ( "time:s", 0), Service.TimeGetStandardUserSystemClock }, - { ( "time:s", 1), Service.TimeGetStandardNetworkSystemClock }, - { ( "time:s", 2), Service.TimeGetStandardSteadyClock }, - { ( "time:s", 3), Service.TimeGetTimeZoneService }, - { ( "time:s", 4), Service.TimeGetStandardLocalSystemClock }, - { ( "vi:m", 2), Service.ViGetDisplayService }, - }; - - 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, - AMemory Memory, - HSession Session, - IpcMessage Request, - long CmdPtr, - int HndId) - { - IpcMessage Response = new IpcMessage(Request.IsDomain); - - using (MemoryStream Raw = new MemoryStream(Request.RawData)) - { - BinaryReader ReqReader = new BinaryReader(Raw); - - if (Request.Type == IpcMessageType.Request) - { - string ServiceName = Session.ServiceName; - - ServiceProcessRequest ProcReq = null; - - bool IgnoreNullPR = false; - - string DbgServiceName = string.Empty; - - if (Session is HDomain Dom) - { - if (Request.DomCmd == IpcDomCmd.SendMsg) - { - long Magic = ReqReader.ReadInt64(); - int CmdId = (int)ReqReader.ReadInt64(); - - object Obj = Dom.GetObject(Request.DomObjId); - - if (Obj is HDomain) - { - ServiceCmds.TryGetValue((ServiceName, CmdId), out ProcReq); - - DbgServiceName = $"{ServiceName} {ProcReq?.Method.Name ?? CmdId.ToString()}"; - } - else if (Obj != null) - { - ((IIpcInterface)Obj).Commands.TryGetValue(CmdId, out ProcReq); - - DbgServiceName = $"{ServiceName} {Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}"; - } - } - else if (Request.DomCmd == IpcDomCmd.DeleteObj) - { - Dom.DeleteObject(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; - - ((IIpcInterface)Obj).Commands.TryGetValue(CmdId, out ProcReq); - - DbgServiceName = $"{ServiceName} {Obj.GetType().Name} {ProcReq?.Method.Name ?? CmdId.ToString()}"; - } - else - { - ServiceCmds.TryGetValue((ServiceName, CmdId), out ProcReq); - - DbgServiceName = $"{ServiceName} {ProcReq?.Method.Name ?? CmdId.ToString()}"; - } - } - - Logging.Debug($"IpcMessage: {DbgServiceName}"); - - if (ProcReq != null) - { - using (MemoryStream ResMS = new MemoryStream()) - { - BinaryWriter ResWriter = new BinaryWriter(ResMS); - - ServiceCtx Context = new ServiceCtx( - Ns, - Memory, - Session, - Request, - Response, - ReqReader, - ResWriter); - - long Result = ProcReq(Context); - - Response = FillResponse(Response, Result, ResMS.ToArray()); - } - } - else if (!IgnoreNullPR) - { - throw new NotImplementedException(DbgServiceName); - } - } - else if (Request.Type == IpcMessageType.Control) - { - long Magic = ReqReader.ReadInt64(); - long CmdId = ReqReader.ReadInt64(); - - switch (CmdId) - { - case 0: Request = IpcConvertSessionToDomain(Ns, Session, Response, HndId); break; - case 3: Request = IpcQueryBufferPointerSize(Response); break; - case 4: Request = IpcDuplicateSessionEx(Ns, Session, Response, ReqReader); break; - - default: throw new NotImplementedException(CmdId.ToString()); - } - } - else if (Request.Type == IpcMessageType.Unknown2) - { - //TODO - } - else - { - throw new NotImplementedException(Request.Type.ToString()); - } - - AMemoryHelper.WriteBytes(Memory, CmdPtr, Response.GetBytes(CmdPtr)); - } - } - - private static IpcMessage IpcConvertSessionToDomain( - Switch Ns, - HSession Session, - IpcMessage Response, - int HndId) - { - HDomain Dom = new HDomain(Session); - - Ns.Os.Handles.ReplaceData(HndId, Dom); - - return FillResponse(Response, 0, Dom.GenerateObjectId(Dom)); - } - - private static IpcMessage IpcDuplicateSessionEx( - Switch Ns, - HSession Session, - IpcMessage Response, - BinaryReader ReqReader) - { - int Unknown = ReqReader.ReadInt32(); - - int Handle = Ns.Os.Handles.GenerateId(Session); - - Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); - - return FillResponse(Response, 0); - } - - private static IpcMessage IpcQueryBufferPointerSize(IpcMessage Response) - { - return FillResponse(Response, 0, 0x500); - } - - private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values) - { - using (MemoryStream MS = new MemoryStream()) - { - BinaryWriter Writer = new BinaryWriter(MS); - - foreach (int Value in Values) - { - Writer.Write(Value); - } - - return FillResponse(Response, Result, MS.ToArray()); - } - } - - private static IpcMessage FillResponse(IpcMessage Response, long Result, byte[] Data = null) - { - Response.Type = IpcMessageType.Response; - - using (MemoryStream MS = new MemoryStream()) - { - BinaryWriter Writer = new BinaryWriter(MS); - - Writer.Write(SfcoMagic); - Writer.Write(Result); - - if (Data != null) - { - Writer.Write(Data); - } - - Response.RawData = MS.ToArray(); - } - - return Response; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Ipc/IpcMessageType.cs b/Ryujinx/OsHle/Ipc/IpcMessageType.cs deleted file mode 100644 index b0e283de7..000000000 --- a/Ryujinx/OsHle/Ipc/IpcMessageType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.OsHle.Ipc -{ - enum IpcMessageType - { - Response = 0, - Unknown2 = 2, - Request = 4, - Control = 5 - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/MemoryInfo.cs b/Ryujinx/OsHle/MemoryInfo.cs deleted file mode 100644 index 395ccf73e..000000000 --- a/Ryujinx/OsHle/MemoryInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -using ChocolArm64.Memory; - -namespace Ryujinx.OsHle -{ - struct MemoryInfo - { - public long BaseAddress; - public long Size; - public int MemType; - public int MemAttr; - public int MemPerm; - public int IpcRefCount; - public int DeviceRefCount; - public int Padding; //SBZ - - public MemoryInfo(AMemoryMapInfo MapInfo) - { - BaseAddress = MapInfo.Position; - Size = MapInfo.Size; - MemType = MapInfo.Type; - MemAttr = MapInfo.Attr; - MemPerm = (int)MapInfo.Perm; - IpcRefCount = 0; - DeviceRefCount = 0; - Padding = 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/FspSrv/IFileSystem.cs b/Ryujinx/OsHle/Objects/FspSrv/IFileSystem.cs deleted file mode 100644 index bf501594f..000000000 --- a/Ryujinx/OsHle/Objects/FspSrv/IFileSystem.cs +++ /dev/null @@ -1,79 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.OsHle.Ipc; -using System.Collections.Generic; -using System.IO; - -using static Ryujinx.OsHle.Objects.ObjHelper; - -namespace Ryujinx.OsHle.Objects.FspSrv -{ - class IFileSystem : IIpcInterface - { - private Dictionary m_Commands; - - public IReadOnlyDictionary Commands => m_Commands; - - private string Path; - - public IFileSystem(string Path) - { - m_Commands = new Dictionary() - { - { 7, GetEntryType }, - { 8, OpenFile }, - { 10, Commit } - }; - - this.Path = Path; - } - - public long GetEntryType(ServiceCtx Context) - { - long Position = Context.Request.PtrBuff[0].Position; - - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); - - string FileName = Context.Ns.VFs.GetFullPath(Path, Name); - - if (FileName == null) - { - //TODO: Correct error code. - return -1; - } - - bool IsFile = File.Exists(FileName); - - Context.ResponseData.Write(IsFile ? 1 : 0); - - return 0; - } - - public long OpenFile(ServiceCtx Context) - { - long Position = Context.Request.PtrBuff[0].Position; - - int FilterFlags = Context.RequestData.ReadInt32(); - - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); - - string FileName = Context.Ns.VFs.GetFullPath(Path, Name); - - if (FileName == null) - { - //TODO: Correct error code. - return -1; - } - - FileStream Stream = new FileStream(FileName, FileMode.OpenOrCreate); - - MakeObject(Context, new IFile(Stream)); - - return 0; - } - - public long Commit(ServiceCtx Context) - { - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Objects/Vi/IHOSBinderDriver.cs b/Ryujinx/OsHle/Objects/Vi/IHOSBinderDriver.cs deleted file mode 100644 index 157d600fb..000000000 --- a/Ryujinx/OsHle/Objects/Vi/IHOSBinderDriver.cs +++ /dev/null @@ -1,214 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Ipc; -using Ryujinx.OsHle.Utilities; -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - -using static Ryujinx.OsHle.Objects.Android.Parcel; - -namespace Ryujinx.OsHle.Objects.Vi -{ - class IHOSBinderDriver : IIpcInterface - { - private delegate long ServiceProcessParcel(ServiceCtx Context, byte[] ParcelData); - - private Dictionary m_Commands; - - private Dictionary<(string, int), ServiceProcessParcel> m_Methods; - - public IReadOnlyDictionary Commands => m_Commands; - - private class BufferObj - { - - } - - private IdPoolWithObj BufferSlots; - - private byte[] Gbfr; - - public IHOSBinderDriver() - { - m_Commands = new Dictionary() - { - { 0, TransactParcel }, - { 1, AdjustRefcount }, - { 2, GetNativeHandle } - }; - - m_Methods = new Dictionary<(string, int), ServiceProcessParcel>() - { - { ("android.gui.IGraphicBufferProducer", 0x1), GraphicBufferProducerRequestBuffer }, - { ("android.gui.IGraphicBufferProducer", 0x3), GraphicBufferProducerDequeueBuffer }, - { ("android.gui.IGraphicBufferProducer", 0x7), GraphicBufferProducerQueueBuffer }, - { ("android.gui.IGraphicBufferProducer", 0x8), GraphicBufferProducerCancelBuffer }, - { ("android.gui.IGraphicBufferProducer", 0x9), GraphicBufferProducerQuery }, - { ("android.gui.IGraphicBufferProducer", 0xa), GraphicBufferProducerConnect }, - { ("android.gui.IGraphicBufferProducer", 0xe), GraphicBufferPreallocateBuffer } - }; - - BufferSlots = new IdPoolWithObj(); - } - - public long TransactParcel(ServiceCtx Context) - { - int Id = Context.RequestData.ReadInt32(); - int Code = Context.RequestData.ReadInt32(); - - long DataPos = Context.Request.SendBuff[0].Position; - long DataSize = Context.Request.SendBuff[0].Size; - - byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, (int)DataSize); - - Data = GetParcelData(Data); - - using (MemoryStream MS = new MemoryStream(Data)) - { - BinaryReader Reader = new BinaryReader(MS); - - MS.Seek(4, SeekOrigin.Current); - - int StrSize = Reader.ReadInt32(); - - string InterfaceName = Encoding.Unicode.GetString(Data, 8, StrSize * 2); - - if (m_Methods.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq)) - { - return ProcReq(Context, Data); - } - else - { - throw new NotImplementedException($"{InterfaceName} {Code}"); - } - } - } - - private long GraphicBufferProducerRequestBuffer(ServiceCtx Context, byte[] ParcelData) - { - int GbfrSize = Gbfr?.Length ?? 0; - - byte[] Data = new byte[GbfrSize + 4]; - - if (Gbfr != null) - { - Buffer.BlockCopy(Gbfr, 0, Data, 0, GbfrSize); - } - - return MakeReplyParcel(Context, Data); - } - - private long GraphicBufferProducerDequeueBuffer(ServiceCtx Context, byte[] ParcelData) - { - //Note: It seems that the maximum number of slots is 64, because if we return - //a Slot number > 63, it seems to cause a buffer overrun and it reads garbage. - //Note 2: The size of each object associated with the slot is 0x30. - int Slot = BufferSlots.GenerateId(new BufferObj()); - - return MakeReplyParcel(Context, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - } - - private long GraphicBufferProducerQueueBuffer(ServiceCtx Context, byte[] ParcelData) - { - return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); - } - - private long GraphicBufferProducerCancelBuffer(ServiceCtx Context, byte[] ParcelData) - { - using (MemoryStream MS = new MemoryStream(ParcelData)) - { - BinaryReader Reader = new BinaryReader(MS); - - MS.Seek(0x50, SeekOrigin.Begin); - - int Slot = Reader.ReadInt32(); - - BufferSlots.Delete(Slot); - - return MakeReplyParcel(Context, 0); - } - } - - private long GraphicBufferProducerQuery(ServiceCtx Context, byte[] ParcelData) - { - return MakeReplyParcel(Context, 0, 0); - } - - private long GraphicBufferProducerConnect(ServiceCtx Context, byte[] ParcelData) - { - return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); - } - - private long GraphicBufferPreallocateBuffer(ServiceCtx Context, byte[] ParcelData) - { - int GbfrSize = ParcelData.Length - 0x54; - - Gbfr = new byte[GbfrSize]; - - Buffer.BlockCopy(ParcelData, 0x54, Gbfr, 0, GbfrSize); - - using (MemoryStream MS = new MemoryStream(ParcelData)) - { - BinaryReader Reader = new BinaryReader(MS); - - MS.Seek(0xd4, SeekOrigin.Begin); - - int Handle = Reader.ReadInt32(); - - HNvMap NvMap = Context.Ns.Os.Handles.GetData(Handle); - - Context.Ns.Gpu.Renderer.FrameBufferPtr = NvMap.Address; - } - - return MakeReplyParcel(Context, 0); - } - - private long MakeReplyParcel(ServiceCtx Context, params int[] Ints) - { - using (MemoryStream MS = new MemoryStream()) - { - BinaryWriter Writer = new BinaryWriter(MS); - - foreach (int Int in Ints) - { - Writer.Write(Int); - } - - return MakeReplyParcel(Context, MS.ToArray()); - } - } - - private long MakeReplyParcel(ServiceCtx Context, byte[] Data) - { - long ReplyPos = Context.Request.ReceiveBuff[0].Position; - long ReplySize = Context.Request.ReceiveBuff[0].Position; - - byte[] Reply = MakeParcel(Data, new byte[0]); - - AMemoryHelper.WriteBytes(Context.Memory, ReplyPos, Reply); - - return 0; - } - - public long AdjustRefcount(ServiceCtx Context) - { - int Id = Context.RequestData.ReadInt32(); - int AddVal = Context.RequestData.ReadInt32(); - int Type = Context.RequestData.ReadInt32(); - - return 0; - } - - public long GetNativeHandle(ServiceCtx Context) - { - int Id = Context.RequestData.ReadInt32(); - uint Unk = Context.RequestData.ReadUInt32(); - - Context.Response.HandleDesc = IpcHandleDesc.MakeMove(0xbadcafe); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Process.cs b/Ryujinx/OsHle/Process.cs deleted file mode 100644 index 4d07b94bd..000000000 --- a/Ryujinx/OsHle/Process.cs +++ /dev/null @@ -1,257 +0,0 @@ -using ChocolArm64; -using ChocolArm64.Memory; -using ChocolArm64.State; -using Ryujinx.Loaders; -using Ryujinx.Loaders.Executables; -using Ryujinx.OsHle.Exceptions; -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Svc; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading; - -namespace Ryujinx.OsHle -{ - class Process : IDisposable - { - private const int MaxStackSize = 8 * 1024 * 1024; - - private const int TlsSize = 0x200; - private const int TotalTlsSlots = 32; - private const int TlsTotalSize = TotalTlsSlots * TlsSize; - private const long TlsPageAddr = (AMemoryMgr.AddrSize - TlsTotalSize) & ~AMemoryMgr.PageMask; - - private Switch Ns; - - public int ProcessId { get; private set; } - - public AMemory Memory { get; private set; } - - public KProcessScheduler Scheduler { get; private set; } - - private SvcHandler SvcHandler; - - private ConcurrentDictionary TlsSlots; - - private ConcurrentDictionary ThreadsByTpidr; - - private List Executables; - - private HThread MainThread; - - private long ImageBase; - - public Process(Switch Ns, AMemoryAlloc Allocator, int ProcessId) - { - this.Ns = Ns; - this.ProcessId = ProcessId; - - Memory = new AMemory(Ns.Ram, Allocator); - - Scheduler = new KProcessScheduler(); - - SvcHandler = new SvcHandler(Ns, this); - - TlsSlots = new ConcurrentDictionary(); - - ThreadsByTpidr = new ConcurrentDictionary(); - - Executables = new List(); - - ImageBase = 0x8000000; - - Memory.Manager.MapPhys( - TlsPageAddr, - TlsTotalSize, - (int)MemoryType.ThreadLocal, - AMemoryPerm.RW); - } - - public void LoadProgram(IExecutable Program) - { - Logging.Info($"Image base at 0x{ImageBase:x16}."); - - Executable Executable = new Executable(Program, Memory, ImageBase); - - Executables.Add(Executable); - - ImageBase = AMemoryHelper.PageRoundUp(Executable.ImageEnd); - } - - public void SetEmptyArgs() - { - ImageBase += AMemoryMgr.PageSize; - } - - public void InitializeHeap() - { - Memory.Manager.SetHeapAddr((ImageBase + 0x3fffffff) & ~0x3fffffff); - } - - public bool Run() - { - if (Executables.Count == 0) - { - return false; - } - - long StackBot = TlsPageAddr - MaxStackSize; - - Memory.Manager.MapPhys(StackBot, MaxStackSize, (int)MemoryType.Normal, AMemoryPerm.RW); - - int Handle = MakeThread(Executables[0].ImageBase, TlsPageAddr, 0, 0, 0); - - if (Handle == -1) - { - return false; - } - - MainThread = Ns.Os.Handles.GetData(Handle); - - Scheduler.StartThread(MainThread); - - return true; - } - - public void StopAllThreads() - { - if (MainThread != null) - { - while (MainThread.Thread.IsAlive) - { - MainThread.Thread.StopExecution(); - } - } - - foreach (AThread Thread in TlsSlots.Values) - { - while (Thread.IsAlive) - { - Thread.StopExecution(); - } - } - } - - public int MakeThread( - long EntryPoint, - long StackTop, - long ArgsPtr, - int Priority, - int ProcessorId) - { - ThreadPriority ThreadPrio; - - if (Priority < 12) - { - ThreadPrio = ThreadPriority.Highest; - } - else if (Priority < 24) - { - ThreadPrio = ThreadPriority.AboveNormal; - } - else if (Priority < 36) - { - ThreadPrio = ThreadPriority.Normal; - } - else if (Priority < 48) - { - ThreadPrio = ThreadPriority.BelowNormal; - } - else - { - ThreadPrio = ThreadPriority.Lowest; - } - - AThread Thread = new AThread(Memory, ThreadPrio, EntryPoint); - - HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority); - - int Handle = Ns.Os.Handles.GenerateId(ThreadHnd); - - int TlsSlot = GetFreeTlsSlot(Thread); - - if (TlsSlot == -1 || Handle == -1) - { - return -1; - } - - Thread.ThreadState.Break += BreakHandler; - Thread.ThreadState.SvcCall += SvcHandler.SvcCall; - Thread.ThreadState.Undefined += UndefinedHandler; - Thread.ThreadState.ProcessId = ProcessId; - Thread.ThreadState.ThreadId = Ns.Os.IdGen.GenerateId(); - Thread.ThreadState.Tpidr = TlsPageAddr + TlsSlot * TlsSize; - Thread.ThreadState.X0 = (ulong)ArgsPtr; - Thread.ThreadState.X1 = (ulong)Handle; - Thread.ThreadState.X31 = (ulong)StackTop; - - Thread.WorkFinished += ThreadFinished; - - ThreadsByTpidr.TryAdd(Thread.ThreadState.Tpidr, ThreadHnd); - - return Handle; - } - - private void BreakHandler(object sender, AInstExceptEventArgs e) - { - throw new GuestBrokeExecutionException(); - } - - private void UndefinedHandler(object sender, AInstUndEventArgs e) - { - throw new UndefinedInstructionException(e.Position, e.RawOpCode); - } - - private int GetFreeTlsSlot(AThread Thread) - { - for (int Index = 1; Index < TotalTlsSlots; Index++) - { - if (TlsSlots.TryAdd(Index, Thread)) - { - return Index; - } - } - - return -1; - } - - private void ThreadFinished(object sender, EventArgs e) - { - if (sender is AThread Thread) - { - TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _); - - Ns.Os.IdGen.DeleteId(Thread.ThreadId); - } - } - - private int GetTlsSlot(long Position) - { - return (int)((Position - TlsPageAddr) / TlsSize); - } - - public HThread GetThread(long Tpidr) - { - if (!ThreadsByTpidr.TryGetValue(Tpidr, out HThread Thread)) - { - Logging.Error($"Thread with TPIDR 0x{Tpidr:x16} not found!"); - } - - return Thread; - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool Disposing) - { - if (Disposing) - { - Scheduler.Dispose(); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceAcc.cs b/Ryujinx/OsHle/Services/ServiceAcc.cs deleted file mode 100644 index 632fb41c2..000000000 --- a/Ryujinx/OsHle/Services/ServiceAcc.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Ryujinx.OsHle.Objects.Acc; - -using static Ryujinx.OsHle.Objects.ObjHelper; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - public static long AccU0ListOpenUsers(ServiceCtx Context) - { - return 0; - } - - public static long AccU0GetProfile(ServiceCtx Context) - { - MakeObject(Context, new IProfile()); - - return 0; - } - - public static long AccU0InitializeApplicationInfo(ServiceCtx Context) - { - return 0; - } - - public static long AccU0GetBaasAccountManagerForApplication(ServiceCtx Context) - { - MakeObject(Context, new IManagerForApplication()); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceApm.cs b/Ryujinx/OsHle/Services/ServiceApm.cs deleted file mode 100644 index 81e68680a..000000000 --- a/Ryujinx/OsHle/Services/ServiceApm.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Ryujinx.OsHle.Objects.Apm; - -using static Ryujinx.OsHle.Objects.ObjHelper; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - public static long ApmOpenSession(ServiceCtx Context) - { - MakeObject(Context, new ISession()); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceAppletOE.cs b/Ryujinx/OsHle/Services/ServiceAppletOE.cs deleted file mode 100644 index 9f2391df7..000000000 --- a/Ryujinx/OsHle/Services/ServiceAppletOE.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Ryujinx.OsHle.Objects.Am; - -using static Ryujinx.OsHle.Objects.ObjHelper; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - public static long AppletOpenApplicationProxy(ServiceCtx Context) - { - MakeObject(Context, new IApplicationProxy()); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceAud.cs b/Ryujinx/OsHle/Services/ServiceAud.cs deleted file mode 100644 index 6d1367cf1..000000000 --- a/Ryujinx/OsHle/Services/ServiceAud.cs +++ /dev/null @@ -1,71 +0,0 @@ -using ChocolArm64.Memory; -using Ryujinx.OsHle.Objects.Aud; -using System.Text; - -using static Ryujinx.OsHle.Objects.ObjHelper; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - public static long AudOutListAudioOuts(ServiceCtx Context) - { - long Position = Context.Request.ReceiveBuff[0].Position; - - AMemoryHelper.WriteBytes(Context.Memory, Position, Encoding.ASCII.GetBytes("iface")); - - Context.ResponseData.Write(1); - - return 0; - } - - public static long AudOutOpenAudioOut(ServiceCtx Context) - { - MakeObject(Context, new IAudioOut()); - - Context.ResponseData.Write(48000); //Sample Rate - Context.ResponseData.Write(2); //Channel Count - Context.ResponseData.Write(2); //PCM Format - /* - 0 - Invalid - 1 - INT8 - 2 - INT16 - 3 - INT24 - 4 - INT32 - 5 - PCM Float - 6 - ADPCM - */ - Context.ResponseData.Write(0); //Unknown - - return 0; - } - - public static long AudRenOpenAudioRenderer(ServiceCtx Context) - { - MakeObject(Context, new IAudioRenderer()); - - return 0; - } - - public static long AudRenGetAudioRendererWorkBufferSize(ServiceCtx Context) - { - int SampleRate = Context.RequestData.ReadInt32(); - int Unknown4 = Context.RequestData.ReadInt32(); - int Unknown8 = Context.RequestData.ReadInt32(); - int UnknownC = Context.RequestData.ReadInt32(); - int Unknown10 = Context.RequestData.ReadInt32(); - int Unknown14 = Context.RequestData.ReadInt32(); - int Unknown18 = Context.RequestData.ReadInt32(); - int Unknown1c = Context.RequestData.ReadInt32(); - int Unknown20 = Context.RequestData.ReadInt32(); - int Unknown24 = Context.RequestData.ReadInt32(); - int Unknown28 = Context.RequestData.ReadInt32(); - int Unknown2c = Context.RequestData.ReadInt32(); - int Rev1Magic = Context.RequestData.ReadInt32(); - - Context.ResponseData.Write(0x400L); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceFriend.cs b/Ryujinx/OsHle/Services/ServiceFriend.cs deleted file mode 100644 index 10c23aaea..000000000 --- a/Ryujinx/OsHle/Services/ServiceFriend.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Ryujinx.OsHle.Objects.Friend; - -using static Ryujinx.OsHle.Objects.ObjHelper; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - public static long FriendCreateFriendService(ServiceCtx Context) - { - MakeObject(Context, new IFriendService()); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceFspSrv.cs b/Ryujinx/OsHle/Services/ServiceFspSrv.cs deleted file mode 100644 index 17aa3bda7..000000000 --- a/Ryujinx/OsHle/Services/ServiceFspSrv.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Ryujinx.OsHle.Objects.FspSrv; - -using static Ryujinx.OsHle.Objects.ObjHelper; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - public static long FspSrvInitialize(ServiceCtx Context) - { - return 0; - } - - public static long FspSrvMountSdCard(ServiceCtx Context) - { - MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetSdCardPath())); - - return 0; - } - - public static long FspSrvMountSaveData(ServiceCtx Context) - { - MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetGameSavesPath())); - - return 0; - } - - public static long FspSrvOpenDataStorageByCurrentProcess(ServiceCtx Context) - { - MakeObject(Context, new IStorage(Context.Ns.VFs.RomFs)); - - return 0; - } - - public static long FspSrvOpenRomStorage(ServiceCtx Context) - { - MakeObject(Context, new IStorage(Context.Ns.VFs.RomFs)); - - return 0; - } - - public static long FspSrvGetGlobalAccessLogMode(ServiceCtx Context) - { - Context.ResponseData.Write(0); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceHid.cs b/Ryujinx/OsHle/Services/ServiceHid.cs deleted file mode 100644 index 176c7842c..000000000 --- a/Ryujinx/OsHle/Services/ServiceHid.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Objects.Hid; - -using static Ryujinx.OsHle.Objects.ObjHelper; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - public static long HidCreateAppletResource(ServiceCtx Context) - { - HSharedMem HidHndData = Context.Ns.Os.Handles.GetData(Context.Ns.Os.HidHandle); - - MakeObject(Context, new IAppletResource(HidHndData)); - - return 0; - } - - public static long HidActivateTouchScreen(ServiceCtx Context) - { - long Unknown = Context.RequestData.ReadInt64(); - - return 0; - } - - public static long HidSetSupportedNpadStyleSet(ServiceCtx Context) - { - long Unknown0 = Context.RequestData.ReadInt64(); - long Unknown8 = Context.RequestData.ReadInt64(); - - return 0; - } - - public static long HidSetSupportedNpadIdType(ServiceCtx Context) - { - long Unknown = Context.RequestData.ReadInt64(); - - return 0; - } - - public static long HidActivateNpad(ServiceCtx Context) - { - long Unknown = Context.RequestData.ReadInt64(); - - return 0; - } - - public static long HidSetNpadJoyHoldType(ServiceCtx Context) - { - long Unknown0 = Context.RequestData.ReadInt64(); - long Unknown8 = Context.RequestData.ReadInt64(); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceLm.cs b/Ryujinx/OsHle/Services/ServiceLm.cs deleted file mode 100644 index dc6acad95..000000000 --- a/Ryujinx/OsHle/Services/ServiceLm.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - public static long LmInitialize(ServiceCtx Context) - { - Context.Session.Initialize(); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServicePctl.cs b/Ryujinx/OsHle/Services/ServicePctl.cs deleted file mode 100644 index a0a5aaf3f..000000000 --- a/Ryujinx/OsHle/Services/ServicePctl.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Ryujinx.OsHle.Objects.Am; - -using static Ryujinx.OsHle.Objects.ObjHelper; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - public static long PctlCreateService(ServiceCtx Context) - { - MakeObject(Context, new IParentalControlService()); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServicePl.cs b/Ryujinx/OsHle/Services/ServicePl.cs deleted file mode 100644 index 6981637f9..000000000 --- a/Ryujinx/OsHle/Services/ServicePl.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Ryujinx.OsHle.Ipc; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - public static long PlGetLoadState(ServiceCtx Context) - { - Context.ResponseData.Write(1); //Loaded - - return 0; - } - - public static long PlGetFontSize(ServiceCtx Context) - { - Context.ResponseData.Write(Horizon.FontSize); - - return 0; - } - - public static long PlGetSharedMemoryAddressOffset(ServiceCtx Context) - { - Context.ResponseData.Write(0); - - return 0; - } - - public static long PlGetSharedMemoryNativeHandle(ServiceCtx Context) - { - Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.FontHandle); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceSet.cs b/Ryujinx/OsHle/Services/ServiceSet.cs deleted file mode 100644 index f98e8f0de..000000000 --- a/Ryujinx/OsHle/Services/ServiceSet.cs +++ /dev/null @@ -1,32 +0,0 @@ -using ChocolArm64.Memory; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - private const int LangCodesCount = 13; - - public static long SetGetAvailableLanguageCodes(ServiceCtx Context) - { - int PtrBuffSize = Context.RequestData.ReadInt32(); - - if (Context.Request.RecvListBuff.Count > 0) - { - long Position = Context.Request.RecvListBuff[0].Position; - short Size = Context.Request.RecvListBuff[0].Size; - - //This should return an array of ints with values matching the LanguageCode enum. - byte[] Data = new byte[Size]; - - Data[0] = 0; - Data[1] = 1; - - AMemoryHelper.WriteBytes(Context.Memory, Position, Data); - } - - Context.ResponseData.Write(LangCodesCount); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceSm.cs b/Ryujinx/OsHle/Services/ServiceSm.cs deleted file mode 100644 index 8af3ed5e4..000000000 --- a/Ryujinx/OsHle/Services/ServiceSm.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Ryujinx.OsHle.Handles; -using Ryujinx.OsHle.Ipc; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - private const int SmNotInitialized = 0x415; - - public static long SmInitialize(ServiceCtx Context) - { - Context.Session.Initialize(); - - return 0; - } - - public static long SmGetService(ServiceCtx Context) - { - //Only for kernel version > 3.0.0. - if (!Context.Session.IsInitialized) - { - //return SmNotInitialized; - } - - string Name = string.Empty; - - for (int Index = 0; Index < 8 && - Context.RequestData.BaseStream.Position < - Context.RequestData.BaseStream.Length; Index++) - { - byte Chr = Context.RequestData.ReadByte(); - - if (Chr >= 0x20 && Chr < 0x7f) - { - Name += (char)Chr; - } - } - - HSession Session = new HSession(Name); - - int Handle = Context.Ns.Os.Handles.GenerateId(Session); - - Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceTime.cs b/Ryujinx/OsHle/Services/ServiceTime.cs deleted file mode 100644 index 8a32aabc1..000000000 --- a/Ryujinx/OsHle/Services/ServiceTime.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Ryujinx.OsHle.Objects.Time; - -using static Ryujinx.OsHle.Objects.ObjHelper; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - public static long TimeGetStandardUserSystemClock(ServiceCtx Context) - { - MakeObject(Context, new ISystemClock(SystemClockType.User)); - - return 0; - } - - public static long TimeGetStandardNetworkSystemClock(ServiceCtx Context) - { - MakeObject(Context, new ISystemClock(SystemClockType.Network)); - - return 0; - } - - public static long TimeGetStandardSteadyClock(ServiceCtx Context) - { - MakeObject(Context, new ISteadyClock()); - - return 0; - } - - public static long TimeGetTimeZoneService(ServiceCtx Context) - { - MakeObject(Context, new ITimeZoneService()); - - return 0; - } - - public static long TimeGetStandardLocalSystemClock(ServiceCtx Context) - { - MakeObject(Context, new ISystemClock(SystemClockType.Local)); - - return 0; - } - - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Services/ServiceVi.cs b/Ryujinx/OsHle/Services/ServiceVi.cs deleted file mode 100644 index 096bc18f0..000000000 --- a/Ryujinx/OsHle/Services/ServiceVi.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Ryujinx.OsHle.Objects.Vi; - -using static Ryujinx.OsHle.Objects.ObjHelper; - -namespace Ryujinx.OsHle.Services -{ - static partial class Service - { - public static long ViGetDisplayService(ServiceCtx Context) - { - int Unknown = Context.RequestData.ReadInt32(); - - MakeObject(Context, new IApplicationDisplayService()); - - return 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Svc/SvcMemory.cs b/Ryujinx/OsHle/Svc/SvcMemory.cs deleted file mode 100644 index fa91397c4..000000000 --- a/Ryujinx/OsHle/Svc/SvcMemory.cs +++ /dev/null @@ -1,121 +0,0 @@ -using ChocolArm64.Memory; -using ChocolArm64.State; -using Ryujinx.OsHle.Handles; - -namespace Ryujinx.OsHle.Svc -{ - partial class SvcHandler - { - private void SvcSetHeapSize(AThreadState ThreadState) - { - uint Size = (uint)ThreadState.X1; - - Memory.Manager.SetHeapSize(Size, (int)MemoryType.Heap); - - ThreadState.X0 = (int)SvcResult.Success; - ThreadState.X1 = (ulong)Memory.Manager.HeapAddr; - } - - private void SvcSetMemoryAttribute(AThreadState ThreadState) - { - long Position = (long)ThreadState.X0; - long Size = (long)ThreadState.X1; - int State0 = (int)ThreadState.X2; - int State1 = (int)ThreadState.X3; - - //TODO - - ThreadState.X0 = (int)SvcResult.Success; - } - - private void SvcMapMemory(AThreadState ThreadState) - { - long Dst = (long)ThreadState.X0; - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - - Memory.Manager.MapMirror(Src, Dst, Size, (int)MemoryType.MappedMemory); - - ThreadState.X0 = (int)SvcResult.Success; - } - - private void SvcQueryMemory(AThreadState ThreadState) - { - long InfoPtr = (long)ThreadState.X0; - long Position = (long)ThreadState.X2; - - AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); - - MemoryInfo Info = new MemoryInfo(MapInfo); - - Memory.WriteInt64(InfoPtr + 0x00, Info.BaseAddress); - Memory.WriteInt64(InfoPtr + 0x08, Info.Size); - Memory.WriteInt32(InfoPtr + 0x10, Info.MemType); - Memory.WriteInt32(InfoPtr + 0x14, Info.MemAttr); - Memory.WriteInt32(InfoPtr + 0x18, Info.MemPerm); - Memory.WriteInt32(InfoPtr + 0x1c, Info.IpcRefCount); - Memory.WriteInt32(InfoPtr + 0x20, Info.DeviceRefCount); - Memory.WriteInt32(InfoPtr + 0x24, Info.Padding); - - //TODO: X1. - - ThreadState.X0 = (int)SvcResult.Success; - ThreadState.X1 = 0; - } - - private void SvcMapSharedMemory(AThreadState ThreadState) - { - int Handle = (int)ThreadState.X0; - long Src = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - int Perm = (int)ThreadState.X3; - - HSharedMem SharedMem = Ns.Os.Handles.GetData(Handle); - - if (SharedMem != null) - { - SharedMem.AddVirtualPosition(Src); - - Memory.Manager.MapPhys(Src, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm); - - ThreadState.X0 = (int)SvcResult.Success; - } - - //TODO: Error codes. - } - - private void SvcUnmapSharedMemory(AThreadState ThreadState) - { - int Handle = (int)ThreadState.X0; - long Position = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - - HSharedMem HndData = Ns.Os.Handles.GetData(Handle); - - if (HndData != null) - { - ThreadState.X0 = (int)SvcResult.Success; - } - - //TODO: Error codes. - } - - private void SvcCreateTransferMemory(AThreadState ThreadState) - { - long Position = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - int Perm = (int)ThreadState.X3; - - AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); - - Memory.Manager.Reprotect(Position, Size, (AMemoryPerm)Perm); - - HTransferMem HndData = new HTransferMem(Memory, MapInfo.Perm, Position, Size); - - int Handle = Ns.Os.Handles.GenerateId(HndData); - - ThreadState.X1 = (ulong)Handle; - ThreadState.X0 = (int)SvcResult.Success; - } - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Svc/SvcResult.cs b/Ryujinx/OsHle/Svc/SvcResult.cs deleted file mode 100644 index 4f0c4f1fd..000000000 --- a/Ryujinx/OsHle/Svc/SvcResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.OsHle.Svc -{ - enum SvcResult - { - Success = 0, - ErrBadHandle = 0xe401, - ErrTimeout = 0xea01, - ErrBadInfo = 0xf001, - ErrBadIpcReq = 0xf601 - } -} \ No newline at end of file diff --git a/Ryujinx/OsHle/Utilities/IdPool.cs b/Ryujinx/OsHle/Utilities/IdPool.cs deleted file mode 100644 index 836d6310e..000000000 --- a/Ryujinx/OsHle/Utilities/IdPool.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; - -namespace Ryujinx.OsHle.Utilities -{ - class IdPool - { - private HashSet Ids; - - private int CurrId; - private int MinId; - private int MaxId; - - public IdPool(int Min, int Max) - { - Ids = new HashSet(); - - CurrId = Min; - MinId = Min; - MaxId = Max; - } - - public IdPool() : this(1, int.MaxValue) { } - - public int GenerateId() - { - lock (Ids) - { - for (int Cnt = MinId; Cnt < MaxId; Cnt++) - { - if (Ids.Add(CurrId)) - { - return CurrId; - } - - if (CurrId++ == MaxId) - { - CurrId = MinId; - } - } - - return -1; - } - } - - public bool DeleteId(int Id) - { - lock (Ids) - { - return Ids.Remove(Id); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf index 8f791df31..e8effac1d 100644 --- a/Ryujinx/Ryujinx.conf +++ b/Ryujinx/Ryujinx.conf @@ -16,32 +16,35 @@ Logging_Enable_Error = true #Enabled print fatal logs Logging_Enable_Fatal = true +#Enabled print Ipc logs +Logging_Enable_Ipc = false + #Saved logs into Ryujinx.log Logging_Enable_LogFile = false #https://github.com/opentk/opentk/blob/develop/src/OpenTK/Input/Key.cs -Controls_Left_FakeJoycon_Stick_Up = 91 -Controls_Left_FakeJoycon_Stick_Down = 93 -Controls_Left_FakeJoycon_Stick_Left = 92 -Controls_Left_FakeJoycon_Stick_Right = 94 -Controls_Left_FakeJoycon_Stick_Button = 0 +Controls_Left_FakeJoycon_Stick_Up = 105 +Controls_Left_FakeJoycon_Stick_Down = 101 +Controls_Left_FakeJoycon_Stick_Left = 83 +Controls_Left_FakeJoycon_Stick_Right = 86 +Controls_Left_FakeJoycon_Stick_Button = 88 Controls_Left_FakeJoycon_DPad_Up = 45 Controls_Left_FakeJoycon_DPad_Down = 46 Controls_Left_FakeJoycon_DPad_Left = 47 Controls_Left_FakeJoycon_DPad_Right = 48 -Controls_Left_FakeJoycon_Button_Minus = 52 -Controls_Left_FakeJoycon_Button_L = 0 -Controls_Left_FakeJoycon_Button_ZL = 0 +Controls_Left_FakeJoycon_Button_Minus = 120 +Controls_Left_FakeJoycon_Button_L = 87 +Controls_Left_FakeJoycon_Button_ZL = 99 -Controls_Right_FakeJoycon_Stick_Up = 45 -Controls_Right_FakeJoycon_Stick_Down = 46 -Controls_Right_FakeJoycon_Stick_Left = 47 -Controls_Right_FakeJoycon_Stick_Right = 48 -Controls_Right_FakeJoycon_Stick_Button = 0 -Controls_Right_FakeJoycon_Button_A = 83 -Controls_Right_FakeJoycon_Button_B = 101 -Controls_Right_FakeJoycon_Button_X = 106 -Controls_Right_FakeJoycon_Button_Y = 108 -Controls_Right_FakeJoycon_Button_Plus = 49 -Controls_Right_FakeJoycon_Button_R = 0 -Controls_Right_FakeJoycon_Button_ZR = 0 +Controls_Right_FakeJoycon_Stick_Up = 91 +Controls_Right_FakeJoycon_Stick_Down = 93 +Controls_Right_FakeJoycon_Stick_Left = 92 +Controls_Right_FakeJoycon_Stick_Right = 94 +Controls_Right_FakeJoycon_Stick_Button = 90 +Controls_Right_FakeJoycon_Button_A = 108 +Controls_Right_FakeJoycon_Button_B = 106 +Controls_Right_FakeJoycon_Button_X = 85 +Controls_Right_FakeJoycon_Button_Y = 104 +Controls_Right_FakeJoycon_Button_Plus = 121 +Controls_Right_FakeJoycon_Button_R = 103 +Controls_Right_FakeJoycon_Button_ZR = 97 diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index 9b0e7396a..bc5dbe042 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -9,6 +9,11 @@ + + + + + PreserveNewest diff --git a/Ryujinx/Switch.cs b/Ryujinx/Switch.cs deleted file mode 100644 index b5874051e..000000000 --- a/Ryujinx/Switch.cs +++ /dev/null @@ -1,54 +0,0 @@ -using ChocolArm64.Memory; -using Gal; -using Ryujinx.Gpu; -using Ryujinx.OsHle; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx -{ - public class Switch : IDisposable - { - public IntPtr Ram {get; private set; } - - internal NsGpu Gpu { get; private set; } - internal Horizon Os { get; private set; } - internal VirtualFs VFs { get; private set; } - internal Hid Hid { get; private set; } - - public event EventHandler Finish; - - public Switch(IGalRenderer Renderer) - { - Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize); - - Gpu = new NsGpu(Renderer); - Os = new Horizon(this); - VFs = new VirtualFs(); - Hid = new Hid(this); - } - - internal virtual void OnFinish(EventArgs e) - { - if (Finish != null) - { - Finish(this, e); - } - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - VFs.Dispose(); - } - - Marshal.FreeHGlobal(Ram); - } - } -} \ No newline at end of file diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 0f03d4a0f..b0dca81b7 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -1,183 +1,26 @@ -// This code was written for the OpenTK library and has been released -// to the Public Domain. -// It is provided "as is" without express or implied warranty of any kind. - -using Gal; using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; +using OpenTK.Input; +using Ryujinx.Core; +using Ryujinx.Core.Input; +using Ryujinx.Graphics.Gal; using System; namespace Ryujinx { public class GLScreen : GameWindow { - class ScreenTexture : IDisposable - { - private Switch Ns; - private IGalRenderer Renderer; - - private int Width; - private int Height; - private int TexHandle; + private const int TouchScreenWidth = 1280; + private const int TouchScreenHeight = 720; - private int[] Pixels; - - public ScreenTexture(Switch Ns, IGalRenderer Renderer, int Width, int Height) - { - this.Ns = Ns; - this.Renderer = Renderer; - this.Width = Width; - this.Height = Height; - - Pixels = new int[Width * Height]; - - TexHandle = GL.GenTexture(); - - GL.BindTexture(TextureTarget.Texture2D, TexHandle); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); - GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - GL.TexImage2D(TextureTarget.Texture2D, - 0, - PixelInternalFormat.Rgba, - Width, - Height, - 0, - PixelFormat.Rgba, - PixelType.UnsignedByte, - IntPtr.Zero); - } - - public int Texture - { - get - { - UploadBitmap(); - - return TexHandle; - } - } - - unsafe void UploadBitmap() - { - int FbSize = Width * Height * 4; - - if (Renderer.FrameBufferPtr == 0 || Renderer.FrameBufferPtr + FbSize > uint.MaxValue) - { - return; - } - - byte* SrcPtr = (byte*)Ns.Ram + (uint)Renderer.FrameBufferPtr; - - for (int Y = 0; Y < Height; Y++) - { - for (int X = 0; X < Width; X++) - { - int SrcOffs = GetSwizzleOffset(X, Y, 4); - - Pixels[X + Y * Width] = *((int*)(SrcPtr + SrcOffs)); - } - } - - GL.BindTexture(TextureTarget.Texture2D, TexHandle); - GL.TexSubImage2D(TextureTarget.Texture2D, - 0, - 0, - 0, - Width, - Height, - PixelFormat.Rgba, - PixelType.UnsignedByte, - Pixels); - } - - private int GetSwizzleOffset(int X, int Y, int Bpp) - { - int Pos; - - Pos = (Y & 0x7f) >> 4; - Pos += (X >> 4) << 3; - Pos += (Y >> 7) * ((Width >> 4) << 3); - Pos *= 1024; - Pos += ((Y & 0xf) >> 3) << 9; - Pos += ((X & 0xf) >> 3) << 8; - Pos += ((Y & 0x7) >> 1) << 6; - Pos += ((X & 0x7) >> 2) << 5; - Pos += ((Y & 0x1) >> 0) << 4; - Pos += ((X & 0x3) >> 0) << 2; - - return Pos; - } - - private bool disposed; - - public void Dispose() - { - Dispose(true); - - GC.SuppressFinalize(this); - } - - void Dispose(bool disposing) - { - if (!disposed) - { - if (disposing) - { - GL.DeleteTexture(TexHandle); - } - - disposed = true; - } - } - } - - private string VtxShaderSource = @" -#version 330 core - -precision highp float; - -layout(location = 0) in vec3 in_position; -layout(location = 1) in vec4 in_color; -layout(location = 2) in vec2 in_tex_coord; - -out vec4 color; -out vec2 tex_coord; - -void main(void) { - color = in_color; - tex_coord = in_tex_coord; - gl_Position = vec4((in_position + vec3(-960, 270, 0)) / vec3(1920, 270, 1), 1); -}"; - - private string FragShaderSource = @" -#version 330 core - -precision highp float; - -uniform sampler2D tex; - -in vec4 color; -in vec2 tex_coord; -out vec4 out_frag_color; - -void main(void) { - out_frag_color = vec4(texture(tex, tex_coord).rgb, color.a); -}"; - - private int VtxShaderHandle, - FragShaderHandle, - PrgShaderHandle; - - private int VaoHandle; - private int VboHandle; + private const float TouchScreenRatioX = (float)TouchScreenWidth / TouchScreenHeight; + private const float TouchScreenRatioY = (float)TouchScreenHeight / TouchScreenWidth; private Switch Ns; private IGalRenderer Renderer; - private ScreenTexture ScreenTex; - public GLScreen(Switch Ns, IGalRenderer Renderer) : base(1280, 720, new GraphicsMode(), "Ryujinx", 0, @@ -187,177 +30,162 @@ void main(void) { this.Ns = Ns; this.Renderer = Renderer; - ScreenTex = new ScreenTexture(Ns, Renderer, 1280, 720); + Location = new Point( + (DisplayDevice.Default.Width / 2) - (Width / 2), + (DisplayDevice.Default.Height / 2) - (Height / 2)); } protected override void OnLoad(EventArgs e) { VSync = VSyncMode.On; - CreateShaders(); - CreateVbo(); - - GL.Enable(EnableCap.Blend); - GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); - } - - protected override void OnUnload(EventArgs e) - { - ScreenTex.Dispose(); - - GL.DeleteVertexArray(VaoHandle); - GL.DeleteBuffer(VboHandle); - } - - private void CreateVbo() - { - VaoHandle = GL.GenVertexArray(); - VboHandle = GL.GenBuffer(); - - uint[] Buffer = new uint[] - { - 0xc4700000, 0x80000000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, - 0x45340000, 0x80000000, 0x00000000, 0xffffffff, 0x00000000, 0x3f800000, 0x00000000, - 0xc4700000, 0xc4070000, 0x00000000, 0xffffffff, 0x00000000, 0x00000000, 0x3f800000, - 0x45340000, 0xc4070000, 0x00000000, 0xffffffff, 0x00000000, 0x3f800000, 0x3f800000 - }; - - IntPtr Length = new IntPtr(Buffer.Length * 4); - - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); - GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); - GL.BindBuffer(BufferTarget.ArrayBuffer, 0); - - GL.BindVertexArray(VaoHandle); - - GL.EnableVertexAttribArray(0); - - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); - - GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 28, 0); - - GL.EnableVertexAttribArray(1); - - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); - - GL.VertexAttribPointer(1, 4, VertexAttribPointerType.UnsignedByte, false, 28, 12); - - GL.EnableVertexAttribArray(2); - - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); - - GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, 28, 20); - - GL.BindVertexArray(0); - } - - private void CreateShaders() - { - VtxShaderHandle = GL.CreateShader(ShaderType.VertexShader); - FragShaderHandle = GL.CreateShader(ShaderType.FragmentShader); - - GL.ShaderSource(VtxShaderHandle, VtxShaderSource); - GL.ShaderSource(FragShaderHandle, FragShaderSource); - GL.CompileShader(VtxShaderHandle); - GL.CompileShader(FragShaderHandle); - - PrgShaderHandle = GL.CreateProgram(); - - GL.AttachShader(PrgShaderHandle, VtxShaderHandle); - GL.AttachShader(PrgShaderHandle, FragShaderHandle); - GL.LinkProgram(PrgShaderHandle); - GL.UseProgram(PrgShaderHandle); - - int TexLocation = GL.GetUniformLocation(PrgShaderHandle, "tex"); - - GL.Uniform1(TexLocation, 0); + Renderer.InitializeFrameBuffer(); } protected override void OnUpdateFrame(FrameEventArgs e) { - HidControllerKeys CurrentButton = 0; - JoystickPosition LeftJoystick; - JoystickPosition RightJoystick; + HidControllerButtons CurrentButton = 0; + HidJoystickPosition LeftJoystick; + HidJoystickPosition RightJoystick; - if (Keyboard[OpenTK.Input.Key.Escape]) this.Exit(); + if (Keyboard[Key.Escape]) this.Exit(); - //RightJoystick int LeftJoystickDX = 0; int LeftJoystickDY = 0; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickUp]) LeftJoystickDY = short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickDown]) LeftJoystickDY = -short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickLeft]) LeftJoystickDX = -short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickRight]) LeftJoystickDX = short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickButton]) CurrentButton |= HidControllerKeys.KEY_LSTICK; - - //LeftButtons - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadUp]) CurrentButton |= HidControllerKeys.KEY_DUP; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadDown]) CurrentButton |= HidControllerKeys.KEY_DDOWN; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadLeft]) CurrentButton |= HidControllerKeys.KEY_DLEFT; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadRight]) CurrentButton |= HidControllerKeys.KEY_DRIGHT; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonMinus]) CurrentButton |= HidControllerKeys.KEY_MINUS; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonL]) CurrentButton |= HidControllerKeys.KEY_L; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonZL]) CurrentButton |= HidControllerKeys.KEY_ZL; - - //RightJoystick int RightJoystickDX = 0; int RightJoystickDY = 0; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickUp]) RightJoystickDY = short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickDown]) RightJoystickDY = -short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickLeft]) RightJoystickDX = -short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickRight]) RightJoystickDX = short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickButton]) CurrentButton |= HidControllerKeys.KEY_RSTICK; + + //RightJoystick + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickUp]) LeftJoystickDY = short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickDown]) LeftJoystickDY = -short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickLeft]) LeftJoystickDX = -short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickRight]) LeftJoystickDX = short.MaxValue; + + //LeftButtons + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickButton]) CurrentButton |= HidControllerButtons.KEY_LSTICK; + if (Keyboard[(Key)Config.FakeJoyCon.Left.DPadUp]) CurrentButton |= HidControllerButtons.KEY_DUP; + if (Keyboard[(Key)Config.FakeJoyCon.Left.DPadDown]) CurrentButton |= HidControllerButtons.KEY_DDOWN; + if (Keyboard[(Key)Config.FakeJoyCon.Left.DPadLeft]) CurrentButton |= HidControllerButtons.KEY_DLEFT; + if (Keyboard[(Key)Config.FakeJoyCon.Left.DPadRight]) CurrentButton |= HidControllerButtons.KEY_DRIGHT; + if (Keyboard[(Key)Config.FakeJoyCon.Left.ButtonMinus]) CurrentButton |= HidControllerButtons.KEY_MINUS; + if (Keyboard[(Key)Config.FakeJoyCon.Left.ButtonL]) CurrentButton |= HidControllerButtons.KEY_L; + if (Keyboard[(Key)Config.FakeJoyCon.Left.ButtonZL]) CurrentButton |= HidControllerButtons.KEY_ZL; + + //RightJoystick + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickUp]) RightJoystickDY = short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickDown]) RightJoystickDY = -short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickLeft]) RightJoystickDX = -short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickRight]) RightJoystickDX = short.MaxValue; //RightButtons - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonA]) CurrentButton |= HidControllerKeys.KEY_A; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonB]) CurrentButton |= HidControllerKeys.KEY_B; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonX]) CurrentButton |= HidControllerKeys.KEY_X; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonY]) CurrentButton |= HidControllerKeys.KEY_Y; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonPlus]) CurrentButton |= HidControllerKeys.KEY_PLUS; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonR]) CurrentButton |= HidControllerKeys.KEY_R; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonZR]) CurrentButton |= HidControllerKeys.KEY_ZR; + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickButton]) CurrentButton |= HidControllerButtons.KEY_RSTICK; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonA]) CurrentButton |= HidControllerButtons.KEY_A; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonB]) CurrentButton |= HidControllerButtons.KEY_B; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonX]) CurrentButton |= HidControllerButtons.KEY_X; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonY]) CurrentButton |= HidControllerButtons.KEY_Y; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonPlus]) CurrentButton |= HidControllerButtons.KEY_PLUS; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonR]) CurrentButton |= HidControllerButtons.KEY_R; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonZR]) CurrentButton |= HidControllerButtons.KEY_ZR; - LeftJoystick = new JoystickPosition + LeftJoystick = new HidJoystickPosition { DX = LeftJoystickDX, DY = LeftJoystickDY }; - RightJoystick = new JoystickPosition + RightJoystick = new HidJoystickPosition { DX = RightJoystickDX, DY = RightJoystickDY }; - //We just need one pair of JoyCon because it's emulate by the keyboard. - Ns.Hid.SendControllerButtons(HidControllerID.CONTROLLER_HANDHELD, HidControllerLayouts.Main, CurrentButton, LeftJoystick, RightJoystick); + bool HasTouch = false; + + //Get screen touch position from left mouse click + //OpenTK always captures mouse events, even if out of focus, so check if window is focused. + if (Focused && Mouse?.GetState().LeftButton == ButtonState.Pressed) + { + int ScrnWidth = Width; + int ScrnHeight = Height; + + if (Width > Height * TouchScreenRatioX) + { + ScrnWidth = (int)(Height * TouchScreenRatioX); + } + else + { + ScrnHeight = (int)(Width * TouchScreenRatioY); + } + + int StartX = (Width - ScrnWidth) >> 1; + int StartY = (Height - ScrnHeight) >> 1; + + int EndX = StartX + ScrnWidth; + int EndY = StartY + ScrnHeight; + + if (Mouse.X >= StartX && + Mouse.Y >= StartY && + Mouse.X < EndX && + Mouse.Y < EndY) + { + int ScrnMouseX = Mouse.X - StartX; + int ScrnMouseY = Mouse.Y - StartY; + + int MX = (int)(((float)ScrnMouseX / ScrnWidth) * TouchScreenWidth); + int MY = (int)(((float)ScrnMouseY / ScrnHeight) * TouchScreenHeight); + + HidTouchPoint CurrentPoint = new HidTouchPoint + { + X = MX, + Y = MY, + + //Placeholder values till more data is acquired + DiameterX = 10, + DiameterY = 10, + Angle = 90 + }; + + HasTouch = true; + + Ns.Hid.SetTouchPoints(CurrentPoint); + } + } + + if (!HasTouch) + { + Ns.Hid.SetTouchPoints(); + } + + Ns.Hid.SetJoyconButton( + HidControllerId.CONTROLLER_HANDHELD, + HidControllerLayouts.Main, + CurrentButton, + LeftJoystick, + RightJoystick); } protected override void OnRenderFrame(FrameEventArgs e) { - GL.Viewport(0, 0, 1280, 720); + Ns.Statistics.StartSystemFrame(); - Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {1f / e.Time:0})"; + GL.Viewport(0, 0, Width, Height); + + Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " + + $"{Ns.Statistics.GameFrameRate:0})"; GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); - RenderFb(); - - GL.UseProgram(PrgShaderHandle); - Renderer.RunActions(); - Renderer.BindTexture(0); - Renderer.Render(); + Renderer.Render(); SwapBuffers(); + + Ns.Statistics.EndSystemFrame(); } - void RenderFb() + protected override void OnResize(EventArgs e) { - GL.ActiveTexture(TextureUnit.Texture0); - GL.BindTexture(TextureTarget.Texture2D, ScreenTex.Texture); - GL.BindVertexArray(VaoHandle); - GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4); + Renderer.SetWindowSize(Width, Height); } } } \ No newline at end of file diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs index 2f29411a9..b67e52bdc 100644 --- a/Ryujinx/Ui/Program.cs +++ b/Ryujinx/Ui/Program.cs @@ -1,5 +1,6 @@ -using Gal; -using Gal.OpenGL; +using Ryujinx.Core; +using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Gal.OpenGL; using System; using System.IO; @@ -9,6 +10,8 @@ namespace Ryujinx { static void Main(string[] args) { + AOptimizations.DisableMemoryChecks = true; + Config.Read(); Console.Title = "Ryujinx Console"; @@ -27,20 +30,20 @@ namespace Ryujinx { Logging.Info("Loading as cart with RomFS."); - Ns.Os.LoadCart(args[0], RomFsFiles[0]); + Ns.LoadCart(args[0], RomFsFiles[0]); } else { Logging.Info("Loading as cart WITHOUT RomFS."); - Ns.Os.LoadCart(args[0]); + Ns.LoadCart(args[0]); } } else if (File.Exists(args[0])) { Logging.Info("Loading as homebrew."); - Ns.Os.LoadProgram(args[0]); + Ns.LoadProgram(args[0]); } } else @@ -58,9 +61,7 @@ namespace Ryujinx Screen.Run(60.0); } - Ns.Os.FinalizeAllProcesses(); - - Ns.Dispose(); + Environment.Exit(0); } } }