Merge remote-tracking branch 'upstream/master'

Merge lol
This commit is contained in:
unknown 2018-03-04 20:36:29 +01:00
commit cd63f0a41f
73 changed files with 1689 additions and 1226 deletions

View file

@ -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>101110000xxxxx0<xxx0xxxxxxxxxx", AInstEmit.Ext_V, typeof(AOpCodeSimdExt));
Set("011111101x1xxxxx110101xxxxxxxxxx", AInstEmit.Fabd_S, typeof(AOpCodeSimdReg));
Set("000111100x100000110000xxxxxxxxxx", AInstEmit.Fabs_S, typeof(AOpCodeSimd));
Set("000111100x1xxxxx001010xxxxxxxxxx", AInstEmit.Fadd_S, typeof(AOpCodeSimdReg));
@ -228,6 +230,7 @@ namespace ChocolArm64
Set("0x10111000100000010110xxxxxxxxxx", AInstEmit.Not_V, typeof(AOpCodeSimd));
Set("0x001110101xxxxx000111xxxxxxxxxx", AInstEmit.Orr_V, typeof(AOpCodeSimdReg));
Set("0x00111100000xxx<<x101xxxxxxxxxx", AInstEmit.Orr_Vi, typeof(AOpCodeSimdImm));
Set("0x001110<<100000000010xxxxxxxxxx", AInstEmit.Rev64_V, typeof(AOpCodeSimd));
Set("0x001110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Saddw_V, typeof(AOpCodeSimdReg));
Set("x0011110xx100010000000xxxxxxxxxx", AInstEmit.Scvtf_Gp, typeof(AOpCodeSimdCvt));
Set("010111100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_S, typeof(AOpCodeSimd));
@ -262,6 +265,7 @@ namespace ChocolArm64
Set("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd));
Set("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimd));
Set("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns));
Set("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg));
Set("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg));
Set("0x10111100>>>xxx101001xxxxxxxxxx", AInstEmit.Ushll_V, typeof(AOpCodeSimdShImm));
Set("011111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm));

View file

@ -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<long> SubCalls { get; private set; }
public ReadOnlyCollection<ARegister> Params { get; private set; }
public List<ARegister> Params { get; private set; }
private HashSet<long> 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<ARegister> Params, HashSet<long> Callees)
{
SubCalls = new HashSet<long>();
}
if (Method == null)
{
throw new ArgumentNullException(nameof(Method));
}
public ATranslatedSub(DynamicMethod Method, List<ARegister> 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;
}
}

View file

@ -0,0 +1,9 @@
namespace ChocolArm64
{
enum ATranslatedSubType
{
SubBlock,
SubTier0,
SubTier1
}
}

View file

@ -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<long> SubBlocks;
private ConcurrentDictionary<long, ATranslatedSub> CachedSubs;
private ConcurrentDictionary<long, string> SymbolTable;
@ -24,6 +27,8 @@ namespace ChocolArm64
public ATranslator(IReadOnlyDictionary<long, string> SymbolTable = null)
{
SubBlocks = new HashSet<long>();
CachedSubs = new ConcurrentDictionary<long, ATranslatedSub>();
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)

View file

@ -18,6 +18,18 @@ namespace ChocolArm64.Decoder
OpActivators = new ConcurrentDictionary<Type, OpActivator>();
}
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);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<long> 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<ARegister, int>();
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<long> Callees)
{
List<ARegister> Params = new List<ARegister>();
@ -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<ARegister, int>();
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<ARegister> Params)
{
Type[] FixedArgs = ATranslatedSub.FixedArgTypes;

View file

@ -12,14 +12,9 @@ namespace ChocolArm64.Translation
{
private ATranslator Translator;
private Dictionary<long, AILLabel> Labels;
private HashSet<long> Callees;
private AILEmitter Emitter;
private AILBlock ILBlock;
private AOpCode OptOpLastCompare;
private AOpCode OptOpLastFlagSet;
private Dictionary<long, AILLabel> 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<long>();
Labels = new Dictionary<long, AILLabel>();
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;

View file

@ -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<AILBlock, PathIo>();
VecPaths = new Dictionary<AILBlock, PathIo>();
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)
{

View file

@ -1,4 +1,5 @@
using System;
using Ryujinx.Core.Input;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

View file

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

239
Ryujinx.Core/Hid/Hid.cs Normal file
View file

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

View file

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

View file

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

View file

@ -0,0 +1,10 @@
using System;
namespace Ryujinx.Core.Input
{
[Flags]
public enum HidControllerColorDesc
{
ColorDesc_ColorsNonexistent = (1 << 1)
}
}

View file

@ -0,0 +1,11 @@
using System;
namespace Ryujinx.Core.Input
{
[Flags]
public enum HidControllerConnState
{
Controller_State_Connected = (1 << 0),
Controller_State_Wired = (1 << 1)
}
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Core.Input
{
public struct HidJoystickPosition
{
public int DX;
public int DY;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,27 +1,6 @@
namespace Ryujinx
//TODO: This is only used by Config, it doesn't belong to Core.
namespace Ryujinx.Core.Input
{
/// <summary>
/// Common RGB color hex codes for JoyCon coloring.
/// </summary>
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;

View file

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

View file

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

View file

@ -12,7 +12,7 @@ namespace Ryujinx.Core.OsHle.Handles
public EventHandler<EventArgs> MemoryMapped;
public EventHandler<EventArgs> MemoryUnmapped;
public HSharedMem(long PhysPos)
public HSharedMem()
{
Positions = new List<long>();
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<int, ServiceProcessRequest> m_Commands;
@ -39,7 +39,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
//IAudioOut
private AudioOutState State = AudioOutState.Stopped;
private Queue<long> KeysQueue = new Queue<long>();
private Queue<long> BufferIdQueue = new Queue<long>();
//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();
}
}
}
}

View file

@ -0,0 +1,96 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Bsd
{
class ServiceBsd : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceBsd()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 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<sockaddr, 0x21, 0>) -> (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<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
public long Send(ServiceCtx Context)
{
Context.ResponseData.Write(0);
Context.ResponseData.Write(0);
//Todo: Stub
return 0;
}
}
}

View file

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

View file

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

View file

@ -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<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IGeneralService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 4, CreateRequest }
};
}
//CreateRequest(i32)
public long CreateRequest(ServiceCtx Context)
{
int Unknown = Context.RequestData.ReadInt32();
MakeObject(Context, new IRequest());
//Todo: Stub
return 0;
}
}
}

