diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 14ca231baf..e192f5ec5e 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -142,11 +142,13 @@ namespace ChocolArm64 Set("0>101110<<1xxxxx001111xxxxxxxxxx", AInstEmit.Cmhs_V, typeof(AOpCodeSimdReg)); Set("0>101110<<100000100110xxxxxxxxxx", AInstEmit.Cmle_V, typeof(AOpCodeSimd)); Set("0>001110<<100000101010xxxxxxxxxx", AInstEmit.Cmlt_V, typeof(AOpCodeSimd)); + Set("0>001110<<1xxxxx100011xxxxxxxxxx", AInstEmit.Cmtst_V, typeof(AOpCodeSimdReg)); Set("0x00111000100000010110xxxxxxxxxx", AInstEmit.Cnt_V, typeof(AOpCodeSimd)); Set("0x001110000xxxxx000011xxxxxxxxxx", AInstEmit.Dup_Gp, typeof(AOpCodeSimdIns)); Set("01011110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_S, typeof(AOpCodeSimdIns)); Set("0x001110000xxxxx000001xxxxxxxxxx", AInstEmit.Dup_V, typeof(AOpCodeSimdIns)); Set("0x101110001xxxxx000111xxxxxxxxxx", AInstEmit.Eor_V, typeof(AOpCodeSimdReg)); + Set("0>101110000xxxxx0101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg)); Set("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm)); Set("011111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm)); diff --git a/ChocolArm64/ATranslatedSub.cs b/ChocolArm64/ATranslatedSub.cs index 71a6793a2a..414038ab6e 100644 --- a/ChocolArm64/ATranslatedSub.cs +++ b/ChocolArm64/ATranslatedSub.cs @@ -2,6 +2,7 @@ using ChocolArm64.Memory; using ChocolArm64.State; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Reflection; using System.Reflection.Emit; @@ -13,35 +14,47 @@ namespace ChocolArm64 private AA64Subroutine ExecDelegate; - private bool HasDelegate; - - public static Type[] FixedArgTypes { get; private set; } - public static int StateArgIdx { get; private set; } public static int MemoryArgIdx { get; private set; } + public static Type[] FixedArgTypes { get; private set; } + public DynamicMethod Method { get; private set; } - public HashSet SubCalls { get; private set; } + public ReadOnlyCollection Params { get; private set; } - public List Params { get; private set; } + private HashSet Callees; - public bool NeedsReJit { get; private set; } + private ATranslatedSubType Type; - public ATranslatedSub() + private int CallCount; + + private bool NeedsReJit; + + private int MinCallCountForReJit = 250; + + public ATranslatedSub(DynamicMethod Method, List Params, HashSet Callees) { - SubCalls = new HashSet(); - } + if (Method == null) + { + throw new ArgumentNullException(nameof(Method)); + } - public ATranslatedSub(DynamicMethod Method, List Params) : this() - { if (Params == null) { throw new ArgumentNullException(nameof(Params)); } - this.Method = Method; - this.Params = Params; + if (Callees == null) + { + throw new ArgumentNullException(nameof(Callees)); + } + + this.Method = Method; + this.Params = Params.AsReadOnly(); + this.Callees = Callees; + + PrepareDelegate(); } static ATranslatedSub() @@ -69,36 +82,53 @@ namespace ChocolArm64 } } - public long Execute(AThreadState ThreadState, AMemory Memory) + private void PrepareDelegate() { - if (!HasDelegate) + string Name = $"{Method.Name}_Dispatch"; + + DynamicMethod Mthd = new DynamicMethod(Name, typeof(long), FixedArgTypes); + + ILGenerator Generator = Mthd.GetILGenerator(); + + Generator.EmitLdargSeq(FixedArgTypes.Length); + + foreach (ARegister Reg in Params) { - string Name = $"{Method.Name}_Dispatch"; + Generator.EmitLdarg(StateArgIdx); - DynamicMethod Mthd = new DynamicMethod(Name, typeof(long), FixedArgTypes); - - ILGenerator Generator = Mthd.GetILGenerator(); - - Generator.EmitLdargSeq(FixedArgTypes.Length); - - foreach (ARegister Reg in Params) - { - Generator.EmitLdarg(StateArgIdx); - - Generator.Emit(OpCodes.Ldfld, Reg.GetField()); - } - - Generator.Emit(OpCodes.Call, Method); - Generator.Emit(OpCodes.Ret); - - ExecDelegate = (AA64Subroutine)Mthd.CreateDelegate(typeof(AA64Subroutine)); - - HasDelegate = true; + Generator.Emit(OpCodes.Ldfld, Reg.GetField()); } + Generator.Emit(OpCodes.Call, Method); + Generator.Emit(OpCodes.Ret); + + ExecDelegate = (AA64Subroutine)Mthd.CreateDelegate(typeof(AA64Subroutine)); + } + + public bool ShouldReJit() + { + if (Type == ATranslatedSubType.SubTier0) + { + if (CallCount < MinCallCountForReJit) + { + CallCount++; + } + + return CallCount == MinCallCountForReJit; + } + + return Type == ATranslatedSubType.SubTier1 && NeedsReJit; + } + + public long Execute(AThreadState ThreadState, AMemory Memory) + { return ExecDelegate(ThreadState, Memory); } - public void MarkForReJit() => NeedsReJit = true; + public void SetType(ATranslatedSubType Type) => this.Type = Type; + + public bool HasCallee(long Position) => Callees.Contains(Position); + + public void MarkForReJit() => NeedsReJit = true; } } \ No newline at end of file diff --git a/ChocolArm64/ATranslatedSubType.cs b/ChocolArm64/ATranslatedSubType.cs new file mode 100644 index 0000000000..e9f3e0bf00 --- /dev/null +++ b/ChocolArm64/ATranslatedSubType.cs @@ -0,0 +1,9 @@ +namespace ChocolArm64 +{ + enum ATranslatedSubType + { + SubBlock, + SubTier0, + SubTier1 + } +} \ No newline at end of file diff --git a/ChocolArm64/ATranslator.cs b/ChocolArm64/ATranslator.cs index 2daf7bbc96..ab434e2267 100644 --- a/ChocolArm64/ATranslator.cs +++ b/ChocolArm64/ATranslator.cs @@ -7,11 +7,14 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection.Emit; +using System.Threading; namespace ChocolArm64 { public class ATranslator { + private HashSet SubBlocks; + private ConcurrentDictionary CachedSubs; private ConcurrentDictionary SymbolTable; @@ -24,6 +27,8 @@ namespace ChocolArm64 public ATranslator(IReadOnlyDictionary SymbolTable = null) { + SubBlocks = new HashSet(); + CachedSubs = new ConcurrentDictionary(); if (SymbolTable != null) @@ -38,9 +43,9 @@ namespace ChocolArm64 KeepRunning = true; } - public void StopExecution() => KeepRunning = false; + internal void StopExecution() => KeepRunning = false; - public void ExecuteSubroutine(AThread Thread, long Position) + internal void ExecuteSubroutine(AThread Thread, long Position) { do { @@ -54,9 +59,14 @@ namespace ChocolArm64 CpuTrace?.Invoke(this, new ACpuTraceEventArgs(Position, SubName)); } - if (!CachedSubs.TryGetValue(Position, out ATranslatedSub Sub) || Sub.NeedsReJit) + if (!CachedSubs.TryGetValue(Position, out ATranslatedSub Sub)) { - Sub = TranslateSubroutine(Thread.Memory, Position); + Sub = TranslateTier0(Thread.Memory, Position); + } + + if (Sub.ShouldReJit()) + { + TranslateTier1(Thread.Memory, Position); } Position = Sub.Execute(Thread.ThreadState, Thread.Memory); @@ -86,19 +96,57 @@ namespace ChocolArm64 return CachedSubs.ContainsKey(Position); } - private ATranslatedSub TranslateSubroutine(AMemory Memory, long Position) + private ATranslatedSub TranslateTier0(AMemory Memory, long Position) + { + ABlock Block = ADecoder.DecodeBasicBlock(this, Memory, Position); + + ABlock[] Graph = new ABlock[] { Block }; + + string SubName = GetSubName(Position); + + AILEmitterCtx Context = new AILEmitterCtx(this, Graph, Block, SubName); + + do + { + Context.EmitOpCode(); + } + while (Context.AdvanceOpCode()); + + ATranslatedSub Subroutine = Context.GetSubroutine(); + + if (SubBlocks.Contains(Position)) + { + SubBlocks.Remove(Position); + + Subroutine.SetType(ATranslatedSubType.SubBlock); + } + else + { + Subroutine.SetType(ATranslatedSubType.SubTier0); + } + + CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine); + + AOpCode LastOp = Block.GetLastOp(); + + if (LastOp.Emitter != AInstEmit.Ret && + LastOp.Emitter != AInstEmit.Br) + { + SubBlocks.Add(LastOp.Position + 4); + } + + return Subroutine; + } + + private void TranslateTier1(AMemory Memory, long Position) { (ABlock[] Graph, ABlock Root) Cfg = ADecoder.DecodeSubroutine(this, Memory, Position); - string SubName = SymbolTable.GetOrAdd(Position, $"Sub{Position:x16}"); + string SubName = GetSubName(Position); PropagateName(Cfg.Graph, SubName); - AILEmitterCtx Context = new AILEmitterCtx( - this, - Cfg.Graph, - Cfg.Root, - SubName); + AILEmitterCtx Context = new AILEmitterCtx(this, Cfg.Graph, Cfg.Root, SubName); if (Context.CurrBlock.Position != Position) { @@ -115,7 +163,7 @@ namespace ChocolArm64 //since we can now call it directly which is faster. foreach (ATranslatedSub TS in CachedSubs.Values) { - if (TS.SubCalls.Contains(Position)) + if (TS.HasCallee(Position)) { TS.MarkForReJit(); } @@ -123,9 +171,14 @@ namespace ChocolArm64 ATranslatedSub Subroutine = Context.GetSubroutine(); - CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine); + Subroutine.SetType(ATranslatedSubType.SubTier1); - return Subroutine; + CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine); + } + + private string GetSubName(long Position) + { + return SymbolTable.GetOrAdd(Position, $"Sub{Position:x16}"); } private void PropagateName(ABlock[] Graph, string Name) diff --git a/ChocolArm64/Decoder/ADecoder.cs b/ChocolArm64/Decoder/ADecoder.cs index 4430229034..2375c18572 100644 --- a/ChocolArm64/Decoder/ADecoder.cs +++ b/ChocolArm64/Decoder/ADecoder.cs @@ -18,6 +18,18 @@ namespace ChocolArm64.Decoder OpActivators = new ConcurrentDictionary(); } + public static ABlock DecodeBasicBlock( + ATranslator Translator, + AMemory Memory, + long Start) + { + ABlock Block = new ABlock(Start); + + FillBlock(Memory, Block); + + return Block; + } + public static (ABlock[] Graph, ABlock Root) DecodeSubroutine( ATranslator Translator, AMemory Memory, @@ -72,8 +84,8 @@ namespace ChocolArm64.Decoder } } - if ((!(LastOp is AOpCodeBImmAl) && - !(LastOp is AOpCodeBReg)) || HasCachedSub) + if (!((LastOp is AOpCodeBImmAl) || + (LastOp is AOpCodeBReg)) || HasCachedSub) { Current.Next = Enqueue(Current.EndPosition); } diff --git a/ChocolArm64/Decoder/AOpCodeSimdExt.cs b/ChocolArm64/Decoder/AOpCodeSimdExt.cs new file mode 100644 index 0000000000..cf22d6546f --- /dev/null +++ b/ChocolArm64/Decoder/AOpCodeSimdExt.cs @@ -0,0 +1,14 @@ +using ChocolArm64.Instruction; + +namespace ChocolArm64.Decoder +{ + class AOpCodeSimdExt : AOpCodeSimdReg + { + public int Imm4 { get; private set; } + + public AOpCodeSimdExt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) + { + int Imm4 = (OpCode >> 11) & 0xf; + } + } +} \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitException.cs b/ChocolArm64/Instruction/AInstEmitException.cs index 209ba56f5b..fe348edd17 100644 --- a/ChocolArm64/Instruction/AInstEmitException.cs +++ b/ChocolArm64/Instruction/AInstEmitException.cs @@ -2,6 +2,7 @@ using ChocolArm64.Decoder; using ChocolArm64.State; using ChocolArm64.Translation; using System.Reflection; +using System.Reflection.Emit; namespace ChocolArm64.Instruction { @@ -37,6 +38,12 @@ namespace ChocolArm64.Instruction { Context.EmitLoadState(Context.CurrBlock.Next); } + else + { + Context.EmitLdc_I8(Op.Position + 4); + + Context.Emit(OpCodes.Ret); + } } public static void Und(AILEmitterCtx Context) @@ -60,6 +67,12 @@ namespace ChocolArm64.Instruction { Context.EmitLoadState(Context.CurrBlock.Next); } + else + { + Context.EmitLdc_I8(Op.Position + 4); + + Context.Emit(OpCodes.Ret); + } } } } \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitFlow.cs b/ChocolArm64/Instruction/AInstEmitFlow.cs index be68aa6c97..91262834f9 100644 --- a/ChocolArm64/Instruction/AInstEmitFlow.cs +++ b/ChocolArm64/Instruction/AInstEmitFlow.cs @@ -11,14 +11,24 @@ namespace ChocolArm64.Instruction { AOpCodeBImmAl Op = (AOpCodeBImmAl)Context.CurrOp; - Context.Emit(OpCodes.Br, Context.GetLabel(Op.Imm)); + if (Context.CurrBlock.Branch != null) + { + Context.Emit(OpCodes.Br, Context.GetLabel(Op.Imm)); + } + else + { + Context.EmitStoreState(); + Context.EmitLdc_I8(Op.Imm); + + Context.Emit(OpCodes.Ret); + } } public static void B_Cond(AILEmitterCtx Context) { AOpCodeBImmCond Op = (AOpCodeBImmCond)Context.CurrOp; - Context.EmitCondBranch(Context.GetLabel(Op.Imm), Op.Cond); + EmitBranch(Context, Op.Cond); } public static void Bl(AILEmitterCtx Context) @@ -48,10 +58,7 @@ namespace ChocolArm64.Instruction Context.Emit(OpCodes.Pop); - if (Context.CurrBlock.Next != null) - { - Context.EmitLoadState(Context.CurrBlock.Next); - } + Context.EmitLoadState(Context.CurrBlock.Next); } else { @@ -93,7 +100,7 @@ namespace ChocolArm64.Instruction Context.EmitLdintzr(Op.Rt); Context.EmitLdc_I(0); - Context.Emit(ILOp, Context.GetLabel(Op.Imm)); + EmitBranch(Context, ILOp); } public static void Ret(AILEmitterCtx Context) @@ -118,7 +125,65 @@ namespace ChocolArm64.Instruction Context.EmitLdc_I(0); - Context.Emit(ILOp, Context.GetLabel(Op.Imm)); + EmitBranch(Context, ILOp); + } + + private static void EmitBranch(AILEmitterCtx Context, ACond Cond) + { + AOpCodeBImm Op = (AOpCodeBImm)Context.CurrOp; + + if (Context.CurrBlock.Next != null && + Context.CurrBlock.Branch != null) + { + Context.EmitCondBranch(Context.GetLabel(Op.Imm), Cond); + } + else + { + Context.EmitStoreState(); + + AILLabel LblTaken = new AILLabel(); + + Context.EmitCondBranch(LblTaken, Cond); + + Context.EmitLdc_I8(Op.Position + 4); + + Context.Emit(OpCodes.Ret); + + Context.MarkLabel(LblTaken); + + Context.EmitLdc_I8(Op.Imm); + + Context.Emit(OpCodes.Ret); + } + } + + private static void EmitBranch(AILEmitterCtx Context, OpCode ILOp) + { + AOpCodeBImm Op = (AOpCodeBImm)Context.CurrOp; + + if (Context.CurrBlock.Next != null && + Context.CurrBlock.Branch != null) + { + Context.Emit(ILOp, Context.GetLabel(Op.Imm)); + } + else + { + Context.EmitStoreState(); + + AILLabel LblTaken = new AILLabel(); + + Context.Emit(ILOp, LblTaken); + + Context.EmitLdc_I8(Op.Position + 4); + + Context.Emit(OpCodes.Ret); + + Context.MarkLabel(LblTaken); + + Context.EmitLdc_I8(Op.Imm); + + Context.Emit(OpCodes.Ret); + } } } } \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index e790d67865..9c1bc28626 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -406,5 +406,10 @@ namespace ChocolArm64.Instruction { EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add)); } + + public static void Umull_V(AILEmitterCtx Context) + { + EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); + } } } \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdCmp.cs b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs index 97ccf0ab4e..76861b73ba 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdCmp.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs @@ -46,6 +46,45 @@ namespace ChocolArm64.Instruction EmitVectorCmp(Context, OpCodes.Blt_S); } + public static void Cmtst_V(AILEmitterCtx Context) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + ulong SzMask = ulong.MaxValue >> (64 - (8 << Op.Size)); + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); + EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size); + + AILLabel LblTrue = new AILLabel(); + AILLabel LblEnd = new AILLabel(); + + Context.Emit(OpCodes.And); + + Context.EmitLdc_I4(0); + + Context.Emit(OpCodes.Bne_Un_S, LblTrue); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size, 0); + + Context.Emit(OpCodes.Br_S, LblEnd); + + Context.MarkLabel(LblTrue); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size, (long)SzMask); + + Context.MarkLabel(LblEnd); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + public static void Fccmp_S(AILEmitterCtx Context) { AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; diff --git a/ChocolArm64/Instruction/AInstEmitSimdLogical.cs b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs index ea4b17b3da..5b71a0bbb8 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdLogical.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs @@ -1,3 +1,5 @@ +using ChocolArm64.Decoder; +using ChocolArm64.State; using ChocolArm64.Translation; using System.Reflection.Emit; @@ -65,5 +67,28 @@ namespace ChocolArm64.Instruction { EmitVectorImmBinaryOp(Context, () => Context.Emit(OpCodes.Or)); } + + public static void Rev64_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + int Elems = Bytes >> Op.Size; + + int RevIndex = Elems - 1; + + for (int Index = 0; Index < (Bytes >> Op.Size); Index++) + { + EmitVectorExtractZx(Context, Op.Rn, RevIndex--, Op.Size); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } } } \ No newline at end of file diff --git a/ChocolArm64/Instruction/AInstEmitSimdMove.cs b/ChocolArm64/Instruction/AInstEmitSimdMove.cs index aabb8f3470..a4e533709e 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdMove.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdMove.cs @@ -57,6 +57,31 @@ namespace ChocolArm64.Instruction } } + public static void Ext_V(AILEmitterCtx Context) + { + AOpCodeSimdExt Op = (AOpCodeSimdExt)Context.CurrOp; + + int Bytes = Context.CurrOp.GetBitsCount() >> 3; + + for (int Index = 0; Index < Bytes; Index++) + { + int Position = Op.Imm4 + Index; + + int Reg = Position < Bytes ? Op.Rn : Op.Rm; + + Position &= Bytes - 1; + + EmitVectorExtractZx(Context, Reg, Position, 0); + + EmitVectorInsert(Context, Op.Rd, Index, 0); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + public static void Fcsel_S(AILEmitterCtx Context) { AOpCodeSimdFcond Op = (AOpCodeSimdFcond)Context.CurrOp; diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs index 5127182ddc..797d815736 100644 --- a/ChocolArm64/Instruction/ASoftFallback.cs +++ b/ChocolArm64/Instruction/ASoftFallback.cs @@ -75,7 +75,7 @@ namespace ChocolArm64.Instruction private static ulong ReverseBytes(ulong Value, RevSize Size) { - Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8); + Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8); if (Size == RevSize.Rev16) { diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs index f2abffbf47..0d202fed30 100644 --- a/ChocolArm64/Memory/AMemory.cs +++ b/ChocolArm64/Memory/AMemory.cs @@ -41,9 +41,9 @@ namespace ChocolArm64.Memory private byte* RamPtr; - public AMemory(IntPtr Ram, AMemoryAlloc Allocator) + public AMemory(IntPtr Ram) { - Manager = new AMemoryMgr(Allocator); + Manager = new AMemoryMgr(); Monitors = new Dictionary(); diff --git a/ChocolArm64/Memory/AMemoryAlloc.cs b/ChocolArm64/Memory/AMemoryAlloc.cs deleted file mode 100644 index b11e779314..0000000000 --- a/ChocolArm64/Memory/AMemoryAlloc.cs +++ /dev/null @@ -1,35 +0,0 @@ -using ChocolArm64.Exceptions; - -namespace ChocolArm64.Memory -{ - public class AMemoryAlloc - { - private long PhysPos; - - public long Alloc(long Size) - { - long Position = PhysPos; - - Size = AMemoryHelper.PageRoundUp(Size); - - PhysPos += Size; - - if (PhysPos > AMemoryMgr.RamSize || PhysPos < 0) - { - throw new VmmOutOfMemoryException(Size); - } - - return Position; - } - - public void Free(long Position) - { - //TODO - } - - public long GetFreeMem() - { - return AMemoryMgr.RamSize - PhysPos; - } - } -} \ No newline at end of file diff --git a/ChocolArm64/Memory/AMemoryMapInfo.cs b/ChocolArm64/Memory/AMemoryMapInfo.cs index 44b2cc079e..02dd3055cc 100644 --- a/ChocolArm64/Memory/AMemoryMapInfo.cs +++ b/ChocolArm64/Memory/AMemoryMapInfo.cs @@ -1,6 +1,6 @@ namespace ChocolArm64.Memory { - public struct AMemoryMapInfo + public class AMemoryMapInfo { public long Position { get; private set; } public long Size { get; private set; } diff --git a/ChocolArm64/Memory/AMemoryMgr.cs b/ChocolArm64/Memory/AMemoryMgr.cs index c8869e03e1..bc36445242 100644 --- a/ChocolArm64/Memory/AMemoryMgr.cs +++ b/ChocolArm64/Memory/AMemoryMgr.cs @@ -1,3 +1,5 @@ +using System; + namespace ChocolArm64.Memory { public class AMemoryMgr @@ -5,22 +7,20 @@ namespace ChocolArm64.Memory public const long AddrSize = RamSize; public const long RamSize = 4L * 1024 * 1024 * 1024; - private const int PTLvl0Bits = 11; - private const int PTLvl1Bits = 13; - private const int PTPageBits = 12; + private const int PTLvl0Bits = 10; + private const int PTLvl1Bits = 10; + private const int PTPageBits = 12; - private const int PTLvl0Size = 1 << PTLvl0Bits; - private const int PTLvl1Size = 1 << PTLvl1Bits; - public const int PageSize = 1 << PTPageBits; + private const int PTLvl0Size = 1 << PTLvl0Bits; + private const int PTLvl1Size = 1 << PTLvl1Bits; + public const int PageSize = 1 << PTPageBits; - private const int PTLvl0Mask = PTLvl0Size - 1; - private const int PTLvl1Mask = PTLvl1Size - 1; - public const int PageMask = PageSize - 1; + private const int PTLvl0Mask = PTLvl0Size - 1; + private const int PTLvl1Mask = PTLvl1Size - 1; + public const int PageMask = PageSize - 1; - private const int PTLvl0Bit = PTPageBits + PTLvl0Bits; - private const int PTLvl1Bit = PTPageBits; - - private AMemoryAlloc Allocator; + private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; + private const int PTLvl1Bit = PTPageBits; private enum PTMap { @@ -47,132 +47,24 @@ namespace ChocolArm64.Memory private PTEntry[][] PageTable; - private bool IsHeapInitialized; - - public long HeapAddr { get; private set; } - public long HeapSize { get; private set; } - - public AMemoryMgr(AMemoryAlloc Allocator) + public AMemoryMgr() { - this.Allocator = Allocator; - PageTable = new PTEntry[PTLvl0Size][]; } - public long GetTotalMemorySize() + public void Map(long Position, long Size, int Type, AMemoryPerm Perm) { - return Allocator.GetFreeMem() + GetUsedMemorySize(); + SetPTEntry(Position, Size, new PTEntry(PTMap.Mapped, Perm, Type, 0)); } - public long GetUsedMemorySize() + public void Unmap(long Position, long Size) { - long Size = 0; - - for (int L0 = 0; L0 < PageTable.Length; L0++) - { - if (PageTable[L0] == null) - { - continue; - } - - for (int L1 = 0; L1 < PageTable[L0].Length; L1++) - { - Size += PageTable[L0][L1].Map != PTMap.Unmapped ? PageSize : 0; - } - } - - return Size; + SetPTEntry(Position, Size, new PTEntry(PTMap.Unmapped, 0, 0, 0)); } - public bool SetHeapAddr(long Position) + public void Unmap(long Position, long Size, int Type) { - if (!IsHeapInitialized) - { - HeapAddr = Position; - - IsHeapInitialized = true; - - return true; - } - - return false; - } - - public void SetHeapSize(long Size, int Type) - { - //TODO: Return error when theres no enough space to allocate heap. - Size = AMemoryHelper.PageRoundUp(Size); - - long Position = HeapAddr; - - if ((ulong)Size < (ulong)HeapSize) - { - //Try to free now free area if size is smaller than old size. - Position += Size; - - while ((ulong)Size < (ulong)HeapSize) - { - Allocator.Free(Position); - - Position += PageSize; - } - } - else - { - //Allocate extra needed size. - Position += HeapSize; - Size -= HeapSize; - - MapPhys(Position, Size, Type, AMemoryPerm.RW); - } - - HeapSize = Size; - } - - public void MapPhys(long Position, long Size, int Type, AMemoryPerm Perm) - { - while (Size > 0) - { - if (!IsMapped(Position)) - { - SetPTEntry(Position, new PTEntry(PTMap.Mapped, Perm, Type, 0)); - } - - long CPgSize = PageSize - (Position & PageMask); - - Position += CPgSize; - Size -= CPgSize; - } - } - - public void MapMirror(long Src, long Dst, long Size, int Type) - { - Src = AMemoryHelper.PageRoundDown(Src); - Dst = AMemoryHelper.PageRoundDown(Dst); - - Size = AMemoryHelper.PageRoundUp(Size); - - long PagesCount = Size / PageSize; - - while (PagesCount-- > 0) - { - PTEntry SrcEntry = GetPTEntry(Src); - PTEntry DstEntry = GetPTEntry(Dst); - - DstEntry.Map = PTMap.Mapped; - DstEntry.Type = Type; - DstEntry.Perm = SrcEntry.Perm; - - SrcEntry.Perm = AMemoryPerm.None; - - SrcEntry.Attr |= 1; - - SetPTEntry(Src, SrcEntry); - SetPTEntry(Dst, DstEntry); - - Src += PageSize; - Dst += PageSize; - } + SetPTEntry(Position, Size, Type, new PTEntry(PTMap.Unmapped, 0, 0, 0)); } public void Reprotect(long Position, long Size, AMemoryPerm Perm) @@ -197,12 +89,22 @@ namespace ChocolArm64.Memory public AMemoryMapInfo GetMapInfo(long Position) { + if (!IsValidPosition(Position)) + { + return null; + } + Position = AMemoryHelper.PageRoundDown(Position); PTEntry BaseEntry = GetPTEntry(Position); bool IsSameSegment(long Pos) { + if (!IsValidPosition(Pos)) + { + return false; + } + PTEntry Entry = GetPTEntry(Pos); return Entry.Map == BaseEntry.Map && @@ -269,6 +171,16 @@ namespace ChocolArm64.Memory return GetPTEntry(Position).Perm.HasFlag(Perm); } + public bool IsValidPosition(long Position) + { + if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0) + { + return false; + } + + return true; + } + public bool IsMapped(long Position) { if (Position >> PTLvl0Bits + PTLvl1Bits + PTPageBits != 0) @@ -300,8 +212,38 @@ namespace ChocolArm64.Memory return PageTable[L0][L1]; } + private void SetPTEntry(long Position, long Size, PTEntry Entry) + { + while (Size > 0) + { + SetPTEntry(Position, Entry); + + Position += PageSize; + Size -= PageSize; + } + } + + private void SetPTEntry(long Position, long Size, int Type, PTEntry Entry) + { + while (Size > 0) + { + if (GetPTEntry(Position).Type == Type) + { + SetPTEntry(Position, Entry); + } + + Position += PageSize; + Size -= PageSize; + } + } + private void SetPTEntry(long Position, PTEntry Entry) { + if (!IsValidPosition(Position)) + { + throw new ArgumentOutOfRangeException(nameof(Position)); + } + long L0 = (Position >> PTLvl0Bit) & PTLvl0Mask; long L1 = (Position >> PTLvl1Bit) & PTLvl1Mask; diff --git a/ChocolArm64/Translation/AILEmitter.cs b/ChocolArm64/Translation/AILEmitter.cs index 8f6e1210f0..af37a6c752 100644 --- a/ChocolArm64/Translation/AILEmitter.cs +++ b/ChocolArm64/Translation/AILEmitter.cs @@ -58,11 +58,13 @@ namespace ChocolArm64.Translation this.Root = ILBlocks[Array.IndexOf(Graph, Root)]; } - public ATranslatedSub GetSubroutine() + public AILBlock GetILBlock(int Index) => ILBlocks[Index]; + + public ATranslatedSub GetSubroutine(HashSet Callees) { LocalAlloc = new ALocalAlloc(ILBlocks, Root); - InitSubroutine(); + InitSubroutine(Callees); InitLocals(); foreach (AILBlock ILBlock in ILBlocks) @@ -73,24 +75,7 @@ namespace ChocolArm64.Translation return Subroutine; } - public AILBlock GetILBlock(int Index) => ILBlocks[Index]; - - private void InitLocals() - { - int ParamsStart = ATranslatedSub.FixedArgTypes.Length; - - Locals = new Dictionary(); - - for (int Index = 0; Index < Subroutine.Params.Count; Index++) - { - ARegister Reg = Subroutine.Params[Index]; - - Generator.EmitLdarg(Index + ParamsStart); - Generator.EmitStloc(GetLocalIndex(Reg)); - } - } - - private void InitSubroutine() + private void InitSubroutine(HashSet Callees) { List Params = new List(); @@ -114,9 +99,24 @@ namespace ChocolArm64.Translation Generator = Mthd.GetILGenerator(); - Subroutine = new ATranslatedSub(Mthd, Params); + Subroutine = new ATranslatedSub(Mthd, Params, Callees); } + private void InitLocals() + { + int ParamsStart = ATranslatedSub.FixedArgTypes.Length; + + Locals = new Dictionary(); + + for (int Index = 0; Index < Subroutine.Params.Count; Index++) + { + ARegister Reg = Subroutine.Params[Index]; + + Generator.EmitLdarg(Index + ParamsStart); + Generator.EmitStloc(GetLocalIndex(Reg)); + } + } + private Type[] GetParamTypes(IList Params) { Type[] FixedArgs = ATranslatedSub.FixedArgTypes; diff --git a/ChocolArm64/Translation/AILEmitterCtx.cs b/ChocolArm64/Translation/AILEmitterCtx.cs index ffcfa851ae..4665946941 100644 --- a/ChocolArm64/Translation/AILEmitterCtx.cs +++ b/ChocolArm64/Translation/AILEmitterCtx.cs @@ -12,14 +12,9 @@ namespace ChocolArm64.Translation { private ATranslator Translator; - private Dictionary Labels; + private HashSet Callees; - private AILEmitter Emitter; - - private AILBlock ILBlock; - - private AOpCode OptOpLastCompare; - private AOpCode OptOpLastFlagSet; + private Dictionary Labels; private int BlkIndex; private int OpcIndex; @@ -29,6 +24,13 @@ namespace ChocolArm64.Translation public ABlock CurrBlock => Graph[BlkIndex]; public AOpCode CurrOp => Graph[BlkIndex].OpCodes[OpcIndex]; + private AILEmitter Emitter; + + private AILBlock ILBlock; + + private AOpCode OptOpLastCompare; + private AOpCode OptOpLastFlagSet; + //This is the index of the temporary register, used to store temporary //values needed by some functions, since IL doesn't have a swap instruction. //You can use any value here as long it doesn't conflict with the indices @@ -45,10 +47,27 @@ namespace ChocolArm64.Translation ABlock Root, string SubName) { + if (Translator == null) + { + throw new ArgumentNullException(nameof(Translator)); + } + + if (Graph == null) + { + throw new ArgumentNullException(nameof(Graph)); + } + + if (Root == null) + { + throw new ArgumentNullException(nameof(Root)); + } + this.Translator = Translator; this.Graph = Graph; this.Root = Root; + Callees = new HashSet(); + Labels = new Dictionary(); Emitter = new AILEmitter(Graph, Root, SubName); @@ -57,23 +76,27 @@ namespace ChocolArm64.Translation OpcIndex = -1; - if (!AdvanceOpCode()) + if (Graph.Length == 0 || !AdvanceOpCode()) { throw new ArgumentException(nameof(Graph)); } } - public ATranslatedSub GetSubroutine() => Emitter.GetSubroutine(); + public ATranslatedSub GetSubroutine() + { + return Emitter.GetSubroutine(Callees); + } public bool AdvanceOpCode() { + if (OpcIndex + 1 == CurrBlock.OpCodes.Count && + BlkIndex + 1 == Graph.Length) + { + return false; + } + while (++OpcIndex >= (CurrBlock?.OpCodes.Count ?? 0)) { - if (BlkIndex + 1 >= Graph.Length) - { - return false; - } - BlkIndex++; OpcIndex = -1; @@ -100,6 +123,13 @@ namespace ChocolArm64.Translation public bool TryOptEmitSubroutineCall() { + Callees.Add(((AOpCodeBImm)CurrOp).Imm); + + if (CurrBlock.Next == null) + { + return false; + } + if (!Translator.TryGetCachedSub(CurrOp, out ATranslatedSub Sub)) { return false; diff --git a/ChocolArm64/Translation/ALocalAlloc.cs b/ChocolArm64/Translation/ALocalAlloc.cs index f23af9c767..8e9047804d 100644 --- a/ChocolArm64/Translation/ALocalAlloc.cs +++ b/ChocolArm64/Translation/ALocalAlloc.cs @@ -67,14 +67,15 @@ namespace ChocolArm64.Translation public long VecOutputs; } - private const int MaxOptGraphLength = 55; + private const int MaxOptGraphLength = 40; public ALocalAlloc(AILBlock[] Graph, AILBlock Root) { IntPaths = new Dictionary(); VecPaths = new Dictionary(); - if (Graph.Length < MaxOptGraphLength) + if (Graph.Length > 1 && + Graph.Length < MaxOptGraphLength) { InitializeOptimal(Graph, Root); } @@ -179,10 +180,8 @@ namespace ChocolArm64.Translation { //This is WAY faster than InitializeOptimal, but results in //uneeded loads and stores, so the resulting code will be slower. - long IntInputs = 0; - long IntOutputs = 0; - long VecInputs = 0; - long VecOutputs = 0; + long IntInputs = 0, IntOutputs = 0; + long VecInputs = 0, VecOutputs = 0; foreach (AILBlock Block in Graph) { @@ -196,8 +195,11 @@ namespace ChocolArm64.Translation //in those cases if we attempt to write an output registers that was //not written, we will be just writing zero and messing up the old register value. //So we just need to ensure that all outputs are loaded. - IntInputs |= IntOutputs; - VecInputs |= VecOutputs; + if (Graph.Length > 1) + { + IntInputs |= IntOutputs; + VecInputs |= VecOutputs; + } foreach (AILBlock Block in Graph) { diff --git a/Ryujinx.Core/Config.cs b/Ryujinx.Core/Config.cs index 7db49319c9..c9a76de343 100644 --- a/Ryujinx.Core/Config.cs +++ b/Ryujinx.Core/Config.cs @@ -1,4 +1,5 @@ -using System; +using Ryujinx.Core.Input; +using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/Ryujinx.Core/Hid.cs b/Ryujinx.Core/Hid.cs deleted file mode 100644 index bd83e92d47..0000000000 --- a/Ryujinx.Core/Hid.cs +++ /dev/null @@ -1,225 +0,0 @@ -using Ryujinx.Core.OsHle; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Core -{ - public class Hid - { - /* - Thanks to: - https://github.com/reswitched/libtransistor/blob/development/lib/hid.c - https://github.com/reswitched/libtransistor/blob/development/include/libtransistor/hid.h - https://github.com/switchbrew/libnx/blob/master/nx/source/services/hid.c - https://github.com/switchbrew/libnx/blob/master/nx/include/switch/services/hid.h - - struct HidSharedMemory - { - header[0x400]; - touchscreen[0x3000]; - mouse[0x400]; - keyboard[0x400]; - unkSection1[0x400]; - unkSection2[0x400]; - unkSection3[0x400]; - unkSection4[0x400]; - unkSection5[0x200]; - unkSection6[0x200]; - unkSection7[0x200]; - unkSection8[0x800]; - controllerSerials[0x4000]; - controllers[0x5000 * 10]; - unkSection9[0x4600]; - } - */ - - private const int Hid_Num_Entries = 17; - private Switch Ns; - private long SharedMemOffset; - - public Hid(Switch Ns) - { - this.Ns = Ns; - } - - public void Init(long HidOffset) - { - unsafe - { - if (HidOffset == 0 || HidOffset + Horizon.HidSize > uint.MaxValue) - { - return; - } - - SharedMemOffset = HidOffset; - - uint InnerOffset = (uint)Marshal.SizeOf(typeof(HidSharedMemHeader)); - - IntPtr HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); - - HidTouchScreen TouchScreen = new HidTouchScreen(); - TouchScreen.Header.TimestampTicks = (ulong)Environment.TickCount; - TouchScreen.Header.NumEntries = (ulong)Hid_Num_Entries; - TouchScreen.Header.LatestEntry = 0; - TouchScreen.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1; - TouchScreen.Header.Timestamp = (ulong)Environment.TickCount; - - Marshal.StructureToPtr(TouchScreen, HidPtr, false); - - InnerOffset += (uint)Marshal.SizeOf(typeof(HidTouchScreen)); - HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); - - HidMouse Mouse = new HidMouse(); - Mouse.Header.TimestampTicks = (ulong)Environment.TickCount; - Mouse.Header.NumEntries = (ulong)Hid_Num_Entries; - Mouse.Header.LatestEntry = 0; - Mouse.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1; - - //TODO: Write this structure when the input is implemented - //Marshal.StructureToPtr(Mouse, HidPtr, false); - - InnerOffset += (uint)Marshal.SizeOf(typeof(HidMouse)); - HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); - - HidKeyboard Keyboard = new HidKeyboard(); - Keyboard.Header.TimestampTicks = (ulong)Environment.TickCount; - Keyboard.Header.NumEntries = (ulong)Hid_Num_Entries; - Keyboard.Header.LatestEntry = 0; - Keyboard.Header.MaxEntryIndex = (ulong)Hid_Num_Entries - 1; - - //TODO: Write this structure when the input is implemented - //Marshal.StructureToPtr(Keyboard, HidPtr, false); - - InnerOffset += (uint)Marshal.SizeOf(typeof(HidKeyboard)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection1)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection2)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection3)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection4)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection5)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection6)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection7)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection8)) + - (uint)Marshal.SizeOf(typeof(HidControllerSerials)); - - //Increase the loop to initialize more controller. - for (int i = 8; i < Enum.GetNames(typeof(HidControllerID)).Length - 1; i++) - { - HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset + (uint)(Marshal.SizeOf(typeof(HidController)) * i)); - - HidController Controller = new HidController(); - Controller.Header.Type = (uint)(HidControllerType.ControllerType_Handheld | HidControllerType.ControllerType_JoyconPair); - Controller.Header.IsHalf = 0; - Controller.Header.SingleColorsDescriptor = (uint)(HidControllerColorDescription.ColorDesc_ColorsNonexistent); - Controller.Header.SingleColorBody = 0; - Controller.Header.SingleColorButtons = 0; - Controller.Header.SplitColorsDescriptor = 0; - Controller.Header.LeftColorBody = (uint)JoyConColor.Body_Neon_Red; - Controller.Header.LeftColorButtons = (uint)JoyConColor.Buttons_Neon_Red; - Controller.Header.RightColorBody = (uint)JoyConColor.Body_Neon_Blue; - Controller.Header.RightColorButtons = (uint)JoyConColor.Buttons_Neon_Blue; - - Controller.Layouts = new HidControllerLayout[Enum.GetNames(typeof(HidControllerLayouts)).Length]; - Controller.Layouts[(int)HidControllerLayouts.Main] = new HidControllerLayout(); - Controller.Layouts[(int)HidControllerLayouts.Main].Header.LatestEntry = (ulong)Hid_Num_Entries; - - Marshal.StructureToPtr(Controller, HidPtr, false); - } - - Logging.Info("HID Initialized!"); - } - } - - public void SendControllerButtons(HidControllerID ControllerId, - HidControllerLayouts Layout, - HidControllerKeys Buttons, - JoystickPosition LeftJoystick, - JoystickPosition RightJoystick) - { - uint InnerOffset = (uint)Marshal.SizeOf(typeof(HidSharedMemHeader)) + - (uint)Marshal.SizeOf(typeof(HidTouchScreen)) + - (uint)Marshal.SizeOf(typeof(HidMouse)) + - (uint)Marshal.SizeOf(typeof(HidKeyboard)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection1)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection2)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection3)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection4)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection5)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection6)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection7)) + - (uint)Marshal.SizeOf(typeof(HidUnknownSection8)) + - (uint)Marshal.SizeOf(typeof(HidControllerSerials)) + - ((uint)(Marshal.SizeOf(typeof(HidController)) * (int)ControllerId)) + - (uint)Marshal.SizeOf(typeof(HidControllerHeader)) + - (uint)Layout * (uint)Marshal.SizeOf(typeof(HidControllerLayout)); - - IntPtr HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); - - HidControllerLayoutHeader OldControllerHeaderLayout = (HidControllerLayoutHeader)Marshal.PtrToStructure(HidPtr, typeof(HidControllerLayoutHeader)); - - HidControllerLayoutHeader ControllerLayoutHeader = new HidControllerLayoutHeader - { - TimestampTicks = (ulong)Environment.TickCount, - NumEntries = (ulong)Hid_Num_Entries, - MaxEntryIndex = (ulong)Hid_Num_Entries - 1, - LatestEntry = (OldControllerHeaderLayout.LatestEntry < (ulong)Hid_Num_Entries ? OldControllerHeaderLayout.LatestEntry + 1 : 0) - }; - - Marshal.StructureToPtr(ControllerLayoutHeader, HidPtr, false); - - InnerOffset += (uint)Marshal.SizeOf(typeof(HidControllerLayoutHeader)) + (uint)((uint)(ControllerLayoutHeader.LatestEntry) * Marshal.SizeOf(typeof(HidControllerInputEntry))); - HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); - - HidControllerInputEntry ControllerInputEntry = new HidControllerInputEntry - { - Timestamp = (ulong)Environment.TickCount, - Timestamp_2 = (ulong)Environment.TickCount, - Buttons = (ulong)Buttons, - Joysticks = new JoystickPosition[(int)HidControllerJoystick.Joystick_Num_Sticks] - }; - ControllerInputEntry.Joysticks[(int)HidControllerJoystick.Joystick_Left] = LeftJoystick; - ControllerInputEntry.Joysticks[(int)HidControllerJoystick.Joystick_Right] = RightJoystick; - ControllerInputEntry.ConnectionState = (ulong)(HidControllerConnectionState.Controller_State_Connected | HidControllerConnectionState.Controller_State_Wired); - - Marshal.StructureToPtr(ControllerInputEntry, HidPtr, false); - } - - public void SendTouchPoint(HidTouchScreenEntryTouch TouchPoint) - { - uint InnerOffset = (uint)Marshal.SizeOf(typeof(HidSharedMemHeader)); - - IntPtr HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); - - HidTouchScreenHeader OldTouchScreenHeader = (HidTouchScreenHeader)Marshal.PtrToStructure(HidPtr,typeof(HidTouchScreenHeader)); - - HidTouchScreenHeader TouchScreenHeader = new HidTouchScreenHeader() - { - TimestampTicks = (ulong)Environment.TickCount, - NumEntries = (ulong)Hid_Num_Entries, - MaxEntryIndex = (ulong)Hid_Num_Entries - 1, - Timestamp = (ulong)Environment.TickCount, - LatestEntry = OldTouchScreenHeader.LatestEntry < Hid_Num_Entries-1 ? OldTouchScreenHeader.LatestEntry + 1 : 0 - }; - - Marshal.StructureToPtr(TouchScreenHeader, HidPtr, false); - - InnerOffset += (uint)Marshal.SizeOf(typeof(HidTouchScreenHeader)) - + (uint)((uint)(OldTouchScreenHeader.LatestEntry) * Marshal.SizeOf(typeof(HidTouchScreenEntry))); - HidPtr = new IntPtr(Ns.Ram.ToInt64() + (uint)SharedMemOffset + InnerOffset); - - HidTouchScreenEntry hidTouchScreenEntry = new HidTouchScreenEntry() - { - Header = new HidTouchScreenEntryHeader() - { - Timestamp = (ulong)Environment.TickCount, - NumTouches = 1 - }, - Touches = new HidTouchScreenEntryTouch[16] - }; - - //Only supports single touch - hidTouchScreenEntry.Touches[0] = TouchPoint; - - Marshal.StructureToPtr(hidTouchScreenEntry, HidPtr, false); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/Hid.cs b/Ryujinx.Core/Hid/Hid.cs new file mode 100644 index 0000000000..d7227889a6 --- /dev/null +++ b/Ryujinx.Core/Hid/Hid.cs @@ -0,0 +1,239 @@ +using ChocolArm64.Memory; +using System.Diagnostics; + +namespace Ryujinx.Core.Input +{ + public class Hid + { + /* + * Reference: + * https://github.com/reswitched/libtransistor/blob/development/lib/hid.c + * https://github.com/reswitched/libtransistor/blob/development/include/libtransistor/hid.h + * https://github.com/switchbrew/libnx/blob/master/nx/source/services/hid.c + * https://github.com/switchbrew/libnx/blob/master/nx/include/switch/services/hid.h + */ + + private const int HidHeaderSize = 0x400; + private const int HidTouchScreenSize = 0x3000; + private const int HidMouseSize = 0x400; + private const int HidKeyboardSize = 0x400; + private const int HidUnkSection1Size = 0x400; + private const int HidUnkSection2Size = 0x400; + private const int HidUnkSection3Size = 0x400; + private const int HidUnkSection4Size = 0x400; + private const int HidUnkSection5Size = 0x200; + private const int HidUnkSection6Size = 0x200; + private const int HidUnkSection7Size = 0x200; + private const int HidUnkSection8Size = 0x800; + private const int HidControllerSerialsSize = 0x4000; + private const int HidControllersSize = 0x32000; + private const int HidUnkSection9Size = 0x800; + + private const int HidTouchHeaderSize = 0x28; + private const int HidTouchEntrySize = 0x298; + + private const int HidTouchEntryHeaderSize = 0x10; + private const int HidTouchEntryTouchSize = 0x28; + + private const int HidControllerSize = 0x5000; + private const int HidControllerHeaderSize = 0x28; + private const int HidControllerLayoutsSize = 0x350; + + private const int HidControllersLayoutHeaderSize = 0x20; + private const int HidControllersInputEntrySize = 0x30; + + private const int HidHeaderOffset = 0; + private const int HidTouchScreenOffset = HidHeaderOffset + HidHeaderSize; + private const int HidMouseOffset = HidTouchScreenOffset + HidTouchScreenSize; + private const int HidKeyboardOffset = HidMouseOffset + HidMouseSize; + private const int HidUnkSection1Offset = HidKeyboardOffset + HidKeyboardSize; + private const int HidUnkSection2Offset = HidUnkSection1Offset + HidUnkSection1Size; + private const int HidUnkSection3Offset = HidUnkSection2Offset + HidUnkSection2Size; + private const int HidUnkSection4Offset = HidUnkSection3Offset + HidUnkSection3Size; + private const int HidUnkSection5Offset = HidUnkSection4Offset + HidUnkSection4Size; + private const int HidUnkSection6Offset = HidUnkSection5Offset + HidUnkSection5Size; + private const int HidUnkSection7Offset = HidUnkSection6Offset + HidUnkSection6Size; + private const int HidUnkSection8Offset = HidUnkSection7Offset + HidUnkSection7Size; + private const int HidControllerSerialsOffset = HidUnkSection8Offset + HidUnkSection8Size; + private const int HidControllersOffset = HidControllerSerialsOffset + HidControllerSerialsSize; + private const int HidUnkSection9Offset = HidControllersOffset + HidControllersSize; + + private const int HidEntryCount = 17; + + private long SharedMemOffset; + + private Switch Ns; + + public Hid(Switch Ns) + { + this.Ns = Ns; + } + + public void Init(long HidOffset) + { + SharedMemOffset = HidOffset; + + InitializeJoyconPair( + JoyConColor.Body_Neon_Red, + JoyConColor.Buttons_Neon_Red, + JoyConColor.Body_Neon_Blue, + JoyConColor.Buttons_Neon_Blue); + } + + public void InitializeJoyconPair( + JoyConColor LeftColorBody, + JoyConColor LeftColorButtons, + JoyConColor RightColorBody, + JoyConColor RightColorButtons) + { + long BaseControllerOffset = HidControllersOffset + 8 * HidControllerSize; + + HidControllerType Type = + HidControllerType.ControllerType_Handheld | + HidControllerType.ControllerType_JoyconPair; + + bool IsHalf = false; + + HidControllerColorDesc SingleColorDesc = + HidControllerColorDesc.ColorDesc_ColorsNonexistent; + + JoyConColor SingleColorBody = JoyConColor.Black; + JoyConColor SingleColorButtons = JoyConColor.Black; + + HidControllerColorDesc SplitColorDesc = 0; + + WriteInt32(BaseControllerOffset + 0x0, (int)Type); + + WriteInt32(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); + + WriteInt32(BaseControllerOffset + 0x18, (int)LeftColorBody); + WriteInt32(BaseControllerOffset + 0x1c, (int)LeftColorButtons); + + WriteInt32(BaseControllerOffset + 0x20, (int)RightColorBody); + WriteInt32(BaseControllerOffset + 0x24, (int)RightColorButtons); + } + + public void SetJoyconButton( + HidControllerId ControllerId, + HidControllerLayouts ControllerLayout, + HidControllerButtons Buttons, + HidJoystickPosition LeftStick, + HidJoystickPosition RightStick) + { + long ControllerOffset = HidControllersOffset + (int)ControllerId * HidControllerSize; + + ControllerOffset += HidControllerHeaderSize; + + ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; + + long LastEntry = ReadInt64(ControllerOffset + 0x10); + + long CurrEntry = (LastEntry + 1) % HidEntryCount; + + long Timestamp = Stopwatch.GetTimestamp(); + + WriteInt64(ControllerOffset + 0x0, Timestamp); + WriteInt64(ControllerOffset + 0x8, HidEntryCount); + WriteInt64(ControllerOffset + 0x10, CurrEntry); + WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1); + + ControllerOffset += HidControllersLayoutHeaderSize; + + ControllerOffset += CurrEntry * HidControllersInputEntrySize; + + WriteInt64(ControllerOffset + 0x0, Timestamp); + WriteInt64(ControllerOffset + 0x8, Timestamp); + + WriteInt64(ControllerOffset + 0x10, (uint)Buttons); + + WriteInt32(ControllerOffset + 0x18, LeftStick.DX); + WriteInt32(ControllerOffset + 0x1c, LeftStick.DY); + + WriteInt64(ControllerOffset + 0x20, RightStick.DX); + WriteInt64(ControllerOffset + 0x24, RightStick.DY); + + WriteInt64(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) + { + 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); + + TouchEntryOffset += HidTouchEntryTouchSize; + } + } + + private unsafe long ReadInt64(long Position) + { + 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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidController.cs b/Ryujinx.Core/Hid/HidController.cs deleted file mode 100644 index 641bc55695..0000000000 --- a/Ryujinx.Core/Hid/HidController.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Core -{ - [Flags] - public enum HidControllerKeys - { - KEY_A = (1 << 0), - KEY_B = (1 << 1), - KEY_X = (1 << 2), - KEY_Y = (1 << 3), - KEY_LSTICK = (1 << 4), - KEY_RSTICK = (1 << 5), - KEY_L = (1 << 6), - KEY_R = (1 << 7), - KEY_ZL = (1 << 8), - KEY_ZR = (1 << 9), - KEY_PLUS = (1 << 10), - KEY_MINUS = (1 << 11), - KEY_DLEFT = (1 << 12), - KEY_DUP = (1 << 13), - KEY_DRIGHT = (1 << 14), - KEY_DDOWN = (1 << 15), - KEY_LSTICK_LEFT = (1 << 16), - KEY_LSTICK_UP = (1 << 17), - KEY_LSTICK_RIGHT = (1 << 18), - KEY_LSTICK_DOWN = (1 << 19), - KEY_RSTICK_LEFT = (1 << 20), - KEY_RSTICK_UP = (1 << 21), - KEY_RSTICK_RIGHT = (1 << 22), - KEY_RSTICK_DOWN = (1 << 23), - KEY_SL = (1 << 24), - KEY_SR = (1 << 25), - - // Pseudo-key for at least one finger on the touch screen - KEY_TOUCH = (1 << 26), - - // Buttons by orientation (for single Joy-Con), also works with Joy-Con pairs, Pro Controller - KEY_JOYCON_RIGHT = (1 << 0), - KEY_JOYCON_DOWN = (1 << 1), - KEY_JOYCON_UP = (1 << 2), - KEY_JOYCON_LEFT = (1 << 3), - - // Generic catch-all directions, also works for single Joy-Con - KEY_UP = KEY_DUP | KEY_LSTICK_UP | KEY_RSTICK_UP, - KEY_DOWN = KEY_DDOWN | KEY_LSTICK_DOWN | KEY_RSTICK_DOWN, - KEY_LEFT = KEY_DLEFT | KEY_LSTICK_LEFT | KEY_RSTICK_LEFT, - KEY_RIGHT = KEY_DRIGHT | KEY_LSTICK_RIGHT | KEY_RSTICK_RIGHT, - } - - public enum HidControllerID - { - CONTROLLER_PLAYER_1 = 0, - CONTROLLER_PLAYER_2 = 1, - CONTROLLER_PLAYER_3 = 2, - CONTROLLER_PLAYER_4 = 3, - CONTROLLER_PLAYER_5 = 4, - CONTROLLER_PLAYER_6 = 5, - CONTROLLER_PLAYER_7 = 6, - CONTROLLER_PLAYER_8 = 7, - CONTROLLER_HANDHELD = 8, - CONTROLLER_UNKNOWN = 9 - } - - public enum HidControllerJoystick - { - Joystick_Left = 0, - Joystick_Right = 1, - Joystick_Num_Sticks = 2 - } - - public enum HidControllerLayouts - { - Pro_Controller, - Handheld_Joined, - Joined, - Left, - Right, - Main_No_Analog, - Main - } - - [Flags] - public enum HidControllerConnectionState - { - Controller_State_Connected = (1 << 0), - Controller_State_Wired = (1 << 1) - } - - [Flags] - public enum HidControllerType - { - ControllerType_ProController = (1 << 0), - ControllerType_Handheld = (1 << 1), - ControllerType_JoyconPair = (1 << 2), - ControllerType_JoyconLeft = (1 << 3), - ControllerType_JoyconRight = (1 << 4) - } - - public enum HidControllerColorDescription - { - ColorDesc_ColorsNonexistent = (1 << 1), - } - - [StructLayout(LayoutKind.Sequential, Size = 0x8)] - public struct JoystickPosition - { - public int DX; - public int DY; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x20)] - public struct HidControllerMAC - { - public ulong Timestamp; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public byte[] MAC; - public ulong Unknown; - public ulong Timestamp_2; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x28)] - public struct HidControllerHeader - { - public uint Type; - public uint IsHalf; - public uint SingleColorsDescriptor; - public uint SingleColorBody; - public uint SingleColorButtons; - public uint SplitColorsDescriptor; - public uint LeftColorBody; - public uint LeftColorButtons; - public uint RightColorBody; - public uint RightColorButtons; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x20)] - public struct HidControllerLayoutHeader - { - public ulong TimestampTicks; - public ulong NumEntries; - public ulong LatestEntry; - public ulong MaxEntryIndex; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x30)] - public struct HidControllerInputEntry - { - public ulong Timestamp; - public ulong Timestamp_2; - public ulong Buttons; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)HidControllerJoystick.Joystick_Num_Sticks)] - public JoystickPosition[] Joysticks; - public ulong ConnectionState; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x350)] - public struct HidControllerLayout - { - public HidControllerLayoutHeader Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public HidControllerInputEntry[] Entries; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x5000)] - public struct HidController - { - public HidControllerHeader Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] - public HidControllerLayout[] Layouts; - /* - pro_controller - handheld_joined - joined - left - right - main_no_analog - main - */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x2A70)] - public byte[] Unknown_1; - public HidControllerMAC MacLeft; - public HidControllerMAC MacRight; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xDF8)] - public byte[] Unknown_2; - } -} diff --git a/Ryujinx.Core/Hid/HidControllerButtons.cs b/Ryujinx.Core/Hid/HidControllerButtons.cs new file mode 100644 index 0000000000..65eb3f82fe --- /dev/null +++ b/Ryujinx.Core/Hid/HidControllerButtons.cs @@ -0,0 +1,35 @@ +using System; + +namespace Ryujinx.Core.Input +{ + [Flags] + public enum HidControllerButtons + { + KEY_A = (1 << 0), + KEY_B = (1 << 1), + KEY_X = (1 << 2), + KEY_Y = (1 << 3), + KEY_LSTICK = (1 << 4), + KEY_RSTICK = (1 << 5), + KEY_L = (1 << 6), + KEY_R = (1 << 7), + KEY_ZL = (1 << 8), + KEY_ZR = (1 << 9), + KEY_PLUS = (1 << 10), + KEY_MINUS = (1 << 11), + KEY_DLEFT = (1 << 12), + KEY_DUP = (1 << 13), + KEY_DRIGHT = (1 << 14), + KEY_DDOWN = (1 << 15), + KEY_LSTICK_LEFT = (1 << 16), + KEY_LSTICK_UP = (1 << 17), + KEY_LSTICK_RIGHT = (1 << 18), + KEY_LSTICK_DOWN = (1 << 19), + KEY_RSTICK_LEFT = (1 << 20), + KEY_RSTICK_UP = (1 << 21), + KEY_RSTICK_RIGHT = (1 << 22), + KEY_RSTICK_DOWN = (1 << 23), + KEY_SL = (1 << 24), + KEY_SR = (1 << 25) + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidControllerColorDesc.cs b/Ryujinx.Core/Hid/HidControllerColorDesc.cs new file mode 100644 index 0000000000..fc7fa2178a --- /dev/null +++ b/Ryujinx.Core/Hid/HidControllerColorDesc.cs @@ -0,0 +1,10 @@ +using System; + +namespace Ryujinx.Core.Input +{ + [Flags] + public enum HidControllerColorDesc + { + ColorDesc_ColorsNonexistent = (1 << 1) + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidControllerConnState.cs b/Ryujinx.Core/Hid/HidControllerConnState.cs new file mode 100644 index 0000000000..7f47a7f92f --- /dev/null +++ b/Ryujinx.Core/Hid/HidControllerConnState.cs @@ -0,0 +1,11 @@ +using System; + +namespace Ryujinx.Core.Input +{ + [Flags] + public enum HidControllerConnState + { + Controller_State_Connected = (1 << 0), + Controller_State_Wired = (1 << 1) + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidControllerId.cs b/Ryujinx.Core/Hid/HidControllerId.cs new file mode 100644 index 0000000000..84b68d27ef --- /dev/null +++ b/Ryujinx.Core/Hid/HidControllerId.cs @@ -0,0 +1,16 @@ +namespace Ryujinx.Core.Input +{ + public enum HidControllerId + { + CONTROLLER_PLAYER_1 = 0, + CONTROLLER_PLAYER_2 = 1, + CONTROLLER_PLAYER_3 = 2, + CONTROLLER_PLAYER_4 = 3, + CONTROLLER_PLAYER_5 = 4, + CONTROLLER_PLAYER_6 = 5, + CONTROLLER_PLAYER_7 = 6, + CONTROLLER_PLAYER_8 = 7, + CONTROLLER_HANDHELD = 8, + CONTROLLER_UNKNOWN = 9 + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidControllerLayouts.cs b/Ryujinx.Core/Hid/HidControllerLayouts.cs new file mode 100644 index 0000000000..e04c40b2d6 --- /dev/null +++ b/Ryujinx.Core/Hid/HidControllerLayouts.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Core.Input +{ + public enum HidControllerLayouts + { + Pro_Controller = 0, + Handheld_Joined = 1, + Joined = 2, + Left = 3, + Right = 4, + Main_No_Analog = 5, + Main = 6 + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidControllerType.cs b/Ryujinx.Core/Hid/HidControllerType.cs new file mode 100644 index 0000000000..a4eb674c8c --- /dev/null +++ b/Ryujinx.Core/Hid/HidControllerType.cs @@ -0,0 +1,14 @@ +using System; + +namespace Ryujinx.Core.Input +{ + [Flags] + public enum HidControllerType + { + ControllerType_ProController = (1 << 0), + ControllerType_Handheld = (1 << 1), + ControllerType_JoyconPair = (1 << 2), + ControllerType_JoyconLeft = (1 << 3), + ControllerType_JoyconRight = (1 << 4) + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidJoystickPosition.cs b/Ryujinx.Core/Hid/HidJoystickPosition.cs new file mode 100644 index 0000000000..61f8189f42 --- /dev/null +++ b/Ryujinx.Core/Hid/HidJoystickPosition.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Core.Input +{ + public struct HidJoystickPosition + { + public int DX; + public int DY; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidKeyboard.cs b/Ryujinx.Core/Hid/HidKeyboard.cs deleted file mode 100644 index f9987f68b2..0000000000 --- a/Ryujinx.Core/Hid/HidKeyboard.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.Core -{ - [StructLayout(LayoutKind.Sequential, Size = 0x20)] - public struct HidKeyboardHeader - { - public ulong TimestampTicks; - public ulong NumEntries; - public ulong LatestEntry; - public ulong MaxEntryIndex; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x38)] - public struct HidKeyboardEntry - { - public ulong Timestamp; - public ulong Timestamp_2; - public ulong Modifier; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public uint[] Keys; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidKeyboard - { - public HidKeyboardHeader Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public HidKeyboardEntry[] Entries; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x28)] - public byte[] Padding; - } -} diff --git a/Ryujinx.Core/Hid/HidMouse.cs b/Ryujinx.Core/Hid/HidMouse.cs deleted file mode 100644 index 9a019dd5ff..0000000000 --- a/Ryujinx.Core/Hid/HidMouse.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.Core -{ - [StructLayout(LayoutKind.Sequential, Size = 0x20)] - public struct HidMouseHeader - { - public ulong TimestampTicks; - public ulong NumEntries; - public ulong LatestEntry; - public ulong MaxEntryIndex; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x30)] - public struct HidMouseEntry - { - public ulong Timestamp; - public ulong Timestamp_2; - public uint X; - public uint Y; - public uint VelocityX; - public uint VelocityY; - public uint ScrollVelocityX; - public uint ScrollVelocityY; - public ulong Buttons; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidMouse - { - public HidMouseHeader Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public HidMouseEntry[] Entries; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xB0)] - public byte[] Padding; - } -} diff --git a/Ryujinx.Core/Hid/HidTouchPoint.cs b/Ryujinx.Core/Hid/HidTouchPoint.cs new file mode 100644 index 0000000000..1207e1d536 --- /dev/null +++ b/Ryujinx.Core/Hid/HidTouchPoint.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Core.Input +{ + public struct HidTouchPoint + { + public int X; + public int Y; + public int DiameterX; + public int DiameterY; + public int Angle; + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidTouchScreen.cs b/Ryujinx.Core/Hid/HidTouchScreen.cs deleted file mode 100644 index 755ebadca9..0000000000 --- a/Ryujinx.Core/Hid/HidTouchScreen.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.Core -{ - [StructLayout(LayoutKind.Sequential, Size = 0x28)] - public struct HidTouchScreenHeader - { - public ulong TimestampTicks; - public ulong NumEntries; - public ulong LatestEntry; - public ulong MaxEntryIndex; - public ulong Timestamp; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x10)] - public struct HidTouchScreenEntryHeader - { - public ulong Timestamp; - public ulong NumTouches; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x28)] - public struct HidTouchScreenEntryTouch - { - public ulong Timestamp; - public uint Padding; - public uint TouchIndex; - public uint X; - public uint Y; - public uint DiameterX; - public uint DiameterY; - public uint Angle; - public uint Padding_2; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x298)] - public struct HidTouchScreenEntry - { - public HidTouchScreenEntryHeader Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public HidTouchScreenEntryTouch[] Touches; - public ulong Unknown; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x3000)] - public struct HidTouchScreen - { - public HidTouchScreenHeader Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public HidTouchScreenEntry[] Entries; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3C0)] - public byte[] Padding; - } - -} \ No newline at end of file diff --git a/Ryujinx.Core/Hid/HidUnknown.cs b/Ryujinx.Core/Hid/HidUnknown.cs deleted file mode 100644 index c3fe68a833..0000000000 --- a/Ryujinx.Core/Hid/HidUnknown.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.Core -{ - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidSharedMemHeader - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidUnknownSection1 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidUnknownSection2 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidUnknownSection3 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x400)] - public struct HidUnknownSection4 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x400)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x200)] - public struct HidUnknownSection5 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x200)] - public struct HidUnknownSection6 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x200)] - public struct HidUnknownSection7 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x200)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x800)] - public struct HidUnknownSection8 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x800)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x4000)] - public struct HidControllerSerials - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4000)] - public byte[] Padding; - } - - [StructLayout(LayoutKind.Sequential, Size = 0x4600)] - public struct HidUnknownSection9 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4600)] - public byte[] Padding; - } -} diff --git a/Ryujinx.Core/Hid/JoyCon.cs b/Ryujinx.Core/Hid/JoyCon.cs index 8c9518e58e..1aef82fe08 100644 --- a/Ryujinx.Core/Hid/JoyCon.cs +++ b/Ryujinx.Core/Hid/JoyCon.cs @@ -1,27 +1,6 @@ -namespace Ryujinx +//TODO: This is only used by Config, it doesn't belong to Core. +namespace Ryujinx.Core.Input { - /// - /// Common RGB color hex codes for JoyCon coloring. - /// - public enum JoyConColor //Thanks to CTCaer - { - Body_Grey = 0x828282, - Body_Neon_Blue = 0x0AB9E6, - Body_Neon_Red = 0xFF3C28, - Body_Neon_Yellow = 0xE6FF00, - Body_Neon_Pink = 0xFF3278, - Body_Neon_Green = 0x1EDC00, - Body_Red = 0xE10F00, - - Buttons_Grey = 0x0F0F0F, - Buttons_Neon_Blue = 0x001E1E, - Buttons_Neon_Red = 0x1E0A0A, - Buttons_Neon_Yellow = 0x142800, - Buttons_Neon_Pink = 0x28001E, - Buttons_Neon_Green = 0x002800, - Buttons_Red = 0x280A0A - } - public struct JoyConLeft { public int StickUp; diff --git a/Ryujinx.Core/Hid/JoyConColor.cs b/Ryujinx.Core/Hid/JoyConColor.cs new file mode 100644 index 0000000000..21d89fe423 --- /dev/null +++ b/Ryujinx.Core/Hid/JoyConColor.cs @@ -0,0 +1,23 @@ +namespace Ryujinx.Core.Input +{ + public enum JoyConColor //Thanks to CTCaer + { + Black = 0, + + Body_Grey = 0x828282, + Body_Neon_Blue = 0x0AB9E6, + Body_Neon_Red = 0xFF3C28, + Body_Neon_Yellow = 0xE6FF00, + Body_Neon_Pink = 0xFF3278, + Body_Neon_Green = 0x1EDC00, + Body_Red = 0xE10F00, + + Buttons_Grey = 0x0F0F0F, + Buttons_Neon_Blue = 0x001E1E, + Buttons_Neon_Red = 0x1E0A0A, + Buttons_Neon_Yellow = 0x142800, + Buttons_Neon_Pink = 0x28001E, + Buttons_Neon_Green = 0x002800, + Buttons_Red = 0x280A0A + } +} \ No newline at end of file diff --git a/Ryujinx.Core/Loaders/Executable.cs b/Ryujinx.Core/Loaders/Executable.cs index 95cb7d6f6c..fa204460f6 100644 --- a/Ryujinx.Core/Loaders/Executable.cs +++ b/Ryujinx.Core/Loaders/Executable.cs @@ -96,7 +96,7 @@ namespace Ryujinx.Core.Loaders MemoryType Type, AMemoryPerm Perm) { - Memory.Manager.MapPhys(Position, Data.Count, (int)Type, AMemoryPerm.Write); + Memory.Manager.Map(Position, Data.Count, (int)Type, AMemoryPerm.Write); for (int Index = 0; Index < Data.Count; Index++) { @@ -108,7 +108,7 @@ namespace Ryujinx.Core.Loaders private void MapBss(long Position, long Size) { - Memory.Manager.MapPhys(Position, Size, (int)MemoryType.Normal, AMemoryPerm.RW); + Memory.Manager.Map(Position, Size, (int)MemoryType.Normal, AMemoryPerm.RW); } private ElfRel GetRelocation(long Position) diff --git a/Ryujinx.Core/OsHle/Handles/HSharedMem.cs b/Ryujinx.Core/OsHle/Handles/HSharedMem.cs index 3d56ff9209..9cd0a0d47e 100644 --- a/Ryujinx.Core/OsHle/Handles/HSharedMem.cs +++ b/Ryujinx.Core/OsHle/Handles/HSharedMem.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Core.OsHle.Handles public EventHandler MemoryMapped; public EventHandler MemoryUnmapped; - public HSharedMem(long PhysPos) + public HSharedMem() { Positions = new List(); } diff --git a/Ryujinx.Core/OsHle/Homebrew.cs b/Ryujinx.Core/OsHle/Homebrew.cs index f177b37e36..e2e95e4d41 100644 --- a/Ryujinx.Core/OsHle/Homebrew.cs +++ b/Ryujinx.Core/OsHle/Homebrew.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Core.OsHle //http://switchbrew.org/index.php?title=Homebrew_ABI public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle) { - Memory.Manager.MapPhys(Position, AMemoryMgr.PageSize, (int)MemoryType.Normal, AMemoryPerm.RW); + Memory.Manager.Map(Position, AMemoryMgr.PageSize, (int)MemoryType.Normal, AMemoryPerm.RW); //MainThreadHandle WriteConfigEntry(Memory, ref Position, 1, 0, MainThreadHandle); diff --git a/Ryujinx.Core/OsHle/Horizon.cs b/Ryujinx.Core/OsHle/Horizon.cs index 9929102d60..c238914398 100644 --- a/Ryujinx.Core/OsHle/Horizon.cs +++ b/Ryujinx.Core/OsHle/Horizon.cs @@ -1,4 +1,3 @@ -using ChocolArm64.Memory; using Ryujinx.Core.Loaders.Executables; using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Utilities; @@ -16,9 +15,6 @@ namespace Ryujinx.Core.OsHle internal int HidHandle { get; private set; } internal int FontHandle { get; private set; } - public long HidOffset { get; private set; } - public long FontOffset { get; private set; } - internal IdPool IdGen { get; private set; } internal IdPool NvMapIds { get; private set; } @@ -33,8 +29,6 @@ namespace Ryujinx.Core.OsHle private HSharedMem HidSharedMem; - private AMemoryAlloc Allocator; - private Switch Ns; public Horizon(Switch Ns) @@ -53,18 +47,13 @@ namespace Ryujinx.Core.OsHle Processes = new ConcurrentDictionary(); - Allocator = new AMemoryAlloc(); - - HidOffset = Allocator.Alloc(HidSize); - FontOffset = Allocator.Alloc(FontSize); - - HidSharedMem = new HSharedMem(HidOffset); + HidSharedMem = new HSharedMem(); HidSharedMem.MemoryMapped += HidInit; HidHandle = Handles.GenerateId(HidSharedMem); - FontHandle = Handles.GenerateId(new HSharedMem(FontOffset)); + FontHandle = Handles.GenerateId(new HSharedMem()); } public void LoadCart(string ExeFsDir, string RomFsFile = null) @@ -76,7 +65,7 @@ namespace Ryujinx.Core.OsHle int ProcessId = IdGen.GenerateId(); - Process MainProcess = new Process(Ns, Allocator, ProcessId); + Process MainProcess = new Process(Ns, ProcessId); void LoadNso(string FileName) { @@ -106,7 +95,6 @@ namespace Ryujinx.Core.OsHle LoadNso("subsdk*"); LoadNso("sdk"); - MainProcess.InitializeHeap(); MainProcess.Run(); Processes.TryAdd(ProcessId, MainProcess); @@ -118,7 +106,7 @@ namespace Ryujinx.Core.OsHle int ProcessId = IdGen.GenerateId(); - Process MainProcess = new Process(Ns, Allocator, ProcessId); + Process MainProcess = new Process(Ns, ProcessId); using (FileStream Input = new FileStream(FileName, FileMode.Open)) { @@ -128,7 +116,6 @@ namespace Ryujinx.Core.OsHle } MainProcess.SetEmptyArgs(); - MainProcess.InitializeHeap(); MainProcess.Run(IsNro); Processes.TryAdd(ProcessId, MainProcess); @@ -186,7 +173,7 @@ namespace Ryujinx.Core.OsHle if (SharedMem.TryGetLastVirtualPosition(out long Position)) { - Logging.Info($"HID shared memory successfully mapped to {Position:x16}!"); + Logging.Info($"HID shared memory successfully mapped to 0x{Position:x16}!"); Ns.Hid.Init(Position); } diff --git a/Ryujinx.Core/OsHle/MemoryInfo.cs b/Ryujinx.Core/OsHle/MemoryInfo.cs deleted file mode 100644 index 76a4bef3ba..0000000000 --- a/Ryujinx.Core/OsHle/MemoryInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -using ChocolArm64.Memory; - -namespace Ryujinx.Core.OsHle -{ - struct MemoryInfo - { - public long BaseAddress; - public long Size; - public int MemType; - public int MemAttr; - public int MemPerm; - public int IpcRefCount; - public int DeviceRefCount; - public int Padding; //SBZ - - public MemoryInfo(AMemoryMapInfo MapInfo) - { - BaseAddress = MapInfo.Position; - Size = MapInfo.Size; - MemType = MapInfo.Type; - MemAttr = MapInfo.Attr; - MemPerm = (int)MapInfo.Perm; - IpcRefCount = 0; - DeviceRefCount = 0; - Padding = 0; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/MemoryRegions.cs b/Ryujinx.Core/OsHle/MemoryRegions.cs index 86d266f60d..e362ba9fd7 100644 --- a/Ryujinx.Core/OsHle/MemoryRegions.cs +++ b/Ryujinx.Core/OsHle/MemoryRegions.cs @@ -1,11 +1,28 @@ +using ChocolArm64.Memory; + namespace Ryujinx.Core.OsHle { static class MemoryRegions { - public const long MapRegionAddress = 0x80000000; - public const long MapRegionSize = 0x40000000; + public const long AddrSpaceStart = 0x08000000; + + public const long MapRegionAddress = 0x10000000; + public const long MapRegionSize = 0x10000000; + + public const long MainStackSize = 0x100000; + + public const long MainStackAddress = AMemoryMgr.AddrSize - MainStackSize; + + public const long TlsPagesSize = 0x4000; + + public const long TlsPagesAddress = MainStackAddress - TlsPagesSize; public const long HeapRegionAddress = MapRegionAddress + MapRegionSize; - public const long HeapRegionSize = 0x40000000; + + public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize; + + public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart; + + public const long AddrSpaceSize = AMemoryMgr.AddrSize - AddrSpaceStart; } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Process.cs b/Ryujinx.Core/OsHle/Process.cs index 9ca9a2bd5c..f549b02792 100644 --- a/Ryujinx.Core/OsHle/Process.cs +++ b/Ryujinx.Core/OsHle/Process.cs @@ -15,19 +15,15 @@ namespace Ryujinx.Core.OsHle { public class Process : IDisposable { - private const int MaxStackSize = 8 * 1024 * 1024; - private const int TlsSize = 0x200; private const int TotalTlsSlots = 32; - private const int TlsTotalSize = TotalTlsSlots * TlsSize; - private const long TlsPageAddr = (AMemoryMgr.AddrSize - TlsTotalSize) & ~AMemoryMgr.PageMask; private Switch Ns; - private ATranslator Translator; - public int ProcessId { get; private set; } + private ATranslator Translator; + public AMemory Memory { get; private set; } public KProcessScheduler Scheduler { get; private set; } @@ -44,12 +40,12 @@ namespace Ryujinx.Core.OsHle private long ImageBase; - public Process(Switch Ns, AMemoryAlloc Allocator, int ProcessId) + public Process(Switch Ns, int ProcessId) { this.Ns = Ns; this.ProcessId = ProcessId; - Memory = new AMemory(Ns.Ram, Allocator); + Memory = new AMemory(Ns.Ram); Scheduler = new KProcessScheduler(); @@ -61,13 +57,12 @@ namespace Ryujinx.Core.OsHle Executables = new List(); - ImageBase = 0x8000000; + ImageBase = MemoryRegions.AddrSpaceStart; - Memory.Manager.MapPhys( - TlsPageAddr, - TlsTotalSize, - (int)MemoryType.ThreadLocal, - AMemoryPerm.RW); + MapRWMemRegion( + MemoryRegions.TlsPagesAddress, + MemoryRegions.TlsPagesSize, + MemoryType.ThreadLocal); } public void LoadProgram(IExecutable Program) @@ -86,11 +81,6 @@ namespace Ryujinx.Core.OsHle ImageBase += AMemoryMgr.PageSize; } - public void InitializeHeap() - { - Memory.Manager.SetHeapAddr(MemoryRegions.HeapRegionAddress); - } - public bool Run(bool UseHbAbi = false) { if (Executables.Count == 0) @@ -98,11 +88,14 @@ namespace Ryujinx.Core.OsHle return false; } - long StackBot = TlsPageAddr - MaxStackSize; + MapRWMemRegion( + MemoryRegions.MainStackAddress, + MemoryRegions.MainStackSize, + MemoryType.Normal); + + long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize; - Memory.Manager.MapPhys(StackBot, MaxStackSize, (int)MemoryType.Normal, AMemoryPerm.RW); - - int Handle = MakeThread(Executables[0].ImageBase, TlsPageAddr, 0, 0, 0); + int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 0, 0); if (Handle == -1) { @@ -113,7 +106,7 @@ namespace Ryujinx.Core.OsHle if (UseHbAbi) { - long HbAbiDataPosition = (Executables[0].ImageEnd + 0xfff) & ~0xfff; + long HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd); Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle); @@ -126,6 +119,11 @@ namespace Ryujinx.Core.OsHle return true; } + private void MapRWMemRegion(long Position, long Size, MemoryType Type) + { + Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW); + } + public void StopAllThreads() { if (MainThread != null) @@ -188,12 +186,14 @@ namespace Ryujinx.Core.OsHle return -1; } + long Tpidr = MemoryRegions.TlsPagesAddress + TlsSlot * 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.Tpidr = TlsPageAddr + TlsSlot * TlsSize; + Thread.ThreadState.Tpidr = Tpidr; Thread.ThreadState.X0 = (ulong)ArgsPtr; Thread.ThreadState.X1 = (ulong)Handle; Thread.ThreadState.X31 = (ulong)StackTop; @@ -239,7 +239,29 @@ namespace Ryujinx.Core.OsHle private void CpuTraceHandler(object sender, ACpuTraceEventArgs e) { - Logging.Trace($"Executing at 0x{e.Position:x16} {e.SubName}"); + string NsoName = string.Empty; + + for (int Index = Executables.Count - 1; Index >= 0; Index--) + { + if (e.Position >= Executables[Index].ImageBase) + { + NsoName = $"{(e.Position - Executables[Index].ImageBase):x16}"; + + break; + } + } + + Logging.Trace($"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}"); + } + + public void EnableCpuTracing() + { + Translator.EnableCpuTrace = true; + } + + public void DisableCpuTracing() + { + Translator.EnableCpuTrace = false; } private int GetFreeTlsSlot(AThread Thread) @@ -267,7 +289,7 @@ namespace Ryujinx.Core.OsHle private int GetTlsSlot(long Position) { - return (int)((Position - TlsPageAddr) / TlsSize); + return (int)((Position - MemoryRegions.TlsPagesAddress) / TlsSize); } public HThread GetThread(long Tpidr) diff --git a/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs index a45b23ccd5..c58d9e804c 100644 --- a/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs +++ b/Ryujinx.Core/OsHle/Services/Aud/IAudioOut.cs @@ -1,4 +1,4 @@ -using ChocolArm64.Memory; +using ChocolArm64.Memory; using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; using OpenTK.Audio; @@ -9,7 +9,7 @@ using System.IO; namespace Ryujinx.Core.OsHle.IpcServices.Aud { - class IAudioOut : IIpcService + class IAudioOut : IIpcService, IDisposable { private Dictionary m_Commands; @@ -39,7 +39,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud //IAudioOut private AudioOutState State = AudioOutState.Stopped; - private Queue KeysQueue = new Queue(); + private Queue BufferIdQueue = new Queue(); //OpenAL private bool OpenALInstalled = true; @@ -71,7 +71,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud OpenALInstalled = false; } - if (OpenALInstalled) AL.Listener(ALListenerf.Gain, (float)8.0); //Add more gain to it + if (OpenALInstalled) AL.Listener(ALListenerf.Gain, 8.0f); //Add more gain to it } return 0; @@ -88,6 +88,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud AL.SourceStop(Source); AL.DeleteSource(Source); + AL.DeleteBuffers(1, ref Buffer); } State = AudioOutState.Stopped; } @@ -99,9 +100,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud { long BufferId = Context.RequestData.ReadInt64(); - KeysQueue.Enqueue(BufferId); - byte[] AudioOutBuffer = AMemoryHelper.ReadBytes(Context.Memory, Context.Request.SendBuff[0].Position, sizeof(long) * 5); + using (MemoryStream MS = new MemoryStream(AudioOutBuffer)) { BinaryReader Reader = new BinaryReader(MS); @@ -111,18 +111,26 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud long SizeDataInSampleBuffer = Reader.ReadInt64(); long OffsetDataInSampleBuffer = Reader.ReadInt64(); - byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerSampleBuffer + OffsetDataInSampleBuffer, (int)SizeDataInSampleBuffer); - - if (OpenALInstalled) + if (SizeDataInSampleBuffer > 0) { - if (AudioCtx == null) //Needed to call the instance of AudioContext() - return 0; + BufferIdQueue.Enqueue(BufferId); - Buffer = AL.GenBuffer(); - AL.BufferData(Buffer, ALFormat.Stereo16, AudioSampleBuffer, AudioSampleBuffer.Length, 48000); + byte[] AudioSampleBuffer = AMemoryHelper.ReadBytes(Context.Memory, PointerSampleBuffer + OffsetDataInSampleBuffer, (int)SizeDataInSampleBuffer); - Source = AL.GenSource(); - AL.SourceQueueBuffer(Source, Buffer); + if (OpenALInstalled) + { + if (AudioCtx == null) //Needed to call the instance of AudioContext() + return 0; + + EnsureAudioFinalized(); + + Source = AL.GenSource(); + Buffer = AL.GenBuffer(); + + AL.BufferData(Buffer, ALFormat.Stereo16, AudioSampleBuffer, AudioSampleBuffer.Length, 48000); + AL.SourceQueueBuffer(Source, Buffer); + AL.SourcePlay(Source); + } } } @@ -140,25 +148,19 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud public long GetReleasedAudioOutBuffer(ServiceCtx Context) { - long TempKey = 0; + int ReleasedBuffersCount = 0; - if (KeysQueue.Count > 0) TempKey = KeysQueue.Dequeue(); - - AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, BitConverter.GetBytes(TempKey)); - - int ReleasedBuffersCount = 1; - Context.ResponseData.Write(ReleasedBuffersCount); - - if (OpenALInstalled) + for(int i = 0; i < BufferIdQueue.Count; i++) { - if (AudioCtx == null) //Needed to call the instance of AudioContext() - return 0; + long BufferId = BufferIdQueue.Dequeue(); - AL.SourcePlay(Source); - int[] FreeBuffers = AL.SourceUnqueueBuffers(Source, 1); - AL.DeleteBuffers(FreeBuffers); + AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position + (8 * i), BitConverter.GetBytes(BufferId)); + + ReleasedBuffersCount++; } + Context.ResponseData.Write(ReleasedBuffersCount); + return 0; } @@ -176,5 +178,33 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud { return 0; } + + private void EnsureAudioFinalized() + { + if (Source != 0 || + Buffer != 0) + { + AL.SourceStop(Source); + AL.SourceUnqueueBuffer(Buffer); + AL.DeleteSource(Source); + AL.DeleteBuffers(1, ref Buffer); + + Source = 0; + Buffer = 0; + } + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + EnsureAudioFinalized(); + } + } } } diff --git a/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs b/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs new file mode 100644 index 0000000000..b594441272 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Bsd/ServiceBsd.cs @@ -0,0 +1,96 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Bsd +{ + class ServiceBsd : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceBsd() + { + m_Commands = new Dictionary() + { + { 0, Initialize }, + { 1, StartMonitoring }, + { 2, Socket }, + { 10, Send }, + { 14, Connect } + }; + } + + //Initialize(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno + public long Initialize(ServiceCtx Context) + { + /* + typedef struct { + u32 version; // Observed 1 on 2.0 LibAppletWeb, 2 on 3.0. + + u32 tcp_tx_buf_size; // Size of the TCP transfer (send) buffer (initial or fixed). + u32 tcp_rx_buf_size; // Size of the TCP recieve buffer (initial or fixed). + u32 tcp_tx_buf_max_size; // Maximum size of the TCP transfer (send) buffer. If it is 0, the size of the buffer is fixed to its initial value. + u32 tcp_rx_buf_max_size; // Maximum size of the TCP receive buffer. If it is 0, the size of the buffer is fixed to its initial value. + + u32 udp_tx_buf_size; // Size of the UDP transfer (send) buffer (typically 0x2400 bytes). + u32 udp_rx_buf_size; // Size of the UDP receive buffer (typically 0xA500 bytes). + + u32 sb_efficiency; // Number of buffers for each socket (standard values range from 1 to 8). + } BsdBufferConfig; + */ + + long Pid = Context.RequestData.ReadInt64(); + long TransferMemorySize = Context.RequestData.ReadInt64(); + + // Two other args are unknown! + + Context.ResponseData.Write(0); + + //Todo: Stub + + return 0; + } + + //StartMonitoring(u64, pid) + public long StartMonitoring(ServiceCtx Context) + { + //Todo: Stub + + return 0; + } + + //Socket(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) + public long Socket(ServiceCtx Context) + { + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + + //Todo: Stub + + return 0; + } + + //Connect(u32 socket, buffer) -> (i32 ret, u32 bsd_errno) + public long Connect(ServiceCtx Context) + { + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + + //Todo: Stub + + return 0; + } + + //Send(u32 socket, u32 flags, buffer) -> (i32 ret, u32 bsd_errno) + public long Send(ServiceCtx Context) + { + Context.ResponseData.Write(0); + Context.ResponseData.Write(0); + + //Todo: Stub + + return 0; + } + } +} \ 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 ee9de8bc8c..569a7dd6b4 100644 --- a/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs +++ b/Ryujinx.Core/OsHle/Services/FspSrv/IFileSystem.cs @@ -1,8 +1,8 @@ -using ChocolArm64.Memory; using Ryujinx.Core.OsHle.Ipc; using System; using System.Collections.Generic; using System.IO; +using System.Text; using static Ryujinx.Core.OsHle.IpcServices.ErrorCode; using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; @@ -49,7 +49,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv { long Position = Context.Request.PtrBuff[0].Position; - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + string Name = ReadUtf8String(Context); long Mode = Context.RequestData.ReadInt64(); int Size = Context.RequestData.ReadInt32(); @@ -83,7 +83,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv { long Position = Context.Request.PtrBuff[0].Position; - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + string Name = ReadUtf8String(Context); string FileName = Context.Ns.VFs.GetFullPath(Path, Name); @@ -106,7 +106,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv { long Position = Context.Request.PtrBuff[0].Position; - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + string Name = ReadUtf8String(Context); string DirName = Context.Ns.VFs.GetFullPath(Path, Name); @@ -144,7 +144,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv { long Position = Context.Request.PtrBuff[0].Position; - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + string Name = ReadUtf8String(Context); string DirName = Context.Ns.VFs.GetFullPath(Path, Name); @@ -165,11 +165,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv public long RenameFile(ServiceCtx Context) { - long OldPosition = Context.Request.PtrBuff[0].Position; - long NewPosition = Context.Request.PtrBuff[0].Position; - - string OldName = AMemoryHelper.ReadAsciiString(Context.Memory, OldPosition); - string NewName = AMemoryHelper.ReadAsciiString(Context.Memory, NewPosition); + string OldName = ReadUtf8String(Context, 0); + string NewName = ReadUtf8String(Context, 1); string OldFileName = Context.Ns.VFs.GetFullPath(Path, OldName); string NewFileName = Context.Ns.VFs.GetFullPath(Path, NewName); @@ -196,11 +193,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv public long RenameDirectory(ServiceCtx Context) { - long OldPosition = Context.Request.PtrBuff[0].Position; - long NewPosition = Context.Request.PtrBuff[0].Position; - - string OldName = AMemoryHelper.ReadAsciiString(Context.Memory, OldPosition); - string NewName = AMemoryHelper.ReadAsciiString(Context.Memory, NewPosition); + string OldName = ReadUtf8String(Context, 0); + string NewName = ReadUtf8String(Context, 1); string OldDirName = Context.Ns.VFs.GetFullPath(Path, OldName); string NewDirName = Context.Ns.VFs.GetFullPath(Path, NewName); @@ -229,7 +223,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv { long Position = Context.Request.PtrBuff[0].Position; - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + string Name = ReadUtf8String(Context); string FileName = Context.Ns.VFs.GetFullPath(Path, Name); @@ -257,7 +251,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv int FilterFlags = Context.RequestData.ReadInt32(); - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + string Name = ReadUtf8String(Context); string FileName = Context.Ns.VFs.GetFullPath(Path, Name); @@ -293,7 +287,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv int FilterFlags = Context.RequestData.ReadInt32(); - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + string Name = ReadUtf8String(Context); string DirName = Context.Ns.VFs.GetFullPath(Path, Name); @@ -330,7 +324,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv { long Position = Context.Request.PtrBuff[0].Position; - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + string Name = ReadUtf8String(Context); Context.ResponseData.Write(Context.Ns.VFs.GetDrive().AvailableFreeSpace); @@ -341,7 +335,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv { long Position = Context.Request.PtrBuff[0].Position; - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position); + string Name = ReadUtf8String(Context); Context.ResponseData.Write(Context.Ns.VFs.GetDrive().TotalSize); @@ -379,5 +373,28 @@ namespace Ryujinx.Core.OsHle.IpcServices.FspSrv OpenPaths.Remove(DirInterface.HostPath); } } + + private string ReadUtf8String(ServiceCtx Context, int Index = 0) + { + long Position = Context.Request.PtrBuff[Index].Position; + long Size = Context.Request.PtrBuff[Index].Size; + + using (MemoryStream MS = new MemoryStream()) + { + while (Size-- > 0) + { + byte Value = Context.Memory.ReadByte(Position++); + + if (Value == 0) + { + break; + } + + MS.WriteByte(Value); + } + + return Encoding.UTF8.GetString(MS.ToArray()); + } + } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs b/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs index b0e5f44e27..cc30a771e0 100644 --- a/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs +++ b/Ryujinx.Core/OsHle/Services/Hid/ServiceHid.cs @@ -26,6 +26,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid { 121, GetNpadJoyHoldType }, { 200, GetVibrationDeviceInfo }, { 203, CreateActiveVibrationDeviceList }, + { 206, SendVibrationValues } }; } @@ -104,5 +105,10 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid return 0; } + + public long SendVibrationValues(ServiceCtx Context) + { + return 0; + } } } \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nifm/IGeneralService.cs b/Ryujinx.Core/OsHle/Services/Nifm/IGeneralService.cs new file mode 100644 index 0000000000..c31ee36b20 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nifm/IGeneralService.cs @@ -0,0 +1,34 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Nifm +{ + class IGeneralService : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public IGeneralService() + { + m_Commands = new Dictionary() + { + { 4, CreateRequest } + }; + } + + //CreateRequest(i32) + public long CreateRequest(ServiceCtx Context) + { + int Unknown = Context.RequestData.ReadInt32(); + + MakeObject(Context, new IRequest()); + + //Todo: Stub + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs b/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs new file mode 100644 index 0000000000..6110e5fbe2 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nifm/IRequest.cs @@ -0,0 +1,49 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Nifm +{ + class IRequest : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public IRequest() + { + m_Commands = new Dictionary() + { + { 0, GetRequestState }, + { 1, GetResult }, + { 2, GetSystemEventReadableHandles } + }; + } + + // -> i32 + public long GetRequestState(ServiceCtx Context) + { + Context.ResponseData.Write(0); + + //Todo: Stub + + return 0; + } + + public long GetResult(ServiceCtx Context) + { + //Todo: Stub + + return 0; + } + + //GetSystemEventReadableHandles() -> (KObject, KObject) + public long GetSystemEventReadableHandles(ServiceCtx Context) + { + Context.Response.HandleDesc = IpcHandleDesc.MakeMove(0xbadcafe); + + //Todo: Stub + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nifm/ServiceNifm.cs b/Ryujinx.Core/OsHle/Services/Nifm/ServiceNifm.cs new file mode 100644 index 0000000000..7e183389b2 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Nifm/ServiceNifm.cs @@ -0,0 +1,29 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; + +namespace Ryujinx.Core.OsHle.IpcServices.Nifm +{ + class ServiceNifm : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceNifm() + { + m_Commands = new Dictionary() + { + { 4, CreateGeneralServiceOld } + }; + } + + public long CreateGeneralServiceOld(ServiceCtx Context) + { + MakeObject(Context, new IGeneralService()); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs index 1de0ab7062..0ea1d2ac0a 100644 --- a/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs +++ b/Ryujinx.Core/OsHle/Services/Nv/ServiceNvDrv.cs @@ -532,6 +532,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices Context.Memory.WriteInt32(Position + 4, Handle); + Logging.Info($"NvMap {Id} created with size {Size:x8}!"); + return 0; } @@ -580,8 +582,6 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices NvMap.Kind = Kind; } - Logging.Debug($"NvMapIocAlloc at {NvMap.Address:x16}"); - return 0; } diff --git a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs b/Ryujinx.Core/OsHle/Services/ServiceFactory.cs index 54fa38ab78..31c8aa2c86 100644 --- a/Ryujinx.Core/OsHle/Services/ServiceFactory.cs +++ b/Ryujinx.Core/OsHle/Services/ServiceFactory.cs @@ -2,16 +2,20 @@ 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; @@ -31,16 +35,21 @@ namespace Ryujinx.Core.OsHle.IpcServices 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(); diff --git a/Ryujinx.Core/OsHle/Services/Set/ServiceSet.cs b/Ryujinx.Core/OsHle/Services/Set/ServiceSet.cs index 05e409b0d1..c60e1712aa 100644 --- a/Ryujinx.Core/OsHle/Services/Set/ServiceSet.cs +++ b/Ryujinx.Core/OsHle/Services/Set/ServiceSet.cs @@ -1,5 +1,6 @@ using ChocolArm64.Memory; using Ryujinx.Core.OsHle.Ipc; +using System; using System.Collections.Generic; namespace Ryujinx.Core.OsHle.IpcServices.Set @@ -30,12 +31,10 @@ namespace Ryujinx.Core.OsHle.IpcServices.Set short Size = Context.Request.RecvListBuff[0].Size; //This should return an array of ints with values matching the LanguageCode enum. - byte[] Data = new byte[Size]; - - Data[0] = 0; - Data[1] = 1; - - AMemoryHelper.WriteBytes(Context.Memory, Position, Data); + foreach (long value in new long[] { 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L }) + { + AMemoryHelper.WriteBytes(Context.Memory, Position += 8, BitConverter.GetBytes(value)); + } } Context.ResponseData.Write(LangCodesCount); diff --git a/Ryujinx.Core/OsHle/Services/Set/ServiceSetSys.cs b/Ryujinx.Core/OsHle/Services/Set/ServiceSetSys.cs new file mode 100644 index 0000000000..dee6573d35 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Set/ServiceSetSys.cs @@ -0,0 +1,33 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Set +{ + class ServiceSetSys : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceSetSys() + { + m_Commands = new Dictionary() + { + { 23, GetColorSetId }, + { 24, SetColorSetId } + }; + } + + public static long GetColorSetId(ServiceCtx Context) + { + Context.ResponseData.Write((int)Context.Ns.Settings.ThemeColor); + + return 0; + } + + public static long SetColorSetId(ServiceCtx Context) + { + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Sfdnsres/ServiceSfdnsres.cs b/Ryujinx.Core/OsHle/Services/Sfdnsres/ServiceSfdnsres.cs new file mode 100644 index 0000000000..f110ae736f --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Sfdnsres/ServiceSfdnsres.cs @@ -0,0 +1,20 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Sfdnsres +{ + class ServiceSfdnsres : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceSfdnsres() + { + m_Commands = new Dictionary() + { + //{ 0, Function } + }; + } + } +} diff --git a/Ryujinx.Core/OsHle/Services/Ssl/ServiceSsl.cs b/Ryujinx.Core/OsHle/Services/Ssl/ServiceSsl.cs new file mode 100644 index 0000000000..23934b1405 --- /dev/null +++ b/Ryujinx.Core/OsHle/Services/Ssl/ServiceSsl.cs @@ -0,0 +1,20 @@ +using Ryujinx.Core.OsHle.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.Core.OsHle.IpcServices.Ssl +{ + class ServiceSsl : IIpcService + { + private Dictionary m_Commands; + + public IReadOnlyDictionary Commands => m_Commands; + + public ServiceSsl() + { + m_Commands = new Dictionary() + { + //{ 0, Function } + }; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs index 85a06cea41..b745867e92 100644 --- a/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs +++ b/Ryujinx.Core/OsHle/Services/Vi/NvFlinger.cs @@ -35,12 +35,22 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android Acquired } + private struct Rect + { + public int Top; + public int Left; + public int Right; + public int Bottom; + } + private struct BufferEntry { public BufferState State; public HalTransform Transform; + public Rect Crop; + public GbpBuffer Data; } @@ -168,6 +178,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android BufferQueue[Slot].Transform = (HalTransform)Transform; + BufferQueue[Slot].Crop.Top = CropTop; + BufferQueue[Slot].Crop.Left = CropLeft; + BufferQueue[Slot].Crop.Right = CropRight; + BufferQueue[Slot].Crop.Bottom = CropBottom; + BufferQueue[Slot].State = BufferState.Queued; SendFrameBuffer(Context, Slot); @@ -256,25 +271,49 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android return; } - BufferQueue[Slot].State = BufferState.Acquired; + BufferQueue[Slot].State = BufferState.Acquired; + + Rect Crop = BufferQueue[Slot].Crop; + + int RealWidth = FbWidth; + int RealHeight = FbHeight; float ScaleX = 1; float ScaleY = 1; + float OffsX = 0; + float OffsY = 0; + + if (Crop.Right != 0 && + Crop.Bottom != 0) + { + RealWidth = Crop.Right - Crop.Left; + RealHeight = Crop.Bottom - Crop.Top; + + ScaleX = (float)FbWidth / RealWidth; + ScaleY = (float)FbHeight / RealHeight; + + OffsX = -(float)Crop.Left / Crop.Right; + OffsY = -(float)Crop.Top / Crop.Bottom; + + OffsX += ScaleX - 1; + OffsY += ScaleY - 1; + } + float Rotate = 0; if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX)) { - ScaleX = -1; + ScaleX = -ScaleX; } if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY)) { - ScaleY = -1; + ScaleY = -ScaleY; } if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90)) { - Rotate = MathF.PI * 0.5f; + Rotate = -MathF.PI * 0.5f; } byte* Fb = (byte*)Context.Ns.Ram + NvMap.Address; @@ -287,6 +326,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android FbHeight, ScaleX, ScaleY, + OffsX, + OffsY, Rotate); BufferQueue[Slot].State = BufferState.Free; diff --git a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs index ec53f47ff2..212d30a6c3 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcHandler.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcHandler.cs @@ -17,6 +17,8 @@ namespace Ryujinx.Core.OsHle.Svc private AMemory Memory; private static Random Rng; + + private ulong CurrentHeapSize; public SvcHandler(Switch Ns, Process Process) { @@ -25,6 +27,7 @@ namespace Ryujinx.Core.OsHle.Svc { 0x01, SvcSetHeapSize }, { 0x03, SvcSetMemoryAttribute }, { 0x04, SvcMapMemory }, + { 0x05, SvcUnmapMemory }, { 0x06, SvcQueryMemory }, { 0x07, SvcExitProcess }, { 0x08, SvcCreateThread }, diff --git a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs index 6ca27f16d0..6969d37936 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcMemory.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcMemory.cs @@ -10,10 +10,21 @@ namespace Ryujinx.Core.OsHle.Svc { uint Size = (uint)ThreadState.X1; - Memory.Manager.SetHeapSize(Size, (int)MemoryType.Heap); + long Position = MemoryRegions.HeapRegionAddress; + + if (Size > CurrentHeapSize) + { + Memory.Manager.Map(Position, Size, (int)MemoryType.Heap, AMemoryPerm.RW); + } + else + { + Memory.Manager.Unmap(Position + Size, (long)CurrentHeapSize - Size); + } + + CurrentHeapSize = Size; ThreadState.X0 = (int)SvcResult.Success; - ThreadState.X1 = (ulong)Memory.Manager.HeapAddr; + ThreadState.X1 = (ulong)Position; } private void SvcSetMemoryAttribute(AThreadState ThreadState) @@ -42,7 +53,30 @@ namespace Ryujinx.Core.OsHle.Svc long Src = (long)ThreadState.X1; long Size = (long)ThreadState.X2; - Memory.Manager.MapMirror(Src, Dst, Size, (int)MemoryType.MappedMemory); + AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src); + + Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm); + + Memory.Manager.Reprotect(Src, Size, AMemoryPerm.None); + + Memory.Manager.SetAttrBit(Src, Size, 0); + + ThreadState.X0 = (int)SvcResult.Success; + } + + private void SvcUnmapMemory(AThreadState ThreadState) + { + long Dst = (long)ThreadState.X0; + long Src = (long)ThreadState.X1; + long Size = (long)ThreadState.X2; + + AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst); + + Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory); + + Memory.Manager.Reprotect(Src, Size, DstInfo.Perm); + + Memory.Manager.ClearAttrBit(Src, Size, 0); ThreadState.X0 = (int)SvcResult.Success; } @@ -54,17 +88,22 @@ namespace Ryujinx.Core.OsHle.Svc AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); - MemoryInfo Info = new MemoryInfo(MapInfo); + if (MapInfo == null) + { + //TODO: Correct error code. + ThreadState.X0 = ulong.MaxValue; - Memory.WriteInt64(InfoPtr + 0x00, Info.BaseAddress); - Memory.WriteInt64(InfoPtr + 0x08, Info.Size); - Memory.WriteInt32(InfoPtr + 0x10, Info.MemType); - Memory.WriteInt32(InfoPtr + 0x14, Info.MemAttr); - Memory.WriteInt32(InfoPtr + 0x18, Info.MemPerm); - Memory.WriteInt32(InfoPtr + 0x1c, Info.IpcRefCount); - Memory.WriteInt32(InfoPtr + 0x20, Info.DeviceRefCount); - Memory.WriteInt32(InfoPtr + 0x24, Info.Padding); + return; + } + Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position); + Memory.WriteInt64(InfoPtr + 0x08, MapInfo.Size); + Memory.WriteInt32(InfoPtr + 0x10, MapInfo.Type); + Memory.WriteInt32(InfoPtr + 0x14, MapInfo.Attr); + Memory.WriteInt32(InfoPtr + 0x18, (int)MapInfo.Perm); + Memory.WriteInt32(InfoPtr + 0x1c, 0); + Memory.WriteInt32(InfoPtr + 0x20, 0); + Memory.WriteInt32(InfoPtr + 0x24, 0); //TODO: X1. ThreadState.X0 = (int)SvcResult.Success; @@ -82,9 +121,11 @@ namespace Ryujinx.Core.OsHle.Svc if (SharedMem != null) { + AMemoryHelper.FillWithZeros(Memory, Src, (int)Size); + SharedMem.AddVirtualPosition(Src); - Memory.Manager.MapPhys(Src, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm); + Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm); ThreadState.X0 = (int)SvcResult.Success; } diff --git a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs index 3b9142c789..51b2e26c6f 100644 --- a/Ryujinx.Core/OsHle/Svc/SvcSystem.cs +++ b/Ryujinx.Core/OsHle/Svc/SvcSystem.cs @@ -11,7 +11,14 @@ namespace Ryujinx.Core.OsHle.Svc { partial class SvcHandler { - private void SvcExitProcess(AThreadState ThreadState) => Ns.Os.ExitProcess(ThreadState.ProcessId); + private const int AllowedCpuIdBitmask = 0b1111; + + private const bool EnableProcessDebugging = false; + + private void SvcExitProcess(AThreadState ThreadState) + { + Ns.Os.ExitProcess(ThreadState.ProcessId); + } private void SvcCloseHandle(AThreadState ThreadState) { @@ -162,79 +169,62 @@ namespace Ryujinx.Core.OsHle.Svc switch (InfoType) { - case 0: ThreadState.X1 = AllowedCpuIdBitmask(); break; - case 2: ThreadState.X1 = GetMapRegionBaseAddr(); break; - case 3: ThreadState.X1 = GetMapRegionSize(); break; - case 4: ThreadState.X1 = GetHeapRegionBaseAddr(); break; - case 5: ThreadState.X1 = GetHeapRegionSize(); break; - case 6: ThreadState.X1 = GetTotalMem(); break; - case 7: ThreadState.X1 = GetUsedMem(); break; - case 8: ThreadState.X1 = IsCurrentProcessBeingDebugged(); break; - case 11: ThreadState.X1 = GetRnd64(); break; - case 12: ThreadState.X1 = GetAddrSpaceBaseAddr(); break; - case 13: ThreadState.X1 = GetAddrSpaceSize(); break; - case 14: ThreadState.X1 = GetMapRegionBaseAddr(); break; - case 15: ThreadState.X1 = GetMapRegionSize(); break; + case 0: + ThreadState.X1 = AllowedCpuIdBitmask; + break; + + case 2: + ThreadState.X1 = MemoryRegions.MapRegionAddress; + break; + + case 3: + ThreadState.X1 = MemoryRegions.MapRegionSize; + break; + + case 4: + ThreadState.X1 = MemoryRegions.HeapRegionAddress; + break; + + case 5: + ThreadState.X1 = CurrentHeapSize; + break; + + case 6: + ThreadState.X1 = MemoryRegions.TotalMemoryAvailable; + break; + + case 7: + ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize; + break; + + case 8: + ThreadState.X1 = EnableProcessDebugging ? 1 : 0; + break; + + case 11: + ThreadState.X1 = (ulong)Rng.Next() + ((ulong)Rng.Next() << 32); + break; + + case 12: + ThreadState.X1 = MemoryRegions.AddrSpaceStart; + break; + + case 13: + ThreadState.X1 = MemoryRegions.AddrSpaceSize; + break; + + case 14: + ThreadState.X1 = MemoryRegions.MapRegionAddress; + break; + + case 15: + ThreadState.X1 = MemoryRegions.MapRegionSize; + break; default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}"); } ThreadState.X0 = (int)SvcResult.Success; } - - private ulong AllowedCpuIdBitmask() - { - return 0xF; //Mephisto value. - } - - private ulong GetMapRegionBaseAddr() - { - return MemoryRegions.MapRegionAddress; - } - - private ulong GetMapRegionSize() - { - return MemoryRegions.MapRegionSize; - } - - private ulong GetHeapRegionBaseAddr() - { - return MemoryRegions.HeapRegionAddress; - } - - private ulong GetHeapRegionSize() - { - return MemoryRegions.HeapRegionSize; - } - - private ulong GetTotalMem() - { - return (ulong)Memory.Manager.GetTotalMemorySize(); - } - - private ulong GetUsedMem() - { - return (ulong)Memory.Manager.GetUsedMemorySize(); - } - - private ulong IsCurrentProcessBeingDebugged() - { - return (ulong)0; - } - - private ulong GetRnd64() - { - return (ulong)Rng.Next() + ((ulong)Rng.Next() << 32); - } - - private ulong GetAddrSpaceBaseAddr() - { - return 0x08000000; - } - - private ulong GetAddrSpaceSize() - { - return AMemoryMgr.AddrSize - GetAddrSpaceBaseAddr(); - } } } diff --git a/Ryujinx.Core/Settings/ColorSet.cs b/Ryujinx.Core/Settings/ColorSet.cs new file mode 100644 index 0000000000..43483363fa --- /dev/null +++ b/Ryujinx.Core/Settings/ColorSet.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Core.Settings +{ + public enum ColorSet + { + BasicWhite = 0, + BasicBlack = 1 + } +} diff --git a/Ryujinx.Core/Settings/SetSys.cs b/Ryujinx.Core/Settings/SetSys.cs new file mode 100644 index 0000000000..d8b6eb6ef5 --- /dev/null +++ b/Ryujinx.Core/Settings/SetSys.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Core.Settings +{ + public class SetSys + { + public ColorSet ThemeColor; + } +} diff --git a/Ryujinx.Core/Switch.cs b/Ryujinx.Core/Switch.cs index 8fd7979e44..6f41da8144 100644 --- a/Ryujinx.Core/Switch.cs +++ b/Ryujinx.Core/Switch.cs @@ -1,5 +1,7 @@ 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; @@ -14,7 +16,9 @@ namespace Ryujinx.Core internal NsGpu Gpu { get; private set; } internal Horizon Os { get; private set; } internal VirtualFs VFs { get; private set; } - internal Hid Hid { get; private set; } + + public Hid Hid { get; private set; } + public SetSys Settings { get; private set; } public event EventHandler Finish; @@ -23,9 +27,11 @@ namespace Ryujinx.Core Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize); Gpu = new NsGpu(Renderer); - Os = new Horizon(this); VFs = new VirtualFs(); - Hid = new Hid(this); + + Hid = new Hid(this); + Os = new Horizon(this); + Settings = new SetSys(); } public void FinalizeAllProcesses() @@ -43,20 +49,6 @@ namespace Ryujinx.Core Os.LoadProgram(FileName); } - public void SendControllerButtons(HidControllerID ControllerId, - HidControllerLayouts Layout, - HidControllerKeys Buttons, - JoystickPosition LeftJoystick, - JoystickPosition RightJoystick) - { - Hid.SendControllerButtons(ControllerId, Layout, Buttons, LeftJoystick, RightJoystick); - } - - public void SendTouchScreenEntry(HidTouchScreenEntryTouch TouchPoint) - { - Hid.SendTouchPoint(TouchPoint); - } - internal virtual void OnFinish(EventArgs e) { Finish?.Invoke(this, e); diff --git a/Ryujinx.Core/VirtualFs.cs b/Ryujinx.Core/VirtualFs.cs index 0c91111659..195fb6a3c6 100644 --- a/Ryujinx.Core/VirtualFs.cs +++ b/Ryujinx.Core/VirtualFs.cs @@ -5,9 +5,9 @@ namespace Ryujinx.Core { class VirtualFs : IDisposable { - private const string BasePath = "Fs"; - private const string SavesPath = "Saves"; - private const string SdCardPath = "SdCard"; + private const string BasePath = "RyuFs"; + private const string NandPath = "nand"; + private const string SdCardPath = "sdmc"; public Stream RomFs { get; private set; } @@ -35,7 +35,7 @@ namespace Ryujinx.Core public string GetSdCardPath() => MakeDirAndGetFullPath(SdCardPath); - public string GetGameSavesPath() => MakeDirAndGetFullPath(SavesPath); + public string GetGameSavesPath() => MakeDirAndGetFullPath(NandPath); private string MakeDirAndGetFullPath(string Dir) { @@ -56,7 +56,9 @@ namespace Ryujinx.Core public string GetBasePath() { - return Path.Combine(Directory.GetCurrentDirectory(), BasePath); + string AppDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + + return Path.Combine(AppDataPath, BasePath); } public void Dispose() diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index 5854c54a6b..83d2c699f8 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -10,9 +10,20 @@ namespace Ryujinx.Graphics.Gal void InitializeFrameBuffer(); void Render(); void SetWindowSize(int Width, int Height); - void SetFrameBuffer(byte* Fb, int Width, int Height, float SX, float SY, float R); + void SetFrameBuffer( + byte* Fb, + int Width, + int Height, + float ScaleX, + float ScaleY, + float OffsX, + float OffsY, + float Rotate); + void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs); + void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height); + void BindTexture(int Index); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl b/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl index 933fa6aa97..35d560c09b 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl +++ b/Ryujinx.Graphics/Gal/OpenGL/FbVtxShader.glsl @@ -2,8 +2,9 @@ precision highp float; -uniform vec2 window_size; uniform mat2 transform; +uniform vec2 window_size; +uniform vec2 offset; layout(location = 0) in vec2 in_position; layout(location = 1) in vec2 in_tex_coord; @@ -22,5 +23,6 @@ vec2 get_scale_ratio(void) { void main(void) { tex_coord = in_tex_coord; - gl_Position = vec4((transform * in_position) * get_scale_ratio(), 0, 1); + vec2 t_pos = (transform * in_position) + offset; + gl_Position = vec4(t_pos * get_scale_ratio(), 0, 1); } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs index c66c0cb754..7dc4bffe34 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/FrameBuffer.cs @@ -135,7 +135,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BindVertexArray(0); } - public unsafe void Set(byte* Fb, int Width, int Height, Matrix2 Transform) + public unsafe void Set(byte* Fb, int Width, int Height, Matrix2 Transform, Vector2 Offs) { if (Fb == null) { @@ -172,6 +172,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size"); GL.Uniform2(WindowSizeUniformLocation, new Vector2(WindowWidth, WindowHeight)); + + int OffsetUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "offset"); + + GL.Uniform2(OffsetUniformLocation, Offs); } public void Render() diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs index 5ae5e22597..bdedfc1a87 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -1,6 +1,7 @@ using OpenTK; using OpenTK.Graphics.OpenGL; using System; +using System.Collections.Concurrent; using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.OpenGL @@ -24,7 +25,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL private Texture[] Textures; - private Queue ActionsQueue; + private ConcurrentQueue ActionsQueue; private FrameBuffer FbRenderer; @@ -34,7 +35,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL Textures = new Texture[8]; - ActionsQueue = new Queue(); + ActionsQueue = new ConcurrentQueue(); } public void InitializeFrameBuffer() @@ -51,9 +52,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL { int Count = ActionsQueue.Count; - while (Count-- > 0) + while (Count-- > 0 && ActionsQueue.TryDequeue(out Action RenderAction)) { - ActionsQueue.Dequeue()(); + RenderAction(); } } @@ -86,6 +87,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL int Height, float ScaleX, float ScaleY, + float OffsX, + float OffsY, float Rotate) { Matrix2 Transform; @@ -93,7 +96,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL Transform = Matrix2.CreateScale(ScaleX, ScaleY); Transform *= Matrix2.CreateRotation(Rotate); - FbRenderer.Set(Fb, Width, Height, Transform); + Vector2 Offs = new Vector2(OffsX, OffsY); + + FbRenderer.Set(Fb, Width, Height, Transform, Offs); } public void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs) diff --git a/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs b/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs index 54fabc6719..7f50640d59 100644 --- a/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs +++ b/Ryujinx.Graphics/Gpu/NsGpuMemoryMgr.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Gpu private const int PTLvl1Mask = PTLvl1Size - 1; private const int PageMask = PageSize - 1; - private const int PTLvl0Bit = PTPageBits + PTLvl0Bits; + private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; private const int PTLvl1Bit = PTPageBits; private const long PteUnmapped = -1; diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index 934f229560..a4a3b33fc5 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -17,7 +17,6 @@ namespace Ryujinx.Tests.Cpu private long EntryPoint; private IntPtr Ram; - private AMemoryAlloc Allocator; private AMemory Memory; private AThread Thread; @@ -31,9 +30,8 @@ namespace Ryujinx.Tests.Cpu Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize); ATranslator Translator = new ATranslator(); - Allocator = new AMemoryAlloc(); - Memory = new AMemory(Ram, Allocator); - Memory.Manager.MapPhys(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute); + Memory = new AMemory(Ram); + Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute); Thread = new AThread(Translator, Memory, ThreadPriority.Normal, EntryPoint); } @@ -42,7 +40,6 @@ namespace Ryujinx.Tests.Cpu { Thread = null; Memory = null; - Allocator = null; Marshal.FreeHGlobal(Ram); } diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 3219303eb5..1e48b28056 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -1,7 +1,9 @@ using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; +using OpenTK.Input; using Ryujinx.Core; +using Ryujinx.Core.Input; using Ryujinx.Graphics.Gal; using System; @@ -9,6 +11,12 @@ namespace Ryujinx { public class GLScreen : GameWindow { + private const int TouchScreenWidth = 1280; + private const int TouchScreenHeight = 720; + + private const float TouchScreenRatioX = (float)TouchScreenWidth / TouchScreenHeight; + private const float TouchScreenRatioY = (float)TouchScreenHeight / TouchScreenWidth; + private Switch Ns; private IGalRenderer Renderer; @@ -32,86 +40,124 @@ namespace Ryujinx protected override void OnUpdateFrame(FrameEventArgs e) { - HidControllerKeys CurrentButton = 0; - JoystickPosition LeftJoystick; - JoystickPosition RightJoystick; + HidControllerButtons CurrentButton = 0; + HidJoystickPosition LeftJoystick; + HidJoystickPosition RightJoystick; + if (Keyboard[Key.Escape]) this.Exit(); - if (Keyboard[OpenTK.Input.Key.Escape]) this.Exit(); - - //RightJoystick int LeftJoystickDX = 0; int LeftJoystickDY = 0; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickUp]) LeftJoystickDY = short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickDown]) LeftJoystickDY = -short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickLeft]) LeftJoystickDX = -short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickRight]) LeftJoystickDX = short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.StickButton]) CurrentButton |= HidControllerKeys.KEY_LSTICK; - - //LeftButtons - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadUp]) CurrentButton |= HidControllerKeys.KEY_DUP; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadDown]) CurrentButton |= HidControllerKeys.KEY_DDOWN; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadLeft]) CurrentButton |= HidControllerKeys.KEY_DLEFT; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.DPadRight]) CurrentButton |= HidControllerKeys.KEY_DRIGHT; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonMinus]) CurrentButton |= HidControllerKeys.KEY_MINUS; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonL]) CurrentButton |= HidControllerKeys.KEY_L; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Left.ButtonZL]) CurrentButton |= HidControllerKeys.KEY_ZL; - - //RightJoystick int RightJoystickDX = 0; int RightJoystickDY = 0; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickUp]) RightJoystickDY = short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickDown]) RightJoystickDY = -short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickLeft]) RightJoystickDX = -short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickRight]) RightJoystickDX = short.MaxValue; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.StickButton]) CurrentButton |= HidControllerKeys.KEY_RSTICK; + + //RightJoystick + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickUp]) LeftJoystickDY = short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickDown]) LeftJoystickDY = -short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickLeft]) LeftJoystickDX = -short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickRight]) LeftJoystickDX = short.MaxValue; + + //LeftButtons + if (Keyboard[(Key)Config.FakeJoyCon.Left.StickButton]) CurrentButton |= HidControllerButtons.KEY_LSTICK; + if (Keyboard[(Key)Config.FakeJoyCon.Left.DPadUp]) CurrentButton |= HidControllerButtons.KEY_DUP; + if (Keyboard[(Key)Config.FakeJoyCon.Left.DPadDown]) CurrentButton |= HidControllerButtons.KEY_DDOWN; + if (Keyboard[(Key)Config.FakeJoyCon.Left.DPadLeft]) CurrentButton |= HidControllerButtons.KEY_DLEFT; + if (Keyboard[(Key)Config.FakeJoyCon.Left.DPadRight]) CurrentButton |= HidControllerButtons.KEY_DRIGHT; + if (Keyboard[(Key)Config.FakeJoyCon.Left.ButtonMinus]) CurrentButton |= HidControllerButtons.KEY_MINUS; + if (Keyboard[(Key)Config.FakeJoyCon.Left.ButtonL]) CurrentButton |= HidControllerButtons.KEY_L; + if (Keyboard[(Key)Config.FakeJoyCon.Left.ButtonZL]) CurrentButton |= HidControllerButtons.KEY_ZL; + + //RightJoystick + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickUp]) RightJoystickDY = short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickDown]) RightJoystickDY = -short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickLeft]) RightJoystickDX = -short.MaxValue; + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickRight]) RightJoystickDX = short.MaxValue; //RightButtons - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonA]) CurrentButton |= HidControllerKeys.KEY_A; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonB]) CurrentButton |= HidControllerKeys.KEY_B; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonX]) CurrentButton |= HidControllerKeys.KEY_X; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonY]) CurrentButton |= HidControllerKeys.KEY_Y; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonPlus]) CurrentButton |= HidControllerKeys.KEY_PLUS; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonR]) CurrentButton |= HidControllerKeys.KEY_R; - if (Keyboard[(OpenTK.Input.Key)Config.FakeJoyCon.Right.ButtonZR]) CurrentButton |= HidControllerKeys.KEY_ZR; + if (Keyboard[(Key)Config.FakeJoyCon.Right.StickButton]) CurrentButton |= HidControllerButtons.KEY_RSTICK; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonA]) CurrentButton |= HidControllerButtons.KEY_A; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonB]) CurrentButton |= HidControllerButtons.KEY_B; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonX]) CurrentButton |= HidControllerButtons.KEY_X; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonY]) CurrentButton |= HidControllerButtons.KEY_Y; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonPlus]) CurrentButton |= HidControllerButtons.KEY_PLUS; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonR]) CurrentButton |= HidControllerButtons.KEY_R; + if (Keyboard[(Key)Config.FakeJoyCon.Right.ButtonZR]) CurrentButton |= HidControllerButtons.KEY_ZR; - LeftJoystick = new JoystickPosition + LeftJoystick = new HidJoystickPosition { DX = LeftJoystickDX, DY = LeftJoystickDY }; - RightJoystick = new JoystickPosition + RightJoystick = new HidJoystickPosition { DX = RightJoystickDX, DY = RightJoystickDY }; + bool HasTouch = false; + //Get screen touch position from left mouse click - //Opentk always captures mouse events, even if out of focus, so check if window is focused. - if (Mouse != null && Focused) - if (Mouse.GetState().LeftButton == OpenTK.Input.ButtonState.Pressed) + //OpenTK always captures mouse events, even if out of focus, so check if window is focused. + if (Focused && Mouse?.GetState().LeftButton == ButtonState.Pressed) + { + int ScrnWidth = Width; + int ScrnHeight = Height; + + if (Width > Height * TouchScreenRatioX) { - HidTouchScreenEntryTouch CurrentPoint = new HidTouchScreenEntryTouch + ScrnWidth = (int)(Height * TouchScreenRatioX); + } + else + { + ScrnHeight = (int)(Width * TouchScreenRatioY); + } + + int StartX = (Width - ScrnWidth) >> 1; + int StartY = (Height - ScrnHeight) >> 1; + + int EndX = StartX + ScrnWidth; + int EndY = StartY + ScrnHeight; + + if (Mouse.X >= StartX && + Mouse.Y >= StartY && + Mouse.X < EndX && + Mouse.Y < EndY) + { + int ScrnMouseX = Mouse.X - StartX; + int ScrnMouseY = Mouse.Y - StartY; + + int MX = (int)(((float)ScrnMouseX / ScrnWidth) * TouchScreenWidth); + int MY = (int)(((float)ScrnMouseY / ScrnHeight) * TouchScreenHeight); + + HidTouchPoint CurrentPoint = new HidTouchPoint { - Timestamp = (uint)Environment.TickCount, - X = (uint)Mouse.X, - Y = (uint)Mouse.Y, + X = MX, + Y = MY, //Placeholder values till more data is acquired DiameterX = 10, DiameterY = 10, - Angle = 90, - - //Only support single touch - TouchIndex = 0, + Angle = 90 }; - if (Mouse.X > -1 && Mouse.Y > -1) - Ns.SendTouchScreenEntry(CurrentPoint); - } - //We just need one pair of JoyCon because it's emulate by the keyboard. - Ns.SendControllerButtons(HidControllerID.CONTROLLER_HANDHELD, HidControllerLayouts.Main, CurrentButton, LeftJoystick, RightJoystick); + HasTouch = true; + + Ns.Hid.SetTouchPoints(CurrentPoint); + } + } + + if (!HasTouch) + { + Ns.Hid.SetTouchPoints(); + } + + Ns.Hid.SetJoyconButton( + HidControllerId.CONTROLLER_HANDHELD, + HidControllerLayouts.Main, + CurrentButton, + LeftJoystick, + RightJoystick); } protected override void OnRenderFrame(FrameEventArgs e)