diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index e192f5ec5e..d32bd9cd24 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -42,6 +42,14 @@ namespace ChocolArm64 Set("x1111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg)); Set("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem)); Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu)); + Set("x0011010110xxxxx010000xxxxxxxxxx", AInstEmit.Crc32b, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010001xxxxxxxxxx", AInstEmit.Crc32h, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010010xxxxxxxxxx", AInstEmit.Crc32w, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010011xxxxxxxxxx", AInstEmit.Crc32x, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010100xxxxxxxxxx", AInstEmit.Crc32cb, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010101xxxxxxxxxx", AInstEmit.Crc32ch, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010110xxxxxxxxxx", AInstEmit.Crc32cw, typeof(AOpCodeAluRs)); + Set("x0011010110xxxxx010111xxxxxxxxxx", AInstEmit.Crc32cx, typeof(AOpCodeAluRs)); Set("x0011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csel, typeof(AOpCodeCsel)); Set("x0011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csinc, typeof(AOpCodeCsel)); Set("x1011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csinv, typeof(AOpCodeCsel)); @@ -161,8 +169,10 @@ namespace ChocolArm64 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)); @@ -181,7 +191,7 @@ namespace ChocolArm64 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)); @@ -223,6 +234,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)); @@ -237,14 +249,18 @@ namespace ChocolArm64 Set("0x0011100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_V, typeof(AOpCodeSimd)); Set("010111110>>>>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("0x1011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Sli_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)); diff --git a/ChocolArm64/AOptimizations.cs b/ChocolArm64/AOptimizations.cs index cbfd1ce51b..a3c82dccde 100644 --- a/ChocolArm64/AOptimizations.cs +++ b/ChocolArm64/AOptimizations.cs @@ -1,4 +1,4 @@ public static class AOptimizations { - + public static bool DisableMemoryChecks = false; } \ No newline at end of file diff --git a/ChocolArm64/AThread.cs b/ChocolArm64/AThread.cs index 6e018db684..62f9d2d396 100644 --- a/ChocolArm64/AThread.cs +++ b/ChocolArm64/AThread.cs @@ -10,47 +10,34 @@ 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(ATranslator Translator, 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(); - 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() @@ -62,11 +49,11 @@ namespace ChocolArm64 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/ATranslator.cs b/ChocolArm64/ATranslator.cs index ab434e2267..02c18efd2b 100644 --- a/ChocolArm64/ATranslator.cs +++ b/ChocolArm64/ATranslator.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection.Emit; -using System.Threading; namespace ChocolArm64 { @@ -23,8 +22,6 @@ namespace ChocolArm64 public bool EnableCpuTrace { get; set; } - private bool KeepRunning; - public ATranslator(IReadOnlyDictionary SymbolTable = null) { SubBlocks = new HashSet(); @@ -39,12 +36,8 @@ namespace ChocolArm64 { this.SymbolTable = new ConcurrentDictionary(); } - - KeepRunning = true; } - internal void StopExecution() => KeepRunning = false; - internal void ExecuteSubroutine(AThread Thread, long Position) { do @@ -71,7 +64,7 @@ namespace ChocolArm64 Position = Sub.Execute(Thread.ThreadState, Thread.Memory); } - while (Position != 0 && KeepRunning); + while (Position != 0 && Thread.ThreadState.Running); } internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub) diff --git a/ChocolArm64/Decoder/AOpCodeSimdExt.cs b/ChocolArm64/Decoder/AOpCodeSimdExt.cs index cf22d6546f..888e447030 100644 --- a/ChocolArm64/Decoder/AOpCodeSimdExt.cs +++ b/ChocolArm64/Decoder/AOpCodeSimdExt.cs @@ -8,7 +8,7 @@ namespace ChocolArm64.Decoder public AOpCodeSimdExt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { - int Imm4 = (OpCode >> 11) & 0xf; + Imm4 = (OpCode >> 11) & 0xf; } } } \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdImm.cs b/ChocolArm64/Decoder/AOpCodeSimdImm.cs index 2959aee6de..e7dfe62114 100644 --- a/ChocolArm64/Decoder/AOpCodeSimdImm.cs +++ b/ChocolArm64/Decoder/AOpCodeSimdImm.cs @@ -88,7 +88,14 @@ namespace ChocolArm64.Decoder private static long ShlOnes(long Value, int Shift) { - return Value << Shift | (long)(ulong.MaxValue >> (64 - Shift)); + if (Shift != 0) + { + return Value << Shift | (long)(ulong.MaxValue >> (64 - Shift)); + } + else + { + return Value; + } } } } \ No newline at end of file diff --git a/ChocolArm64/Decoder/AOpCodeSimdReg.cs b/ChocolArm64/Decoder/AOpCodeSimdReg.cs index 10a4aff8b4..702ffed1e2 100644 --- a/ChocolArm64/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 index d878b12ea7..721da88faa 100644 --- a/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs +++ b/ChocolArm64/Decoder/AOpCodeSimdRegElem.cs @@ -8,14 +8,24 @@ namespace ChocolArm64.Decoder public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { - if ((Size & 1) != 0) + switch (Size) { - Index = (OpCode >> 11) & 1; - } - else - { - Index = (OpCode >> 21) & 1 | - (OpCode >> 10) & 2; + 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; } } } diff --git a/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs b/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs new file mode 100644 index 0000000000..e61d7093a7 --- /dev/null +++ b/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs @@ -0,0 +1,22 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdRegElemF : AOpCodeSimdReg + { + public int Index { get; private set; } + + public AOpCodeSimdRegElemF(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + if ((Size & 1) != 0) + { + Index = (OpCode >> 11) & 1; + } + else + { + Index = (OpCode >> 21) & 1 | + (OpCode >> 10) & 2; + } + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitException.cs b/ChocolArm64/Instruction/AInstEmitException.cs index fe348edd17..3964c94979 100644 --- a/ChocolArm64/Instruction/AInstEmitException.cs +++ b/ChocolArm64/Instruction/AInstEmitException.cs @@ -34,6 +34,22 @@ 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); diff --git a/ChocolArm64/Instruction/AInstEmitHash.cs b/ChocolArm64/Instruction/AInstEmitHash.cs new file mode 100644 index 0000000000..94e03f6c1f --- /dev/null +++ b/ChocolArm64/Instruction/AInstEmitHash.cs @@ -0,0 +1,73 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; +using ChocolArm64.Translation; +using System.Reflection.Emit; + +namespace ChocolArm64.Instruction +{ + static partial class AInstEmit + { + public static void Crc32b(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32b)); + } + + public static void Crc32h(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32h)); + } + + public static void Crc32w(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32w)); + } + + public static void Crc32x(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32x)); + } + + public static void Crc32cb(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32cb)); + } + + public static void Crc32ch(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32ch)); + } + + public static void Crc32cw(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32cw)); + } + + public static void Crc32cx(AILEmitterCtx Context) + { + EmitCrc32(Context, nameof(ASoftFallback.Crc32cx)); + } + + private static void EmitCrc32(AILEmitterCtx Context, string Name) + { + AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp; + + Context.EmitLdintzr(Op.Rn); + + if (Op.RegisterSize != ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U4); + } + + Context.EmitLdintzr(Op.Rm); + + ASoftFallback.EmitCall(Context, Name); + + if (Op.RegisterSize != ARegisterSize.Int32) + { + Context.Emit(OpCodes.Conv_U8); + } + + Context.EmitStintzr(Op.Rd); + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs index d5a0051b5a..6ffcf2dc95 100644 --- a/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitMemoryHelper.cs @@ -45,21 +45,46 @@ namespace ChocolArm64.Instruction { 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; + 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 = 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; + 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; } } @@ -107,21 +132,46 @@ namespace ChocolArm64.Instruction { 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; + 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 = 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; + 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; } } diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index 9c1bc28626..bf980a581e 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -267,6 +267,14 @@ 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, () => @@ -341,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)); @@ -369,6 +382,15 @@ 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)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs index 688f05a209..b9944e565f 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs @@ -31,6 +31,36 @@ namespace ChocolArm64.Instruction 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, () => EmitUnaryMathCall(Context, nameof(Math.Floor))); @@ -41,6 +71,41 @@ namespace ChocolArm64.Instruction 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, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling))); diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 68ee3d3e4e..4e45a11d6a 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -190,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); @@ -200,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; @@ -236,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(); @@ -250,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; @@ -341,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); @@ -411,15 +464,25 @@ namespace ChocolArm64.Instruction public static void EmitVectorWidenRnRmBinaryOpSx(AILEmitterCtx Context, Action Emit) { - EmitVectorWidenRnRmBinaryOp(Context, Emit, true); + EmitVectorWidenRnRmOp(Context, Emit, false, true); } public static void EmitVectorWidenRnRmBinaryOpZx(AILEmitterCtx Context, Action Emit) { - EmitVectorWidenRnRmBinaryOp(Context, Emit, false); + EmitVectorWidenRnRmOp(Context, Emit, false, false); } - public static void EmitVectorWidenRnRmBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed) + 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; @@ -429,6 +492,11 @@ namespace ChocolArm64.Instruction 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); diff --git a/ChocolArm64/Instruction/AInstEmitSimdMove.cs b/ChocolArm64/Instruction/AInstEmitSimdMove.cs index a4e533709e..3f427ad8ad 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdMove.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdMove.cs @@ -63,15 +63,18 @@ namespace ChocolArm64.Instruction int Bytes = Context.CurrOp.GetBitsCount() >> 3; + int Position = Op.Imm4; + for (int Index = 0; Index < Bytes; Index++) { - int Position = Op.Imm4 + Index; + int Reg = Op.Imm4 + Index < Bytes ? Op.Rn : Op.Rm; - int Reg = Position < Bytes ? Op.Rn : Op.Rm; + if (Position == Bytes) + { + Position = 0; + } - Position &= Bytes - 1; - - EmitVectorExtractZx(Context, Reg, Position, 0); + EmitVectorExtractZx(Context, Reg, Position++, 0); EmitVectorInsert(Context, Op.Rd, Index, 0); } diff --git a/ChocolArm64/Instruction/AInstEmitSimdShift.cs b/ChocolArm64/Instruction/AInstEmitSimdShift.cs index 8740ba4d69..24d35abe4c 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdShift.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdShift.cs @@ -32,6 +32,15 @@ namespace ChocolArm64.Instruction EmitVectorShImmBinaryZx(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) { AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; @@ -41,6 +50,40 @@ namespace ChocolArm64.Instruction EmitVectorShImmNarrowBinaryZx(Context, () => Context.Emit(OpCodes.Shr_Un), Shift); } + public static void Sli_V(AILEmitterCtx Context) + { + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Shift = Op.Imm - (8 << Op.Size); + + ulong Mask = Shift != 0 ? ulong.MaxValue >> (64 - Shift) : 0; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); + + Context.EmitLdc_I4(Shift); + + Context.Emit(OpCodes.Shl); + + EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); + + Context.EmitLdc_I8((long)Mask); + + Context.Emit(OpCodes.And); + Context.Emit(OpCodes.Or); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + public static void Sshl_V(AILEmitterCtx Context) { EmitVectorShl(Context, Signed: true); @@ -77,6 +120,21 @@ namespace ChocolArm64.Instruction EmitVectorShImmBinarySx(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); + }; + + EmitVectorShImmTernarySx(Context, Emit, Shift); + } + public static void Ushl_V(AILEmitterCtx Context) { EmitVectorShl(Context, Signed: false); @@ -195,22 +253,32 @@ namespace ChocolArm64.Instruction private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) { - EmitVectorShImmBinaryOp(Context, Emit, Imm, true); + EmitVectorShImmOp(Context, Emit, Imm, false, true); + } + + private static void EmitVectorShImmTernarySx(AILEmitterCtx Context, Action Emit, int Imm) + { + EmitVectorShImmOp(Context, Emit, Imm, true, true); } private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) { - EmitVectorShImmBinaryOp(Context, Emit, Imm, false); + EmitVectorShImmOp(Context, Emit, Imm, false, false); } - private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) + private static void EmitVectorShImmOp(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); @@ -238,7 +306,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; @@ -273,7 +341,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/ChocolArm64/Instruction/AInstEmitSystem.cs b/ChocolArm64/Instruction/AInstEmitSystem.cs index f9d186020e..1c5d02634f 100644 --- a/ChocolArm64/Instruction/AInstEmitSystem.cs +++ b/ChocolArm64/Instruction/AInstEmitSystem.cs @@ -30,6 +30,7 @@ namespace ChocolArm64.Instruction case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break; case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break; case 0b11_011_1101_0000_011: PropName = nameof(AThreadState.Tpidr); break; + case 0b11_011_1110_0000_000: PropName = nameof(AThreadState.CntfrqEl0); break; case 0b11_011_1110_0000_001: PropName = nameof(AThreadState.CntpctEl0); break; default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}"); @@ -97,7 +98,7 @@ namespace ChocolArm64.Instruction for (int Offs = 0; Offs < (4 << AThreadState.DczSizeLog2); Offs += 8) { Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); - Context.EmitLdint(Op.Rt); + Context.EmitLdintzr(Op.Rt); Context.EmitLdc_I(Offs); Context.Emit(OpCodes.Add); @@ -106,8 +107,13 @@ namespace ChocolArm64.Instruction AInstEmitMemoryHelper.EmitWriteCall(Context, 3); } + break; } + + //No-op + case 0b11_011_0111_1110_001: //DC CIVAC + break; } } diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs index 797d815736..f79628ad03 100644 --- a/ChocolArm64/Instruction/ASoftFallback.cs +++ b/ChocolArm64/Instruction/ASoftFallback.cs @@ -38,6 +38,65 @@ namespace ChocolArm64.Instruction return (ulong)Size; } + private const uint Crc32RevPoly = 0xedb88320; + private const uint Crc32cRevPoly = 0x82f63b78; + + public static uint Crc32b(uint Crc, byte Val) => Crc32 (Crc, Crc32RevPoly, Val); + public static uint Crc32h(uint Crc, byte Val) => Crc32h(Crc, Crc32RevPoly, Val); + public static uint Crc32w(uint Crc, byte Val) => Crc32w(Crc, Crc32RevPoly, Val); + public static uint Crc32x(uint Crc, byte Val) => Crc32x(Crc, Crc32RevPoly, Val); + + public static uint Crc32cb(uint Crc, byte Val) => Crc32 (Crc, Crc32cRevPoly, Val); + public static uint Crc32ch(uint Crc, byte Val) => Crc32h(Crc, Crc32cRevPoly, Val); + public static uint Crc32cw(uint Crc, byte Val) => Crc32w(Crc, Crc32cRevPoly, Val); + public static uint Crc32cx(uint Crc, byte Val) => Crc32x(Crc, Crc32cRevPoly, Val); + + private static uint Crc32h(uint Crc, uint Poly, ushort Val) + { + Crc = Crc32(Crc, Poly, (byte)(Val >> 0)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 8)); + + return Crc; + } + + private static uint Crc32w(uint Crc, uint Poly, uint Val) + { + Crc = Crc32(Crc, Poly, (byte)(Val >> 0)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 8)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 16)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 24)); + + return Crc; + } + + private static uint Crc32x(uint Crc, uint Poly, ulong Val) + { + Crc = Crc32(Crc, Poly, (byte)(Val >> 0)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 8)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 16)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 24)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 32)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 40)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 48)); + Crc = Crc32(Crc, Poly, (byte)(Val >> 56)); + + return Crc; + } + + private static uint Crc32(uint Crc, uint Poly, byte Val) + { + Crc ^= Val; + + for (int Bit = 7; Bit >= 0; Bit--) + { + uint Mask = (uint)(-(int)(Crc & 1)); + + Crc = (Crc >> 1) ^ (Poly & Mask); + } + + return Crc; + } + public static uint ReverseBits32(uint Value) { Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1); diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs index 0d202fed30..d7e11189f6 100644 --- a/ChocolArm64/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,9 +39,11 @@ namespace ChocolArm64.Memory private HashSet ExAddrs; + public IntPtr Ram { get; private set; } + private byte* RamPtr; - public AMemory(IntPtr Ram) + public AMemory() { Manager = new AMemoryMgr(); @@ -49,6 +51,8 @@ namespace ChocolArm64.Memory 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/ChocolArm64/Memory/AMemoryMgr.cs b/ChocolArm64/Memory/AMemoryMgr.cs index bc36445242..8a165b0796 100644 --- a/ChocolArm64/Memory/AMemoryMgr.cs +++ b/ChocolArm64/Memory/AMemoryMgr.cs @@ -4,8 +4,8 @@ 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 = 10; private const int PTLvl1Bits = 10; @@ -19,8 +19,8 @@ namespace ChocolArm64.Memory private const int PTLvl1Mask = PTLvl1Size - 1; public const int PageMask = PageSize - 1; - private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; - private const int PTLvl1Bit = PTPageBits; + private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; + private const int PTLvl1Bit = PTPageBits; private enum PTMap { diff --git a/ChocolArm64/State/AThreadState.cs b/ChocolArm64/State/AThreadState.cs index 2d988a6536..6f3f62f69f 100644 --- a/ChocolArm64/State/AThreadState.cs +++ b/ChocolArm64/State/AThreadState.cs @@ -1,5 +1,6 @@ using ChocolArm64.Events; using System; +using System.Diagnostics; namespace ChocolArm64.State { @@ -29,6 +30,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; } @@ -38,15 +41,34 @@ 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; + public ulong CntfrqEl0 { get; set; } + public ulong CntpctEl0 + { + get + { + double Ticks = TickCounter.ElapsedTicks * HostTickFreq; - public long CntpctEl0 => Environment.TickCount * TicksPerMS; + return (ulong)(Ticks * CntfrqEl0); + } + } public event EventHandler Break; public event EventHandler SvcCall; public event EventHandler Undefined; + private static Stopwatch TickCounter; + + private static double HostTickFreq; + + static AThreadState() + { + HostTickFreq = 1.0 / Stopwatch.Frequency; + + TickCounter = new Stopwatch(); + + TickCounter.Start(); + } + internal void OnBreak(int Imm) { Break?.Invoke(this, new AInstExceptionEventArgs(Imm)); diff --git a/Ryujinx.Core/Hid/Hid.cs b/Ryujinx.Core/Hid/Hid.cs index d7227889a6..f25a943752 100644 --- a/Ryujinx.Core/Hid/Hid.cs +++ b/Ryujinx.Core/Hid/Hid.cs @@ -1,5 +1,6 @@ using ChocolArm64.Memory; -using System.Diagnostics; +using Ryujinx.Core.OsHle.Handles; +using System; namespace Ryujinx.Core.Input { @@ -60,33 +61,63 @@ namespace Ryujinx.Core.Input private const int HidEntryCount = 17; - private long SharedMemOffset; + private object ShMemLock; - private Switch Ns; + private (AMemory, long)[] ShMemPositions; - public Hid(Switch Ns) + public Hid() { - this.Ns = Ns; + ShMemLock = new object(); + + ShMemPositions = new (AMemory, long)[0]; } - public void Init(long HidOffset) + internal void ShMemMap(object sender, EventArgs e) { - SharedMemOffset = HidOffset; + 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); } - public void InitializeJoyconPair( + private void InitializeJoyconPair( + AMemory Memory, + long Position, JoyConColor LeftColorBody, JoyConColor LeftColorButtons, JoyConColor RightColorBody, JoyConColor RightColorButtons) { - long BaseControllerOffset = HidControllersOffset + 8 * HidControllerSize; + long BaseControllerOffset = Position + HidControllersOffset + 8 * HidControllerSize; HidControllerType Type = HidControllerType.ControllerType_Handheld | @@ -102,20 +133,20 @@ namespace Ryujinx.Core.Input HidControllerColorDesc SplitColorDesc = 0; - WriteInt32(BaseControllerOffset + 0x0, (int)Type); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x0, (int)Type); - WriteInt32(BaseControllerOffset + 0x4, IsHalf ? 1 : 0); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x4, IsHalf ? 1 : 0); - WriteInt32(BaseControllerOffset + 0x8, (int)SingleColorDesc); - WriteInt32(BaseControllerOffset + 0xc, (int)SingleColorBody); - WriteInt32(BaseControllerOffset + 0x10, (int)SingleColorButtons); - WriteInt32(BaseControllerOffset + 0x14, (int)SplitColorDesc); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x8, (int)SingleColorDesc); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0xc, (int)SingleColorBody); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x10, (int)SingleColorButtons); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x14, (int)SplitColorDesc); - WriteInt32(BaseControllerOffset + 0x18, (int)LeftColorBody); - WriteInt32(BaseControllerOffset + 0x1c, (int)LeftColorButtons); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x18, (int)LeftColorBody); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x1c, (int)LeftColorButtons); - WriteInt32(BaseControllerOffset + 0x20, (int)RightColorBody); - WriteInt32(BaseControllerOffset + 0x24, (int)RightColorButtons); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x20, (int)RightColorBody); + Memory.WriteInt32Unchecked(BaseControllerOffset + 0x24, (int)RightColorButtons); } public void SetJoyconButton( @@ -125,115 +156,109 @@ namespace Ryujinx.Core.Input HidJoystickPosition LeftStick, HidJoystickPosition RightStick) { - long ControllerOffset = HidControllersOffset + (int)ControllerId * HidControllerSize; + lock (ShMemLock) + { + foreach ((AMemory Memory, long Position) in ShMemPositions) + { + long ControllerOffset = Position + HidControllersOffset; - ControllerOffset += HidControllerHeaderSize; + ControllerOffset += (int)ControllerId * HidControllerSize; - ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; + ControllerOffset += HidControllerHeaderSize; - long LastEntry = ReadInt64(ControllerOffset + 0x10); + ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; - long CurrEntry = (LastEntry + 1) % HidEntryCount; + long LastEntry = Memory.ReadInt64Unchecked(ControllerOffset + 0x10); - long Timestamp = Stopwatch.GetTimestamp(); + long CurrEntry = (LastEntry + 1) % HidEntryCount; - WriteInt64(ControllerOffset + 0x0, Timestamp); - WriteInt64(ControllerOffset + 0x8, HidEntryCount); - WriteInt64(ControllerOffset + 0x10, CurrEntry); - WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1); + long Timestamp = GetTimestamp(); - ControllerOffset += HidControllersLayoutHeaderSize; + Memory.WriteInt64Unchecked(ControllerOffset + 0x0, Timestamp); + Memory.WriteInt64Unchecked(ControllerOffset + 0x8, HidEntryCount); + Memory.WriteInt64Unchecked(ControllerOffset + 0x10, CurrEntry); + Memory.WriteInt64Unchecked(ControllerOffset + 0x18, HidEntryCount - 1); - ControllerOffset += CurrEntry * HidControllersInputEntrySize; + ControllerOffset += HidControllersLayoutHeaderSize; - WriteInt64(ControllerOffset + 0x0, Timestamp); - WriteInt64(ControllerOffset + 0x8, Timestamp); + ControllerOffset += CurrEntry * HidControllersInputEntrySize; - WriteInt64(ControllerOffset + 0x10, (uint)Buttons); + Memory.WriteInt64Unchecked(ControllerOffset + 0x0, Timestamp); + Memory.WriteInt64Unchecked(ControllerOffset + 0x8, Timestamp); - WriteInt32(ControllerOffset + 0x18, LeftStick.DX); - WriteInt32(ControllerOffset + 0x1c, LeftStick.DY); + Memory.WriteInt64Unchecked(ControllerOffset + 0x10, (uint)Buttons); - WriteInt64(ControllerOffset + 0x20, RightStick.DX); - WriteInt64(ControllerOffset + 0x24, RightStick.DY); + Memory.WriteInt32Unchecked(ControllerOffset + 0x18, LeftStick.DX); + Memory.WriteInt32Unchecked(ControllerOffset + 0x1c, LeftStick.DY); - WriteInt64(ControllerOffset + 0x28, - (uint)HidControllerConnState.Controller_State_Connected | - (uint)HidControllerConnState.Controller_State_Wired); + 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) { - long LastEntry = ReadInt64(HidTouchScreenOffset + 0x10); - - long CurrEntry = (LastEntry + 1) % HidEntryCount; - - long Timestamp = Stopwatch.GetTimestamp(); - - WriteInt64(HidTouchScreenOffset + 0x0, Timestamp); - WriteInt64(HidTouchScreenOffset + 0x8, HidEntryCount); - WriteInt64(HidTouchScreenOffset + 0x10, CurrEntry); - WriteInt64(HidTouchScreenOffset + 0x18, HidEntryCount - 1); - WriteInt64(HidTouchScreenOffset + 0x20, Timestamp); - - long TouchEntryOffset = HidTouchScreenOffset + HidTouchHeaderSize; - - long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize; - - long LastTimestamp = ReadInt64(LastEntryOffset); - - TouchEntryOffset += CurrEntry * HidTouchEntrySize; - - WriteInt64(TouchEntryOffset + 0x0, LastTimestamp + 1); - WriteInt64(TouchEntryOffset + 0x8, Points.Length); - - TouchEntryOffset += HidTouchEntryHeaderSize; - - const int Padding = 0; - - int Index = 0; - - foreach (HidTouchPoint Point in Points) + lock (ShMemLock) { - WriteInt64(TouchEntryOffset + 0x0, Timestamp); - WriteInt32(TouchEntryOffset + 0x8, Padding); - WriteInt32(TouchEntryOffset + 0xc, Index++); - WriteInt32(TouchEntryOffset + 0x10, Point.X); - WriteInt32(TouchEntryOffset + 0x14, Point.Y); - WriteInt32(TouchEntryOffset + 0x18, Point.DiameterX); - WriteInt32(TouchEntryOffset + 0x1c, Point.DiameterY); - WriteInt32(TouchEntryOffset + 0x20, Point.Angle); - WriteInt32(TouchEntryOffset + 0x24, Padding); + foreach ((AMemory Memory, long Position) in ShMemPositions) + { + long TouchScreenOffset = Position + HidTouchScreenOffset; - TouchEntryOffset += HidTouchEntryTouchSize; + 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 unsafe long ReadInt64(long Position) + private static long GetTimestamp() { - Position += SharedMemOffset; - - if ((ulong)Position + 8 > AMemoryMgr.AddrSize) return 0; - - return *((long*)((byte*)Ns.Ram + Position)); - } - - private unsafe void WriteInt32(long Position, int Value) - { - Position += SharedMemOffset; - - if ((ulong)Position + 4 > AMemoryMgr.AddrSize) return; - - *((int*)((byte*)Ns.Ram + Position)) = Value; - } - - private unsafe void WriteInt64(long Position, long Value) - { - Position += SharedMemOffset; - - if ((ulong)Position + 8 > AMemoryMgr.AddrSize) return; - - *((long*)((byte*)Ns.Ram + Position)) = Value; + return (long)((ulong)Environment.TickCount * 19_200); } } } \ No newline at end of file diff --git a/Ryujinx.Core/Loaders/Executable.cs b/Ryujinx.Core/Loaders/Executable.cs index fa204460f6..943b8e5107 100644 --- a/Ryujinx.Core/Loaders/Executable.cs +++ b/Ryujinx.Core/Loaders/Executable.cs @@ -34,7 +34,7 @@ namespace Ryujinx.Core.Loaders if (Exe.Mod0Offset == 0) { - int BssOffset = Exe.DataOffset + Exe.Data.Count; + int BssOffset = Exe.DataOffset + Exe.Data.Length; int BssSize = Exe.BssSize; MapBss(ImageBase + BssOffset, BssSize); @@ -92,18 +92,15 @@ namespace Ryujinx.Core.Loaders private void WriteData( long Position, - IList Data, + byte[] Data, MemoryType Type, AMemoryPerm Perm) { - Memory.Manager.Map(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) diff --git a/Ryujinx.Core/Loaders/Executables/IExecutable.cs b/Ryujinx.Core/Loaders/Executables/IExecutable.cs index 73787b1d24..09d0aab236 100644 --- a/Ryujinx.Core/Loaders/Executables/IExecutable.cs +++ b/Ryujinx.Core/Loaders/Executables/IExecutable.cs @@ -1,12 +1,10 @@ -using System.Collections.ObjectModel; - namespace Ryujinx.Core.Loaders.Executables { public interface IExecutable { - ReadOnlyCollection Text { get; } - ReadOnlyCollection RO { get; } - ReadOnlyCollection Data { get; } + byte[] Text { get; } + byte[] RO { get; } + byte[] Data { get; } int Mod0Offset { get; } int TextOffset { get; } diff --git a/Ryujinx.Core/Loaders/Executables/Nro.cs b/Ryujinx.Core/Loaders/Executables/Nro.cs index 3cbc4c5d49..9f4ef59f57 100644 --- a/Ryujinx.Core/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.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.Core.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.Core/Loaders/Executables/Nso.cs b/Ryujinx.Core/Loaders/Executables/Nso.cs index 7b8bf253a7..7341ba6226 100644 --- a/Ryujinx.Core/Loaders/Executables/Nso.cs +++ b/Ryujinx.Core/Loaders/Executables/Nso.cs @@ -1,19 +1,14 @@ using Ryujinx.Core.Loaders.Compression; using System; -using System.Collections.ObjectModel; using System.IO; 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.Core.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.Core.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 index b14f266539..89064ccbc8 100644 --- a/Ryujinx.Core/Logging.cs +++ b/Ryujinx.Core/Logging.cs @@ -8,7 +8,8 @@ namespace Ryujinx.Core { public static class Logging { - private static Stopwatch ExecutionTime = new Stopwatch(); + private static Stopwatch ExecutionTime; + private const string LogFileName = "Ryujinx.log"; private static bool EnableInfo = Config.LoggingEnableInfo; @@ -22,9 +23,11 @@ namespace Ryujinx.Core static Logging() { - ExecutionTime.Start(); - if (File.Exists(LogFileName)) File.Delete(LogFileName); + + ExecutionTime = new Stopwatch(); + + ExecutionTime.Start(); } public static string GetExecutionTime() diff --git a/Ryujinx.Core/OsHle/CondVar.cs b/Ryujinx.Core/OsHle/CondVar.cs index 7b3e18521a..f5fe3d292e 100644 --- a/Ryujinx.Core/OsHle/CondVar.cs +++ b/Ryujinx.Core/OsHle/CondVar.cs @@ -4,7 +4,7 @@ using System.Threading; namespace Ryujinx.Core.OsHle { - public class CondVar + class CondVar { private Process Process; @@ -24,7 +24,7 @@ namespace Ryujinx.Core.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.Core.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.Core.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 0000000000..4210b23000 --- /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/Services/ErrorModule.cs b/Ryujinx.Core/OsHle/ErrorModule.cs similarity index 98% rename from Ryujinx.Core/OsHle/Services/ErrorModule.cs rename to Ryujinx.Core/OsHle/ErrorModule.cs index 78af6195db..f23e8d2703 100644 --- a/Ryujinx.Core/OsHle/Services/ErrorModule.cs +++ b/Ryujinx.Core/OsHle/ErrorModule.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Core.OsHle.IpcServices +namespace Ryujinx.Core.OsHle { enum ErrorModule { diff --git a/Ryujinx.Core/OsHle/Handles/HDomain.cs b/Ryujinx.Core/OsHle/Handles/HDomain.cs index ca287a5fda..26c604554f 100644 --- a/Ryujinx.Core/OsHle/Handles/HDomain.cs +++ b/Ryujinx.Core/OsHle/Handles/HDomain.cs @@ -1,58 +1,48 @@ -using Ryujinx.Core.OsHle.Utilities; using System; -using System.Collections.Generic; namespace Ryujinx.Core.OsHle.Handles { - class HDomain : HSession + class HDomain : HSession, IDisposable { - private Dictionary Objects; - - private IdPool ObjIds; + private IdDictionary Objects; public HDomain(HSession Session) : base(Session) { - Objects = new Dictionary(); - - ObjIds = new IdPool(); + Objects = new IdDictionary(); } - public int GenerateObjectId(object Obj) + public int Add(object Obj) { - int Id = ObjIds.GenerateId(); - - if (Id == -1) - { - throw new InvalidOperationException(); - } - - Objects.Add(Id, Obj); - - return Id; + return Objects.Add(Obj); } - public void DeleteObject(int Id) + public bool Delete(int Id) { - if (Objects.TryGetValue(Id, out object Obj)) - { - if (Obj is IDisposable DisposableObj) - { - DisposableObj.Dispose(); - } - - ObjIds.DeleteId(Id); - Objects.Remove(Id); - } + return Objects.Delete(Id); } public object GetObject(int Id) { - if (Objects.TryGetValue(Id, out object Obj)) - { - return Obj; - } + return Objects.GetData(Id); + } - return null; + 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/HNvMap.cs b/Ryujinx.Core/OsHle/Handles/HNvMap.cs deleted file mode 100644 index 09173730b3..0000000000 --- a/Ryujinx.Core/OsHle/Handles/HNvMap.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.Core.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.Core/OsHle/Handles/HSharedMem.cs b/Ryujinx.Core/OsHle/Handles/HSharedMem.cs index 9cd0a0d47e..b6bdc89878 100644 --- a/Ryujinx.Core/OsHle/Handles/HSharedMem.cs +++ b/Ryujinx.Core/OsHle/Handles/HSharedMem.cs @@ -1,3 +1,4 @@ +using ChocolArm64.Memory; using System; using System.Collections.Generic; @@ -5,66 +6,39 @@ namespace Ryujinx.Core.OsHle.Handles { class HSharedMem { - private List Positions; - - public int PositionsCount => Positions.Count; + private List<(AMemory, long)> Positions; public EventHandler MemoryMapped; public EventHandler MemoryUnmapped; public HSharedMem() { - Positions = new List(); + Positions = new List<(AMemory, long)>(); } - public void AddVirtualPosition(long Position) + public void AddVirtualPosition(AMemory Memory, long Position) { lock (Positions) { - Positions.Add(Position); + Positions.Add((Memory, Position)); MemoryMapped?.Invoke(this, EventArgs.Empty); } } - public void RemoveVirtualPosition(long Position) + public void RemoveVirtualPosition(AMemory Memory, long Position) { lock (Positions) { - Positions.Remove(Position); + Positions.Remove((Memory, Position)); MemoryUnmapped?.Invoke(this, EventArgs.Empty); } } - public long GetVirtualPosition(int Index) + public (AMemory, long)[] GetVirtualPositions() { - 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; - } + return Positions.ToArray(); } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs b/Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs new file mode 100644 index 0000000000..1156e035fd --- /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.Core/OsHle/Handles/KProcessScheduler.cs b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs index 2045f8a17a..b111607811 100644 --- a/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs +++ b/Ryujinx.Core/OsHle/Handles/KProcessScheduler.cs @@ -183,7 +183,7 @@ namespace Ryujinx.Core.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.Core.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 index e2e95e4d41..2a717ca738 100644 --- a/Ryujinx.Core/OsHle/Homebrew.cs +++ b/Ryujinx.Core/OsHle/Homebrew.cs @@ -37,5 +37,33 @@ namespace Ryujinx.Core.OsHle 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 index c238914398..c3f8cd8b00 100644 --- a/Ryujinx.Core/OsHle/Horizon.cs +++ b/Ryujinx.Core/OsHle/Horizon.cs @@ -1,33 +1,23 @@ using Ryujinx.Core.Loaders.Executables; using Ryujinx.Core.OsHle.Handles; -using Ryujinx.Core.OsHle.Utilities; using System; using System.Collections.Concurrent; using System.IO; namespace Ryujinx.Core.OsHle { - public class Horizon + public class Horizon : IDisposable { internal const int HidSize = 0x40000; internal const int FontSize = 0x50; - internal int HidHandle { get; private set; } - internal int FontHandle { 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; } + internal ConcurrentDictionary Mutexes { get; private set; } + internal ConcurrentDictionary CondVars { get; private set; } private ConcurrentDictionary Processes; - private HSharedMem HidSharedMem; + internal HSharedMem HidSharedMem; + internal HSharedMem FontSharedMem; private Switch Ns; @@ -35,25 +25,13 @@ namespace Ryujinx.Core.OsHle { 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(); - HidSharedMem = new HSharedMem(); - - HidSharedMem.MemoryMapped += HidInit; - - HidHandle = Handles.GenerateId(HidSharedMem); - - FontHandle = Handles.GenerateId(new HSharedMem()); + HidSharedMem = new HSharedMem(); + FontSharedMem = new HSharedMem(); } public void LoadCart(string ExeFsDir, string RomFsFile = null) @@ -63,9 +41,7 @@ namespace Ryujinx.Core.OsHle Ns.VFs.LoadRomFs(RomFsFile); } - int ProcessId = IdGen.GenerateId(); - - Process MainProcess = new Process(Ns, ProcessId); + Process MainProcess = MakeProcess(); void LoadNso(string FileName) { @@ -96,17 +72,13 @@ namespace Ryujinx.Core.OsHle LoadNso("sdk"); MainProcess.Run(); - - Processes.TryAdd(ProcessId, MainProcess); } public void LoadProgram(string FileName) { bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro"; - int ProcessId = IdGen.GenerateId(); - - Process MainProcess = new Process(Ns, ProcessId); + Process MainProcess = MakeProcess(); using (FileStream Input = new FileStream(FileName, FileMode.Open)) { @@ -117,34 +89,67 @@ namespace Ryujinx.Core.OsHle MainProcess.SetEmptyArgs(); MainProcess.Run(IsNro); - - Processes.TryAdd(ProcessId, MainProcess); } - public void FinalizeAllProcesses() + private Process MakeProcess() { - foreach (Process Process in Processes.Values) + Process Process; + + lock (Processes) { - Process.StopAllThreads(); + 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(); - } - } - 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); + } } - - if (Processes.Count == 0) - { - Ns.OnFinish(EventArgs.Empty); - } - - return Success; } internal bool TryGetProcess(int ProcessId, out Process Process) @@ -152,30 +157,20 @@ namespace Ryujinx.Core.OsHle return Processes.TryGetValue(ProcessId, out Process); } - internal void CloseHandle(int Handle) + public void Dispose() { - object HndData = Handles.GetData(Handle); - - if (HndData is HTransferMem TransferMem) - { - TransferMem.Memory.Manager.Reprotect( - TransferMem.Position, - TransferMem.Size, - TransferMem.Perm); - } - - Handles.Delete(Handle); + Dispose(true); } - private void HidInit(object sender, EventArgs e) + protected virtual void Dispose(bool Disposing) { - HSharedMem SharedMem = (HSharedMem)sender; - - if (SharedMem.TryGetLastVirtualPosition(out long Position)) + if (Disposing) { - Logging.Info($"HID shared memory successfully mapped to 0x{Position:x16}!"); - - Ns.Hid.Init(Position); + foreach (Process Process in Processes.Values) + { + Process.StopAllThreadsAsync(); + Process.Dispose(); + } } } } diff --git a/Ryujinx.Core/OsHle/Utilities/IdPoolWithObj.cs b/Ryujinx.Core/OsHle/IdDictionary.cs similarity index 50% rename from Ryujinx.Core/OsHle/Utilities/IdPoolWithObj.cs rename to Ryujinx.Core/OsHle/IdDictionary.cs index f0a339df3a..0b90924616 100644 --- a/Ryujinx.Core/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.Core.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.Core.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.Core.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.Core.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.Core/OsHle/Ipc/IpcHandler.cs b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs index 2495d7014c..f2179a962c 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcHandler.cs @@ -13,6 +13,7 @@ namespace Ryujinx.Core.OsHle.Ipc public static void IpcCall( Switch Ns, + Process Process, AMemory Memory, HSession Session, IpcMessage Request, @@ -60,7 +61,7 @@ namespace Ryujinx.Core.OsHle.Ipc } else if (Request.DomCmd == IpcDomCmd.DeleteObj) { - Dom.DeleteObject(Request.DomObjId); + Dom.Delete(Request.DomObjId); Response = FillResponse(Response, 0); @@ -100,6 +101,7 @@ namespace Ryujinx.Core.OsHle.Ipc ServiceCtx Context = new ServiceCtx( Ns, + Process, Memory, Session, Request, @@ -124,15 +126,43 @@ namespace Ryujinx.Core.OsHle.Ipc switch (CmdId) { - case 0: Request = IpcConvertSessionToDomain(Ns, Session, Response, HndId); break; - case 3: Request = IpcQueryBufferPointerSize(Response); break; - case 2: //IpcDuplicateSession, differences is unknown. - case 4: Request = IpcDuplicateSessionEx(Ns, Session, Response, ReqReader); break; + 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.Unknown2) + else if (Request.Type == IpcMessageType.CloseSession) { //TODO } @@ -145,39 +175,6 @@ namespace Ryujinx.Core.OsHle.Ipc } } - 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()) diff --git a/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs index cc26df1082..ebb3dbca04 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcMessage.cs @@ -217,16 +217,26 @@ namespace Ryujinx.Core.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; } } diff --git a/Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs b/Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs index 8027508de2..560af41e74 100644 --- a/Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs +++ b/Ryujinx.Core/OsHle/Ipc/IpcMessageType.cs @@ -2,9 +2,9 @@ namespace Ryujinx.Core.OsHle.Ipc { enum IpcMessageType { - Response = 0, - Unknown2 = 2, - Request = 4, - Control = 5 + Response = 0, + CloseSession = 2, + Request = 4, + Control = 5 } } \ 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 0000000000..19983af19d --- /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 index e362ba9fd7..75b97b1f2c 100644 --- a/Ryujinx.Core/OsHle/MemoryRegions.cs +++ b/Ryujinx.Core/OsHle/MemoryRegions.cs @@ -7,7 +7,10 @@ namespace Ryujinx.Core.OsHle public const long AddrSpaceStart = 0x08000000; public const long MapRegionAddress = 0x10000000; - public const long MapRegionSize = 0x10000000; + public const long MapRegionSize = 0x20000000; + + public const long HeapRegionAddress = MapRegionAddress + MapRegionSize; + public const long HeapRegionSize = TlsPagesAddress - HeapRegionAddress; public const long MainStackSize = 0x100000; @@ -17,8 +20,6 @@ namespace Ryujinx.Core.OsHle public const long TlsPagesAddress = MainStackAddress - TlsPagesSize; - public const long HeapRegionAddress = MapRegionAddress + MapRegionSize; - public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize; public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart; diff --git a/Ryujinx.Core/OsHle/Mutex.cs b/Ryujinx.Core/OsHle/Mutex.cs index c95ed77164..c619e12174 100644 --- a/Ryujinx.Core/OsHle/Mutex.cs +++ b/Ryujinx.Core/OsHle/Mutex.cs @@ -4,7 +4,7 @@ using System.Threading; namespace Ryujinx.Core.OsHle { - public class Mutex + class Mutex { private const int MutexHasListenersMask = 0x40000000; diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs index f549b02792..239b198039 100644 --- a/Ryujinx.Core/OsHle/Process.cs +++ b/Ryujinx.Core/OsHle/Process.cs @@ -9,25 +9,34 @@ using Ryujinx.Core.OsHle.Svc; using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Threading; namespace Ryujinx.Core.OsHle { - public class Process : IDisposable + class Process : IDisposable { - private const int TlsSize = 0x200; - private const int TotalTlsSlots = 32; + private const int TlsSize = 0x200; + private const int TotalTlsSlots = 32; + + private const int TickFreq = 19_200_000; 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; @@ -40,14 +49,22 @@ namespace Ryujinx.Core.OsHle private long ImageBase; + private bool ShouldDispose; + + private bool Disposed; + public Process(Switch Ns, int ProcessId) { this.Ns = Ns; this.ProcessId = ProcessId; - Memory = new AMemory(Ns.Ram); + Memory = new AMemory(); - Scheduler = new KProcessScheduler(); + Services = new ServiceMgr(); + + HandleTable = new KProcessHandleTable(); + + Scheduler = new KProcessScheduler(); SvcHandler = new SvcHandler(Ns, this); @@ -67,6 +84,11 @@ namespace Ryujinx.Core.OsHle 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); @@ -78,11 +100,19 @@ namespace Ryujinx.Core.OsHle public void SetEmptyArgs() { + //TODO: This should be part of Run. ImageBase += AMemoryMgr.PageSize; } - public bool Run(bool UseHbAbi = false) + public bool Run(bool NeedsHbAbi = false) { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Process)); + } + + this.NeedsHbAbi = NeedsHbAbi; + if (Executables.Count == 0) { return false; @@ -102,11 +132,11 @@ namespace Ryujinx.Core.OsHle return false; } - MainThread = Ns.Os.Handles.GetData(Handle); + MainThread = HandleTable.GetData(Handle); - if (UseHbAbi) + if (NeedsHbAbi) { - long HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd); + HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd); Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle); @@ -124,22 +154,21 @@ namespace Ryujinx.Core.OsHle Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW); } - public void StopAllThreads() + public void StopAllThreadsAsync() { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Process)); + } + if (MainThread != null) { - while (MainThread.Thread.IsAlive) - { - MainThread.Thread.StopExecution(); - } + MainThread.Thread.StopExecution(); } foreach (AThread Thread in TlsSlots.Values) { - while (Thread.IsAlive) - { - Thread.StopExecution(); - } + Thread.StopExecution(); } } @@ -150,49 +179,27 @@ namespace Ryujinx.Core.OsHle int Priority, int ProcessorId) { - ThreadPriority ThreadPrio; - - if (Priority < 12) + if (Disposed) { - 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; + throw new ObjectDisposedException(nameof(Process)); } - AThread Thread = new AThread(GetTranslator(), Memory, ThreadPrio, EntryPoint); + AThread Thread = new AThread(GetTranslator(), Memory, EntryPoint); HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority); - int Handle = Ns.Os.Handles.GenerateId(ThreadHnd); + int Handle = HandleTable.OpenHandle(ThreadHnd); - int TlsSlot = GetFreeTlsSlot(Thread); + int ThreadId = GetFreeTlsSlot(Thread); - if (TlsSlot == -1 || Handle == -1) - { - return -1; - } - - long Tpidr = MemoryRegions.TlsPagesAddress + TlsSlot * TlsSize; + 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 = Ns.Os.IdGen.GenerateId(); + Thread.ThreadState.ThreadId = ThreadId; + Thread.ThreadState.CntfrqEl0 = TickFreq; Thread.ThreadState.Tpidr = Tpidr; Thread.ThreadState.X0 = (ulong)ArgsPtr; Thread.ThreadState.X1 = (ulong)Handle; @@ -224,7 +231,7 @@ namespace Ryujinx.Core.OsHle foreach (Executable Exe in Executables) { foreach (KeyValuePair KV in Exe.SymbolTable) - { + { SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value); } } @@ -274,16 +281,28 @@ namespace Ryujinx.Core.OsHle } } - return -1; + throw new InvalidOperationException(); } private void ThreadFinished(object sender, EventArgs e) { if (sender is AThread Thread) { - TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _); + Logging.Info($"Thread {Thread.ThreadId} exiting..."); - Ns.Os.IdGen.DeleteId(Thread.ThreadId); + 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); } } @@ -309,9 +328,30 @@ namespace Ryujinx.Core.OsHle protected virtual void Dispose(bool Disposing) { - if (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..."); } } } diff --git a/Ryujinx.Core/OsHle/ServiceCtx.cs b/Ryujinx.Core/OsHle/ServiceCtx.cs index 31ecce3dec..60c378d5a7 100644 --- a/Ryujinx.Core/OsHle/ServiceCtx.cs +++ b/Ryujinx.Core/OsHle/ServiceCtx.cs @@ -8,6 +8,7 @@ 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.Core.OsHle public ServiceCtx( Switch Ns, + Process Process, AMemory Memory, HSession Session, IpcMessage Request, @@ -25,6 +27,7 @@ namespace Ryujinx.Core.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 0000000000..f59647afef --- /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.Core/OsHle/Services/Aud/IAudioDevice.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs new file mode 100644 index 0000000000..9ebf140a26 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs @@ -0,0 +1,63 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; +using System.Text; + +namespace Ryujinx.Core.OsHle.IpcServices.Aud +{ + class IAudioDevice : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public IAudioDevice() + { + m_Commands = new Dictionary() + { + { 0, ListAudioDeviceName }, + { 1, SetAudioDeviceOutputVolume }, + }; + } + + public long ListAudioDeviceName(ServiceCtx Context) + { + string[] Names = new string[] { "FIXME" }; + + Context.ResponseData.Write(Names.Length); + + long Position = Context.Request.ReceiveBuff[0].Position; + long Size = Context.Request.ReceiveBuff[0].Size; + + long BasePosition = Position; + + foreach (string Name in Names) + { + byte[] Buffer = Encoding.ASCII.GetBytes(Name + '\0'); + + if ((Position - BasePosition) + Buffer.Length > Size) + { + break; + } + + AMemoryHelper.WriteBytes(Context.Memory, Position, Buffer); + + Position += Buffer.Length; + } + + return 0; + } + + public long SetAudioDeviceOutputVolume(ServiceCtx Context) + { + float Volume = Context.RequestData.ReadSingle(); + + long Position = Context.Request.SendBuff[0].Position; + long Size = Context.Request.SendBuff[0].Size; + + string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position, (int)Size); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs index c58d9e804c..2312920f6c 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs @@ -139,7 +139,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.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); diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs index bfde0b65b1..4d29371fbd 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioRenderer.cs @@ -56,7 +56,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.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/ServiceAudRen.cs b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs index eccc699c43..c3a0a8b436 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/ServiceAudRen.cs @@ -17,6 +17,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud { { 0, OpenAudioRenderer }, { 1, GetAudioRendererWorkBufferSize }, + { 2, GetAudioDevice } }; } @@ -47,5 +48,14 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud return 0; } + + public long GetAudioDevice(ServiceCtx Context) + { + long UserId = Context.RequestData.ReadInt64(); + + MakeObject(Context, new IAudioDevice()); + + 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 index b594441272..da1e51e17c 100644 --- a/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs +++ b/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs @@ -1,14 +1,64 @@ +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() @@ -16,26 +66,31 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd { 0, Initialize }, { 1, StartMonitoring }, { 2, Socket }, + { 6, Poll }, + { 8, Recv }, { 10, Send }, - { 14, Connect } + { 11, SendTo }, + { 12, Accept }, + { 13, Bind }, + { 14, Connect }, + { 18, Listen }, + { 21, SetSockOpt }, + { 26, Close } }; } - //Initialize(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno + //(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; */ @@ -52,7 +107,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd return 0; } - //StartMonitoring(u64, pid) + //(u64, pid) public long StartMonitoring(ServiceCtx Context) { //Todo: Stub @@ -60,37 +115,378 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd return 0; } - //Socket(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) + //(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) public long Socket(ServiceCtx Context) { - Context.ResponseData.Write(0); - Context.ResponseData.Write(0); + SocketBsd NewBSDSocket = new SocketBsd + { + Family = Context.RequestData.ReadInt32(), + Type = Context.RequestData.ReadInt32(), + Protocol = Context.RequestData.ReadInt32() + }; - //Todo: Stub + 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; } - //Connect(u32 socket, buffer) -> (i32 ret, u32 bsd_errno) - public long Connect(ServiceCtx Context) + //(u32, u32, buffer) -> (i32 ret, u32 bsd_errno, buffer) + public long Poll(ServiceCtx Context) { - Context.ResponseData.Write(0); - Context.ResponseData.Write(0); + int PollCount = Context.RequestData.ReadInt32(); + int TimeOut = Context.RequestData.ReadInt32(); - //Todo: Stub + //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; } - //Send(u32 socket, u32 flags, buffer) -> (i32 ret, u32 bsd_errno) + //(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) { - Context.ResponseData.Write(0); - Context.ResponseData.Write(0); + 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); - //Todo: Stub + 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.Core/OsHle/Services/ErrorCode.cs b/Ryujinx.Core/OsHle/Services/ErrorCode.cs deleted file mode 100644 index a4e197b225..0000000000 --- a/Ryujinx.Core/OsHle/Services/ErrorCode.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.Core.OsHle.IpcServices -{ - static class ErrorCode - { - public static long MakeError(ErrorModule Module, int Code) - { - return (int)Module | (Code << 9); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs index 569a7dd6b4..62bcb8e8b2 100644 --- a/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs +++ b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Text; -using static Ryujinx.Core.OsHle.IpcServices.ErrorCode; +using static Ryujinx.Core.OsHle.ErrorCode; using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; namespace Ryujinx.Core.OsHle.IpcServices.FspSrv diff --git a/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs b/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs index d22e0ff23b..ef437c022f 100644 --- a/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs +++ b/Ryujinx.Core/OsHle/Services/Hid/IAppletResource.cs @@ -10,21 +10,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid public IReadOnlyDictionary Commands => m_Commands; - private 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 index cc30a771e0..6735c6ad2c 100644 --- a/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs +++ b/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs @@ -1,6 +1,6 @@ -using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; +using Ryujinx.Core.Input; using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; @@ -16,25 +16,27 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid { 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 } + { 0, CreateAppletResource }, + { 11, ActivateTouchScreen }, + { 66, StartSixAxisSensor }, + { 100, SetSupportedNpadStyleSet }, + { 101, GetSupportedNpadStyleSet }, + { 102, SetSupportedNpadIdType }, + { 103, ActivateNpad }, + { 120, SetNpadJoyHoldType }, + { 122, SetNpadJoyAssignmentModeSingleByDefault }, + { 123, SetNpadJoyAssignmentModeSingle }, + { 124, SetNpadJoyAssignmentModeDual }, + { 125, MergeSingleJoyAsDualJoy }, + { 200, GetVibrationDeviceInfo }, + { 203, CreateActiveVibrationDeviceList }, + { 206, SendVibrationValues } }; } public long CreateAppletResource(ServiceCtx Context) { - HSharedMem HidHndData = Context.Ns.Os.Handles.GetData(Context.Ns.Os.HidHandle); - - MakeObject(Context, new IAppletResource(HidHndData)); + MakeObject(Context, new IAppletResource(Context.Ns.Os.HidSharedMem)); return 0; } @@ -46,6 +48,15 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid return 0; } + public long StartSixAxisSensor(ServiceCtx Context) + { + int Handle = Context.RequestData.ReadInt32(); + + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + return 0; + } + public long GetSupportedNpadStyleSet(ServiceCtx Context) { Context.ResponseData.Write(0); @@ -90,6 +101,40 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid return 0; } + public long SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx Context) + { + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + long AppletUserResourseId = Context.RequestData.ReadInt64(); + + return 0; + } + + public long SetNpadJoyAssignmentModeSingle(ServiceCtx Context) + { + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + long AppletUserResourseId = Context.RequestData.ReadInt64(); + long NpadJoyDeviceType = Context.RequestData.ReadInt64(); + + return 0; + } + + public long SetNpadJoyAssignmentModeDual(ServiceCtx Context) + { + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + long AppletUserResourseId = Context.RequestData.ReadInt64(); + + return 0; + } + + public long MergeSingleJoyAsDualJoy(ServiceCtx Context) + { + long Unknown0 = Context.RequestData.ReadInt32(); + long Unknown8 = Context.RequestData.ReadInt32(); + long AppletUserResourseId = Context.RequestData.ReadInt64(); + + return 0; + } + public long GetVibrationDeviceInfo(ServiceCtx Context) { int VibrationDeviceHandle = Context.RequestData.ReadInt32(); diff --git a/Ryujinx.Core/OsHle/Services/Nv/NvFd.cs b/Ryujinx.Core/OsHle/Services/Nv/NvFd.cs new file mode 100644 index 0000000000..dbce74adfc --- /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 0000000000..ca844f9f28 --- /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.Core/OsHle/Services/Nv/ServiceNvDrv.cs b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs index 0ea1d2ac0a..67ad44919a 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs @@ -1,5 +1,4 @@ using ChocolArm64.Memory; -using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Utilities; using Ryujinx.Graphics.Gpu; @@ -12,40 +11,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices { private delegate long ServiceProcessIoctl(ServiceCtx Context); - private static Dictionary<(string, int), ServiceProcessIoctl> 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", 0x0109), NvMapIocParam }, - { ("/dev/nvmap", 0x010e), NvMapIocGetId }, - }; - 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() { m_Commands = new Dictionary() @@ -57,15 +33,50 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices { 4, QueryEvent }, { 8, SetClientPid }, }; + + 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 static long Open(ServiceCtx Context) + 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); @@ -73,14 +84,14 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - public static long Ioctl(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); @@ -94,18 +105,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices } } - public static long Close(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 Initialize(ServiceCtx Context) + public long Initialize(ServiceCtx Context) { long TransferMemSize = Context.RequestData.ReadInt64(); int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0]; @@ -115,7 +126,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - public static long QueryEvent(ServiceCtx Context) + public long QueryEvent(ServiceCtx Context) { int Fd = Context.RequestData.ReadInt32(); int EventId = Context.RequestData.ReadInt32(); @@ -127,7 +138,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices return 0; } - public static long SetClientPid(ServiceCtx Context) + public long SetClientPid(ServiceCtx Context) { long Pid = Context.RequestData.ReadInt64(); @@ -136,18 +147,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -171,9 +182,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -185,18 +196,29 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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.MapMemory(NvMap.Address, Offset, NvMap.Size); - } - else - { - Offset = Context.Ns.Gpu.MapMemory(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); @@ -204,9 +226,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -234,9 +256,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -251,9 +273,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -266,9 +288,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -282,18 +304,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -311,9 +333,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -373,9 +395,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -387,9 +409,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -397,25 +419,25 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -452,9 +474,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -464,9 +486,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -477,9 +499,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -491,18 +513,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -520,47 +542,46 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); - Logging.Info($"NvMap {Id} created with size {Size:x8}!"); + 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(); @@ -573,19 +594,49 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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. } + 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(); @@ -594,16 +645,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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); @@ -611,17 +669,29 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices 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.Core/OsHle/Services/ObjHelper.cs b/Ryujinx.Core/OsHle/Services/ObjHelper.cs index ff71838a69..89d986aeb0 100644 --- a/Ryujinx.Core/OsHle/Services/ObjHelper.cs +++ b/Ryujinx.Core/OsHle/Services/ObjHelper.cs @@ -9,13 +9,13 @@ namespace Ryujinx.Core.OsHle.IpcServices { 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.Core/OsHle/Services/Pl/ServicePl.cs b/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs index abc34ed213..9a61779934 100644 --- a/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs +++ b/Ryujinx.Core/OsHle/Services/Pl/ServicePl.cs @@ -13,6 +13,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Pl { m_Commands = new Dictionary() { + { 0, RequestLoad }, { 1, GetLoadState }, { 2, GetFontSize }, { 3, GetSharedMemoryAddressOffset }, @@ -20,30 +21,39 @@ namespace Ryujinx.Core.OsHle.IpcServices.Pl }; } - public static long GetLoadState(ServiceCtx Context) + public long RequestLoad(ServiceCtx Context) + { + SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32(); + + return 0; + } + + public long GetLoadState(ServiceCtx Context) { Context.ResponseData.Write(1); //Loaded return 0; } - public static long GetFontSize(ServiceCtx Context) + public long GetFontSize(ServiceCtx Context) { Context.ResponseData.Write(Horizon.FontSize); return 0; } - public static long GetSharedMemoryAddressOffset(ServiceCtx Context) + public long GetSharedMemoryAddressOffset(ServiceCtx Context) { Context.ResponseData.Write(0); return 0; } - public static long GetSharedMemoryNativeHandle(ServiceCtx Context) + public long GetSharedMemoryNativeHandle(ServiceCtx Context) { - Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.FontHandle); + int Handle = Context.Process.HandleTable.OpenHandle(Context.Ns.Os.FontSharedMem); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); return 0; } diff --git a/Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs b/Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs new file mode 100644 index 0000000000..29fe02b8a4 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Core.OsHle.IpcServices.Pl +{ + enum SharedFontType + { + JapanUsEurope = 0, + SimplifiedChinese = 1, + SimplifiedChineseEx = 2, + TraditionalChinese = 3, + Korean = 4, + NintendoEx = 5 + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs b/Ryujinx.Core/OsHle/Services/ServiceFactory.cs deleted file mode 100644 index 31c8aa2c86..0000000000 --- a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs +++ /dev/null @@ -1,63 +0,0 @@ -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; - -namespace Ryujinx.Core.OsHle.IpcServices -{ - static class ServiceFactory - { - public static IIpcService MakeService(string Name) - { - switch (Name) - { - case "acc:u0": return new ServiceAcc(); - case "aoc:u": return new ServiceNs(); - case "apm": return new ServiceApm(); - case "apm:p": return new ServiceApm(); - case "appletOE": return new ServiceAppletOE(); - case "audout:u": return new ServiceAudOut(); - case "audren:u": return new ServiceAudRen(); - case "bsd:u": return new ServiceBsd(); - case "friend:a": return new ServiceFriend(); - case "fsp-srv": return new ServiceFspSrv(); - case "hid": return new ServiceHid(); - case "lm": return new ServiceLm(); - case "nifm:u": return new ServiceNifm(); - case "nvdrv": return new ServiceNvDrv(); - case "nvdrv:a": return new ServiceNvDrv(); - case "pctl:a": return new ServicePctl(); - case "pl:u": return new ServicePl(); - case "set": return new ServiceSet(); - case "set:sys": return new ServiceSetSys(); - case "sfdnsres": return new ServiceSfdnsres(); - case "sm:": return new ServiceSm(); - case "ssl": return new ServiceSsl(); - case "time:s": return new ServiceTime(); - case "time:u": return new ServiceTime(); - case "vi:m": return new ServiceVi(); - case "vi:s": return new ServiceVi(); - case "vi:u": return new ServiceVi(); - } - - throw new NotImplementedException(Name); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs b/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs index a5f1b329b8..cb745e3738 100644 --- a/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs +++ b/Ryujinx.Core/OsHle/Services/Sm/ServiceSm.cs @@ -55,9 +55,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Sm return 0; } - HSession Session = new HSession(ServiceFactory.MakeService(Name)); + HSession Session = new HSession(Context.Process.Services.GetService(Name)); - int Handle = Context.Ns.Os.Handles.GenerateId(Session); + int Handle = Context.Process.HandleTable.OpenHandle(Session); Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); diff --git a/Ryujinx.Core/OsHle/Display.cs b/Ryujinx.Core/OsHle/Services/Vi/Display.cs similarity index 79% rename from Ryujinx.Core/OsHle/Display.cs rename to Ryujinx.Core/OsHle/Services/Vi/Display.cs index 590841fc50..ceadc39311 100644 --- a/Ryujinx.Core/OsHle/Display.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/Display.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Core.OsHle +namespace Ryujinx.Core.OsHle.IpcServices.Vi { class Display { diff --git a/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs b/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs index a899cdd56d..0ff1f90993 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/IApplicationDisplayService.cs @@ -15,6 +15,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi public IReadOnlyDictionary Commands => m_Commands; + private IdDictionary Displays; + public IApplicationDisplayService() { m_Commands = new Dictionary() @@ -24,16 +26,21 @@ namespace Ryujinx.Core.OsHle.IpcServices.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.Core.OsHle.IpcServices.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.Core.OsHle.IpcServices.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.Core.OsHle.IpcServices.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.Core.OsHle.IpcServices.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.Core.OsHle.IpcServices.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.Core.OsHle.IpcServices.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 index a89c1df824..b24a773bfe 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/IHOSBinderDriver.cs @@ -1,6 +1,7 @@ using ChocolArm64.Memory; using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.IpcServices.Android; +using Ryujinx.Graphics.Gal; using System; using System.Collections.Generic; @@ -14,7 +15,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi private NvFlinger Flinger; - public IHOSBinderDriver() + public IHOSBinderDriver(IGalRenderer Renderer) { m_Commands = new Dictionary() { @@ -23,7 +24,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi { 2, GetNativeHandle } }; - Flinger = new NvFlinger(); + Flinger = new NvFlinger(Renderer); } public long TransactParcel(ServiceCtx Context) diff --git a/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs b/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs index 5adee78d1e..69dbff47a9 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/IManagerDisplayService.cs @@ -13,8 +13,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi { m_Commands = new Dictionary() { - { 2010, CreateManagedLayer }, - { 6000, AddToLayerStack } + { 2010, CreateManagedLayer }, + { 2011, DestroyManagedLayer }, + { 6000, AddToLayerStack } }; } @@ -25,6 +26,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi return 0; } + public long DestroyManagedLayer(ServiceCtx Context) + { + return 0; + } + public static long AddToLayerStack(ServiceCtx Context) { return 0; diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index b745867e92..5309dcabb7 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -1,5 +1,6 @@ using ChocolArm64.Memory; -using Ryujinx.Core.OsHle.Handles; +using Ryujinx.Core.OsHle.IpcServices.NvServices; +using Ryujinx.Graphics.Gal; using System; using System.IO; using System.Collections.Generic; @@ -54,29 +55,43 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android 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() + 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; } @@ -154,6 +169,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader) { + Context.Ns.Statistics.RecordGameFrameTime(); + //TODO: Errors. int Slot = ParcelReader.ReadInt32(); int Unknown4 = ParcelReader.ReadInt32(); @@ -190,6 +207,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android 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. @@ -210,6 +232,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android 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(); @@ -256,11 +283,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android int FbWidth = BufferQueue[Slot].Data.Width; int FbHeight = BufferQueue[Slot].Data.Height; - int FbSize = FbWidth * FbHeight * 4; + long FbSize = (uint)FbWidth * FbHeight * 4; - HNvMap NvMap = GetNvMap(Context, Slot); + NvMap NvMap = GetNvMap(Context, Slot); - if (FbSize < 0 || NvMap.Address < 0 || NvMap.Address + FbSize > AMemoryMgr.AddrSize) + if ((ulong)(NvMap.Address + FbSize) > AMemoryMgr.AddrSize) { Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!"); @@ -278,45 +305,61 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android 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; - ScaleX = (float)FbWidth / RealWidth; - ScaleY = (float)FbHeight / RealHeight; + if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90)) + { + ScaleY = (float)FbHeight / RealHeight; + ScaleX = (float)FbWidth / RealWidth; - OffsX = -(float)Crop.Left / Crop.Right; - OffsY = -(float)Crop.Top / Crop.Bottom; + 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 += ScaleX - 1; - OffsY += ScaleY - 1; + 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.FlipX)) - { - ScaleX = -ScaleX; - } - - if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY)) - { - ScaleY = -ScaleY; - } - if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90)) { Rotate = -MathF.PI * 0.5f; } - byte* Fb = (byte*)Context.Ns.Ram + NvMap.Address; + lock (RenderQueueLock) + { + if (NvFlingerDisposed) + { + return; + } + + Interlocked.Increment(ref RenderQueueCount); + } + + byte* Fb = (byte*)Context.Memory.Ram + NvMap.Address; Context.Ns.Gpu.Renderer.QueueAction(delegate() { @@ -332,6 +375,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android BufferQueue[Slot].State = BufferState.Free; + Interlocked.Decrement(ref RenderQueueCount); + lock (WaitBufferFree) { WaitBufferFree.Set(); @@ -339,7 +384,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android }); } - private HNvMap GetNvMap(ServiceCtx Context, int Slot) + private NvMap GetNvMap(ServiceCtx Context, int Slot) { int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c); @@ -352,7 +397,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android NvMapHandle = BitConverter.ToInt32(RawValue, 0); } - return Context.Ns.Os.Handles.GetData(NvMapHandle); + ServiceNvDrv NvDrv = (ServiceNvDrv)Context.Process.Services.GetService("nvdrv"); + + return NvDrv.GetNvMap(NvMapHandle); } private int GetFreeSlotBlocking(int Width, int Height) @@ -418,10 +465,24 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android Dispose(true); } - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool Disposing) { - if (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; diff --git a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs index 212d30a6c3..3ce56a3cd7 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs @@ -1,12 +1,13 @@ using ChocolArm64.Events; using ChocolArm64.Memory; using ChocolArm64.State; +using Ryujinx.Core.OsHle.Handles; using System; using System.Collections.Generic; namespace Ryujinx.Core.OsHle.Svc { - partial class SvcHandler + partial class SvcHandler : IDisposable { private delegate void SvcFunc(AThreadState ThreadState); @@ -16,10 +17,12 @@ namespace Ryujinx.Core.OsHle.Svc private Process Process; private AMemory Memory; - private static Random Rng; - + private HashSet<(HSharedMem, long)> MappedSharedMems; + private ulong CurrentHeapSize; + private static Random Rng; + public SvcHandler(Switch Ns, Process Process) { SvcFuncs = new Dictionary() @@ -32,10 +35,12 @@ namespace Ryujinx.Core.OsHle.Svc { 0x07, SvcExitProcess }, { 0x08, SvcCreateThread }, { 0x09, SvcStartThread }, + { 0x0a, SvcExitThread }, { 0x0b, SvcSleepThread }, { 0x0c, SvcGetThreadPriority }, { 0x0d, SvcSetThreadPriority }, { 0x0f, SvcSetThreadCoreMask }, + { 0x12, SvcClearEvent }, { 0x13, SvcMapSharedMemory }, { 0x14, SvcUnmapSharedMemory }, { 0x15, SvcCreateTransferMemory }, @@ -59,6 +64,8 @@ namespace Ryujinx.Core.OsHle.Svc this.Ns = Ns; this.Process = Process; this.Memory = Process.Memory; + + MappedSharedMems = new HashSet<(HSharedMem, long)>(); } static SvcHandler() @@ -83,5 +90,26 @@ namespace Ryujinx.Core.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 index 6969d37936..80f24d2bd5 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs @@ -2,6 +2,8 @@ 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 @@ -23,7 +25,7 @@ namespace Ryujinx.Core.OsHle.Svc CurrentHeapSize = Size; - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; ThreadState.X1 = (ulong)Position; } @@ -44,7 +46,7 @@ namespace Ryujinx.Core.OsHle.Svc Memory.Manager.SetAttrBit(Position, Size, 3); } - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcMapMemory(AThreadState ThreadState) @@ -53,6 +55,24 @@ namespace Ryujinx.Core.OsHle.Svc 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); @@ -61,7 +81,7 @@ namespace Ryujinx.Core.OsHle.Svc Memory.Manager.SetAttrBit(Src, Size, 0); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcUnmapMemory(AThreadState ThreadState) @@ -70,6 +90,24 @@ namespace Ryujinx.Core.OsHle.Svc 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); @@ -78,7 +116,7 @@ namespace Ryujinx.Core.OsHle.Svc Memory.Manager.ClearAttrBit(Src, Size, 0); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcQueryMemory(AThreadState ThreadState) @@ -90,10 +128,11 @@ namespace Ryujinx.Core.OsHle.Svc if (MapInfo == null) { - //TODO: Correct error code. - ThreadState.X0 = ulong.MaxValue; + long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize; - return; + 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); @@ -106,7 +145,7 @@ namespace Ryujinx.Core.OsHle.Svc Memory.WriteInt32(InfoPtr + 0x24, 0); //TODO: X1. - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; ThreadState.X1 = 0; } @@ -117,17 +156,33 @@ namespace Ryujinx.Core.OsHle.Svc long Size = (long)ThreadState.X2; int Perm = (int)ThreadState.X3; - HSharedMem SharedMem = Ns.Os.Handles.GetData(Handle); + 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); - SharedMem.AddVirtualPosition(Src); + Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm); - Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm); + lock (MappedSharedMems) + { + MappedSharedMems.Add((SharedMem, Src)); + } - ThreadState.X0 = (int)SvcResult.Success; + SharedMem.AddVirtualPosition(Memory, Src); + + ThreadState.X0 = 0; } //TODO: Error codes. @@ -135,15 +190,33 @@ namespace Ryujinx.Core.OsHle.Svc private void SvcUnmapSharedMemory(AThreadState ThreadState) { - int Handle = (int)ThreadState.X0; - long Position = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; + int Handle = (int)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; - HSharedMem HndData = Ns.Os.Handles.GetData(Handle); - - if (HndData != null) + if (!IsValidPosition(Src)) { - ThreadState.X0 = (int)SvcResult.Success; + 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. @@ -151,20 +224,41 @@ namespace Ryujinx.Core.OsHle.Svc private void SvcCreateTransferMemory(AThreadState ThreadState) { - long Position = (long)ThreadState.X1; - long Size = (long)ThreadState.X2; - int Perm = (int)ThreadState.X3; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + int Perm = (int)ThreadState.X3; - AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); + if (!IsValidPosition(Src)) + { + Logging.Warn($"Tried to create TransferMemory at invalid address {Src:x16}!"); - Memory.Manager.Reprotect(Position, Size, (AMemoryPerm)Perm); + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); - HTransferMem HndData = new HTransferMem(Memory, MapInfo.Perm, Position, Size); + return; + } - int Handle = Ns.Os.Handles.GenerateId(HndData); + AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src); - ThreadState.X1 = (ulong)Handle; - ThreadState.X0 = (int)SvcResult.Success; + 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.Core/OsHle/Svc/SvcResult.cs b/Ryujinx.Core/OsHle/Svc/SvcResult.cs deleted file mode 100644 index a5be9a9457..0000000000 --- a/Ryujinx.Core/OsHle/Svc/SvcResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.Core.OsHle.Svc -{ - enum SvcResult - { - Success = 0, - ErrBadHandle = 0xe401, - ErrTimeout = 0xea01, - ErrBadInfo = 0xf001, - ErrBadIpcReq = 0xf601 - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs index 51b2e26c6f..671a32d361 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs @@ -3,10 +3,11 @@ using ChocolArm64.State; using Ryujinx.Core.OsHle.Exceptions; using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; -using Ryujinx.Core.OsHle.IpcServices; using System; using System.Threading; +using static Ryujinx.Core.OsHle.ErrorCode; + namespace Ryujinx.Core.OsHle.Svc { partial class SvcHandler @@ -20,13 +21,22 @@ namespace Ryujinx.Core.OsHle.Svc 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) @@ -35,7 +45,7 @@ namespace Ryujinx.Core.OsHle.Svc //TODO: Implement events. - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcWaitSynchronization(AThreadState ThreadState) @@ -51,12 +61,12 @@ namespace Ryujinx.Core.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) @@ -69,10 +79,12 @@ namespace Ryujinx.Core.OsHle.Svc //TODO: Validate that app has perms to access the service, and that the service //actually exists, return error codes otherwise. - HSession Session = new HSession(ServiceFactory.MakeService(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) @@ -108,21 +120,29 @@ namespace Ryujinx.Core.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, ThreadState.ThreadId, 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(); @@ -148,7 +168,7 @@ namespace Ryujinx.Core.OsHle.Svc Logging.Info($"SvcOutputDebugString: {Str}"); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcGetInfo(AThreadState ThreadState) @@ -162,7 +182,7 @@ namespace Ryujinx.Core.OsHle.Svc if (InfoType == 18 || InfoType == 19) { - ThreadState.X0 = (int)SvcResult.ErrBadInfo; + ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo); return; } @@ -186,7 +206,7 @@ namespace Ryujinx.Core.OsHle.Svc break; case 5: - ThreadState.X1 = CurrentHeapSize; + ThreadState.X1 = MemoryRegions.HeapRegionSize; break; case 6: @@ -224,7 +244,7 @@ namespace Ryujinx.Core.OsHle.Svc default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}"); } - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } } } diff --git a/Ryujinx.Core/OsHle/Svc/SvcThread.cs b/Ryujinx.Core/OsHle/Svc/SvcThread.cs index cc0f980be3..231ee2a237 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcThread.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcThread.cs @@ -28,7 +28,7 @@ namespace Ryujinx.Core.OsHle.Svc Priority, ProcessorId); - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; ThreadState.X1 = (ulong)Handle; } @@ -39,18 +39,25 @@ namespace Ryujinx.Core.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,12 @@ namespace Ryujinx.Core.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. @@ -87,13 +94,13 @@ namespace Ryujinx.Core.OsHle.Svc int Handle = (int)ThreadState.X1; int Prio = (int)ThreadState.X0; - HThread Thread = Ns.Os.Handles.GetData(Handle); + HThread Thread = Process.HandleTable.GetData(Handle); if (Thread != null) { Thread.Priority = Prio; - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } //TODO: Error codes. @@ -101,7 +108,7 @@ namespace Ryujinx.Core.OsHle.Svc private void SvcSetThreadCoreMask(AThreadState ThreadState) { - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; //TODO: Error codes. } @@ -110,12 +117,12 @@ namespace Ryujinx.Core.OsHle.Svc { int Handle = (int)ThreadState.X0; - HThread Thread = Ns.Os.Handles.GetData(Handle); + HThread Thread = Process.HandleTable.GetData(Handle); if (Thread != null) { + ThreadState.X0 = 0; ThreadState.X1 = (ulong)Thread.ThreadId; - ThreadState.X0 = (int)SvcResult.Success; } //TODO: Error codes. diff --git a/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs b/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs index dec13f75e3..38356073e6 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcThreadSync.cs @@ -1,6 +1,8 @@ using ChocolArm64.State; using Ryujinx.Core.OsHle.Handles; +using static Ryujinx.Core.OsHle.ErrorCode; + namespace Ryujinx.Core.OsHle.Svc { partial class SvcHandler @@ -11,7 +13,7 @@ namespace Ryujinx.Core.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.Core.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.Core.OsHle.Svc M.Unlock(); } - ThreadState.X0 = (int)SvcResult.Success; + ThreadState.X0 = 0; } private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState) @@ -41,7 +43,7 @@ namespace Ryujinx.Core.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.Core.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.Core.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 0000000000..6758f1f2f3 --- /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.Core/OsHle/Utilities/IdPool.cs b/Ryujinx.Core/OsHle/Utilities/IdPool.cs deleted file mode 100644 index a7e181fa23..0000000000 --- a/Ryujinx.Core/OsHle/Utilities/IdPool.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; - -namespace Ryujinx.Core.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.Core/PerformanceStatistics.cs b/Ryujinx.Core/PerformanceStatistics.cs new file mode 100644 index 0000000000..e5d930f934 --- /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/Switch.cs b/Ryujinx.Core/Switch.cs index 6f41da8144..487f3bdb99 100644 --- a/Ryujinx.Core/Switch.cs +++ b/Ryujinx.Core/Switch.cs @@ -1,42 +1,40 @@ -using ChocolArm64.Memory; using Ryujinx.Core.Input; using Ryujinx.Core.OsHle; using Ryujinx.Core.Settings; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gpu; using System; -using System.Runtime.InteropServices; namespace Ryujinx.Core { 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; } - public Hid Hid { get; private set; } - public SetSys Settings { 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) { - Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize); - Gpu = new NsGpu(Renderer); + VFs = new VirtualFs(); - Hid = new Hid(this); - Os = new Horizon(this); - Settings = new SetSys(); - } + Hid = new Hid(); - public void FinalizeAllProcesses() - { - Os.FinalizeAllProcesses(); + 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) @@ -59,14 +57,13 @@ namespace Ryujinx.Core Dispose(true); } - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool Disposing) { - if (disposing) + if (Disposing) { + Os.Dispose(); VFs.Dispose(); } - - Marshal.FreeHGlobal(Ram); } } } \ No newline at end of file diff --git a/Ryujinx.Core/VirtualFs.cs b/Ryujinx.Core/VirtualFs.cs index 195fb6a3c6..c0858e0ee6 100644 --- a/Ryujinx.Core/VirtualFs.cs +++ b/Ryujinx.Core/VirtualFs.cs @@ -18,10 +18,18 @@ namespace Ryujinx.Core 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)); diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index 83d2c699f8..aa4eac4e14 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gal void RunActions(); void InitializeFrameBuffer(); + void ResetFrameBuffer(); void Render(); void SetWindowSize(int Width, int Height); void SetFrameBuffer( diff --git a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs index 7dc4bffe34..b761811f64 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs @@ -24,6 +24,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL private byte* FbPtr; + private object FbPtrLock; + public FrameBuffer(int Width, int Height) { if (Width < 0) @@ -36,6 +38,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL throw new ArgumentOutOfRangeException(nameof(Height)); } + FbPtrLock = new object(); + TexWidth = Width; TexHeight = Height; @@ -152,7 +156,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL throw new ArgumentOutOfRangeException(nameof(Height)); } - FbPtr = Fb; + lock (FbPtrLock) + { + FbPtr = Fb; + } if (Width != TexWidth || Height != TexHeight) @@ -178,17 +185,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.Uniform2(OffsetUniformLocation, Offs); } + public void Reset() + { + lock (FbPtrLock) + { + FbPtr = null; + } + } + public void Render() { - if (FbPtr == null) + lock (FbPtrLock) { - return; - } + 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))); + 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); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs index bdedfc1a87..002e54dadc 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -43,6 +43,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL FbRenderer = new FrameBuffer(1280, 720); } + public void ResetFrameBuffer() + { + FbRenderer.Reset(); + } + public void QueueAction(Action ActionMthd) { ActionsQueue.Enqueue(ActionMthd); diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index a4a3b33fc5..b5bbdbc171 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -2,8 +2,6 @@ using ChocolArm64; using ChocolArm64.Memory; using ChocolArm64.State; using NUnit.Framework; -using System; -using System.Runtime.InteropServices; using System.Threading; namespace Ryujinx.Tests.Cpu @@ -16,7 +14,6 @@ namespace Ryujinx.Tests.Cpu private long EntryPoint; - private IntPtr Ram; private AMemory Memory; private AThread Thread; @@ -28,19 +25,18 @@ namespace Ryujinx.Tests.Cpu EntryPoint = Position; - Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize); ATranslator Translator = new ATranslator(); - Memory = new AMemory(Ram); + Memory = new AMemory(); Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute); - Thread = new AThread(Translator, Memory, ThreadPriority.Normal, EntryPoint); + Thread = new AThread(Translator, Memory, EntryPoint); } [TearDown] public void Teardown() { + Memory.Dispose(); Thread = null; Memory = null; - Marshal.FreeHGlobal(Ram); } protected void Reset() @@ -51,7 +47,7 @@ namespace Ryujinx.Tests.Cpu protected void Opcode(uint Opcode) { - Thread.Memory.WriteUInt32(Position, Opcode); + Thread.Memory.WriteUInt32Unchecked(Position, Opcode); Position += 4; } diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 1e48b28056..b0dca81b77 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -29,6 +29,10 @@ namespace Ryujinx { this.Ns = Ns; this.Renderer = Renderer; + + Location = new Point( + (DisplayDevice.Default.Width / 2) - (Width / 2), + (DisplayDevice.Default.Height / 2) - (Height / 2)); } protected override void OnLoad(EventArgs e) @@ -162,9 +166,12 @@ namespace Ryujinx protected override void OnRenderFrame(FrameEventArgs e) { + Ns.Statistics.StartSystemFrame(); + GL.Viewport(0, 0, Width, Height); - Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {1f / e.Time:0})"; + Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " + + $"{Ns.Statistics.GameFrameRate:0})"; GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); @@ -172,6 +179,8 @@ namespace Ryujinx Renderer.Render(); SwapBuffers(); + + Ns.Statistics.EndSystemFrame(); } protected override void OnResize(EventArgs e) diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs index b5f8cb9766..b67e52bdc3 100644 --- a/Ryujinx/Ui/Program.cs +++ b/Ryujinx/Ui/Program.cs @@ -10,6 +10,8 @@ namespace Ryujinx { static void Main(string[] args) { + AOptimizations.DisableMemoryChecks = true; + Config.Read(); Console.Title = "Ryujinx Console";