View file

@ -0,0 +1,49 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Nifm
{
class IRequest : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IRequest()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 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;
}
}
}

View file

@ -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<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceNifm()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 4, CreateGeneralServiceOld }
};
}
public long CreateGeneralServiceOld(ServiceCtx Context)
{
MakeObject(Context, new IGeneralService());
return 0;
}
}
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,33 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Set
{
class ServiceSetSys : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceSetSys()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 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;
}
}
}

View file

@ -0,0 +1,20 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Sfdnsres
{
class ServiceSfdnsres : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceSfdnsres()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//{ 0, Function }
};
}
}
}

View file

@ -0,0 +1,20 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.IpcServices.Ssl
{
class ServiceSsl : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceSsl()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//{ 0, Function }
};
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Core.Settings
{
public enum ColorSet
{
BasicWhite = 0,
BasicBlack = 1
}
}

View file

@ -0,0 +1,7 @@
namespace Ryujinx.Core.Settings
{
public class SetSys
{
public ColorSet ThemeColor;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<Action> ActionsQueue;
private ConcurrentQueue<Action> ActionsQueue;
private FrameBuffer FbRenderer;
@ -34,7 +35,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Textures = new Texture[8];
ActionsQueue = new Queue<Action>();
ActionsQueue = new ConcurrentQueue<Action>();
}
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)

View file

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

View file

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

View file

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