Merge remote-tracking branch 'upstream/master'

This commit is contained in:
MS-DOS1999 2018-03-15 09:22:15 +01:00
commit 71de819ba1
87 changed files with 2830 additions and 1096 deletions

View file

@ -42,6 +42,14 @@ namespace ChocolArm64
Set("x1111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg)); Set("x1111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg));
Set("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem)); Set("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem));
Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu)); Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu));
Set("x0011010110xxxxx010000xxxxxxxxxx", AInstEmit.Crc32b, typeof(AOpCodeAluRs));
Set("x0011010110xxxxx010001xxxxxxxxxx", AInstEmit.Crc32h, typeof(AOpCodeAluRs));
Set("x0011010110xxxxx010010xxxxxxxxxx", AInstEmit.Crc32w, typeof(AOpCodeAluRs));
Set("x0011010110xxxxx010011xxxxxxxxxx", AInstEmit.Crc32x, typeof(AOpCodeAluRs));
Set("x0011010110xxxxx010100xxxxxxxxxx", AInstEmit.Crc32cb, typeof(AOpCodeAluRs));
Set("x0011010110xxxxx010101xxxxxxxxxx", AInstEmit.Crc32ch, typeof(AOpCodeAluRs));
Set("x0011010110xxxxx010110xxxxxxxxxx", AInstEmit.Crc32cw, typeof(AOpCodeAluRs));
Set("x0011010110xxxxx010111xxxxxxxxxx", AInstEmit.Crc32cx, typeof(AOpCodeAluRs));
Set("x0011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csel, typeof(AOpCodeCsel)); Set("x0011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csel, typeof(AOpCodeCsel));
Set("x0011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csinc, typeof(AOpCodeCsel)); Set("x0011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csinc, typeof(AOpCodeCsel));
Set("x1011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csinv, typeof(AOpCodeCsel)); Set("x1011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csinv, typeof(AOpCodeCsel));
@ -161,8 +169,10 @@ namespace ChocolArm64
Set("000111100x10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd)); Set("000111100x10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd));
Set("x00111100x100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt)); Set("x00111100x100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt));
Set("x00111100x100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt)); Set("x00111100x100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt));
Set("0x0011100x100001011110xxxxxxxxxx", AInstEmit.Fcvtl_V, typeof(AOpCodeSimd));
Set("x00111100x110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt)); Set("x00111100x110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt));
Set("x00111100x110001000000xxxxxxxxxx", AInstEmit.Fcvtmu_Gp, typeof(AOpCodeSimdCvt)); Set("x00111100x110001000000xxxxxxxxxx", AInstEmit.Fcvtmu_Gp, typeof(AOpCodeSimdCvt));
Set("0x0011100x100001011010xxxxxxxxxx", AInstEmit.Fcvtn_V, typeof(AOpCodeSimd));
Set("x00111100x101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt)); Set("x00111100x101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt));
Set("x00111100x101001000000xxxxxxxxxx", AInstEmit.Fcvtpu_Gp, typeof(AOpCodeSimdCvt)); Set("x00111100x101001000000xxxxxxxxxx", AInstEmit.Fcvtpu_Gp, typeof(AOpCodeSimdCvt));
Set("x00111100x111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt)); Set("x00111100x111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt));
@ -181,7 +191,7 @@ namespace ChocolArm64
Set("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg)); Set("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg)); Set("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg));
Set("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg)); Set("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg));
Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElem)); Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElemF));
Set("000111100x100000010000xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimd)); Set("000111100x100000010000xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimd));
Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov)); Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov));
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm)); Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
@ -192,12 +202,13 @@ namespace ChocolArm64
Set("000111110x0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg)); Set("000111110x0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, typeof(AOpCodeSimdReg)); Set("000111100x1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, typeof(AOpCodeSimdReg));
Set("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg)); Set("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
Set("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElem)); Set("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElemF));
Set("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimdReg)); Set("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimdReg));
Set("000111110x1xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fnmsub_S, typeof(AOpCodeSimdReg)); Set("000111110x1xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fnmsub_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg)); Set("000111100x1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg));
Set("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd)); Set("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
Set("000111100x100101010000xxxxxxxxxx", AInstEmit.Frintm_S, typeof(AOpCodeSimd)); Set("000111100x100101010000xxxxxxxxxx", AInstEmit.Frintm_S, typeof(AOpCodeSimd));
Set("0>0011100<100001100110xxxxxxxxxx", AInstEmit.Frintm_V, typeof(AOpCodeSimd));
Set("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S, typeof(AOpCodeSimd)); Set("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S, typeof(AOpCodeSimd));
Set("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S, typeof(AOpCodeSimd)); Set("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S, typeof(AOpCodeSimd));
Set("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd)); Set("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd));
@ -223,6 +234,7 @@ namespace ChocolArm64
Set("0x00111100000xxx110x01xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); Set("0x00111100000xxx110x01xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
Set("0xx0111100000xxx111001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm)); Set("0xx0111100000xxx111001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
Set("0x001110<<1xxxxx100111xxxxxxxxxx", AInstEmit.Mul_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx100111xxxxxxxxxx", AInstEmit.Mul_V, typeof(AOpCodeSimdReg));
Set("0x001111xxxxxxxx1000x0xxxxxxxxxx", AInstEmit.Mul_Ve, typeof(AOpCodeSimdRegElem));
Set("0x10111100000xxx0xx001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); Set("0x10111100000xxx0xx001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
Set("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); Set("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
Set("0x10111100000xxx110x01xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm)); Set("0x10111100000xxx110x01xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
@ -237,14 +249,18 @@ namespace ChocolArm64
Set("0x0011100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_V, typeof(AOpCodeSimd)); Set("0x0011100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_V, typeof(AOpCodeSimd));
Set("010111110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm)); Set("010111110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm));
Set("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm)); Set("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm));
Set("0x101110<<100001001110xxxxxxxxxx", AInstEmit.Shll_V, typeof(AOpCodeSimd));
Set("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm)); Set("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm));
Set("0x1011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Sli_V, typeof(AOpCodeSimdShImm));
Set("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg));
Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg));
Set("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg));
Set("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg)); Set("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg));
Set("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg)); Set("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg));
Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm)); Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm));
Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm)); Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm));
Set("0x0011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm)); Set("0x0011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm));
Set("0x0011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm));
Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs)); Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
Set("0x00110100000000xx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs)); Set("0x00110100000000xx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));

View file

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

View file

@ -10,47 +10,34 @@ namespace ChocolArm64
public AThreadState ThreadState { get; private set; } public AThreadState ThreadState { get; private set; }
public AMemory Memory { get; private set; } public AMemory Memory { get; private set; }
public long EntryPoint { get; private set; } private long EntryPoint;
private ATranslator Translator; private ATranslator Translator;
private ThreadPriority Priority;
private Thread Work; private Thread Work;
public event EventHandler WorkFinished; public event EventHandler WorkFinished;
public int ThreadId => ThreadState.ThreadId; public int ThreadId => ThreadState.ThreadId;
public bool IsAlive => Work.IsAlive; private int IsExecuting;
private bool IsExecuting; public AThread(ATranslator Translator, AMemory Memory, long EntryPoint)
private object ExecuteLock;
public AThread(ATranslator Translator, AMemory Memory, ThreadPriority Priority, long EntryPoint)
{ {
this.Translator = Translator; this.Translator = Translator;
this.Memory = Memory; this.Memory = Memory;
this.Priority = Priority;
this.EntryPoint = EntryPoint; this.EntryPoint = EntryPoint;
ThreadState = new AThreadState(); ThreadState = new AThreadState();
ExecuteLock = new object();
}
public void StopExecution() => Translator.StopExecution(); ThreadState.Running = true;
}
public bool Execute() public bool Execute()
{ {
lock (ExecuteLock) if (Interlocked.Exchange(ref IsExecuting, 1) == 1)
{ {
if (IsExecuting) return false;
{
return false;
}
IsExecuting = true;
} }
Work = new Thread(delegate() Work = new Thread(delegate()
@ -62,11 +49,11 @@ namespace ChocolArm64
WorkFinished?.Invoke(this, EventArgs.Empty); WorkFinished?.Invoke(this, EventArgs.Empty);
}); });
Work.Priority = Priority;
Work.Start(); Work.Start();
return true; return true;
} }
public void StopExecution() => ThreadState.Running = false;
} }
} }

View file

@ -7,7 +7,6 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection.Emit; using System.Reflection.Emit;
using System.Threading;
namespace ChocolArm64 namespace ChocolArm64
{ {
@ -23,8 +22,6 @@ namespace ChocolArm64
public bool EnableCpuTrace { get; set; } public bool EnableCpuTrace { get; set; }
private bool KeepRunning;
public ATranslator(IReadOnlyDictionary<long, string> SymbolTable = null) public ATranslator(IReadOnlyDictionary<long, string> SymbolTable = null)
{ {
SubBlocks = new HashSet<long>(); SubBlocks = new HashSet<long>();
@ -39,12 +36,8 @@ namespace ChocolArm64
{ {
this.SymbolTable = new ConcurrentDictionary<long, string>(); this.SymbolTable = new ConcurrentDictionary<long, string>();
} }
KeepRunning = true;
} }
internal void StopExecution() => KeepRunning = false;
internal void ExecuteSubroutine(AThread Thread, long Position) internal void ExecuteSubroutine(AThread Thread, long Position)
{ {
do do
@ -71,7 +64,7 @@ namespace ChocolArm64
Position = Sub.Execute(Thread.ThreadState, Thread.Memory); Position = Sub.Execute(Thread.ThreadState, Thread.Memory);
} }
while (Position != 0 && KeepRunning); while (Position != 0 && Thread.ThreadState.Running);
} }
internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub) internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub)

View file

@ -8,7 +8,7 @@ namespace ChocolArm64.Decoder
public AOpCodeSimdExt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) public AOpCodeSimdExt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
{ {
int Imm4 = (OpCode >> 11) & 0xf; Imm4 = (OpCode >> 11) & 0xf;
} }
} }
} }

View file

@ -88,7 +88,14 @@ namespace ChocolArm64.Decoder
private static long ShlOnes(long Value, int Shift) private static long ShlOnes(long Value, int Shift)
{ {
return Value << Shift | (long)(ulong.MaxValue >> (64 - Shift)); if (Shift != 0)
{
return Value << Shift | (long)(ulong.MaxValue >> (64 - Shift));
}
else
{
return Value;
}
} }
} }
} }

View file

@ -4,9 +4,9 @@ namespace ChocolArm64.Decoder
{ {
class AOpCodeSimdReg : AOpCodeSimd class AOpCodeSimdReg : AOpCodeSimd
{ {
public bool Bit3 { get; private set; } public bool Bit3 { get; private set; }
public int Ra { get; private set; } public int Ra { get; private set; }
public int Rm { get; private set; } public int Rm { get; protected set; }
public AOpCodeSimdReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) public AOpCodeSimdReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
{ {

View file

@ -8,14 +8,24 @@ namespace ChocolArm64.Decoder
public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
{ {
if ((Size & 1) != 0) switch (Size)
{ {
Index = (OpCode >> 11) & 1; case 1:
} Index = (OpCode >> 21) & 1 |
else (OpCode >> 10) & 2 |
{ (OpCode >> 18) & 4;
Index = (OpCode >> 21) & 1 |
(OpCode >> 10) & 2; Rm &= 0xf;
break;
case 2:
Index = (OpCode >> 21) & 1 |
(OpCode >> 10) & 2;
break;
default: Emitter = AInstEmit.Und; return;
} }
} }
} }

View file

@ -0,0 +1,22 @@
using ChocolArm64.Instruction;
namespace ChocolArm64.Decoder
{
class AOpCodeSimdRegElemF : AOpCodeSimdReg
{
public int Index { get; private set; }
public AOpCodeSimdRegElemF(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
{
if ((Size & 1) != 0)
{
Index = (OpCode >> 11) & 1;
}
else
{
Index = (OpCode >> 21) & 1 |
(OpCode >> 10) & 2;
}
}
}
}

View file

@ -34,6 +34,22 @@ namespace ChocolArm64.Instruction
Context.EmitCall(MthdInfo); Context.EmitCall(MthdInfo);
//Check if the thread should still be running, if it isn't then we return 0
//to force a return to the dispatcher and then exit the thread.
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Running));
AILLabel LblEnd = new AILLabel();
Context.Emit(OpCodes.Brtrue_S, LblEnd);
Context.EmitLdc_I8(0);
Context.Emit(OpCodes.Ret);
Context.MarkLabel(LblEnd);
if (Context.CurrBlock.Next != null) if (Context.CurrBlock.Next != null)
{ {
Context.EmitLoadState(Context.CurrBlock.Next); Context.EmitLoadState(Context.CurrBlock.Next);

View file

@ -0,0 +1,73 @@
using ChocolArm64.Decoder;
using ChocolArm64.State;
using ChocolArm64.Translation;
using System.Reflection.Emit;
namespace ChocolArm64.Instruction
{
static partial class AInstEmit
{
public static void Crc32b(AILEmitterCtx Context)
{
EmitCrc32(Context, nameof(ASoftFallback.Crc32b));
}
public static void Crc32h(AILEmitterCtx Context)
{
EmitCrc32(Context, nameof(ASoftFallback.Crc32h));
}
public static void Crc32w(AILEmitterCtx Context)
{
EmitCrc32(Context, nameof(ASoftFallback.Crc32w));
}
public static void Crc32x(AILEmitterCtx Context)
{
EmitCrc32(Context, nameof(ASoftFallback.Crc32x));
}
public static void Crc32cb(AILEmitterCtx Context)
{
EmitCrc32(Context, nameof(ASoftFallback.Crc32cb));
}
public static void Crc32ch(AILEmitterCtx Context)
{
EmitCrc32(Context, nameof(ASoftFallback.Crc32ch));
}
public static void Crc32cw(AILEmitterCtx Context)
{
EmitCrc32(Context, nameof(ASoftFallback.Crc32cw));
}
public static void Crc32cx(AILEmitterCtx Context)
{
EmitCrc32(Context, nameof(ASoftFallback.Crc32cx));
}
private static void EmitCrc32(AILEmitterCtx Context, string Name)
{
AOpCodeAluRs Op = (AOpCodeAluRs)Context.CurrOp;
Context.EmitLdintzr(Op.Rn);
if (Op.RegisterSize != ARegisterSize.Int32)
{
Context.Emit(OpCodes.Conv_U4);
}
Context.EmitLdintzr(Op.Rm);
ASoftFallback.EmitCall(Context, Name);
if (Op.RegisterSize != ARegisterSize.Int32)
{
Context.Emit(OpCodes.Conv_U8);
}
Context.EmitStintzr(Op.Rd);
}
}
}

View file

@ -45,21 +45,46 @@ namespace ChocolArm64.Instruction
{ {
switch (Size) switch (Size)
{ {
case 0: Name = nameof(AMemory.ReadVector8); break; case 0: Name = AOptimizations.DisableMemoryChecks
case 1: Name = nameof(AMemory.ReadVector16); break; ? nameof(AMemory.ReadVector8Unchecked)
case 2: Name = nameof(AMemory.ReadVector32); break; : nameof(AMemory.ReadVector8); break;
case 3: Name = nameof(AMemory.ReadVector64); break;
case 4: Name = nameof(AMemory.ReadVector128); break; case 1: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.ReadVector16Unchecked)
: nameof(AMemory.ReadVector16); break;
case 2: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.ReadVector32Unchecked)
: nameof(AMemory.ReadVector32); break;
case 3: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.ReadVector64Unchecked)
: nameof(AMemory.ReadVector64); break;
case 4: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.ReadVector128Unchecked)
: nameof(AMemory.ReadVector128); break;
} }
} }
else else
{ {
switch (Size) switch (Size)
{ {
case 0: Name = nameof(AMemory.ReadByte); break; case 0: Name = AOptimizations.DisableMemoryChecks
case 1: Name = nameof(AMemory.ReadUInt16); break; ? nameof(AMemory.ReadByteUnchecked)
case 2: Name = nameof(AMemory.ReadUInt32); break; : nameof(AMemory.ReadByte); break;
case 3: Name = nameof(AMemory.ReadUInt64); break;
case 1: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.ReadUInt16Unchecked)
: nameof(AMemory.ReadUInt16); break;
case 2: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.ReadUInt32Unchecked)
: nameof(AMemory.ReadUInt32); break;
case 3: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.ReadUInt64Unchecked)
: nameof(AMemory.ReadUInt64); break;
} }
} }
@ -107,21 +132,46 @@ namespace ChocolArm64.Instruction
{ {
switch (Size) switch (Size)
{ {
case 0: Name = nameof(AMemory.WriteVector8); break; case 0: Name = AOptimizations.DisableMemoryChecks
case 1: Name = nameof(AMemory.WriteVector16); break; ? nameof(AMemory.WriteVector8Unchecked)
case 2: Name = nameof(AMemory.WriteVector32); break; : nameof(AMemory.WriteVector8); break;
case 3: Name = nameof(AMemory.WriteVector64); break;
case 4: Name = nameof(AMemory.WriteVector128); break; case 1: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.WriteVector16Unchecked)
: nameof(AMemory.WriteVector16); break;
case 2: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.WriteVector32Unchecked)
: nameof(AMemory.WriteVector32); break;
case 3: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.WriteVector64Unchecked)
: nameof(AMemory.WriteVector64); break;
case 4: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.WriteVector128Unchecked)
: nameof(AMemory.WriteVector128); break;
} }
} }
else else
{ {
switch (Size) switch (Size)
{ {
case 0: Name = nameof(AMemory.WriteByte); break; case 0: Name = AOptimizations.DisableMemoryChecks
case 1: Name = nameof(AMemory.WriteUInt16); break; ? nameof(AMemory.WriteByteUnchecked)
case 2: Name = nameof(AMemory.WriteUInt32); break; : nameof(AMemory.WriteByte); break;
case 3: Name = nameof(AMemory.WriteUInt64); break;
case 1: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.WriteUInt16Unchecked)
: nameof(AMemory.WriteUInt16); break;
case 2: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.WriteUInt32Unchecked)
: nameof(AMemory.WriteUInt32); break;
case 3: Name = AOptimizations.DisableMemoryChecks
? nameof(AMemory.WriteUInt64Unchecked)
: nameof(AMemory.WriteUInt64); break;
} }
} }

View file

@ -267,6 +267,14 @@ namespace ChocolArm64.Instruction
}); });
} }
public static void Frintm_V(AILEmitterCtx Context)
{
EmitVectorUnaryOpF(Context, () =>
{
EmitUnaryMathCall(Context, nameof(Math.Floor));
});
}
public static void Frintp_S(AILEmitterCtx Context) public static void Frintp_S(AILEmitterCtx Context)
{ {
EmitScalarUnaryOpF(Context, () => EmitScalarUnaryOpF(Context, () =>
@ -341,6 +349,11 @@ namespace ChocolArm64.Instruction
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul)); EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));
} }
public static void Mul_Ve(AILEmitterCtx Context)
{
EmitVectorBinaryOpByElemZx(Context, () => Context.Emit(OpCodes.Mul));
}
public static void Neg_V(AILEmitterCtx Context) public static void Neg_V(AILEmitterCtx Context)
{ {
EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg)); EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
@ -369,6 +382,15 @@ namespace ChocolArm64.Instruction
EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo)); EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo));
} }
public static void Smlal_V(AILEmitterCtx Context)
{
EmitVectorWidenRnRmTernaryOpSx(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Add);
});
}
public static void Smull_V(AILEmitterCtx Context) public static void Smull_V(AILEmitterCtx Context)
{ {
EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul)); EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul));

View file

@ -31,6 +31,36 @@ namespace ChocolArm64.Instruction
EmitFcvt_u_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero)); EmitFcvt_u_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero));
} }
public static void Fcvtl_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
int Elems = 4 >> SizeF;
int Part = Context.CurrOp.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
for (int Index = 0; Index < Elems; Index++)
{
if (SizeF == 0)
{
//TODO: This need the half precision floating point type,
//that is not yet supported on .NET. We should probably
//do our own implementation on the meantime.
throw new NotImplementedException();
}
else /* if (SizeF == 1) */
{
EmitVectorExtractF(Context, Op.Rn, Part + Index, 0);
Context.Emit(OpCodes.Conv_R8);
}
EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
}
}
public static void Fcvtms_Gp(AILEmitterCtx Context) public static void Fcvtms_Gp(AILEmitterCtx Context)
{ {
EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor))); EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor)));
@ -41,6 +71,41 @@ namespace ChocolArm64.Instruction
EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor))); EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor)));
} }
public static void Fcvtn_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
int Elems = 4 >> SizeF;
int Part = Context.CurrOp.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
for (int Index = 0; Index < Elems; Index++)
{
EmitVectorExtractF(Context, Op.Rd, Index, SizeF);
if (SizeF == 0)
{
//TODO: This need the half precision floating point type,
//that is not yet supported on .NET. We should probably
//do our own implementation on the meantime.
throw new NotImplementedException();
}
else /* if (SizeF == 1) */
{
Context.Emit(OpCodes.Conv_R4);
EmitVectorInsertF(Context, Op.Rd, Part + Index, 0);
}
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
public static void Fcvtps_Gp(AILEmitterCtx Context) public static void Fcvtps_Gp(AILEmitterCtx Context)
{ {
EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling))); EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling)));

View file

@ -190,6 +190,11 @@ namespace ChocolArm64.Instruction
EmitScalarSetF(Context, Op.Rd, SizeF); EmitScalarSetF(Context, Op.Rd, SizeF);
} }
public static void EmitVectorUnaryOpF(AILEmitterCtx Context, Action Emit)
{
EmitVectorOpF(Context, Emit, OperFlags.Rn);
}
public static void EmitVectorBinaryOpF(AILEmitterCtx Context, Action Emit) public static void EmitVectorBinaryOpF(AILEmitterCtx Context, Action Emit)
{ {
EmitVectorOpF(Context, Emit, OperFlags.RnRm); EmitVectorOpF(Context, Emit, OperFlags.RnRm);
@ -200,23 +205,9 @@ namespace ChocolArm64.Instruction
EmitVectorOpF(Context, Emit, OperFlags.RdRnRm); EmitVectorOpF(Context, Emit, OperFlags.RdRnRm);
} }
public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit)
{
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false);
}
public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit)
{
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true);
}
public static void EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers) public static void EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers)
{ {
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1; int SizeF = Op.Size & 1;
@ -236,7 +227,7 @@ namespace ChocolArm64.Instruction
if (Opers.HasFlag(OperFlags.Rm)) if (Opers.HasFlag(OperFlags.Rm))
{ {
EmitVectorExtractF(Context, Op.Rm, Index, SizeF); EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, Index, SizeF);
} }
Emit(); Emit();
@ -250,6 +241,20 @@ namespace ChocolArm64.Instruction
} }
} }
public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit)
{
AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp;
EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false);
}
public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit)
{
AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp;
EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true);
}
public static void EmitVectorOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary) public static void EmitVectorOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary)
{ {
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
@ -341,6 +346,54 @@ namespace ChocolArm64.Instruction
} }
} }
public static void EmitVectorBinaryOpByElemSx(AILEmitterCtx Context, Action Emit)
{
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
EmitVectorOpByElem(Context, Emit, Op.Index, false, true);
}
public static void EmitVectorBinaryOpByElemZx(AILEmitterCtx Context, Action Emit)
{
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
EmitVectorOpByElem(Context, Emit, Op.Index, false, false);
}
public static void EmitVectorTernaryOpByElemZx(AILEmitterCtx Context, Action Emit)
{
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
EmitVectorOpByElem(Context, Emit, Op.Index, true, false);
}
public static void EmitVectorOpByElem(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary, bool Signed)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
{
if (Ternary)
{
EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed);
}
EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
EmitVectorExtract(Context, Op.Rm, Index, Op.Size, Signed);
Emit();
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
public static void EmitVectorImmUnaryOp(AILEmitterCtx Context, Action Emit) public static void EmitVectorImmUnaryOp(AILEmitterCtx Context, Action Emit)
{ {
EmitVectorImmOp(Context, Emit, false); EmitVectorImmOp(Context, Emit, false);
@ -411,15 +464,25 @@ namespace ChocolArm64.Instruction
public static void EmitVectorWidenRnRmBinaryOpSx(AILEmitterCtx Context, Action Emit) public static void EmitVectorWidenRnRmBinaryOpSx(AILEmitterCtx Context, Action Emit)
{ {
EmitVectorWidenRnRmBinaryOp(Context, Emit, true); EmitVectorWidenRnRmOp(Context, Emit, false, true);
} }
public static void EmitVectorWidenRnRmBinaryOpZx(AILEmitterCtx Context, Action Emit) public static void EmitVectorWidenRnRmBinaryOpZx(AILEmitterCtx Context, Action Emit)
{ {
EmitVectorWidenRnRmBinaryOp(Context, Emit, false); EmitVectorWidenRnRmOp(Context, Emit, false, false);
} }
public static void EmitVectorWidenRnRmBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed) public static void EmitVectorWidenRnRmTernaryOpSx(AILEmitterCtx Context, Action Emit)
{
EmitVectorWidenRnRmOp(Context, Emit, true, true);
}
public static void EmitVectorWidenRnRmTernaryOpZx(AILEmitterCtx Context, Action Emit)
{
EmitVectorWidenRnRmOp(Context, Emit, true, false);
}
public static void EmitVectorWidenRnRmOp(AILEmitterCtx Context, Action Emit, bool Ternary, bool Signed)
{ {
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
@ -429,6 +492,11 @@ namespace ChocolArm64.Instruction
for (int Index = 0; Index < Elems; Index++) for (int Index = 0; Index < Elems; Index++)
{ {
if (Ternary)
{
EmitVectorExtract(Context, Op.Rd, Index, Op.Size + 1, Signed);
}
EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed); EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed);
EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed); EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed);

View file

@ -63,15 +63,18 @@ namespace ChocolArm64.Instruction
int Bytes = Context.CurrOp.GetBitsCount() >> 3; int Bytes = Context.CurrOp.GetBitsCount() >> 3;
int Position = Op.Imm4;
for (int Index = 0; Index < Bytes; Index++) for (int Index = 0; Index < Bytes; Index++)
{ {
int Position = Op.Imm4 + Index; int Reg = Op.Imm4 + Index < Bytes ? Op.Rn : Op.Rm;
int Reg = Position < Bytes ? Op.Rn : Op.Rm; if (Position == Bytes)
{
Position = 0;
}
Position &= Bytes - 1; EmitVectorExtractZx(Context, Reg, Position++, 0);
EmitVectorExtractZx(Context, Reg, Position, 0);
EmitVectorInsert(Context, Op.Rd, Index, 0); EmitVectorInsert(Context, Op.Rd, Index, 0);
} }

View file

@ -32,6 +32,15 @@ namespace ChocolArm64.Instruction
EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift); EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift);
} }
public static void Shll_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int Shift = 8 << Op.Size;
EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift);
}
public static void Shrn_V(AILEmitterCtx Context) public static void Shrn_V(AILEmitterCtx Context)
{ {
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
@ -41,6 +50,40 @@ namespace ChocolArm64.Instruction
EmitVectorShImmNarrowBinaryZx(Context, () => Context.Emit(OpCodes.Shr_Un), Shift); EmitVectorShImmNarrowBinaryZx(Context, () => Context.Emit(OpCodes.Shr_Un), Shift);
} }
public static void Sli_V(AILEmitterCtx Context)
{
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
int Shift = Op.Imm - (8 << Op.Size);
ulong Mask = Shift != 0 ? ulong.MaxValue >> (64 - Shift) : 0;
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
{
EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size);
Context.EmitLdc_I4(Shift);
Context.Emit(OpCodes.Shl);
EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size);
Context.EmitLdc_I8((long)Mask);
Context.Emit(OpCodes.And);
Context.Emit(OpCodes.Or);
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
public static void Sshl_V(AILEmitterCtx Context) public static void Sshl_V(AILEmitterCtx Context)
{ {
EmitVectorShl(Context, Signed: true); EmitVectorShl(Context, Signed: true);
@ -77,6 +120,21 @@ namespace ChocolArm64.Instruction
EmitVectorShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift); EmitVectorShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift);
} }
public static void Ssra_V(AILEmitterCtx Context)
{
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
int Shift = (8 << (Op.Size + 1)) - Op.Imm;
Action Emit = () =>
{
Context.Emit(OpCodes.Shr);
Context.Emit(OpCodes.Add);
};
EmitVectorShImmTernarySx(Context, Emit, Shift);
}
public static void Ushl_V(AILEmitterCtx Context) public static void Ushl_V(AILEmitterCtx Context)
{ {
EmitVectorShl(Context, Signed: false); EmitVectorShl(Context, Signed: false);
@ -195,22 +253,32 @@ namespace ChocolArm64.Instruction
private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm) private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm)
{ {
EmitVectorShImmBinaryOp(Context, Emit, Imm, true); EmitVectorShImmOp(Context, Emit, Imm, false, true);
}
private static void EmitVectorShImmTernarySx(AILEmitterCtx Context, Action Emit, int Imm)
{
EmitVectorShImmOp(Context, Emit, Imm, true, true);
} }
private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm) private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm)
{ {
EmitVectorShImmBinaryOp(Context, Emit, Imm, false); EmitVectorShImmOp(Context, Emit, Imm, false, false);
} }
private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) private static void EmitVectorShImmOp(AILEmitterCtx Context, Action Emit, int Imm, bool Ternary, bool Signed)
{ {
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int Bytes = Context.CurrOp.GetBitsCount() >> 3; int Bytes = Context.CurrOp.GetBitsCount() >> 3;
for (int Index = 0; Index < (Bytes >> Op.Size); Index++) for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
{ {
if (Ternary)
{
EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed);
}
EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed); EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
Context.EmitLdc_I4(Imm); Context.EmitLdc_I4(Imm);
@ -238,7 +306,7 @@ namespace ChocolArm64.Instruction
private static void EmitVectorShImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) private static void EmitVectorShImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
{ {
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int Elems = 8 >> Op.Size; int Elems = 8 >> Op.Size;
@ -273,7 +341,7 @@ namespace ChocolArm64.Instruction
private static void EmitVectorShImmWidenBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed) private static void EmitVectorShImmWidenBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
{ {
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int Elems = 8 >> Op.Size; int Elems = 8 >> Op.Size;

View file

@ -30,6 +30,7 @@ namespace ChocolArm64.Instruction
case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break; case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break;
case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break; case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break;
case 0b11_011_1101_0000_011: PropName = nameof(AThreadState.Tpidr); break; case 0b11_011_1101_0000_011: PropName = nameof(AThreadState.Tpidr); break;
case 0b11_011_1110_0000_000: PropName = nameof(AThreadState.CntfrqEl0); break;
case 0b11_011_1110_0000_001: PropName = nameof(AThreadState.CntpctEl0); break; case 0b11_011_1110_0000_001: PropName = nameof(AThreadState.CntpctEl0); break;
default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}"); default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}");
@ -97,7 +98,7 @@ namespace ChocolArm64.Instruction
for (int Offs = 0; Offs < (4 << AThreadState.DczSizeLog2); Offs += 8) for (int Offs = 0; Offs < (4 << AThreadState.DczSizeLog2); Offs += 8)
{ {
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx); Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
Context.EmitLdint(Op.Rt); Context.EmitLdintzr(Op.Rt);
Context.EmitLdc_I(Offs); Context.EmitLdc_I(Offs);
Context.Emit(OpCodes.Add); Context.Emit(OpCodes.Add);
@ -106,8 +107,13 @@ namespace ChocolArm64.Instruction
AInstEmitMemoryHelper.EmitWriteCall(Context, 3); AInstEmitMemoryHelper.EmitWriteCall(Context, 3);
} }
break; break;
} }
//No-op
case 0b11_011_0111_1110_001: //DC CIVAC
break;
} }
} }

View file

@ -38,6 +38,65 @@ namespace ChocolArm64.Instruction
return (ulong)Size; return (ulong)Size;
} }
private const uint Crc32RevPoly = 0xedb88320;
private const uint Crc32cRevPoly = 0x82f63b78;
public static uint Crc32b(uint Crc, byte Val) => Crc32 (Crc, Crc32RevPoly, Val);
public static uint Crc32h(uint Crc, byte Val) => Crc32h(Crc, Crc32RevPoly, Val);
public static uint Crc32w(uint Crc, byte Val) => Crc32w(Crc, Crc32RevPoly, Val);
public static uint Crc32x(uint Crc, byte Val) => Crc32x(Crc, Crc32RevPoly, Val);
public static uint Crc32cb(uint Crc, byte Val) => Crc32 (Crc, Crc32cRevPoly, Val);
public static uint Crc32ch(uint Crc, byte Val) => Crc32h(Crc, Crc32cRevPoly, Val);
public static uint Crc32cw(uint Crc, byte Val) => Crc32w(Crc, Crc32cRevPoly, Val);
public static uint Crc32cx(uint Crc, byte Val) => Crc32x(Crc, Crc32cRevPoly, Val);
private static uint Crc32h(uint Crc, uint Poly, ushort Val)
{
Crc = Crc32(Crc, Poly, (byte)(Val >> 0));
Crc = Crc32(Crc, Poly, (byte)(Val >> 8));
return Crc;
}
private static uint Crc32w(uint Crc, uint Poly, uint Val)
{
Crc = Crc32(Crc, Poly, (byte)(Val >> 0));
Crc = Crc32(Crc, Poly, (byte)(Val >> 8));
Crc = Crc32(Crc, Poly, (byte)(Val >> 16));
Crc = Crc32(Crc, Poly, (byte)(Val >> 24));
return Crc;
}
private static uint Crc32x(uint Crc, uint Poly, ulong Val)
{
Crc = Crc32(Crc, Poly, (byte)(Val >> 0));
Crc = Crc32(Crc, Poly, (byte)(Val >> 8));
Crc = Crc32(Crc, Poly, (byte)(Val >> 16));
Crc = Crc32(Crc, Poly, (byte)(Val >> 24));
Crc = Crc32(Crc, Poly, (byte)(Val >> 32));
Crc = Crc32(Crc, Poly, (byte)(Val >> 40));
Crc = Crc32(Crc, Poly, (byte)(Val >> 48));
Crc = Crc32(Crc, Poly, (byte)(Val >> 56));
return Crc;
}
private static uint Crc32(uint Crc, uint Poly, byte Val)
{
Crc ^= Val;
for (int Bit = 7; Bit >= 0; Bit--)
{
uint Mask = (uint)(-(int)(Crc & 1));
Crc = (Crc >> 1) ^ (Poly & Mask);
}
return Crc;
}
public static uint ReverseBits32(uint Value) public static uint ReverseBits32(uint Value)
{ {
Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1); Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1);

View file

@ -2,11 +2,11 @@ using ChocolArm64.Exceptions;
using ChocolArm64.State; using ChocolArm64.State;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.InteropServices;
namespace ChocolArm64.Memory namespace ChocolArm64.Memory
{ {
public unsafe class AMemory public unsafe class AMemory : IDisposable
{ {
private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1; private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1;
@ -39,9 +39,11 @@ namespace ChocolArm64.Memory
private HashSet<long> ExAddrs; private HashSet<long> ExAddrs;
public IntPtr Ram { get; private set; }
private byte* RamPtr; private byte* RamPtr;
public AMemory(IntPtr Ram) public AMemory()
{ {
Manager = new AMemoryMgr(); Manager = new AMemoryMgr();
@ -49,6 +51,8 @@ namespace ChocolArm64.Memory
ExAddrs = new HashSet<long>(); ExAddrs = new HashSet<long>();
Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize + AMemoryMgr.PageSize);
RamPtr = (byte*)Ram; RamPtr = (byte*)Ram;
} }
@ -134,98 +138,79 @@ namespace ChocolArm64.Memory
} }
} }
public sbyte ReadSByte(long Position) => (sbyte)ReadByte (Position); public sbyte ReadSByte(long Position)
public short ReadInt16(long Position) => (short)ReadUInt16(Position); {
public int ReadInt32(long Position) => (int)ReadUInt32(Position); return (sbyte)ReadByte(Position);
public long ReadInt64(long Position) => (long)ReadUInt64(Position); }
public short ReadInt16(long Position)
{
return (short)ReadUInt16(Position);
}
public int ReadInt32(long Position)
{
return (int)ReadUInt32(Position);
}
public long ReadInt64(long Position)
{
return (long)ReadUInt64(Position);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte ReadByte(long Position) public byte ReadByte(long Position)
{ {
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Read); EnsureAccessIsValid(Position, AMemoryPerm.Read);
#endif
return *((byte*)(RamPtr + (uint)Position)); return ReadByteUnchecked(Position);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ushort ReadUInt16(long Position) public ushort ReadUInt16(long Position)
{ {
#if DEBUG EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
EnsureAccessIsValid(Position, AMemoryPerm.Read); EnsureAccessIsValid(Position + 1, AMemoryPerm.Read);
#endif
return *((ushort*)(RamPtr + (uint)Position)); return ReadUInt16Unchecked(Position);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint ReadUInt32(long Position) public uint ReadUInt32(long Position)
{ {
#if DEBUG EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
EnsureAccessIsValid(Position, AMemoryPerm.Read); EnsureAccessIsValid(Position + 3, AMemoryPerm.Read);
#endif
return *((uint*)(RamPtr + (uint)Position)); return ReadUInt32Unchecked(Position);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong ReadUInt64(long Position) public ulong ReadUInt64(long Position)
{ {
#if DEBUG EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
EnsureAccessIsValid(Position, AMemoryPerm.Read); EnsureAccessIsValid(Position + 7, AMemoryPerm.Read);
#endif
return *((ulong*)(RamPtr + (uint)Position)); return ReadUInt64Unchecked(Position);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AVec ReadVector8(long Position) public AVec ReadVector8(long Position)
{ {
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Read);
#endif
return new AVec() { B0 = ReadByte(Position) }; return new AVec() { B0 = ReadByte(Position) };
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AVec ReadVector16(long Position) public AVec ReadVector16(long Position)
{ {
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Read);
#endif
return new AVec() { H0 = ReadUInt16(Position) }; return new AVec() { H0 = ReadUInt16(Position) };
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AVec ReadVector32(long Position) public AVec ReadVector32(long Position)
{ {
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Read);
#endif
return new AVec() { W0 = ReadUInt32(Position) }; return new AVec() { W0 = ReadUInt32(Position) };
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AVec ReadVector64(long Position) public AVec ReadVector64(long Position)
{ {
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Read);
#endif
return new AVec() { X0 = ReadUInt64(Position) }; return new AVec() { X0 = ReadUInt64(Position) };
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AVec ReadVector128(long Position) public AVec ReadVector128(long Position)
{ {
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Read);
#endif
return new AVec() return new AVec()
{ {
X0 = ReadUInt64(Position + 0), X0 = ReadUInt64(Position + 0),
@ -233,102 +218,218 @@ namespace ChocolArm64.Memory
}; };
} }
public void WriteSByte(long Position, sbyte Value) => WriteByte (Position, (byte)Value); public sbyte ReadSByteUnchecked(long Position)
public void WriteInt16(long Position, short Value) => WriteUInt16(Position, (ushort)Value); {
public void WriteInt32(long Position, int Value) => WriteUInt32(Position, (uint)Value); return (sbyte)ReadByteUnchecked(Position);
public void WriteInt64(long Position, long Value) => WriteUInt64(Position, (ulong)Value); }
public short ReadInt16Unchecked(long Position)
{
return (short)ReadUInt16Unchecked(Position);
}
public int ReadInt32Unchecked(long Position)
{
return (int)ReadUInt32Unchecked(Position);
}
public long ReadInt64Unchecked(long Position)
{
return (long)ReadUInt64Unchecked(Position);
}
public byte ReadByteUnchecked(long Position)
{
return *((byte*)(RamPtr + (uint)Position));
}
public ushort ReadUInt16Unchecked(long Position)
{
return *((ushort*)(RamPtr + (uint)Position));
}
public uint ReadUInt32Unchecked(long Position)
{
return *((uint*)(RamPtr + (uint)Position));
}
public ulong ReadUInt64Unchecked(long Position)
{
return *((ulong*)(RamPtr + (uint)Position));
}
public AVec ReadVector8Unchecked(long Position)
{
return new AVec() { B0 = ReadByteUnchecked(Position) };
}
public AVec ReadVector16Unchecked(long Position)
{
return new AVec() { H0 = ReadUInt16Unchecked(Position) };
}
public AVec ReadVector32Unchecked(long Position)
{
return new AVec() { W0 = ReadUInt32Unchecked(Position) };
}
public AVec ReadVector64Unchecked(long Position)
{
return new AVec() { X0 = ReadUInt64Unchecked(Position) };
}
public AVec ReadVector128Unchecked(long Position)
{
return new AVec()
{
X0 = ReadUInt64Unchecked(Position + 0),
X1 = ReadUInt64Unchecked(Position + 8)
};
}
public void WriteSByte(long Position, sbyte Value)
{
WriteByte(Position, (byte)Value);
}
public void WriteInt16(long Position, short Value)
{
WriteUInt16(Position, (ushort)Value);
}
public void WriteInt32(long Position, int Value)
{
WriteUInt32(Position, (uint)Value);
}
public void WriteInt64(long Position, long Value)
{
WriteUInt64(Position, (ulong)Value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteByte(long Position, byte Value) public void WriteByte(long Position, byte Value)
{ {
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Write); EnsureAccessIsValid(Position, AMemoryPerm.Write);
#endif
*((byte*)(RamPtr + (uint)Position)) = Value; WriteByteUnchecked(Position, Value);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUInt16(long Position, ushort Value) public void WriteUInt16(long Position, ushort Value)
{ {
#if DEBUG EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
EnsureAccessIsValid(Position, AMemoryPerm.Write); EnsureAccessIsValid(Position + 1, AMemoryPerm.Write);
#endif
*((ushort*)(RamPtr + (uint)Position)) = Value; WriteUInt16Unchecked(Position, Value);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUInt32(long Position, uint Value) public void WriteUInt32(long Position, uint Value)
{ {
#if DEBUG EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
EnsureAccessIsValid(Position, AMemoryPerm.Write); EnsureAccessIsValid(Position + 3, AMemoryPerm.Write);
#endif
*((uint*)(RamPtr + (uint)Position)) = Value; WriteUInt32Unchecked(Position, Value);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUInt64(long Position, ulong Value) public void WriteUInt64(long Position, ulong Value)
{ {
#if DEBUG EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
EnsureAccessIsValid(Position, AMemoryPerm.Write); EnsureAccessIsValid(Position + 7, AMemoryPerm.Write);
#endif
*((ulong*)(RamPtr + (uint)Position)) = Value; WriteUInt64Unchecked(Position, Value);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector8(long Position, AVec Value) public void WriteVector8(long Position, AVec Value)
{ {
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Write);
#endif
WriteByte(Position, Value.B0); WriteByte(Position, Value.B0);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector16(long Position, AVec Value) public void WriteVector16(long Position, AVec Value)
{ {
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Write);
#endif
WriteUInt16(Position, Value.H0); WriteUInt16(Position, Value.H0);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector32(long Position, AVec Value) public void WriteVector32(long Position, AVec Value)
{ {
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Write);
#endif
WriteUInt32(Position, Value.W0); WriteUInt32(Position, Value.W0);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector64(long Position, AVec Value) public void WriteVector64(long Position, AVec Value)
{ {
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Write);
#endif
WriteUInt64(Position, Value.X0); WriteUInt64(Position, Value.X0);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteVector128(long Position, AVec Value) public void WriteVector128(long Position, AVec Value)
{ {
#if DEBUG
EnsureAccessIsValid(Position, AMemoryPerm.Write);
#endif
WriteUInt64(Position + 0, Value.X0); WriteUInt64(Position + 0, Value.X0);
WriteUInt64(Position + 8, Value.X1); WriteUInt64(Position + 8, Value.X1);
} }
public void WriteSByteUnchecked(long Position, sbyte Value)
{
WriteByteUnchecked(Position, (byte)Value);
}
public void WriteInt16Unchecked(long Position, short Value)
{
WriteUInt16Unchecked(Position, (ushort)Value);
}
public void WriteInt32Unchecked(long Position, int Value)
{
WriteUInt32Unchecked(Position, (uint)Value);
}
public void WriteInt64Unchecked(long Position, long Value)
{
WriteUInt64Unchecked(Position, (ulong)Value);
}
public void WriteByteUnchecked(long Position, byte Value)
{
*((byte*)(RamPtr + (uint)Position)) = Value;
}
public void WriteUInt16Unchecked(long Position, ushort Value)
{
*((ushort*)(RamPtr + (uint)Position)) = Value;
}
public void WriteUInt32Unchecked(long Position, uint Value)
{
*((uint*)(RamPtr + (uint)Position)) = Value;
}
public void WriteUInt64Unchecked(long Position, ulong Value)
{
*((ulong*)(RamPtr + (uint)Position)) = Value;
}
public void WriteVector8Unchecked(long Position, AVec Value)
{
WriteByteUnchecked(Position, Value.B0);
}
public void WriteVector16Unchecked(long Position, AVec Value)
{
WriteUInt16Unchecked(Position, Value.H0);
}
public void WriteVector32Unchecked(long Position, AVec Value)
{
WriteUInt32Unchecked(Position, Value.W0);
}
public void WriteVector64Unchecked(long Position, AVec Value)
{
WriteUInt64Unchecked(Position, Value.X0);
}
public void WriteVector128Unchecked(long Position, AVec Value)
{
WriteUInt64Unchecked(Position + 0, Value.X0);
WriteUInt64Unchecked(Position + 8, Value.X1);
}
private void EnsureAccessIsValid(long Position, AMemoryPerm Perm) private void EnsureAccessIsValid(long Position, AMemoryPerm Perm)
{ {
if (!Manager.IsMapped(Position)) if (!Manager.IsMapped(Position))
@ -341,5 +442,20 @@ namespace ChocolArm64.Memory
throw new VmmAccessViolationException(Position, Perm); throw new VmmAccessViolationException(Position, Perm);
} }
} }
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (Ram != IntPtr.Zero)
{
Marshal.FreeHGlobal(Ram);
Ram = IntPtr.Zero;
}
}
} }
} }

View file

@ -4,8 +4,8 @@ namespace ChocolArm64.Memory
{ {
public class AMemoryMgr public class AMemoryMgr
{ {
public const long AddrSize = RamSize;
public const long RamSize = 4L * 1024 * 1024 * 1024; public const long RamSize = 4L * 1024 * 1024 * 1024;
public const long AddrSize = RamSize;
private const int PTLvl0Bits = 10; private const int PTLvl0Bits = 10;
private const int PTLvl1Bits = 10; private const int PTLvl1Bits = 10;
@ -19,8 +19,8 @@ namespace ChocolArm64.Memory
private const int PTLvl1Mask = PTLvl1Size - 1; private const int PTLvl1Mask = PTLvl1Size - 1;
public const int PageMask = PageSize - 1; public const int PageMask = PageSize - 1;
private const int PTLvl0Bit = PTPageBits + PTLvl1Bits; private const int PTLvl0Bit = PTPageBits + PTLvl1Bits;
private const int PTLvl1Bit = PTPageBits; private const int PTLvl1Bit = PTPageBits;
private enum PTMap private enum PTMap
{ {

View file

@ -1,5 +1,6 @@
using ChocolArm64.Events; using ChocolArm64.Events;
using System; using System;
using System.Diagnostics;
namespace ChocolArm64.State namespace ChocolArm64.State
{ {
@ -29,6 +30,8 @@ namespace ChocolArm64.State
public int ProcessId; public int ProcessId;
public int ThreadId; public int ThreadId;
public bool Running { get; set; }
public long TpidrEl0 { get; set; } public long TpidrEl0 { get; set; }
public long Tpidr { get; set; } public long Tpidr { get; set; }
@ -38,15 +41,34 @@ namespace ChocolArm64.State
public uint CtrEl0 => 0x8444c004; public uint CtrEl0 => 0x8444c004;
public uint DczidEl0 => 0x00000004; public uint DczidEl0 => 0x00000004;
private const long TicksPerS = 19_200_000; public ulong CntfrqEl0 { get; set; }
private const long TicksPerMS = TicksPerS / 1_000; public ulong CntpctEl0
{
get
{
double Ticks = TickCounter.ElapsedTicks * HostTickFreq;
public long CntpctEl0 => Environment.TickCount * TicksPerMS; return (ulong)(Ticks * CntfrqEl0);
}
}
public event EventHandler<AInstExceptionEventArgs> Break; public event EventHandler<AInstExceptionEventArgs> Break;
public event EventHandler<AInstExceptionEventArgs> SvcCall; public event EventHandler<AInstExceptionEventArgs> SvcCall;
public event EventHandler<AInstUndefinedEventArgs> Undefined; public event EventHandler<AInstUndefinedEventArgs> Undefined;
private static Stopwatch TickCounter;
private static double HostTickFreq;
static AThreadState()
{
HostTickFreq = 1.0 / Stopwatch.Frequency;
TickCounter = new Stopwatch();
TickCounter.Start();
}
internal void OnBreak(int Imm) internal void OnBreak(int Imm)
{ {
Break?.Invoke(this, new AInstExceptionEventArgs(Imm)); Break?.Invoke(this, new AInstExceptionEventArgs(Imm));

View file

@ -1,5 +1,6 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using System.Diagnostics; using Ryujinx.Core.OsHle.Handles;
using System;
namespace Ryujinx.Core.Input namespace Ryujinx.Core.Input
{ {
@ -60,33 +61,63 @@ namespace Ryujinx.Core.Input
private const int HidEntryCount = 17; private const int HidEntryCount = 17;
private long SharedMemOffset; private object ShMemLock;
private Switch Ns; private (AMemory, long)[] ShMemPositions;
public Hid(Switch Ns) public Hid()
{ {
this.Ns = Ns; ShMemLock = new object();
ShMemPositions = new (AMemory, long)[0];
} }
public void Init(long HidOffset) internal void ShMemMap(object sender, EventArgs e)
{ {
SharedMemOffset = HidOffset; HSharedMem SharedMem = (HSharedMem)sender;
lock (ShMemLock)
{
ShMemPositions = SharedMem.GetVirtualPositions();
(AMemory Memory, long Position) ShMem = ShMemPositions[ShMemPositions.Length - 1];
Logging.Info($"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!");
Init(ShMem.Memory, ShMem.Position);
}
}
internal void ShMemUnmap(object sender, EventArgs e)
{
HSharedMem SharedMem = (HSharedMem)sender;
lock (ShMemLock)
{
ShMemPositions = SharedMem.GetVirtualPositions();
}
}
private void Init(AMemory Memory, long Position)
{
InitializeJoyconPair( InitializeJoyconPair(
Memory,
Position,
JoyConColor.Body_Neon_Red, JoyConColor.Body_Neon_Red,
JoyConColor.Buttons_Neon_Red, JoyConColor.Buttons_Neon_Red,
JoyConColor.Body_Neon_Blue, JoyConColor.Body_Neon_Blue,
JoyConColor.Buttons_Neon_Blue); JoyConColor.Buttons_Neon_Blue);
} }
public void InitializeJoyconPair( private void InitializeJoyconPair(
AMemory Memory,
long Position,
JoyConColor LeftColorBody, JoyConColor LeftColorBody,
JoyConColor LeftColorButtons, JoyConColor LeftColorButtons,
JoyConColor RightColorBody, JoyConColor RightColorBody,
JoyConColor RightColorButtons) JoyConColor RightColorButtons)
{ {
long BaseControllerOffset = HidControllersOffset + 8 * HidControllerSize; long BaseControllerOffset = Position + HidControllersOffset + 8 * HidControllerSize;
HidControllerType Type = HidControllerType Type =
HidControllerType.ControllerType_Handheld | HidControllerType.ControllerType_Handheld |
@ -102,20 +133,20 @@ namespace Ryujinx.Core.Input
HidControllerColorDesc SplitColorDesc = 0; HidControllerColorDesc SplitColorDesc = 0;
WriteInt32(BaseControllerOffset + 0x0, (int)Type); Memory.WriteInt32Unchecked(BaseControllerOffset + 0x0, (int)Type);
WriteInt32(BaseControllerOffset + 0x4, IsHalf ? 1 : 0); Memory.WriteInt32Unchecked(BaseControllerOffset + 0x4, IsHalf ? 1 : 0);
WriteInt32(BaseControllerOffset + 0x8, (int)SingleColorDesc); Memory.WriteInt32Unchecked(BaseControllerOffset + 0x8, (int)SingleColorDesc);
WriteInt32(BaseControllerOffset + 0xc, (int)SingleColorBody); Memory.WriteInt32Unchecked(BaseControllerOffset + 0xc, (int)SingleColorBody);
WriteInt32(BaseControllerOffset + 0x10, (int)SingleColorButtons); Memory.WriteInt32Unchecked(BaseControllerOffset + 0x10, (int)SingleColorButtons);
WriteInt32(BaseControllerOffset + 0x14, (int)SplitColorDesc); Memory.WriteInt32Unchecked(BaseControllerOffset + 0x14, (int)SplitColorDesc);
WriteInt32(BaseControllerOffset + 0x18, (int)LeftColorBody); Memory.WriteInt32Unchecked(BaseControllerOffset + 0x18, (int)LeftColorBody);
WriteInt32(BaseControllerOffset + 0x1c, (int)LeftColorButtons); Memory.WriteInt32Unchecked(BaseControllerOffset + 0x1c, (int)LeftColorButtons);
WriteInt32(BaseControllerOffset + 0x20, (int)RightColorBody); Memory.WriteInt32Unchecked(BaseControllerOffset + 0x20, (int)RightColorBody);
WriteInt32(BaseControllerOffset + 0x24, (int)RightColorButtons); Memory.WriteInt32Unchecked(BaseControllerOffset + 0x24, (int)RightColorButtons);
} }
public void SetJoyconButton( public void SetJoyconButton(
@ -125,115 +156,109 @@ namespace Ryujinx.Core.Input
HidJoystickPosition LeftStick, HidJoystickPosition LeftStick,
HidJoystickPosition RightStick) HidJoystickPosition RightStick)
{ {
long ControllerOffset = HidControllersOffset + (int)ControllerId * HidControllerSize; lock (ShMemLock)
{
foreach ((AMemory Memory, long Position) in ShMemPositions)
{
long ControllerOffset = Position + HidControllersOffset;
ControllerOffset += HidControllerHeaderSize; ControllerOffset += (int)ControllerId * HidControllerSize;
ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize; ControllerOffset += HidControllerHeaderSize;
long LastEntry = ReadInt64(ControllerOffset + 0x10); ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize;
long CurrEntry = (LastEntry + 1) % HidEntryCount; long LastEntry = Memory.ReadInt64Unchecked(ControllerOffset + 0x10);
long Timestamp = Stopwatch.GetTimestamp(); long CurrEntry = (LastEntry + 1) % HidEntryCount;
WriteInt64(ControllerOffset + 0x0, Timestamp); long Timestamp = GetTimestamp();
WriteInt64(ControllerOffset + 0x8, HidEntryCount);
WriteInt64(ControllerOffset + 0x10, CurrEntry);
WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1);
ControllerOffset += HidControllersLayoutHeaderSize; Memory.WriteInt64Unchecked(ControllerOffset + 0x0, Timestamp);
Memory.WriteInt64Unchecked(ControllerOffset + 0x8, HidEntryCount);
Memory.WriteInt64Unchecked(ControllerOffset + 0x10, CurrEntry);
Memory.WriteInt64Unchecked(ControllerOffset + 0x18, HidEntryCount - 1);
ControllerOffset += CurrEntry * HidControllersInputEntrySize; ControllerOffset += HidControllersLayoutHeaderSize;
WriteInt64(ControllerOffset + 0x0, Timestamp); ControllerOffset += CurrEntry * HidControllersInputEntrySize;
WriteInt64(ControllerOffset + 0x8, Timestamp);
WriteInt64(ControllerOffset + 0x10, (uint)Buttons); Memory.WriteInt64Unchecked(ControllerOffset + 0x0, Timestamp);
Memory.WriteInt64Unchecked(ControllerOffset + 0x8, Timestamp);
WriteInt32(ControllerOffset + 0x18, LeftStick.DX); Memory.WriteInt64Unchecked(ControllerOffset + 0x10, (uint)Buttons);
WriteInt32(ControllerOffset + 0x1c, LeftStick.DY);
WriteInt64(ControllerOffset + 0x20, RightStick.DX); Memory.WriteInt32Unchecked(ControllerOffset + 0x18, LeftStick.DX);
WriteInt64(ControllerOffset + 0x24, RightStick.DY); Memory.WriteInt32Unchecked(ControllerOffset + 0x1c, LeftStick.DY);
WriteInt64(ControllerOffset + 0x28, Memory.WriteInt64Unchecked(ControllerOffset + 0x20, RightStick.DX);
(uint)HidControllerConnState.Controller_State_Connected | Memory.WriteInt64Unchecked(ControllerOffset + 0x24, RightStick.DY);
(uint)HidControllerConnState.Controller_State_Wired);
Memory.WriteInt64Unchecked(ControllerOffset + 0x28,
(uint)HidControllerConnState.Controller_State_Connected |
(uint)HidControllerConnState.Controller_State_Wired);
}
}
} }
public void SetTouchPoints(params HidTouchPoint[] Points) public void SetTouchPoints(params HidTouchPoint[] Points)
{ {
long LastEntry = ReadInt64(HidTouchScreenOffset + 0x10); lock (ShMemLock)
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); foreach ((AMemory Memory, long Position) in ShMemPositions)
WriteInt32(TouchEntryOffset + 0x8, Padding); {
WriteInt32(TouchEntryOffset + 0xc, Index++); long TouchScreenOffset = Position + HidTouchScreenOffset;
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; long LastEntry = Memory.ReadInt64Unchecked(TouchScreenOffset + 0x10);
long CurrEntry = (LastEntry + 1) % HidEntryCount;
long Timestamp = GetTimestamp();
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x0, Timestamp);
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x8, HidEntryCount);
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x10, CurrEntry);
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x18, HidEntryCount - 1);
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp);
long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize;
long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize;
long SampleCounter = Memory.ReadInt64Unchecked(LastEntryOffset) + 1;
TouchEntryOffset += CurrEntry * HidTouchEntrySize;
Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, SampleCounter);
Memory.WriteInt64Unchecked(TouchEntryOffset + 0x8, Points.Length);
TouchEntryOffset += HidTouchEntryHeaderSize;
const int Padding = 0;
int Index = 0;
foreach (HidTouchPoint Point in Points)
{
Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, Timestamp);
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x8, Padding);
Memory.WriteInt32Unchecked(TouchEntryOffset + 0xc, Index++);
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x10, Point.X);
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x14, Point.Y);
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x18, Point.DiameterX);
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x1c, Point.DiameterY);
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x20, Point.Angle);
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x24, Padding);
TouchEntryOffset += HidTouchEntryTouchSize;
}
}
} }
} }
private unsafe long ReadInt64(long Position) private static long GetTimestamp()
{ {
Position += SharedMemOffset; return (long)((ulong)Environment.TickCount * 19_200);
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

@ -34,7 +34,7 @@ namespace Ryujinx.Core.Loaders
if (Exe.Mod0Offset == 0) if (Exe.Mod0Offset == 0)
{ {
int BssOffset = Exe.DataOffset + Exe.Data.Count; int BssOffset = Exe.DataOffset + Exe.Data.Length;
int BssSize = Exe.BssSize; int BssSize = Exe.BssSize;
MapBss(ImageBase + BssOffset, BssSize); MapBss(ImageBase + BssOffset, BssSize);
@ -92,18 +92,15 @@ namespace Ryujinx.Core.Loaders
private void WriteData( private void WriteData(
long Position, long Position,
IList<byte> Data, byte[] Data,
MemoryType Type, MemoryType Type,
AMemoryPerm Perm) AMemoryPerm Perm)
{ {
Memory.Manager.Map(Position, Data.Count, (int)Type, AMemoryPerm.Write); Memory.Manager.Map(Position, Data.Length, (int)Type, AMemoryPerm.Write);
for (int Index = 0; Index < Data.Count; Index++) AMemoryHelper.WriteBytes(Memory, Position, Data);
{
Memory.WriteByte(Position + Index, Data[Index]);
}
Memory.Manager.Reprotect(Position, Data.Count, Perm); Memory.Manager.Reprotect(Position, Data.Length, Perm);
} }
private void MapBss(long Position, long Size) private void MapBss(long Position, long Size)

View file

@ -1,12 +1,10 @@
using System.Collections.ObjectModel;
namespace Ryujinx.Core.Loaders.Executables namespace Ryujinx.Core.Loaders.Executables
{ {
public interface IExecutable public interface IExecutable
{ {
ReadOnlyCollection<byte> Text { get; } byte[] Text { get; }
ReadOnlyCollection<byte> RO { get; } byte[] RO { get; }
ReadOnlyCollection<byte> Data { get; } byte[] Data { get; }
int Mod0Offset { get; } int Mod0Offset { get; }
int TextOffset { get; } int TextOffset { get; }

View file

@ -1,18 +1,12 @@
using System;
using System.Collections.ObjectModel;
using System.IO; using System.IO;
namespace Ryujinx.Core.Loaders.Executables namespace Ryujinx.Core.Loaders.Executables
{ {
class Nro : IExecutable class Nro : IExecutable
{ {
private byte[] m_Text; public byte[] Text { get; private set; }
private byte[] m_RO; public byte[] RO { get; private set; }
private byte[] m_Data; public byte[] Data { get; private set; }
public ReadOnlyCollection<byte> Text => Array.AsReadOnly(m_Text);
public ReadOnlyCollection<byte> RO => Array.AsReadOnly(m_RO);
public ReadOnlyCollection<byte> Data => Array.AsReadOnly(m_Data);
public int Mod0Offset { get; private set; } public int Mod0Offset { get; private set; }
public int TextOffset { get; private set; } public int TextOffset { get; private set; }
@ -54,9 +48,9 @@ namespace Ryujinx.Core.Loaders.Executables
return Reader.ReadBytes(Size); return Reader.ReadBytes(Size);
} }
m_Text = Read(TextOffset, TextSize); Text = Read(TextOffset, TextSize);
m_RO = Read(ROOffset, ROSize); RO = Read(ROOffset, ROSize);
m_Data = Read(DataOffset, DataSize); Data = Read(DataOffset, DataSize);
} }
} }
} }

View file

@ -1,19 +1,14 @@
using Ryujinx.Core.Loaders.Compression; using Ryujinx.Core.Loaders.Compression;
using System; using System;
using System.Collections.ObjectModel;
using System.IO; using System.IO;
namespace Ryujinx.Core.Loaders.Executables namespace Ryujinx.Core.Loaders.Executables
{ {
class Nso : IExecutable class Nso : IExecutable
{ {
private byte[] m_Text; public byte[] Text { get; private set; }
private byte[] m_RO; public byte[] RO { get; private set; }
private byte[] m_Data; public byte[] Data { get; private set; }
public ReadOnlyCollection<byte> Text => Array.AsReadOnly(m_Text);
public ReadOnlyCollection<byte> RO => Array.AsReadOnly(m_RO);
public ReadOnlyCollection<byte> Data => Array.AsReadOnly(m_Data);
public int Mod0Offset { get; private set; } public int Mod0Offset { get; private set; }
public int TextOffset { get; private set; } public int TextOffset { get; private set; }
@ -57,9 +52,9 @@ namespace Ryujinx.Core.Loaders.Executables
byte[] BuildId = Reader.ReadBytes(0x20); byte[] BuildId = Reader.ReadBytes(0x20);
int TextSize = Reader.ReadInt32(); int TextSize = Reader.ReadInt32();
int ROSize = Reader.ReadInt32(); int ROSize = Reader.ReadInt32();
int DataSize = Reader.ReadInt32(); int DataSize = Reader.ReadInt32();
Input.Seek(0x24, SeekOrigin.Current); Input.Seek(0x24, SeekOrigin.Current);
@ -82,38 +77,38 @@ namespace Ryujinx.Core.Loaders.Executables
//Text segment //Text segment
Input.Seek(TextOffset, SeekOrigin.Begin); Input.Seek(TextOffset, SeekOrigin.Begin);
m_Text = Reader.ReadBytes(TextSize); Text = Reader.ReadBytes(TextSize);
if (Flags.HasFlag(NsoFlags.IsTextCompressed) || true) if (Flags.HasFlag(NsoFlags.IsTextCompressed) || true)
{ {
m_Text = Lz4.Decompress(m_Text, TextDecSize); Text = Lz4.Decompress(Text, TextDecSize);
} }
//Read-only data segment //Read-only data segment
Input.Seek(ROOffset, SeekOrigin.Begin); Input.Seek(ROOffset, SeekOrigin.Begin);
m_RO = Reader.ReadBytes(ROSize); RO = Reader.ReadBytes(ROSize);
if (Flags.HasFlag(NsoFlags.IsROCompressed) || true) if (Flags.HasFlag(NsoFlags.IsROCompressed) || true)
{ {
m_RO = Lz4.Decompress(m_RO, RODecSize); RO = Lz4.Decompress(RO, RODecSize);
} }
//Data segment //Data segment
Input.Seek(DataOffset, SeekOrigin.Begin); Input.Seek(DataOffset, SeekOrigin.Begin);
m_Data = Reader.ReadBytes(DataSize); Data = Reader.ReadBytes(DataSize);
if (Flags.HasFlag(NsoFlags.IsDataCompressed) || true) if (Flags.HasFlag(NsoFlags.IsDataCompressed) || true)
{ {
m_Data = Lz4.Decompress(m_Data, DataDecSize); Data = Lz4.Decompress(Data, DataDecSize);
} }
using (MemoryStream Text = new MemoryStream(m_Text)) using (MemoryStream TextMS = new MemoryStream(Text))
{ {
BinaryReader TextReader = new BinaryReader(Text); BinaryReader TextReader = new BinaryReader(TextMS);
Text.Seek(4, SeekOrigin.Begin); TextMS.Seek(4, SeekOrigin.Begin);
Mod0Offset = TextReader.ReadInt32(); Mod0Offset = TextReader.ReadInt32();
} }

View file

@ -8,7 +8,8 @@ namespace Ryujinx.Core
{ {
public static class Logging public static class Logging
{ {
private static Stopwatch ExecutionTime = new Stopwatch(); private static Stopwatch ExecutionTime;
private const string LogFileName = "Ryujinx.log"; private const string LogFileName = "Ryujinx.log";
private static bool EnableInfo = Config.LoggingEnableInfo; private static bool EnableInfo = Config.LoggingEnableInfo;
@ -22,9 +23,11 @@ namespace Ryujinx.Core
static Logging() static Logging()
{ {
ExecutionTime.Start();
if (File.Exists(LogFileName)) File.Delete(LogFileName); if (File.Exists(LogFileName)) File.Delete(LogFileName);
ExecutionTime = new Stopwatch();
ExecutionTime.Start();
} }
public static string GetExecutionTime() public static string GetExecutionTime()

View file

@ -4,7 +4,7 @@ using System.Threading;
namespace Ryujinx.Core.OsHle namespace Ryujinx.Core.OsHle
{ {
public class CondVar class CondVar
{ {
private Process Process; private Process Process;
@ -24,7 +24,7 @@ namespace Ryujinx.Core.OsHle
WaitingThreads = new List<HThread>(); WaitingThreads = new List<HThread>();
} }
public void WaitForSignal(HThread Thread) public bool WaitForSignal(HThread Thread)
{ {
int Count = Process.Memory.ReadInt32(CondVarAddress); int Count = Process.Memory.ReadInt32(CondVarAddress);
@ -41,12 +41,14 @@ namespace Ryujinx.Core.OsHle
} }
else else
{ {
Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000)); bool Result = Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000));
lock (WaitingThreads) lock (WaitingThreads)
{ {
WaitingThreads.Remove(Thread); WaitingThreads.Remove(Thread);
} }
return Result;
} }
} }
@ -60,6 +62,8 @@ namespace Ryujinx.Core.OsHle
} }
ReleaseCondVarValue(); ReleaseCondVarValue();
return true;
} }
public void SetSignal(HThread Thread, int Count) public void SetSignal(HThread Thread, int Count)

View file

@ -0,0 +1,10 @@
namespace Ryujinx.Core.OsHle
{
static class ErrorCode
{
public static uint MakeError(ErrorModule Module, int Code)
{
return (uint)Module | ((uint)Code << 9);
}
}
}

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Core.OsHle.IpcServices namespace Ryujinx.Core.OsHle
{ {
enum ErrorModule enum ErrorModule
{ {

View file

@ -1,58 +1,48 @@
using Ryujinx.Core.OsHle.Utilities;
using System; using System;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Handles namespace Ryujinx.Core.OsHle.Handles
{ {
class HDomain : HSession class HDomain : HSession, IDisposable
{ {
private Dictionary<int, object> Objects; private IdDictionary Objects;
private IdPool ObjIds;
public HDomain(HSession Session) : base(Session) public HDomain(HSession Session) : base(Session)
{ {
Objects = new Dictionary<int, object>(); Objects = new IdDictionary();
ObjIds = new IdPool();
} }
public int GenerateObjectId(object Obj) public int Add(object Obj)
{ {
int Id = ObjIds.GenerateId(); return Objects.Add(Obj);
if (Id == -1)
{
throw new InvalidOperationException();
}
Objects.Add(Id, Obj);
return Id;
} }
public void DeleteObject(int Id) public bool Delete(int Id)
{ {
if (Objects.TryGetValue(Id, out object Obj)) return Objects.Delete(Id);
{
if (Obj is IDisposable DisposableObj)
{
DisposableObj.Dispose();
}
ObjIds.DeleteId(Id);
Objects.Remove(Id);
}
} }
public object GetObject(int Id) public object GetObject(int Id)
{ {
if (Objects.TryGetValue(Id, out object Obj)) return Objects.GetData(Id);
{ }
return Obj;
}
return null; public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
foreach (object Obj in Objects)
{
if (Obj != this && Obj is IDisposable DisposableObj)
{
DisposableObj.Dispose();
}
}
}
} }
} }
} }

View file

@ -1,18 +0,0 @@
namespace Ryujinx.Core.OsHle.Handles
{
class HNvMap
{
public int Id { get; private set; }
public int Size { get; private set; }
public int Align { get; set; }
public int Kind { get; set; }
public long Address { get; set; }
public HNvMap(int Id, int Size)
{
this.Id = Id;
this.Size = Size;
}
}
}

View file

@ -1,3 +1,4 @@
using ChocolArm64.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -5,66 +6,39 @@ namespace Ryujinx.Core.OsHle.Handles
{ {
class HSharedMem class HSharedMem
{ {
private List<long> Positions; private List<(AMemory, long)> Positions;
public int PositionsCount => Positions.Count;
public EventHandler<EventArgs> MemoryMapped; public EventHandler<EventArgs> MemoryMapped;
public EventHandler<EventArgs> MemoryUnmapped; public EventHandler<EventArgs> MemoryUnmapped;
public HSharedMem() public HSharedMem()
{ {
Positions = new List<long>(); Positions = new List<(AMemory, long)>();
} }
public void AddVirtualPosition(long Position) public void AddVirtualPosition(AMemory Memory, long Position)
{ {
lock (Positions) lock (Positions)
{ {
Positions.Add(Position); Positions.Add((Memory, Position));
MemoryMapped?.Invoke(this, EventArgs.Empty); MemoryMapped?.Invoke(this, EventArgs.Empty);
} }
} }
public void RemoveVirtualPosition(long Position) public void RemoveVirtualPosition(AMemory Memory, long Position)
{ {
lock (Positions) lock (Positions)
{ {
Positions.Remove(Position); Positions.Remove((Memory, Position));
MemoryUnmapped?.Invoke(this, EventArgs.Empty); MemoryUnmapped?.Invoke(this, EventArgs.Empty);
} }
} }
public long GetVirtualPosition(int Index) public (AMemory, long)[] GetVirtualPositions()
{ {
lock (Positions) return Positions.ToArray();
{
if (Index < 0 || Index >= Positions.Count)
{
throw new ArgumentOutOfRangeException(nameof(Index));
}
return Positions[Index];
}
}
public bool TryGetLastVirtualPosition(out long Position)
{
lock (Positions)
{
if (Positions.Count > 0)
{
Position = Positions[Positions.Count - 1];
return true;
}
Position = 0;
return false;
}
} }
} }
} }

View file

@ -0,0 +1,63 @@
using System;
namespace Ryujinx.Core.OsHle.Handles
{
class KProcessHandleTable : IDisposable
{
private IdDictionary Handles;
public KProcessHandleTable()
{
Handles = new IdDictionary();
}
public int OpenHandle(object Obj)
{
return Handles.Add(Obj);
}
public T GetData<T>(int Handle)
{
return Handles.GetData<T>(Handle);
}
public bool ReplaceData(int Id, object Data)
{
return Handles.ReplaceData(Id, Data);
}
public bool CloseHandle(int Handle)
{
object Data = Handles.GetData(Handle);
if (Data is HTransferMem TMem)
{
TMem.Memory.Manager.Reprotect(
TMem.Position,
TMem.Size,
TMem.Perm);
}
return Handles.Delete(Handle);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
foreach (object Obj in Handles)
{
if (Obj is IDisposable DisposableObj)
{
DisposableObj.Dispose();
}
}
}
}
}
}

View file

@ -183,7 +183,7 @@ namespace Ryujinx.Core.OsHle.Handles
TryResumingExecution(SchedThread); TryResumingExecution(SchedThread);
} }
public void WaitForSignal(HThread Thread, int Timeout = -1) public bool WaitForSignal(HThread Thread, int Timeout = -1)
{ {
SchedulerThread SchedThread; SchedulerThread SchedThread;
@ -206,22 +206,26 @@ namespace Ryujinx.Core.OsHle.Handles
{ {
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!"); Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
return; return false;
} }
} }
bool Result;
if (Timeout >= 0) if (Timeout >= 0)
{ {
Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms."); Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
SchedThread.WaitEvent.WaitOne(Timeout); Result = SchedThread.WaitEvent.WaitOne(Timeout);
} }
else else
{ {
SchedThread.WaitEvent.WaitOne(); Result = SchedThread.WaitEvent.WaitOne();
} }
TryResumingExecution(SchedThread); TryResumingExecution(SchedThread);
return Result;
} }
private void TryResumingExecution(SchedulerThread SchedThread) private void TryResumingExecution(SchedulerThread SchedThread)

View file

@ -37,5 +37,33 @@ namespace Ryujinx.Core.OsHle
Position += 0x18; Position += 0x18;
} }
public static string ReadHbAbiNextLoadPath(AMemory Memory, long Position)
{
string FileName = null;
while (true)
{
long Key = Memory.ReadInt64(Position);
if (Key == 2)
{
long Value0 = Memory.ReadInt64(Position + 0x08);
long Value1 = Memory.ReadInt64(Position + 0x10);
FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, (int)(Value1 - Value0));
break;
}
else if (Key == 0)
{
break;
}
Position += 0x18;
}
return FileName;
}
} }
} }

View file

@ -1,33 +1,23 @@
using Ryujinx.Core.Loaders.Executables; using Ryujinx.Core.Loaders.Executables;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Utilities;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.IO; using System.IO;
namespace Ryujinx.Core.OsHle namespace Ryujinx.Core.OsHle
{ {
public class Horizon public class Horizon : IDisposable
{ {
internal const int HidSize = 0x40000; internal const int HidSize = 0x40000;
internal const int FontSize = 0x50; internal const int FontSize = 0x50;
internal int HidHandle { get; private set; } internal ConcurrentDictionary<long, Mutex> Mutexes { get; private set; }
internal int FontHandle { get; private set; } internal ConcurrentDictionary<long, CondVar> CondVars { get; private set; }
internal IdPool IdGen { get; private set; }
internal IdPool NvMapIds { get; private set; }
internal IdPoolWithObj Handles { get; private set; }
internal IdPoolWithObj Fds { get; private set; }
internal IdPoolWithObj Displays { get; private set; }
public ConcurrentDictionary<long, Mutex> Mutexes { get; private set; }
public ConcurrentDictionary<long, CondVar> CondVars { get; private set; }
private ConcurrentDictionary<int, Process> Processes; private ConcurrentDictionary<int, Process> Processes;
private HSharedMem HidSharedMem; internal HSharedMem HidSharedMem;
internal HSharedMem FontSharedMem;
private Switch Ns; private Switch Ns;
@ -35,25 +25,13 @@ namespace Ryujinx.Core.OsHle
{ {
this.Ns = Ns; this.Ns = Ns;
IdGen = new IdPool();
NvMapIds = new IdPool();
Handles = new IdPoolWithObj();
Fds = new IdPoolWithObj();
Displays = new IdPoolWithObj();
Mutexes = new ConcurrentDictionary<long, Mutex>(); Mutexes = new ConcurrentDictionary<long, Mutex>();
CondVars = new ConcurrentDictionary<long, CondVar>(); CondVars = new ConcurrentDictionary<long, CondVar>();
Processes = new ConcurrentDictionary<int, Process>(); Processes = new ConcurrentDictionary<int, Process>();
HidSharedMem = new HSharedMem(); HidSharedMem = new HSharedMem();
FontSharedMem = new HSharedMem();
HidSharedMem.MemoryMapped += HidInit;
HidHandle = Handles.GenerateId(HidSharedMem);
FontHandle = Handles.GenerateId(new HSharedMem());
} }
public void LoadCart(string ExeFsDir, string RomFsFile = null) public void LoadCart(string ExeFsDir, string RomFsFile = null)
@ -63,9 +41,7 @@ namespace Ryujinx.Core.OsHle
Ns.VFs.LoadRomFs(RomFsFile); Ns.VFs.LoadRomFs(RomFsFile);
} }
int ProcessId = IdGen.GenerateId(); Process MainProcess = MakeProcess();
Process MainProcess = new Process(Ns, ProcessId);
void LoadNso(string FileName) void LoadNso(string FileName)
{ {
@ -96,17 +72,13 @@ namespace Ryujinx.Core.OsHle
LoadNso("sdk"); LoadNso("sdk");
MainProcess.Run(); MainProcess.Run();
Processes.TryAdd(ProcessId, MainProcess);
} }
public void LoadProgram(string FileName) public void LoadProgram(string FileName)
{ {
bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro"; bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
int ProcessId = IdGen.GenerateId(); Process MainProcess = MakeProcess();
Process MainProcess = new Process(Ns, ProcessId);
using (FileStream Input = new FileStream(FileName, FileMode.Open)) using (FileStream Input = new FileStream(FileName, FileMode.Open))
{ {
@ -117,34 +89,67 @@ namespace Ryujinx.Core.OsHle
MainProcess.SetEmptyArgs(); MainProcess.SetEmptyArgs();
MainProcess.Run(IsNro); MainProcess.Run(IsNro);
Processes.TryAdd(ProcessId, MainProcess);
} }
public void FinalizeAllProcesses() private Process MakeProcess()
{ {
foreach (Process Process in Processes.Values) Process Process;
lock (Processes)
{ {
Process.StopAllThreads(); int ProcessId = 0;
while (Processes.ContainsKey(ProcessId))
{
ProcessId++;
}
Process = new Process(Ns, ProcessId);
Processes.TryAdd(ProcessId, Process);
}
return Process;
}
internal void ExitProcess(int ProcessId)
{
if (Processes.TryGetValue(ProcessId, out Process Process) && Process.NeedsHbAbi)
{
string NextNro = Homebrew.ReadHbAbiNextLoadPath(Process.Memory, Process.HbAbiDataPosition);
Logging.Info($"HbAbi NextLoadPath {NextNro}");
if (NextNro == string.Empty)
{
NextNro = "sdmc:/hbmenu.nro";
}
NextNro = NextNro.Replace("sdmc:", string.Empty);
NextNro = Ns.VFs.GetFullPath(Ns.VFs.GetSdCardPath(), NextNro);
if (File.Exists(NextNro))
{
//TODO: Those dictionaries shouldn't even exist,
//the Mutex and CondVar helper classes should be static.
Mutexes.Clear();
CondVars.Clear();
LoadProgram(NextNro);
}
}
if (Processes.TryRemove(ProcessId, out Process))
{
Process.StopAllThreadsAsync();
Process.Dispose(); Process.Dispose();
}
}
internal bool ExitProcess(int ProcessId) if (Processes.Count == 0)
{ {
bool Success = Processes.TryRemove(ProcessId, out Process Process); Ns.OnFinish(EventArgs.Empty);
}
if (Success)
{
Process.StopAllThreads();
} }
if (Processes.Count == 0)
{
Ns.OnFinish(EventArgs.Empty);
}
return Success;
} }
internal bool TryGetProcess(int ProcessId, out Process Process) internal bool TryGetProcess(int ProcessId, out Process Process)
@ -152,30 +157,20 @@ namespace Ryujinx.Core.OsHle
return Processes.TryGetValue(ProcessId, out Process); return Processes.TryGetValue(ProcessId, out Process);
} }
internal void CloseHandle(int Handle) public void Dispose()
{ {
object HndData = Handles.GetData<object>(Handle); Dispose(true);
if (HndData is HTransferMem TransferMem)
{
TransferMem.Memory.Manager.Reprotect(
TransferMem.Position,
TransferMem.Size,
TransferMem.Perm);
}
Handles.Delete(Handle);
} }
private void HidInit(object sender, EventArgs e) protected virtual void Dispose(bool Disposing)
{ {
HSharedMem SharedMem = (HSharedMem)sender; if (Disposing)
if (SharedMem.TryGetLastVirtualPosition(out long Position))
{ {
Logging.Info($"HID shared memory successfully mapped to 0x{Position:x16}!"); foreach (Process Process in Processes.Values)
{
Ns.Hid.Init(Position); Process.StopAllThreadsAsync();
Process.Dispose();
}
} }
} }
} }

View file

@ -3,31 +3,40 @@ using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Utilities namespace Ryujinx.Core.OsHle
{ {
class IdPoolWithObj : IEnumerable<KeyValuePair<int, object>> class IdDictionary : IEnumerable<object>
{ {
private IdPool Ids;
private ConcurrentDictionary<int, object> Objs; private ConcurrentDictionary<int, object> Objs;
public IdPoolWithObj() private int FreeIdHint = 1;
{
Ids = new IdPool();
public IdDictionary()
{
Objs = new ConcurrentDictionary<int, object>(); Objs = new ConcurrentDictionary<int, object>();
} }
public int GenerateId(object Data) public int Add(object Data)
{ {
int Id = Ids.GenerateId(); if (Objs.TryAdd(FreeIdHint, Data))
if (Id == -1 || !Objs.TryAdd(Id, Data))
{ {
throw new InvalidOperationException(); return FreeIdHint++;
} }
return Id; return AddSlow(Data);
}
private int AddSlow(object Data)
{
for (int Id = 1; Id < int.MaxValue; Id++)
{
if (Objs.TryAdd(Id, Data))
{
return Id;
}
}
throw new InvalidOperationException();
} }
public bool ReplaceData(int Id, object Data) public bool ReplaceData(int Id, object Data)
@ -42,6 +51,16 @@ namespace Ryujinx.Core.OsHle.Utilities
return false; return false;
} }
public object GetData(int Id)
{
if (Objs.TryGetValue(Id, out object Data))
{
return Data;
}
return null;
}
public T GetData<T>(int Id) public T GetData<T>(int Id)
{ {
if (Objs.TryGetValue(Id, out object Data) && Data is T) if (Objs.TryGetValue(Id, out object Data) && Data is T)
@ -52,7 +71,7 @@ namespace Ryujinx.Core.OsHle.Utilities
return default(T); return default(T);
} }
public void Delete(int Id) public bool Delete(int Id)
{ {
if (Objs.TryRemove(Id, out object Obj)) if (Objs.TryRemove(Id, out object Obj))
{ {
@ -61,18 +80,22 @@ namespace Ryujinx.Core.OsHle.Utilities
DisposableObj.Dispose(); DisposableObj.Dispose();
} }
Ids.DeleteId(Id); FreeIdHint = Id;
return true;
} }
return false;
} }
IEnumerator<KeyValuePair<int, object>> IEnumerable<KeyValuePair<int, object>>.GetEnumerator() IEnumerator<object> IEnumerable<object>.GetEnumerator()
{ {
return Objs.GetEnumerator(); return Objs.Values.GetEnumerator();
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return Objs.GetEnumerator(); return Objs.Values.GetEnumerator();
} }
} }
} }

View file

@ -13,6 +13,7 @@ namespace Ryujinx.Core.OsHle.Ipc
public static void IpcCall( public static void IpcCall(
Switch Ns, Switch Ns,
Process Process,
AMemory Memory, AMemory Memory,
HSession Session, HSession Session,
IpcMessage Request, IpcMessage Request,
@ -60,7 +61,7 @@ namespace Ryujinx.Core.OsHle.Ipc
} }
else if (Request.DomCmd == IpcDomCmd.DeleteObj) else if (Request.DomCmd == IpcDomCmd.DeleteObj)
{ {
Dom.DeleteObject(Request.DomObjId); Dom.Delete(Request.DomObjId);
Response = FillResponse(Response, 0); Response = FillResponse(Response, 0);
@ -100,6 +101,7 @@ namespace Ryujinx.Core.OsHle.Ipc
ServiceCtx Context = new ServiceCtx( ServiceCtx Context = new ServiceCtx(
Ns, Ns,
Process,
Memory, Memory,
Session, Session,
Request, Request,
@ -124,15 +126,43 @@ namespace Ryujinx.Core.OsHle.Ipc
switch (CmdId) switch (CmdId)
{ {
case 0: Request = IpcConvertSessionToDomain(Ns, Session, Response, HndId); break; case 0:
case 3: Request = IpcQueryBufferPointerSize(Response); break; {
case 2: //IpcDuplicateSession, differences is unknown. HDomain Dom = new HDomain(Session);
case 4: Request = IpcDuplicateSessionEx(Ns, Session, Response, ReqReader); break;
Process.HandleTable.ReplaceData(HndId, Dom);
Request = FillResponse(Response, 0, Dom.Add(Dom));
break;
}
case 3:
{
Request = FillResponse(Response, 0, 0x500);
break;
}
//TODO: Whats the difference between IpcDuplicateSession/Ex?
case 2:
case 4:
{
int Unknown = ReqReader.ReadInt32();
int Handle = Process.HandleTable.OpenHandle(Session);
Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
Request = FillResponse(Response, 0);
break;
}
default: throw new NotImplementedException(CmdId.ToString()); default: throw new NotImplementedException(CmdId.ToString());
} }
} }
else if (Request.Type == IpcMessageType.Unknown2) else if (Request.Type == IpcMessageType.CloseSession)
{ {
//TODO //TODO
} }
@ -145,39 +175,6 @@ namespace Ryujinx.Core.OsHle.Ipc
} }
} }
private static IpcMessage IpcConvertSessionToDomain(
Switch Ns,
HSession Session,
IpcMessage Response,
int HndId)
{
HDomain Dom = new HDomain(Session);
Ns.Os.Handles.ReplaceData(HndId, Dom);
return FillResponse(Response, 0, Dom.GenerateObjectId(Dom));
}
private static IpcMessage IpcDuplicateSessionEx(
Switch Ns,
HSession Session,
IpcMessage Response,
BinaryReader ReqReader)
{
int Unknown = ReqReader.ReadInt32();
int Handle = Ns.Os.Handles.GenerateId(Session);
Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
return FillResponse(Response, 0);
}
private static IpcMessage IpcQueryBufferPointerSize(IpcMessage Response)
{
return FillResponse(Response, 0, 0x500);
}
private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values) private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values)
{ {
using (MemoryStream MS = new MemoryStream()) using (MemoryStream MS = new MemoryStream())

View file

@ -217,16 +217,26 @@ namespace Ryujinx.Core.OsHle.Ipc
public long GetSendBuffPtr() public long GetSendBuffPtr()
{ {
if (SendBuff.Count > 0 && SendBuff[0].Position != 0) if (SendBuff.Count > 0 && SendBuff[0].Size != 0)
{ {
return SendBuff[0].Position; return SendBuff[0].Position;
} }
if (PtrBuff.Count > 0 && PtrBuff[0].Position != 0) if (PtrBuff.Count > 0 && PtrBuff[0].Size != 0)
{ {
return PtrBuff[0].Position; return PtrBuff[0].Position;
} }
if (ReceiveBuff.Count > 0 && ReceiveBuff[0].Size != 0)
{
return ReceiveBuff[0].Position;
}
if (RecvListBuff.Count > 0 && RecvListBuff[0].Size != 0)
{
return RecvListBuff[0].Position;
}
return -1; return -1;
} }
} }

View file

@ -2,9 +2,9 @@ namespace Ryujinx.Core.OsHle.Ipc
{ {
enum IpcMessageType enum IpcMessageType
{ {
Response = 0, Response = 0,
Unknown2 = 2, CloseSession = 2,
Request = 4, Request = 4,
Control = 5 Control = 5
} }
} }

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Core.OsHle
{
static class KernelErr
{
public const int InvalidMemRange = 110;
public const int InvalidHandle = 114;
public const int Timeout = 117;
public const int InvalidInfo = 120;
public const int InvalidIpcReq = 123;
}
}

View file

@ -7,7 +7,10 @@ namespace Ryujinx.Core.OsHle
public const long AddrSpaceStart = 0x08000000; public const long AddrSpaceStart = 0x08000000;
public const long MapRegionAddress = 0x10000000; public const long MapRegionAddress = 0x10000000;
public const long MapRegionSize = 0x10000000; public const long MapRegionSize = 0x20000000;
public const long HeapRegionAddress = MapRegionAddress + MapRegionSize;
public const long HeapRegionSize = TlsPagesAddress - HeapRegionAddress;
public const long MainStackSize = 0x100000; public const long MainStackSize = 0x100000;
@ -17,8 +20,6 @@ namespace Ryujinx.Core.OsHle
public const long TlsPagesAddress = MainStackAddress - TlsPagesSize; public const long TlsPagesAddress = MainStackAddress - TlsPagesSize;
public const long HeapRegionAddress = MapRegionAddress + MapRegionSize;
public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize; public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize;
public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart; public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart;

View file

@ -4,7 +4,7 @@ using System.Threading;
namespace Ryujinx.Core.OsHle namespace Ryujinx.Core.OsHle
{ {
public class Mutex class Mutex
{ {
private const int MutexHasListenersMask = 0x40000000; private const int MutexHasListenersMask = 0x40000000;

View file

@ -9,25 +9,34 @@ using Ryujinx.Core.OsHle.Svc;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.Core.OsHle namespace Ryujinx.Core.OsHle
{ {
public class Process : IDisposable class Process : IDisposable
{ {
private const int TlsSize = 0x200; private const int TlsSize = 0x200;
private const int TotalTlsSlots = 32; private const int TotalTlsSlots = 32;
private const int TickFreq = 19_200_000;
private Switch Ns; private Switch Ns;
public bool NeedsHbAbi { get; private set; }
public long HbAbiDataPosition { get; private set; }
public int ProcessId { get; private set; } public int ProcessId { get; private set; }
private ATranslator Translator; private ATranslator Translator;
public AMemory Memory { get; private set; } public AMemory Memory { get; private set; }
public ServiceMgr Services { get; private set; }
public KProcessScheduler Scheduler { get; private set; } public KProcessScheduler Scheduler { get; private set; }
public KProcessHandleTable HandleTable { get; private set; }
private SvcHandler SvcHandler; private SvcHandler SvcHandler;
private ConcurrentDictionary<int, AThread> TlsSlots; private ConcurrentDictionary<int, AThread> TlsSlots;
@ -40,14 +49,22 @@ namespace Ryujinx.Core.OsHle
private long ImageBase; private long ImageBase;
private bool ShouldDispose;
private bool Disposed;
public Process(Switch Ns, int ProcessId) public Process(Switch Ns, int ProcessId)
{ {
this.Ns = Ns; this.Ns = Ns;
this.ProcessId = ProcessId; this.ProcessId = ProcessId;
Memory = new AMemory(Ns.Ram); Memory = new AMemory();
Scheduler = new KProcessScheduler(); Services = new ServiceMgr();
HandleTable = new KProcessHandleTable();
Scheduler = new KProcessScheduler();
SvcHandler = new SvcHandler(Ns, this); SvcHandler = new SvcHandler(Ns, this);
@ -67,6 +84,11 @@ namespace Ryujinx.Core.OsHle
public void LoadProgram(IExecutable Program) public void LoadProgram(IExecutable Program)
{ {
if (Disposed)
{
throw new ObjectDisposedException(nameof(Process));
}
Logging.Info($"Image base at 0x{ImageBase:x16}."); Logging.Info($"Image base at 0x{ImageBase:x16}.");
Executable Executable = new Executable(Program, Memory, ImageBase); Executable Executable = new Executable(Program, Memory, ImageBase);
@ -78,11 +100,19 @@ namespace Ryujinx.Core.OsHle
public void SetEmptyArgs() public void SetEmptyArgs()
{ {
//TODO: This should be part of Run.
ImageBase += AMemoryMgr.PageSize; ImageBase += AMemoryMgr.PageSize;
} }
public bool Run(bool UseHbAbi = false) public bool Run(bool NeedsHbAbi = false)
{ {
if (Disposed)
{
throw new ObjectDisposedException(nameof(Process));
}
this.NeedsHbAbi = NeedsHbAbi;
if (Executables.Count == 0) if (Executables.Count == 0)
{ {
return false; return false;
@ -102,11 +132,11 @@ namespace Ryujinx.Core.OsHle
return false; return false;
} }
MainThread = Ns.Os.Handles.GetData<HThread>(Handle); MainThread = HandleTable.GetData<HThread>(Handle);
if (UseHbAbi) if (NeedsHbAbi)
{ {
long HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd); HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd);
Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle); Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle);
@ -124,22 +154,21 @@ namespace Ryujinx.Core.OsHle
Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW); Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW);
} }
public void StopAllThreads() public void StopAllThreadsAsync()
{ {
if (Disposed)
{
throw new ObjectDisposedException(nameof(Process));
}
if (MainThread != null) if (MainThread != null)
{ {
while (MainThread.Thread.IsAlive) MainThread.Thread.StopExecution();
{
MainThread.Thread.StopExecution();
}
} }
foreach (AThread Thread in TlsSlots.Values) foreach (AThread Thread in TlsSlots.Values)
{ {
while (Thread.IsAlive) Thread.StopExecution();
{
Thread.StopExecution();
}
} }
} }
@ -150,49 +179,27 @@ namespace Ryujinx.Core.OsHle
int Priority, int Priority,
int ProcessorId) int ProcessorId)
{ {
ThreadPriority ThreadPrio; if (Disposed)
if (Priority < 12)
{ {
ThreadPrio = ThreadPriority.Highest; throw new ObjectDisposedException(nameof(Process));
}
else if (Priority < 24)
{
ThreadPrio = ThreadPriority.AboveNormal;
}
else if (Priority < 36)
{
ThreadPrio = ThreadPriority.Normal;
}
else if (Priority < 48)
{
ThreadPrio = ThreadPriority.BelowNormal;
}
else
{
ThreadPrio = ThreadPriority.Lowest;
} }
AThread Thread = new AThread(GetTranslator(), Memory, ThreadPrio, EntryPoint); AThread Thread = new AThread(GetTranslator(), Memory, EntryPoint);
HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority); HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority);
int Handle = Ns.Os.Handles.GenerateId(ThreadHnd); int Handle = HandleTable.OpenHandle(ThreadHnd);
int TlsSlot = GetFreeTlsSlot(Thread); int ThreadId = GetFreeTlsSlot(Thread);
if (TlsSlot == -1 || Handle == -1) long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize;
{
return -1;
}
long Tpidr = MemoryRegions.TlsPagesAddress + TlsSlot * TlsSize;
Thread.ThreadState.Break += BreakHandler; Thread.ThreadState.Break += BreakHandler;
Thread.ThreadState.SvcCall += SvcHandler.SvcCall; Thread.ThreadState.SvcCall += SvcHandler.SvcCall;
Thread.ThreadState.Undefined += UndefinedHandler; Thread.ThreadState.Undefined += UndefinedHandler;
Thread.ThreadState.ProcessId = ProcessId; Thread.ThreadState.ProcessId = ProcessId;
Thread.ThreadState.ThreadId = Ns.Os.IdGen.GenerateId(); Thread.ThreadState.ThreadId = ThreadId;
Thread.ThreadState.CntfrqEl0 = TickFreq;
Thread.ThreadState.Tpidr = Tpidr; Thread.ThreadState.Tpidr = Tpidr;
Thread.ThreadState.X0 = (ulong)ArgsPtr; Thread.ThreadState.X0 = (ulong)ArgsPtr;
Thread.ThreadState.X1 = (ulong)Handle; Thread.ThreadState.X1 = (ulong)Handle;
@ -224,7 +231,7 @@ namespace Ryujinx.Core.OsHle
foreach (Executable Exe in Executables) foreach (Executable Exe in Executables)
{ {
foreach (KeyValuePair<long, string> KV in Exe.SymbolTable) foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
{ {
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value); SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
} }
} }
@ -274,16 +281,28 @@ namespace Ryujinx.Core.OsHle
} }
} }
return -1; throw new InvalidOperationException();
} }
private void ThreadFinished(object sender, EventArgs e) private void ThreadFinished(object sender, EventArgs e)
{ {
if (sender is AThread Thread) if (sender is AThread Thread)
{ {
TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _); Logging.Info($"Thread {Thread.ThreadId} exiting...");
Ns.Os.IdGen.DeleteId(Thread.ThreadId); TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
}
if (TlsSlots.Count == 0)
{
if (ShouldDispose)
{
Dispose();
}
Logging.Info($"No threads running, now exiting Process {ProcessId}...");
Ns.Os.ExitProcess(ProcessId);
} }
} }
@ -309,9 +328,30 @@ namespace Ryujinx.Core.OsHle
protected virtual void Dispose(bool Disposing) protected virtual void Dispose(bool Disposing)
{ {
if (Disposing) if (Disposing && !Disposed)
{ {
//If there is still some thread running, disposing the objects is not
//safe as the thread may try to access those resources. Instead, we set
//the flag to have the Process disposed when all threads finishes.
//Note: This may not happen if the guest code gets stuck on a infinite loop.
if (TlsSlots.Count > 0)
{
ShouldDispose = true;
Logging.Info($"Process {ProcessId} waiting all threads terminate...");
return;
}
Disposed = true;
Services.Dispose();
HandleTable.Dispose();
Scheduler.Dispose(); Scheduler.Dispose();
SvcHandler.Dispose();
Memory.Dispose();
Logging.Info($"Process {ProcessId} exiting...");
} }
} }
} }

View file

@ -8,6 +8,7 @@ namespace Ryujinx.Core.OsHle
class ServiceCtx class ServiceCtx
{ {
public Switch Ns { get; private set; } public Switch Ns { get; private set; }
public Process Process { get; private set; }
public AMemory Memory { get; private set; } public AMemory Memory { get; private set; }
public HSession Session { get; private set; } public HSession Session { get; private set; }
public IpcMessage Request { get; private set; } public IpcMessage Request { get; private set; }
@ -17,6 +18,7 @@ namespace Ryujinx.Core.OsHle
public ServiceCtx( public ServiceCtx(
Switch Ns, Switch Ns,
Process Process,
AMemory Memory, AMemory Memory,
HSession Session, HSession Session,
IpcMessage Request, IpcMessage Request,
@ -25,6 +27,7 @@ namespace Ryujinx.Core.OsHle
BinaryWriter ResponseData) BinaryWriter ResponseData)
{ {
this.Ns = Ns; this.Ns = Ns;
this.Process = Process;
this.Memory = Memory; this.Memory = Memory;
this.Session = Session; this.Session = Session;
this.Request = Request; this.Request = Request;

View file

@ -0,0 +1,110 @@
using Ryujinx.Core.OsHle.IpcServices;
using Ryujinx.Core.OsHle.IpcServices.Acc;
using Ryujinx.Core.OsHle.IpcServices.Am;
using Ryujinx.Core.OsHle.IpcServices.Apm;
using Ryujinx.Core.OsHle.IpcServices.Aud;
using Ryujinx.Core.OsHle.IpcServices.Bsd;
using Ryujinx.Core.OsHle.IpcServices.Friend;
using Ryujinx.Core.OsHle.IpcServices.FspSrv;
using Ryujinx.Core.OsHle.IpcServices.Hid;
using Ryujinx.Core.OsHle.IpcServices.Lm;
using Ryujinx.Core.OsHle.IpcServices.Nifm;
using Ryujinx.Core.OsHle.IpcServices.Ns;
using Ryujinx.Core.OsHle.IpcServices.NvServices;
using Ryujinx.Core.OsHle.IpcServices.Pctl;
using Ryujinx.Core.OsHle.IpcServices.Pl;
using Ryujinx.Core.OsHle.IpcServices.Set;
using Ryujinx.Core.OsHle.IpcServices.Sfdnsres;
using Ryujinx.Core.OsHle.IpcServices.Sm;
using Ryujinx.Core.OsHle.IpcServices.Ssl;
using Ryujinx.Core.OsHle.IpcServices.Time;
using Ryujinx.Core.OsHle.IpcServices.Vi;
using System;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle
{
class ServiceMgr : IDisposable
{
private Dictionary<string, IIpcService> Services;
public ServiceMgr()
{
Services = new Dictionary<string, IIpcService>();
}
public IIpcService GetService(string Name)
{
lock (Services)
{
if (!Services.TryGetValue(Name, out IIpcService Service))
{
switch (Name)
{
case "acc:u0": Service = new ServiceAcc(); break;
case "aoc:u": Service = new ServiceNs(); break;
case "apm": Service = new ServiceApm(); break;
case "apm:p": Service = new ServiceApm(); break;
case "appletOE": Service = new ServiceAppletOE(); break;
case "audout:u": Service = new ServiceAudOut(); break;
case "audren:u": Service = new ServiceAudRen(); break;
case "bsd:s": Service = new ServiceBsd(); break;
case "bsd:u": Service = new ServiceBsd(); break;
case "friend:a": Service = new ServiceFriend(); break;
case "fsp-srv": Service = new ServiceFspSrv(); break;
case "hid": Service = new ServiceHid(); break;
case "lm": Service = new ServiceLm(); break;
case "nifm:u": Service = new ServiceNifm(); break;
case "nvdrv": Service = new ServiceNvDrv(); break;
case "nvdrv:a": Service = new ServiceNvDrv(); break;
case "pctl:a": Service = new ServicePctl(); break;
case "pl:u": Service = new ServicePl(); break;
case "set": Service = new ServiceSet(); break;
case "set:sys": Service = new ServiceSetSys(); break;
case "sfdnsres": Service = new ServiceSfdnsres(); break;
case "sm:": Service = new ServiceSm(); break;
case "ssl": Service = new ServiceSsl(); break;
case "time:s": Service = new ServiceTime(); break;
case "time:u": Service = new ServiceTime(); break;
case "vi:m": Service = new ServiceVi(); break;
case "vi:s": Service = new ServiceVi(); break;
case "vi:u": Service = new ServiceVi(); break;
}
if (Service == null)
{
throw new NotImplementedException(Name);
}
Services.Add(Name, Service);
}
return Service;
}
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
lock (Services)
{
foreach (IIpcService Service in Services.Values)
{
if (Service is IDisposable DisposableSrv)
{
DisposableSrv.Dispose();
}
}
Services.Clear();
}
}
}
}
}

View file

@ -0,0 +1,63 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
using System.Text;
namespace Ryujinx.Core.OsHle.IpcServices.Aud
{
class IAudioDevice : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IAudioDevice()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, ListAudioDeviceName },
{ 1, SetAudioDeviceOutputVolume },
};
}
public long ListAudioDeviceName(ServiceCtx Context)
{
string[] Names = new string[] { "FIXME" };
Context.ResponseData.Write(Names.Length);
long Position = Context.Request.ReceiveBuff[0].Position;
long Size = Context.Request.ReceiveBuff[0].Size;
long BasePosition = Position;
foreach (string Name in Names)
{
byte[] Buffer = Encoding.ASCII.GetBytes(Name + '\0');
if ((Position - BasePosition) + Buffer.Length > Size)
{
break;
}
AMemoryHelper.WriteBytes(Context.Memory, Position, Buffer);
Position += Buffer.Length;
}
return 0;
}
public long SetAudioDeviceOutputVolume(ServiceCtx Context)
{
float Volume = Context.RequestData.ReadSingle();
long Position = Context.Request.SendBuff[0].Position;
long Size = Context.Request.SendBuff[0].Size;
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position, (int)Size);
return 0;
}
}
}

View file

@ -139,7 +139,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
public long RegisterBufferEvent(ServiceCtx Context) public long RegisterBufferEvent(ServiceCtx Context)
{ {
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent()); int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);

View file

@ -56,7 +56,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
public long QuerySystemEvent(ServiceCtx Context) public long QuerySystemEvent(ServiceCtx Context)
{ {
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent()); int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);

View file

@ -17,6 +17,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
{ {
{ 0, OpenAudioRenderer }, { 0, OpenAudioRenderer },
{ 1, GetAudioRendererWorkBufferSize }, { 1, GetAudioRendererWorkBufferSize },
{ 2, GetAudioDevice }
}; };
} }
@ -47,5 +48,14 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
return 0; return 0;
} }
public long GetAudioDevice(ServiceCtx Context)
{
long UserId = Context.RequestData.ReadInt64();
MakeObject(Context, new IAudioDevice());
return 0;
}
} }
} }

View file

@ -1,14 +1,64 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.Utilities;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace Ryujinx.Core.OsHle.IpcServices.Bsd namespace Ryujinx.Core.OsHle.IpcServices.Bsd
{ {
//bsd_errno == (SocketException.ErrorCode - 10000)
//https://github.com/freebsd/freebsd/blob/master/sys/sys/errno.h
public enum BsdError
{
ENOTSOCK = 38, /* Socket operation on non-socket */
EDESTADDRREQ = 39, /* Destination address required */
EMSGSIZE = 40, /* Message too long */
EPROTOTYPE = 41, /* Protocol wrong type for socket */
ENOPROTOOPT = 42, /* Protocol not available */
EPROTONOSUPPORT = 43, /* Protocol not supported */
ESOCKTNOSUPPORT = 44, /* Socket type not supported */
EOPNOTSUPP = 45, /* Operation not supported */
EPFNOSUPPORT = 46, /* Protocol family not supported */
EAFNOSUPPORT = 47, /* Address family not supported by protocol family */
EADDRINUSE = 48, /* Address already in use */
EADDRNOTAVAIL = 49, /* Can't assign requested address */
ENETDOWN = 50, /* Network is down */
ENETUNREACH = 51, /* Network is unreachable */
ENETRESET = 52, /* Network dropped connection on reset */
ECONNABORTED = 53, /* Software caused connection abort */
ECONNRESET = 54, /* Connection reset by peer */
ENOBUFS = 55, /* No buffer space available */
EISCONN = 56, /* Socket is already connected */
ENOTCONN = 57, /* Socket is not connected */
ESHUTDOWN = 58, /* Can't send after socket shutdown */
ETOOMANYREFS = 59, /* Too many references: can't splice */
ETIMEDOUT = 60, /* Operation timed out */
ECONNREFUSED = 61 /* Connection refused */
}
class SocketBsd
{
public int Family;
public int Type;
public int Protocol;
public IPAddress IpAddress;
public IPEndPoint RemoteEP;
public Socket Handle;
}
class ServiceBsd : IIpcService class ServiceBsd : IIpcService
{ {
private Dictionary<int, ServiceProcessRequest> m_Commands; private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private List<SocketBsd> Sockets = new List<SocketBsd>();
public ServiceBsd() public ServiceBsd()
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
@ -16,26 +66,31 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
{ 0, Initialize }, { 0, Initialize },
{ 1, StartMonitoring }, { 1, StartMonitoring },
{ 2, Socket }, { 2, Socket },
{ 6, Poll },
{ 8, Recv },
{ 10, Send }, { 10, Send },
{ 14, Connect } { 11, SendTo },
{ 12, Accept },
{ 13, Bind },
{ 14, Connect },
{ 18, Listen },
{ 21, SetSockOpt },
{ 26, Close }
}; };
} }
//Initialize(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno //(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno
public long Initialize(ServiceCtx Context) public long Initialize(ServiceCtx Context)
{ {
/* /*
typedef struct { typedef struct {
u32 version; // Observed 1 on 2.0 LibAppletWeb, 2 on 3.0. 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_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_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_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 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_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 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). u32 sb_efficiency; // Number of buffers for each socket (standard values range from 1 to 8).
} BsdBufferConfig; } BsdBufferConfig;
*/ */
@ -52,7 +107,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
return 0; return 0;
} }
//StartMonitoring(u64, pid) //(u64, pid)
public long StartMonitoring(ServiceCtx Context) public long StartMonitoring(ServiceCtx Context)
{ {
//Todo: Stub //Todo: Stub
@ -60,37 +115,378 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
return 0; return 0;
} }
//Socket(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno) //(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno)
public long Socket(ServiceCtx Context) public long Socket(ServiceCtx Context)
{ {
Context.ResponseData.Write(0); SocketBsd NewBSDSocket = new SocketBsd
Context.ResponseData.Write(0); {
Family = Context.RequestData.ReadInt32(),
Type = Context.RequestData.ReadInt32(),
Protocol = Context.RequestData.ReadInt32()
};
//Todo: Stub Sockets.Add(NewBSDSocket);
Sockets[Sockets.Count - 1].Handle = new Socket((AddressFamily)Sockets[Sockets.Count - 1].Family,
(SocketType)Sockets[Sockets.Count - 1].Type,
(ProtocolType)Sockets[Sockets.Count - 1].Protocol);
Context.ResponseData.Write(Sockets.Count - 1);
Context.ResponseData.Write(0);
return 0; return 0;
} }
//Connect(u32 socket, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno) //(u32, u32, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>)
public long Connect(ServiceCtx Context) public long Poll(ServiceCtx Context)
{ {
Context.ResponseData.Write(0); int PollCount = Context.RequestData.ReadInt32();
Context.ResponseData.Write(0); int TimeOut = Context.RequestData.ReadInt32();
//Todo: Stub //https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h
//https://msdn.microsoft.com/fr-fr/library/system.net.sockets.socket.poll(v=vs.110).aspx
//https://github.com/switchbrew/libnx/blob/e0457c4534b3c37426d83e1a620f82cb28c3b528/nx/source/services/bsd.c#L343
//https://github.com/TuxSH/ftpd/blob/switch_pr/source/ftp.c#L1634
//https://linux.die.net/man/2/poll
byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
(int)Context.Request.SendBuff[0].Size);
int SocketId = Get32(SentBuffer, 0);
short RequestedEvents = (short)Get16(SentBuffer, 4);
short ReturnedEvents = (short)Get16(SentBuffer, 6);
//Todo: Stub - Need to implemented the Type-22 buffer.
Context.ResponseData.Write(1);
Context.ResponseData.Write(0);
return 0; return 0;
} }
//Send(u32 socket, u32 flags, buffer<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno) //(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, buffer<i8, 0x22, 0> message)
public long Recv(ServiceCtx Context)
{
try
{
int SocketId = Context.RequestData.ReadInt32();
int SocketFlags = Context.RequestData.ReadInt32();
byte[] ReceivedBuffer = new byte[Context.Request.ReceiveBuff[0].Size];
int ReadedBytes = Sockets[SocketId].Handle.Receive(ReceivedBuffer);
//Logging.Debug("Received Buffer:" + Environment.NewLine + Logging.HexDump(ReceivedBuffer));
AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, ReceivedBuffer);
Context.ResponseData.Write(ReadedBytes);
Context.ResponseData.Write(0);
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
return 0;
}
//(u32 socket, u32 flags, buffer<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
public long Send(ServiceCtx Context) public long Send(ServiceCtx Context)
{ {
Context.ResponseData.Write(0); int SocketId = Context.RequestData.ReadInt32();
Context.ResponseData.Write(0); int SocketFlags = Context.RequestData.ReadInt32();
byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
(int)Context.Request.SendBuff[0].Size);
//Todo: Stub try
{
//Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer));
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
Context.ResponseData.Write(BytesSent);
Context.ResponseData.Write(0);
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
return 0; return 0;
} }
//(u32 socket, u32 flags, buffer<i8, 0x21, 0>, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
public long SendTo(ServiceCtx Context)
{
int SocketId = Context.RequestData.ReadInt32();
int SocketFlags = Context.RequestData.ReadInt32();
byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
(int)Context.Request.SendBuff[0].Size);
byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[1].Position,
(int)Context.Request.SendBuff[1].Size);
if (!Sockets[SocketId].Handle.Connected)
{
try
{
ParseAddrBuffer(SocketId, AddressBuffer);
Sockets[SocketId].Handle.Connect(Sockets[SocketId].RemoteEP);
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
}
try
{
//Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer));
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
Context.ResponseData.Write(BytesSent);
Context.ResponseData.Write(0);
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
return 0;
}
//(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<sockaddr, 0x22, 0> addr)
public long Accept(ServiceCtx Context)
{
int SocketId = Context.RequestData.ReadInt32();
long AddrBufferPtr = Context.Request.ReceiveBuff[0].Position;
Socket HandleAccept = null;
var TimeOut = Task.Factory.StartNew(() =>
{
try
{
HandleAccept = Sockets[SocketId].Handle.Accept();
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
});
TimeOut.Wait(10000);
if (HandleAccept != null)
{
SocketBsd NewBSDSocket = new SocketBsd
{
IpAddress = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint).Address,
RemoteEP = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint),
Handle = HandleAccept
};
Sockets.Add(NewBSDSocket);
using (MemoryStream MS = new MemoryStream())
{
BinaryWriter Writer = new BinaryWriter(MS);
Writer.Write((byte)0);
Writer.Write((byte)Sockets[Sockets.Count - 1].Handle.AddressFamily);
Writer.Write((Int16)((IPEndPoint)Sockets[Sockets.Count - 1].Handle.LocalEndPoint).Port);
string[] IpAdress = Sockets[Sockets.Count - 1].IpAddress.ToString().Split('.');
Writer.Write(byte.Parse(IpAdress[0]));
Writer.Write(byte.Parse(IpAdress[1]));
Writer.Write(byte.Parse(IpAdress[2]));
Writer.Write(byte.Parse(IpAdress[3]));
AMemoryHelper.WriteBytes(Context.Memory, AddrBufferPtr, MS.ToArray());
Context.ResponseData.Write(Sockets.Count - 1);
Context.ResponseData.Write(0);
Context.ResponseData.Write(MS.Length);
}
}
else
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write((int)BsdError.ETIMEDOUT);
}
return 0;
}
//(u32 socket, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
public long Bind(ServiceCtx Context)
{
int SocketId = Context.RequestData.ReadInt32();
byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
(int)Context.Request.SendBuff[0].Size);
try
{
ParseAddrBuffer(SocketId, AddressBuffer);
Context.ResponseData.Write(0);
Context.ResponseData.Write(0);
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
return 0;
}
//(u32 socket, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
public long Connect(ServiceCtx Context)
{
int SocketId = Context.RequestData.ReadInt32();
byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
(int)Context.Request.SendBuff[0].Size);
try
{
ParseAddrBuffer(SocketId, AddressBuffer);
Sockets[SocketId].Handle.Connect(Sockets[SocketId].RemoteEP);
Context.ResponseData.Write(0);
Context.ResponseData.Write(0);
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
return 0;
}
//(u32 socket, u32 backlog) -> (i32 ret, u32 bsd_errno)
public long Listen(ServiceCtx Context)
{
int SocketId = Context.RequestData.ReadInt32();
int BackLog = Context.RequestData.ReadInt32();
try
{
Sockets[SocketId].Handle.Bind(Sockets[SocketId].RemoteEP);
Sockets[SocketId].Handle.Listen(BackLog);
Context.ResponseData.Write(0);
Context.ResponseData.Write(0);
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
return 0;
}
//(u32 socket, u32 level, u32 option_name, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
public long SetSockOpt(ServiceCtx Context)
{
int SocketId = Context.RequestData.ReadInt32();
int SocketLevel = Context.RequestData.ReadInt32();
int SocketOptionName = Context.RequestData.ReadInt32();
byte[] SocketOptionValue = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.PtrBuff[0].Position,
Context.Request.PtrBuff[0].Size);
try
{
Sockets[SocketId].Handle.SetSocketOption((SocketOptionLevel)SocketLevel,
(SocketOptionName)SocketOptionName,
Get32(SocketOptionValue, 0));
Context.ResponseData.Write(0);
Context.ResponseData.Write(0);
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
return 0;
}
//(u32 socket) -> (i32 ret, u32 bsd_errno)
public long Close(ServiceCtx Context)
{
int SocketId = Context.RequestData.ReadInt32();
try
{
Sockets[SocketId].Handle.Close();
Sockets[SocketId] = null;
Context.ResponseData.Write(0);
Context.ResponseData.Write(0);
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
return 0;
}
public void ParseAddrBuffer(int SocketId, byte[] AddrBuffer)
{
using (MemoryStream MS = new MemoryStream(AddrBuffer))
{
BinaryReader Reader = new BinaryReader(MS);
int Size = Reader.ReadByte();
int Family = Reader.ReadByte();
int Port = EndianSwap.Swap16(Reader.ReadInt16());
string IpAddress = Reader.ReadByte().ToString() +
"." + Reader.ReadByte().ToString() +
"." + Reader.ReadByte().ToString() +
"." + Reader.ReadByte().ToString();
Logging.Debug($"Try to connect to {IpAddress}:{Port}");
Sockets[SocketId].IpAddress = IPAddress.Parse(IpAddress);
Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port);
}
}
private int Get16(byte[] Data, int Address)
{
return
Data[Address + 0] << 0 |
Data[Address + 1] << 8;
}
private int Get32(byte[] Data, int Address)
{
return
Data[Address + 0] << 0 |
Data[Address + 1] << 8 |
Data[Address + 2] << 16 |
Data[Address + 3] << 24;
}
} }
} }

View file

@ -1,10 +0,0 @@
namespace Ryujinx.Core.OsHle.IpcServices
{
static class ErrorCode
{
public static long MakeError(ErrorModule Module, int Code)
{
return (int)Module | (Code << 9);
}
}
}

View file

@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using static Ryujinx.Core.OsHle.IpcServices.ErrorCode; using static Ryujinx.Core.OsHle.ErrorCode;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.FspSrv namespace Ryujinx.Core.OsHle.IpcServices.FspSrv

View file

@ -10,21 +10,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private HSharedMem Handle; private HSharedMem HidSharedMem;
public IAppletResource(HSharedMem Handle) public IAppletResource(HSharedMem HidSharedMem)
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
{ {
{ 0, GetSharedMemoryHandle } { 0, GetSharedMemoryHandle }
}; };
this.Handle = Handle; this.HidSharedMem = HidSharedMem;
} }
public static long GetSharedMemoryHandle(ServiceCtx Context) public long GetSharedMemoryHandle(ServiceCtx Context)
{ {
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.HidHandle); int Handle = Context.Process.HandleTable.OpenHandle(HidSharedMem);
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
return 0; return 0;
} }

View file

@ -1,6 +1,6 @@
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic; using System.Collections.Generic;
using Ryujinx.Core.Input;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper; using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
@ -16,25 +16,27 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
{ {
{ 0, CreateAppletResource }, { 0, CreateAppletResource },
{ 11, ActivateTouchScreen }, { 11, ActivateTouchScreen },
{ 100, SetSupportedNpadStyleSet }, { 66, StartSixAxisSensor },
{ 101, GetSupportedNpadStyleSet }, { 100, SetSupportedNpadStyleSet },
{ 102, SetSupportedNpadIdType }, { 101, GetSupportedNpadStyleSet },
{ 103, ActivateNpad }, { 102, SetSupportedNpadIdType },
{ 120, SetNpadJoyHoldType }, { 103, ActivateNpad },
{ 121, GetNpadJoyHoldType }, { 120, SetNpadJoyHoldType },
{ 200, GetVibrationDeviceInfo }, { 122, SetNpadJoyAssignmentModeSingleByDefault },
{ 203, CreateActiveVibrationDeviceList }, { 123, SetNpadJoyAssignmentModeSingle },
{ 206, SendVibrationValues } { 124, SetNpadJoyAssignmentModeDual },
{ 125, MergeSingleJoyAsDualJoy },
{ 200, GetVibrationDeviceInfo },
{ 203, CreateActiveVibrationDeviceList },
{ 206, SendVibrationValues }
}; };
} }
public long CreateAppletResource(ServiceCtx Context) public long CreateAppletResource(ServiceCtx Context)
{ {
HSharedMem HidHndData = Context.Ns.Os.Handles.GetData<HSharedMem>(Context.Ns.Os.HidHandle); MakeObject(Context, new IAppletResource(Context.Ns.Os.HidSharedMem));
MakeObject(Context, new IAppletResource(HidHndData));
return 0; return 0;
} }
@ -46,6 +48,15 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
return 0; return 0;
} }
public long StartSixAxisSensor(ServiceCtx Context)
{
int Handle = Context.RequestData.ReadInt32();
long AppletResourceUserId = Context.RequestData.ReadInt64();
return 0;
}
public long GetSupportedNpadStyleSet(ServiceCtx Context) public long GetSupportedNpadStyleSet(ServiceCtx Context)
{ {
Context.ResponseData.Write(0); Context.ResponseData.Write(0);
@ -90,6 +101,40 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
return 0; return 0;
} }
public long SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx Context)
{
HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32();
long AppletUserResourseId = Context.RequestData.ReadInt64();
return 0;
}
public long SetNpadJoyAssignmentModeSingle(ServiceCtx Context)
{
HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32();
long AppletUserResourseId = Context.RequestData.ReadInt64();
long NpadJoyDeviceType = Context.RequestData.ReadInt64();
return 0;
}
public long SetNpadJoyAssignmentModeDual(ServiceCtx Context)
{
HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32();
long AppletUserResourseId = Context.RequestData.ReadInt64();
return 0;
}
public long MergeSingleJoyAsDualJoy(ServiceCtx Context)
{
long Unknown0 = Context.RequestData.ReadInt32();
long Unknown8 = Context.RequestData.ReadInt32();
long AppletUserResourseId = Context.RequestData.ReadInt64();
return 0;
}
public long GetVibrationDeviceInfo(ServiceCtx Context) public long GetVibrationDeviceInfo(ServiceCtx Context)
{ {
int VibrationDeviceHandle = Context.RequestData.ReadInt32(); int VibrationDeviceHandle = Context.RequestData.ReadInt32();

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Core.OsHle.IpcServices.NvServices
{
class NvFd
{
public string Name { get; private set; }
public NvFd(string Name)
{
this.Name = Name;
}
}
}

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Core.OsHle.IpcServices.NvServices
{
class NvMap
{
public int Handle;
public int Id;
public int Size;
public int Align;
public int Kind;
public long Address;
}
}

View file

@ -1,5 +1,4 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.Utilities; using Ryujinx.Core.OsHle.Utilities;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
@ -12,40 +11,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
{ {
private delegate long ServiceProcessIoctl(ServiceCtx Context); private delegate long ServiceProcessIoctl(ServiceCtx Context);
private static Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds =
new Dictionary<(string, int), ServiceProcessIoctl>()
{
{ ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel },
{ ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace },
{ ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx },
{ ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions },
{ ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx },
{ ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig },
{ ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait },
{ ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize },
{ ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo },
{ ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics },
{ ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks },
{ ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask },
{ ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData },
{ ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap },
{ ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo },
{ ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx },
{ ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind },
{ ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier },
{ ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority },
{ ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 },
{ ("/dev/nvmap", 0x0101), NvMapIocCreate },
{ ("/dev/nvmap", 0x0103), NvMapIocFromId },
{ ("/dev/nvmap", 0x0104), NvMapIocAlloc },
{ ("/dev/nvmap", 0x0109), NvMapIocParam },
{ ("/dev/nvmap", 0x010e), NvMapIocGetId },
};
private Dictionary<int, ServiceProcessRequest> m_Commands; private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds;
private IdDictionary Fds;
private IdDictionary NvMaps;
private IdDictionary NvMapsById;
public ServiceNvDrv() public ServiceNvDrv()
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
@ -57,15 +33,50 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
{ 4, QueryEvent }, { 4, QueryEvent },
{ 8, SetClientPid }, { 8, SetClientPid },
}; };
IoctlCmds = new Dictionary<(string, int), ServiceProcessIoctl>()
{
{ ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel },
{ ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace },
{ ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx },
{ ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions },
{ ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx },
{ ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig },
{ ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait },
{ ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize },
{ ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo },
{ ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics },
{ ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks },
{ ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask },
{ ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData },
{ ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap },
{ ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo },
{ ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx },
{ ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind },
{ ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier },
{ ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority },
{ ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 },
{ ("/dev/nvmap", 0x0101), NvMapIocCreate },
{ ("/dev/nvmap", 0x0103), NvMapIocFromId },
{ ("/dev/nvmap", 0x0104), NvMapIocAlloc },
{ ("/dev/nvmap", 0x0105), NvMapIocFree },
{ ("/dev/nvmap", 0x0109), NvMapIocParam },
{ ("/dev/nvmap", 0x010e), NvMapIocGetId },
};
Fds = new IdDictionary();
NvMaps = new IdDictionary();
NvMapsById = new IdDictionary();
} }
public static long Open(ServiceCtx Context) public long Open(ServiceCtx Context)
{ {
long NamePtr = Context.Request.SendBuff[0].Position; long NamePtr = Context.Request.SendBuff[0].Position;
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr); string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr);
int Fd = Context.Ns.Os.Fds.GenerateId(new FileDesc(Name)); int Fd = Fds.Add(new NvFd(Name));
Context.ResponseData.Write(Fd); Context.ResponseData.Write(Fd);
Context.ResponseData.Write(0); Context.ResponseData.Write(0);
@ -73,14 +84,14 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
public static long Ioctl(ServiceCtx Context) public long Ioctl(ServiceCtx Context)
{ {
int Fd = Context.RequestData.ReadInt32(); int Fd = Context.RequestData.ReadInt32();
int Cmd = Context.RequestData.ReadInt32() & 0xffff; int Cmd = Context.RequestData.ReadInt32() & 0xffff;
FileDesc FdData = Context.Ns.Os.Fds.GetData<FileDesc>(Fd); NvFd FdData = Fds.GetData<NvFd>(Fd);
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
Context.ResponseData.Write(0); Context.ResponseData.Write(0);
@ -94,18 +105,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
} }
} }
public static long Close(ServiceCtx Context) public long Close(ServiceCtx Context)
{ {
int Fd = Context.RequestData.ReadInt32(); int Fd = Context.RequestData.ReadInt32();
Context.Ns.Os.Fds.Delete(Fd); Fds.Delete(Fd);
Context.ResponseData.Write(0); Context.ResponseData.Write(0);
return 0; return 0;
} }
public static long Initialize(ServiceCtx Context) public long Initialize(ServiceCtx Context)
{ {
long TransferMemSize = Context.RequestData.ReadInt64(); long TransferMemSize = Context.RequestData.ReadInt64();
int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0]; int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0];
@ -115,7 +126,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
public static long QueryEvent(ServiceCtx Context) public long QueryEvent(ServiceCtx Context)
{ {
int Fd = Context.RequestData.ReadInt32(); int Fd = Context.RequestData.ReadInt32();
int EventId = Context.RequestData.ReadInt32(); int EventId = Context.RequestData.ReadInt32();
@ -127,7 +138,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
public static long SetClientPid(ServiceCtx Context) public long SetClientPid(ServiceCtx Context)
{ {
long Pid = Context.RequestData.ReadInt64(); long Pid = Context.RequestData.ReadInt64();
@ -136,18 +147,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuAsIoctlBindChannel(ServiceCtx Context) private long NvGpuAsIoctlBindChannel(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
int Fd = Context.Memory.ReadInt32(Position); int Fd = Context.Memory.ReadInt32(Position);
return 0; return 0;
} }
private static long NvGpuAsIoctlAllocSpace(ServiceCtx Context) private long NvGpuAsIoctlAllocSpace(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
@ -171,9 +182,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuAsIoctlMapBufferEx(ServiceCtx Context) private long NvGpuAsIoctlMapBufferEx(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
@ -185,18 +196,29 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
long MapSize = Reader.ReadInt64(); long MapSize = Reader.ReadInt64();
long Offset = Reader.ReadInt64(); long Offset = Reader.ReadInt64();
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle); if (Handle == 0)
if (NvMap != null)
{ {
if ((Flags & 1) != 0) //Handle 0 is valid here, but it refers to something else.
{ //TODO: Figure out what, for now just return success.
Offset = Context.Ns.Gpu.MapMemory(NvMap.Address, Offset, NvMap.Size); return 0;
} }
else
{ NvMap Map = NvMaps.GetData<NvMap>(Handle);
Offset = Context.Ns.Gpu.MapMemory(NvMap.Address, NvMap.Size);
} if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
if ((Flags & 1) != 0)
{
Offset = Context.Ns.Gpu.MapMemory(Map.Address, Offset, Map.Size);
}
else
{
Offset = Context.Ns.Gpu.MapMemory(Map.Address, Map.Size);
} }
Context.Memory.WriteInt64(Position + 0x20, Offset); Context.Memory.WriteInt64(Position + 0x20, Offset);
@ -204,9 +226,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuAsIoctlGetVaRegions(ServiceCtx Context) private long NvGpuAsIoctlGetVaRegions(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position);
@ -234,9 +256,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuAsIoctlInitializeEx(ServiceCtx Context) private long NvGpuAsIoctlInitializeEx(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
@ -251,9 +273,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvHostIoctlCtrlGetConfig(ServiceCtx Context) private long NvHostIoctlCtrlGetConfig(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position + 0x82); MemWriter Writer = new MemWriter(Context.Memory, Position + 0x82);
@ -266,9 +288,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvHostIoctlCtrlEventWait(ServiceCtx Context) private long NvHostIoctlCtrlEventWait(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
@ -282,18 +304,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context) private long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
Context.Memory.WriteInt32(Position, 1); Context.Memory.WriteInt32(Position, 1);
return 0; return 0;
} }
private static long NvGpuIoctlZcullGetInfo(ServiceCtx Context) private long NvGpuIoctlZcullGetInfo(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemWriter Writer = new MemWriter(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position);
@ -311,9 +333,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuIoctlGetCharacteristics(ServiceCtx Context) private long NvGpuIoctlGetCharacteristics(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position); MemWriter Writer = new MemWriter(Context.Memory, Position);
@ -373,9 +395,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuIoctlGetTpcMasks(ServiceCtx Context) private long NvGpuIoctlGetTpcMasks(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
@ -387,9 +409,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context) private long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
Context.Memory.WriteInt32(Position + 0, 7); Context.Memory.WriteInt32(Position + 0, 7);
Context.Memory.WriteInt32(Position + 4, 1); Context.Memory.WriteInt32(Position + 4, 1);
@ -397,25 +419,25 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIoctlChannelSetUserData(ServiceCtx Context) private long NvMapIoctlChannelSetUserData(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
return 0; return 0;
} }
private static long NvMapIoctlChannelSetNvMap(ServiceCtx Context) private long NvMapIoctlChannelSetNvMap(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
int Fd = Context.Memory.ReadInt32(Position); int Fd = Context.Memory.ReadInt32(Position);
return 0; return 0;
} }
private static long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context) private long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position + 0x10); MemWriter Writer = new MemWriter(Context.Memory, Position + 0x10);
@ -452,9 +474,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context) private long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
int ClassNum = Context.Memory.ReadInt32(Position + 0); int ClassNum = Context.Memory.ReadInt32(Position + 0);
int Flags = Context.Memory.ReadInt32(Position + 4); int Flags = Context.Memory.ReadInt32(Position + 4);
@ -464,9 +486,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIoctlChannelZcullBind(ServiceCtx Context) private long NvMapIoctlChannelZcullBind(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
@ -477,9 +499,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context) private long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
@ -491,18 +513,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIoctlChannelSetPriority(ServiceCtx Context) private long NvMapIoctlChannelSetPriority(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
int Priority = Context.Memory.ReadInt32(Position); int Priority = Context.Memory.ReadInt32(Position);
return 0; return 0;
} }
private static long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context) private long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context)
{ {
long Position = Context.Request.PtrBuff[0].Position; long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position); MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position + 0xc); MemWriter Writer = new MemWriter(Context.Memory, Position + 0xc);
@ -520,47 +542,46 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIocCreate(ServiceCtx Context) private long NvMapIocCreate(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
int Size = Context.Memory.ReadInt32(Position); int Size = Context.Memory.ReadInt32(Position);
int Id = Context.Ns.Os.NvMapIds.GenerateId(); NvMap Map = new NvMap() { Size = Size };
int Handle = Context.Ns.Os.Handles.GenerateId(new HNvMap(Id, Size)); Map.Handle = NvMaps.Add(Map);
Context.Memory.WriteInt32(Position + 4, Handle); Map.Id = NvMapsById.Add(Map);
Logging.Info($"NvMap {Id} created with size {Size:x8}!"); Context.Memory.WriteInt32(Position + 4, Map.Handle);
Logging.Info($"NvMap {Map.Id} created with size {Size:x8}!");
return 0; return 0;
} }
private static long NvMapIocFromId(ServiceCtx Context) private long NvMapIocFromId(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
int Id = Context.Memory.ReadInt32(Position); int Id = Context.Memory.ReadInt32(Position);
int Handle = -1; NvMap Map = NvMapsById.GetData<NvMap>(Id);
foreach (KeyValuePair<int, object> KV in Context.Ns.Os.Handles) if (Map == null)
{ {
if (KV.Value is HNvMap NvMap && NvMap.Id == Id) Logging.Warn($"Trying to use invalid NvMap Id {Id}!");
{
Handle = KV.Key; return -1; //TODO: Corrent error code.
break;
}
} }
Context.Memory.WriteInt32(Position + 4, Handle); Context.Memory.WriteInt32(Position + 4, Map.Handle);
return 0; return 0;
} }
private static long NvMapIocAlloc(ServiceCtx Context) private long NvMapIocAlloc(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -573,19 +594,49 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
byte Kind = (byte)Reader.ReadInt64(); byte Kind = (byte)Reader.ReadInt64();
long Addr = Reader.ReadInt64(); long Addr = Reader.ReadInt64();
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle); NvMap Map = NvMaps.GetData<NvMap>(Handle);
if (NvMap != null) if (Map == null)
{ {
NvMap.Address = Addr; Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
NvMap.Align = Align;
NvMap.Kind = Kind; return -1; //TODO: Corrent error code.
} }
Map.Address = Addr;
Map.Align = Align;
Map.Kind = Kind;
return 0; return 0;
} }
private static long NvMapIocParam(ServiceCtx Context) private long NvMapIocFree(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
MemWriter Writer = new MemWriter(Context.Memory, Position + 8);
int Handle = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
NvMap Map = NvMaps.GetData<NvMap>(Handle);
if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
Writer.WriteInt64(0);
Writer.WriteInt32(Map.Size);
Writer.WriteInt32(0);
return 0;
}
private long NvMapIocParam(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
@ -594,16 +645,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
int Handle = Reader.ReadInt32(); int Handle = Reader.ReadInt32();
int Param = Reader.ReadInt32(); int Param = Reader.ReadInt32();
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle); NvMap Map = NvMaps.GetData<NvMap>(Handle);
if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
int Response = 0; int Response = 0;
switch (Param) switch (Param)
{ {
case 1: Response = NvMap.Size; break; case 1: Response = Map.Size; break;
case 2: Response = NvMap.Align; break; case 2: Response = Map.Align; break;
case 4: Response = 0x40000000; break; case 4: Response = 0x40000000; break;
case 5: Response = NvMap.Kind; break; case 5: Response = Map.Kind; break;
} }
Context.Memory.WriteInt32(Position + 8, Response); Context.Memory.WriteInt32(Position + 8, Response);
@ -611,17 +669,29 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
return 0; return 0;
} }
private static long NvMapIocGetId(ServiceCtx Context) private long NvMapIocGetId(ServiceCtx Context)
{ {
long Position = Context.Request.GetSendBuffPtr(); long Position = Context.Request.GetSendBuffPtr();
int Handle = Context.Memory.ReadInt32(Position + 4); int Handle = Context.Memory.ReadInt32(Position + 4);
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle); NvMap Map = NvMaps.GetData<NvMap>(Handle);
Context.Memory.WriteInt32(Position, NvMap.Id); if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
Context.Memory.WriteInt32(Position, Map.Id);
return 0; return 0;
} }
public NvMap GetNvMap(int Handle)
{
return NvMaps.GetData<NvMap>(Handle);
}
} }
} }

View file

@ -9,13 +9,13 @@ namespace Ryujinx.Core.OsHle.IpcServices
{ {
if (Context.Session is HDomain Dom) if (Context.Session is HDomain Dom)
{ {
Context.Response.ResponseObjIds.Add(Dom.GenerateObjectId(Obj)); Context.Response.ResponseObjIds.Add(Dom.Add(Obj));
} }
else else
{ {
HSessionObj HndData = new HSessionObj(Context.Session, Obj); HSessionObj HndData = new HSessionObj(Context.Session, Obj);
int VHandle = Context.Ns.Os.Handles.GenerateId(HndData); int VHandle = Context.Process.HandleTable.OpenHandle(HndData);
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(VHandle); Context.Response.HandleDesc = IpcHandleDesc.MakeMove(VHandle);
} }

View file

@ -13,6 +13,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Pl
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
{ {
{ 0, RequestLoad },
{ 1, GetLoadState }, { 1, GetLoadState },
{ 2, GetFontSize }, { 2, GetFontSize },
{ 3, GetSharedMemoryAddressOffset }, { 3, GetSharedMemoryAddressOffset },
@ -20,30 +21,39 @@ namespace Ryujinx.Core.OsHle.IpcServices.Pl
}; };
} }
public static long GetLoadState(ServiceCtx Context) public long RequestLoad(ServiceCtx Context)
{
SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
return 0;
}
public long GetLoadState(ServiceCtx Context)
{ {
Context.ResponseData.Write(1); //Loaded Context.ResponseData.Write(1); //Loaded
return 0; return 0;
} }
public static long GetFontSize(ServiceCtx Context) public long GetFontSize(ServiceCtx Context)
{ {
Context.ResponseData.Write(Horizon.FontSize); Context.ResponseData.Write(Horizon.FontSize);
return 0; return 0;
} }
public static long GetSharedMemoryAddressOffset(ServiceCtx Context) public long GetSharedMemoryAddressOffset(ServiceCtx Context)
{ {
Context.ResponseData.Write(0); Context.ResponseData.Write(0);
return 0; return 0;
} }
public static long GetSharedMemoryNativeHandle(ServiceCtx Context) public long GetSharedMemoryNativeHandle(ServiceCtx Context)
{ {
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.FontHandle); int Handle = Context.Process.HandleTable.OpenHandle(Context.Ns.Os.FontSharedMem);
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
return 0; return 0;
} }

View file

@ -0,0 +1,12 @@
namespace Ryujinx.Core.OsHle.IpcServices.Pl
{
enum SharedFontType
{
JapanUsEurope = 0,
SimplifiedChinese = 1,
SimplifiedChineseEx = 2,
TraditionalChinese = 3,
Korean = 4,
NintendoEx = 5
}
}

View file

@ -1,63 +0,0 @@
using Ryujinx.Core.OsHle.IpcServices.Acc;
using Ryujinx.Core.OsHle.IpcServices.Am;
using Ryujinx.Core.OsHle.IpcServices.Apm;
using Ryujinx.Core.OsHle.IpcServices.Aud;
using Ryujinx.Core.OsHle.IpcServices.Bsd;
using Ryujinx.Core.OsHle.IpcServices.Friend;
using Ryujinx.Core.OsHle.IpcServices.FspSrv;
using Ryujinx.Core.OsHle.IpcServices.Hid;
using Ryujinx.Core.OsHle.IpcServices.Lm;
using Ryujinx.Core.OsHle.IpcServices.Nifm;
using Ryujinx.Core.OsHle.IpcServices.Ns;
using Ryujinx.Core.OsHle.IpcServices.NvServices;
using Ryujinx.Core.OsHle.IpcServices.Pctl;
using Ryujinx.Core.OsHle.IpcServices.Pl;
using Ryujinx.Core.OsHle.IpcServices.Set;
using Ryujinx.Core.OsHle.IpcServices.Sfdnsres;
using Ryujinx.Core.OsHle.IpcServices.Sm;
using Ryujinx.Core.OsHle.IpcServices.Ssl;
using Ryujinx.Core.OsHle.IpcServices.Time;
using Ryujinx.Core.OsHle.IpcServices.Vi;
using System;
namespace Ryujinx.Core.OsHle.IpcServices
{
static class ServiceFactory
{
public static IIpcService MakeService(string Name)
{
switch (Name)
{
case "acc:u0": return new ServiceAcc();
case "aoc:u": return new ServiceNs();
case "apm": return new ServiceApm();
case "apm:p": return new ServiceApm();
case "appletOE": return new ServiceAppletOE();
case "audout:u": return new ServiceAudOut();
case "audren:u": return new ServiceAudRen();
case "bsd:u": return new ServiceBsd();
case "friend:a": return new ServiceFriend();
case "fsp-srv": return new ServiceFspSrv();
case "hid": return new ServiceHid();
case "lm": return new ServiceLm();
case "nifm:u": return new ServiceNifm();
case "nvdrv": return new ServiceNvDrv();
case "nvdrv:a": return new ServiceNvDrv();
case "pctl:a": return new ServicePctl();
case "pl:u": return new ServicePl();
case "set": return new ServiceSet();
case "set:sys": return new ServiceSetSys();
case "sfdnsres": return new ServiceSfdnsres();
case "sm:": return new ServiceSm();
case "ssl": return new ServiceSsl();
case "time:s": return new ServiceTime();
case "time:u": return new ServiceTime();
case "vi:m": return new ServiceVi();
case "vi:s": return new ServiceVi();
case "vi:u": return new ServiceVi();
}
throw new NotImplementedException(Name);
}
}
}

View file

@ -55,9 +55,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Sm
return 0; return 0;
} }
HSession Session = new HSession(ServiceFactory.MakeService(Name)); HSession Session = new HSession(Context.Process.Services.GetService(Name));
int Handle = Context.Ns.Os.Handles.GenerateId(Session); int Handle = Context.Process.HandleTable.OpenHandle(Session);
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Core.OsHle namespace Ryujinx.Core.OsHle.IpcServices.Vi
{ {
class Display class Display
{ {

View file

@ -15,6 +15,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private IdDictionary Displays;
public IApplicationDisplayService() public IApplicationDisplayService()
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
@ -24,16 +26,21 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
{ 102, GetManagerDisplayService }, { 102, GetManagerDisplayService },
{ 103, GetIndirectDisplayTransactionService }, { 103, GetIndirectDisplayTransactionService },
{ 1010, OpenDisplay }, { 1010, OpenDisplay },
{ 1020, CloseDisplay },
{ 2020, OpenLayer }, { 2020, OpenLayer },
{ 2021, CloseLayer },
{ 2030, CreateStrayLayer }, { 2030, CreateStrayLayer },
{ 2031, DestroyStrayLayer },
{ 2101, SetLayerScalingMode }, { 2101, SetLayerScalingMode },
{ 5202, GetDisplayVSyncEvent } { 5202, GetDisplayVSyncEvent }
}; };
Displays = new IdDictionary();
} }
public long GetRelayService(ServiceCtx Context) public long GetRelayService(ServiceCtx Context)
{ {
MakeObject(Context, new IHOSBinderDriver()); MakeObject(Context, new IHOSBinderDriver(Context.Ns.Gpu.Renderer));
return 0; return 0;
} }
@ -54,7 +61,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
public long GetIndirectDisplayTransactionService(ServiceCtx Context) public long GetIndirectDisplayTransactionService(ServiceCtx Context)
{ {
MakeObject(Context, new IHOSBinderDriver()); MakeObject(Context, new IHOSBinderDriver(Context.Ns.Gpu.Renderer));
return 0; return 0;
} }
@ -63,13 +70,22 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
{ {
string Name = GetDisplayName(Context); string Name = GetDisplayName(Context);
long DisplayId = Context.Ns.Os.Displays.GenerateId(new Display(Name)); long DisplayId = Displays.Add(new Display(Name));
Context.ResponseData.Write(DisplayId); Context.ResponseData.Write(DisplayId);
return 0; return 0;
} }
public long CloseDisplay(ServiceCtx Context)
{
int DisplayId = Context.RequestData.ReadInt32();
Displays.Delete(DisplayId);
return 0;
}
public long OpenLayer(ServiceCtx Context) public long OpenLayer(ServiceCtx Context)
{ {
long LayerId = Context.RequestData.ReadInt64(); long LayerId = Context.RequestData.ReadInt64();
@ -86,6 +102,13 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
return 0; return 0;
} }
public long CloseLayer(ServiceCtx Context)
{
long LayerId = Context.RequestData.ReadInt64();
return 0;
}
public long CreateStrayLayer(ServiceCtx Context) public long CreateStrayLayer(ServiceCtx Context)
{ {
long LayerFlags = Context.RequestData.ReadInt64(); long LayerFlags = Context.RequestData.ReadInt64();
@ -93,7 +116,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
long ParcelPtr = Context.Request.ReceiveBuff[0].Position; long ParcelPtr = Context.Request.ReceiveBuff[0].Position;
Display Disp = Context.Ns.Os.Displays.GetData<Display>((int)DisplayId); Display Disp = Displays.GetData<Display>((int)DisplayId);
byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr); byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr);
@ -105,6 +128,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
return 0; return 0;
} }
public long DestroyStrayLayer(ServiceCtx Context)
{
return 0;
}
public long SetLayerScalingMode(ServiceCtx Context) public long SetLayerScalingMode(ServiceCtx Context)
{ {
int ScalingMode = Context.RequestData.ReadInt32(); int ScalingMode = Context.RequestData.ReadInt32();
@ -117,7 +145,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
{ {
string Name = GetDisplayName(Context); string Name = GetDisplayName(Context);
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent()); int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);

View file

@ -1,6 +1,7 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.IpcServices.Android; using Ryujinx.Core.OsHle.IpcServices.Android;
using Ryujinx.Graphics.Gal;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -14,7 +15,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
private NvFlinger Flinger; private NvFlinger Flinger;
public IHOSBinderDriver() public IHOSBinderDriver(IGalRenderer Renderer)
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
{ {
@ -23,7 +24,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
{ 2, GetNativeHandle } { 2, GetNativeHandle }
}; };
Flinger = new NvFlinger(); Flinger = new NvFlinger(Renderer);
} }
public long TransactParcel(ServiceCtx Context) public long TransactParcel(ServiceCtx Context)

View file

@ -13,8 +13,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
{ {
m_Commands = new Dictionary<int, ServiceProcessRequest>() m_Commands = new Dictionary<int, ServiceProcessRequest>()
{ {
{ 2010, CreateManagedLayer }, { 2010, CreateManagedLayer },
{ 6000, AddToLayerStack } { 2011, DestroyManagedLayer },
{ 6000, AddToLayerStack }
}; };
} }
@ -25,6 +26,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
return 0; return 0;
} }
public long DestroyManagedLayer(ServiceCtx Context)
{
return 0;
}
public static long AddToLayerStack(ServiceCtx Context) public static long AddToLayerStack(ServiceCtx Context)
{ {
return 0; return 0;

View file

@ -1,5 +1,6 @@
using ChocolArm64.Memory; using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.IpcServices.NvServices;
using Ryujinx.Graphics.Gal;
using System; using System;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
@ -54,29 +55,43 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
public GbpBuffer Data; public GbpBuffer Data;
} }
private IGalRenderer Renderer;
private BufferEntry[] BufferQueue; private BufferEntry[] BufferQueue;
private ManualResetEvent WaitBufferFree; private ManualResetEvent WaitBufferFree;
private object RenderQueueLock;
private int RenderQueueCount;
private bool NvFlingerDisposed;
private bool KeepRunning; private bool KeepRunning;
public NvFlinger() public NvFlinger(IGalRenderer Renderer)
{ {
Commands = new Dictionary<(string, int), ServiceProcessParcel>() Commands = new Dictionary<(string, int), ServiceProcessParcel>()
{ {
{ ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer }, { ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer }, { ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x4), GbpDetachBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer }, { ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer }, { ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer },
{ ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery }, { ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery },
{ ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect }, { ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect },
{ ("android.gui.IGraphicBufferProducer", 0xb), GbpDisconnect },
{ ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer } { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
}; };
this.Renderer = Renderer;
BufferQueue = new BufferEntry[0x40]; BufferQueue = new BufferEntry[0x40];
WaitBufferFree = new ManualResetEvent(false); WaitBufferFree = new ManualResetEvent(false);
RenderQueueLock = new object();
KeepRunning = true; KeepRunning = true;
} }
@ -154,6 +169,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader) private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{ {
Context.Ns.Statistics.RecordGameFrameTime();
//TODO: Errors. //TODO: Errors.
int Slot = ParcelReader.ReadInt32(); int Slot = ParcelReader.ReadInt32();
int Unknown4 = ParcelReader.ReadInt32(); int Unknown4 = ParcelReader.ReadInt32();
@ -190,6 +207,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
} }
private long GbpDetachBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{
return MakeReplyParcel(Context, 0);
}
private long GbpCancelBuffer(ServiceCtx Context, BinaryReader ParcelReader) private long GbpCancelBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{ {
//TODO: Errors. //TODO: Errors.
@ -210,6 +232,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
} }
private long GbpDisconnect(ServiceCtx Context, BinaryReader ParcelReader)
{
return MakeReplyParcel(Context, 0);
}
private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader) private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{ {
int Slot = ParcelReader.ReadInt32(); int Slot = ParcelReader.ReadInt32();
@ -256,11 +283,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
int FbWidth = BufferQueue[Slot].Data.Width; int FbWidth = BufferQueue[Slot].Data.Width;
int FbHeight = BufferQueue[Slot].Data.Height; int FbHeight = BufferQueue[Slot].Data.Height;
int FbSize = FbWidth * FbHeight * 4; long FbSize = (uint)FbWidth * FbHeight * 4;
HNvMap NvMap = GetNvMap(Context, Slot); NvMap NvMap = GetNvMap(Context, Slot);
if (FbSize < 0 || NvMap.Address < 0 || NvMap.Address + FbSize > AMemoryMgr.AddrSize) if ((ulong)(NvMap.Address + FbSize) > AMemoryMgr.AddrSize)
{ {
Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!"); Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!");
@ -278,45 +305,61 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
int RealWidth = FbWidth; int RealWidth = FbWidth;
int RealHeight = FbHeight; int RealHeight = FbHeight;
float XSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX) ? -1 : 1;
float YSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY) ? -1 : 1;
float ScaleX = 1; float ScaleX = 1;
float ScaleY = 1; float ScaleY = 1;
float OffsX = 0; float OffsX = 0;
float OffsY = 0; float OffsY = 0;
if (Crop.Right != 0 && if (Crop.Right != 0 &&
Crop.Bottom != 0) Crop.Bottom != 0)
{ {
//Who knows if this is right, I was never good with math...
RealWidth = Crop.Right - Crop.Left; RealWidth = Crop.Right - Crop.Left;
RealHeight = Crop.Bottom - Crop.Top; RealHeight = Crop.Bottom - Crop.Top;
ScaleX = (float)FbWidth / RealWidth; if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90))
ScaleY = (float)FbHeight / RealHeight; {
ScaleY = (float)FbHeight / RealHeight;
ScaleX = (float)FbWidth / RealWidth;
OffsX = -(float)Crop.Left / Crop.Right; OffsY = ((-(float)Crop.Left / Crop.Right) + ScaleX - 1) * -XSign;
OffsY = -(float)Crop.Top / Crop.Bottom; OffsX = ((-(float)Crop.Top / Crop.Bottom) + ScaleY - 1) * -YSign;
}
else
{
ScaleX = (float)FbWidth / RealWidth;
ScaleY = (float)FbHeight / RealHeight;
OffsX += ScaleX - 1; OffsX = ((-(float)Crop.Left / Crop.Right) + ScaleX - 1) * XSign;
OffsY += ScaleY - 1; OffsY = ((-(float)Crop.Top / Crop.Bottom) + ScaleY - 1) * -YSign;
}
} }
ScaleX *= XSign;
ScaleY *= YSign;
float Rotate = 0; float Rotate = 0;
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX))
{
ScaleX = -ScaleX;
}
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY))
{
ScaleY = -ScaleY;
}
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90)) 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; lock (RenderQueueLock)
{
if (NvFlingerDisposed)
{
return;
}
Interlocked.Increment(ref RenderQueueCount);
}
byte* Fb = (byte*)Context.Memory.Ram + NvMap.Address;
Context.Ns.Gpu.Renderer.QueueAction(delegate() Context.Ns.Gpu.Renderer.QueueAction(delegate()
{ {
@ -332,6 +375,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
BufferQueue[Slot].State = BufferState.Free; BufferQueue[Slot].State = BufferState.Free;
Interlocked.Decrement(ref RenderQueueCount);
lock (WaitBufferFree) lock (WaitBufferFree)
{ {
WaitBufferFree.Set(); WaitBufferFree.Set();
@ -339,7 +384,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
}); });
} }
private HNvMap GetNvMap(ServiceCtx Context, int Slot) private NvMap GetNvMap(ServiceCtx Context, int Slot)
{ {
int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c); int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c);
@ -352,7 +397,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
NvMapHandle = BitConverter.ToInt32(RawValue, 0); NvMapHandle = BitConverter.ToInt32(RawValue, 0);
} }
return Context.Ns.Os.Handles.GetData<HNvMap>(NvMapHandle); ServiceNvDrv NvDrv = (ServiceNvDrv)Context.Process.Services.GetService("nvdrv");
return NvDrv.GetNvMap(NvMapHandle);
} }
private int GetFreeSlotBlocking(int Width, int Height) private int GetFreeSlotBlocking(int Width, int Height)
@ -418,10 +465,24 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
Dispose(true); Dispose(true);
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool Disposing)
{ {
if (disposing) if (Disposing && !NvFlingerDisposed)
{ {
lock (RenderQueueLock)
{
NvFlingerDisposed = true;
}
//Ensure that all pending actions was sent before
//we can safely assume that the class was disposed.
while (RenderQueueCount > 0)
{
Thread.Yield();
}
Renderer.ResetFrameBuffer();
lock (WaitBufferFree) lock (WaitBufferFree)
{ {
KeepRunning = false; KeepRunning = false;

View file

@ -1,12 +1,13 @@
using ChocolArm64.Events; using ChocolArm64.Events;
using ChocolArm64.Memory; using ChocolArm64.Memory;
using ChocolArm64.State; using ChocolArm64.State;
using Ryujinx.Core.OsHle.Handles;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Svc namespace Ryujinx.Core.OsHle.Svc
{ {
partial class SvcHandler partial class SvcHandler : IDisposable
{ {
private delegate void SvcFunc(AThreadState ThreadState); private delegate void SvcFunc(AThreadState ThreadState);
@ -16,10 +17,12 @@ namespace Ryujinx.Core.OsHle.Svc
private Process Process; private Process Process;
private AMemory Memory; private AMemory Memory;
private static Random Rng; private HashSet<(HSharedMem, long)> MappedSharedMems;
private ulong CurrentHeapSize; private ulong CurrentHeapSize;
private static Random Rng;
public SvcHandler(Switch Ns, Process Process) public SvcHandler(Switch Ns, Process Process)
{ {
SvcFuncs = new Dictionary<int, SvcFunc>() SvcFuncs = new Dictionary<int, SvcFunc>()
@ -32,10 +35,12 @@ namespace Ryujinx.Core.OsHle.Svc
{ 0x07, SvcExitProcess }, { 0x07, SvcExitProcess },
{ 0x08, SvcCreateThread }, { 0x08, SvcCreateThread },
{ 0x09, SvcStartThread }, { 0x09, SvcStartThread },
{ 0x0a, SvcExitThread },
{ 0x0b, SvcSleepThread }, { 0x0b, SvcSleepThread },
{ 0x0c, SvcGetThreadPriority }, { 0x0c, SvcGetThreadPriority },
{ 0x0d, SvcSetThreadPriority }, { 0x0d, SvcSetThreadPriority },
{ 0x0f, SvcSetThreadCoreMask }, { 0x0f, SvcSetThreadCoreMask },
{ 0x12, SvcClearEvent },
{ 0x13, SvcMapSharedMemory }, { 0x13, SvcMapSharedMemory },
{ 0x14, SvcUnmapSharedMemory }, { 0x14, SvcUnmapSharedMemory },
{ 0x15, SvcCreateTransferMemory }, { 0x15, SvcCreateTransferMemory },
@ -59,6 +64,8 @@ namespace Ryujinx.Core.OsHle.Svc
this.Ns = Ns; this.Ns = Ns;
this.Process = Process; this.Process = Process;
this.Memory = Process.Memory; this.Memory = Process.Memory;
MappedSharedMems = new HashSet<(HSharedMem, long)>();
} }
static SvcHandler() static SvcHandler()
@ -83,5 +90,26 @@ namespace Ryujinx.Core.OsHle.Svc
throw new NotImplementedException(e.Id.ToString("x4")); throw new NotImplementedException(e.Id.ToString("x4"));
} }
} }
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing)
{
lock (MappedSharedMems)
{
foreach ((HSharedMem SharedMem, long Position) in MappedSharedMems)
{
SharedMem.RemoveVirtualPosition(Memory, Position);
}
MappedSharedMems.Clear();
}
}
}
} }
} }

View file

@ -2,6 +2,8 @@ using ChocolArm64.Memory;
using ChocolArm64.State; using ChocolArm64.State;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Handles;
using static Ryujinx.Core.OsHle.ErrorCode;
namespace Ryujinx.Core.OsHle.Svc namespace Ryujinx.Core.OsHle.Svc
{ {
partial class SvcHandler partial class SvcHandler
@ -23,7 +25,7 @@ namespace Ryujinx.Core.OsHle.Svc
CurrentHeapSize = Size; CurrentHeapSize = Size;
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
ThreadState.X1 = (ulong)Position; ThreadState.X1 = (ulong)Position;
} }
@ -44,7 +46,7 @@ namespace Ryujinx.Core.OsHle.Svc
Memory.Manager.SetAttrBit(Position, Size, 3); Memory.Manager.SetAttrBit(Position, Size, 3);
} }
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
private void SvcMapMemory(AThreadState ThreadState) private void SvcMapMemory(AThreadState ThreadState)
@ -53,6 +55,24 @@ namespace Ryujinx.Core.OsHle.Svc
long Src = (long)ThreadState.X1; long Src = (long)ThreadState.X1;
long Size = (long)ThreadState.X2; long Size = (long)ThreadState.X2;
if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to map Memory at invalid src address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
return;
}
if (!IsValidMapPosition(Dst))
{
Logging.Warn($"Tried to map Memory at invalid dst address {Dst:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
return;
}
AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src); AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src);
Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm); Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm);
@ -61,7 +81,7 @@ namespace Ryujinx.Core.OsHle.Svc
Memory.Manager.SetAttrBit(Src, Size, 0); Memory.Manager.SetAttrBit(Src, Size, 0);
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
private void SvcUnmapMemory(AThreadState ThreadState) private void SvcUnmapMemory(AThreadState ThreadState)
@ -70,6 +90,24 @@ namespace Ryujinx.Core.OsHle.Svc
long Src = (long)ThreadState.X1; long Src = (long)ThreadState.X1;
long Size = (long)ThreadState.X2; long Size = (long)ThreadState.X2;
if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to unmap Memory at invalid src address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
return;
}
if (!IsValidMapPosition(Dst))
{
Logging.Warn($"Tried to unmap Memory at invalid dst address {Dst:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
return;
}
AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst); AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst);
Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory); Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory);
@ -78,7 +116,7 @@ namespace Ryujinx.Core.OsHle.Svc
Memory.Manager.ClearAttrBit(Src, Size, 0); Memory.Manager.ClearAttrBit(Src, Size, 0);
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
private void SvcQueryMemory(AThreadState ThreadState) private void SvcQueryMemory(AThreadState ThreadState)
@ -90,10 +128,11 @@ namespace Ryujinx.Core.OsHle.Svc
if (MapInfo == null) if (MapInfo == null)
{ {
//TODO: Correct error code. long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
ThreadState.X0 = ulong.MaxValue;
return; long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1;
MapInfo = new AMemoryMapInfo(AddrSpaceEnd, ReservedSize, (int)MemoryType.Reserved, 0, AMemoryPerm.None);
} }
Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position); Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position);
@ -106,7 +145,7 @@ namespace Ryujinx.Core.OsHle.Svc
Memory.WriteInt32(InfoPtr + 0x24, 0); Memory.WriteInt32(InfoPtr + 0x24, 0);
//TODO: X1. //TODO: X1.
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
ThreadState.X1 = 0; ThreadState.X1 = 0;
} }
@ -117,17 +156,33 @@ namespace Ryujinx.Core.OsHle.Svc
long Size = (long)ThreadState.X2; long Size = (long)ThreadState.X2;
int Perm = (int)ThreadState.X3; int Perm = (int)ThreadState.X3;
HSharedMem SharedMem = Ns.Os.Handles.GetData<HSharedMem>(Handle); if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to map SharedMemory at invalid address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
return;
}
HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
if (SharedMem != null) if (SharedMem != null)
{ {
Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, AMemoryPerm.Write);
AMemoryHelper.FillWithZeros(Memory, Src, (int)Size); AMemoryHelper.FillWithZeros(Memory, Src, (int)Size);
SharedMem.AddVirtualPosition(Src); Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm); lock (MappedSharedMems)
{
MappedSharedMems.Add((SharedMem, Src));
}
ThreadState.X0 = (int)SvcResult.Success; SharedMem.AddVirtualPosition(Memory, Src);
ThreadState.X0 = 0;
} }
//TODO: Error codes. //TODO: Error codes.
@ -135,15 +190,33 @@ namespace Ryujinx.Core.OsHle.Svc
private void SvcUnmapSharedMemory(AThreadState ThreadState) private void SvcUnmapSharedMemory(AThreadState ThreadState)
{ {
int Handle = (int)ThreadState.X0; int Handle = (int)ThreadState.X0;
long Position = (long)ThreadState.X1; long Src = (long)ThreadState.X1;
long Size = (long)ThreadState.X2; long Size = (long)ThreadState.X2;
HSharedMem HndData = Ns.Os.Handles.GetData<HSharedMem>(Handle); if (!IsValidPosition(Src))
if (HndData != null)
{ {
ThreadState.X0 = (int)SvcResult.Success; Logging.Warn($"Tried to unmap SharedMemory at invalid address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
return;
}
HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
if (SharedMem != null)
{
Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory);
SharedMem.RemoveVirtualPosition(Memory, Src);
lock (MappedSharedMems)
{
MappedSharedMems.Remove((SharedMem, Src));
}
ThreadState.X0 = 0;
} }
//TODO: Error codes. //TODO: Error codes.
@ -151,20 +224,41 @@ namespace Ryujinx.Core.OsHle.Svc
private void SvcCreateTransferMemory(AThreadState ThreadState) private void SvcCreateTransferMemory(AThreadState ThreadState)
{ {
long Position = (long)ThreadState.X1; long Src = (long)ThreadState.X1;
long Size = (long)ThreadState.X2; long Size = (long)ThreadState.X2;
int Perm = (int)ThreadState.X3; int Perm = (int)ThreadState.X3;
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position); if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to create TransferMemory at invalid address {Src:x16}!");
Memory.Manager.Reprotect(Position, Size, (AMemoryPerm)Perm); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
HTransferMem HndData = new HTransferMem(Memory, MapInfo.Perm, Position, Size); return;
}
int Handle = Ns.Os.Handles.GenerateId(HndData); AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src);
ThreadState.X1 = (ulong)Handle; Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
ThreadState.X0 = (int)SvcResult.Success;
HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size);
ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem);
ThreadState.X0 = 0;
ThreadState.X1 = Handle;
}
private static bool IsValidPosition(long Position)
{
return Position >= MemoryRegions.AddrSpaceStart &&
Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
}
private static bool IsValidMapPosition(long Position)
{
return Position >= MemoryRegions.MapRegionAddress &&
Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize;
} }
} }
} }

View file

@ -1,11 +0,0 @@
namespace Ryujinx.Core.OsHle.Svc
{
enum SvcResult
{
Success = 0,
ErrBadHandle = 0xe401,
ErrTimeout = 0xea01,
ErrBadInfo = 0xf001,
ErrBadIpcReq = 0xf601
}
}

View file

@ -3,10 +3,11 @@ using ChocolArm64.State;
using Ryujinx.Core.OsHle.Exceptions; using Ryujinx.Core.OsHle.Exceptions;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Ipc; using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.IpcServices;
using System; using System;
using System.Threading; using System.Threading;
using static Ryujinx.Core.OsHle.ErrorCode;
namespace Ryujinx.Core.OsHle.Svc namespace Ryujinx.Core.OsHle.Svc
{ {
partial class SvcHandler partial class SvcHandler
@ -20,13 +21,22 @@ namespace Ryujinx.Core.OsHle.Svc
Ns.Os.ExitProcess(ThreadState.ProcessId); Ns.Os.ExitProcess(ThreadState.ProcessId);
} }
private void SvcClearEvent(AThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
//TODO: Implement events.
ThreadState.X0 = 0;
}
private void SvcCloseHandle(AThreadState ThreadState) private void SvcCloseHandle(AThreadState ThreadState)
{ {
int Handle = (int)ThreadState.X0; int Handle = (int)ThreadState.X0;
Ns.Os.CloseHandle(Handle); Process.HandleTable.CloseHandle(Handle);
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
private void SvcResetSignal(AThreadState ThreadState) private void SvcResetSignal(AThreadState ThreadState)
@ -35,7 +45,7 @@ namespace Ryujinx.Core.OsHle.Svc
//TODO: Implement events. //TODO: Implement events.
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
private void SvcWaitSynchronization(AThreadState ThreadState) private void SvcWaitSynchronization(AThreadState ThreadState)
@ -51,12 +61,12 @@ namespace Ryujinx.Core.OsHle.Svc
Process.Scheduler.Suspend(CurrThread.ProcessorId); Process.Scheduler.Suspend(CurrThread.ProcessorId);
Process.Scheduler.Resume(CurrThread); Process.Scheduler.Resume(CurrThread);
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
private void SvcGetSystemTick(AThreadState ThreadState) private void SvcGetSystemTick(AThreadState ThreadState)
{ {
ThreadState.X0 = (ulong)ThreadState.CntpctEl0; ThreadState.X0 = ThreadState.CntpctEl0;
} }
private void SvcConnectToNamedPort(AThreadState ThreadState) private void SvcConnectToNamedPort(AThreadState ThreadState)
@ -69,10 +79,12 @@ namespace Ryujinx.Core.OsHle.Svc
//TODO: Validate that app has perms to access the service, and that the service //TODO: Validate that app has perms to access the service, and that the service
//actually exists, return error codes otherwise. //actually exists, return error codes otherwise.
HSession Session = new HSession(ServiceFactory.MakeService(Name)); HSession Session = new HSession(Process.Services.GetService(Name));
ThreadState.X1 = (ulong)Ns.Os.Handles.GenerateId(Session); ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session);
ThreadState.X0 = (int)SvcResult.Success;
ThreadState.X0 = 0;
ThreadState.X1 = Handle;
} }
private void SvcSendSyncRequest(AThreadState ThreadState) private void SvcSendSyncRequest(AThreadState ThreadState)
@ -108,21 +120,29 @@ namespace Ryujinx.Core.OsHle.Svc
byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size); byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
HSession Session = Ns.Os.Handles.GetData<HSession>(Handle); HSession Session = Process.HandleTable.GetData<HSession>(Handle);
IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr, Session is HDomain); IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr, Session is HDomain);
if (Session != null) if (Session != null)
{ {
IpcHandler.IpcCall(Ns, Memory, Session, Cmd, ThreadState.ThreadId, CmdPtr, Handle); IpcHandler.IpcCall(
Ns,
Process,
Memory,
Session,
Cmd,
ThreadState.ThreadId,
CmdPtr,
Handle);
byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size); byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
else else
{ {
ThreadState.X0 = (int)SvcResult.ErrBadIpcReq; ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidIpcReq);
} }
Thread.Yield(); Thread.Yield();
@ -148,7 +168,7 @@ namespace Ryujinx.Core.OsHle.Svc
Logging.Info($"SvcOutputDebugString: {Str}"); Logging.Info($"SvcOutputDebugString: {Str}");
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
private void SvcGetInfo(AThreadState ThreadState) private void SvcGetInfo(AThreadState ThreadState)
@ -162,7 +182,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (InfoType == 18 || if (InfoType == 18 ||
InfoType == 19) InfoType == 19)
{ {
ThreadState.X0 = (int)SvcResult.ErrBadInfo; ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo);
return; return;
} }
@ -186,7 +206,7 @@ namespace Ryujinx.Core.OsHle.Svc
break; break;
case 5: case 5:
ThreadState.X1 = CurrentHeapSize; ThreadState.X1 = MemoryRegions.HeapRegionSize;
break; break;
case 6: case 6:
@ -224,7 +244,7 @@ namespace Ryujinx.Core.OsHle.Svc
default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}"); default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}");
} }
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
} }
} }

View file

@ -28,7 +28,7 @@ namespace Ryujinx.Core.OsHle.Svc
Priority, Priority,
ProcessorId); ProcessorId);
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
ThreadState.X1 = (ulong)Handle; ThreadState.X1 = (ulong)Handle;
} }
@ -39,18 +39,25 @@ namespace Ryujinx.Core.OsHle.Svc
{ {
int Handle = (int)ThreadState.X0; int Handle = (int)ThreadState.X0;
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle); HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
if (Thread != null) if (Thread != null)
{ {
Process.Scheduler.StartThread(Thread); Process.Scheduler.StartThread(Thread);
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
//TODO: Error codes. //TODO: Error codes.
} }
private void SvcExitThread(AThreadState ThreadState)
{
HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
CurrThread.Thread.StopExecution();
}
private void SvcSleepThread(AThreadState ThreadState) private void SvcSleepThread(AThreadState ThreadState)
{ {
ulong NanoSecs = ThreadState.X0; ulong NanoSecs = ThreadState.X0;
@ -71,12 +78,12 @@ namespace Ryujinx.Core.OsHle.Svc
{ {
int Handle = (int)ThreadState.X1; int Handle = (int)ThreadState.X1;
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle); HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
if (Thread != null) if (Thread != null)
{ {
ThreadState.X0 = 0;
ThreadState.X1 = (ulong)Thread.Priority; ThreadState.X1 = (ulong)Thread.Priority;
ThreadState.X0 = (int)SvcResult.Success;
} }
//TODO: Error codes. //TODO: Error codes.
@ -87,13 +94,13 @@ namespace Ryujinx.Core.OsHle.Svc
int Handle = (int)ThreadState.X1; int Handle = (int)ThreadState.X1;
int Prio = (int)ThreadState.X0; int Prio = (int)ThreadState.X0;
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle); HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
if (Thread != null) if (Thread != null)
{ {
Thread.Priority = Prio; Thread.Priority = Prio;
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
//TODO: Error codes. //TODO: Error codes.
@ -101,7 +108,7 @@ namespace Ryujinx.Core.OsHle.Svc
private void SvcSetThreadCoreMask(AThreadState ThreadState) private void SvcSetThreadCoreMask(AThreadState ThreadState)
{ {
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
//TODO: Error codes. //TODO: Error codes.
} }
@ -110,12 +117,12 @@ namespace Ryujinx.Core.OsHle.Svc
{ {
int Handle = (int)ThreadState.X0; int Handle = (int)ThreadState.X0;
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle); HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
if (Thread != null) if (Thread != null)
{ {
ThreadState.X0 = 0;
ThreadState.X1 = (ulong)Thread.ThreadId; ThreadState.X1 = (ulong)Thread.ThreadId;
ThreadState.X0 = (int)SvcResult.Success;
} }
//TODO: Error codes. //TODO: Error codes.

View file

@ -1,6 +1,8 @@
using ChocolArm64.State; using ChocolArm64.State;
using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Handles;
using static Ryujinx.Core.OsHle.ErrorCode;
namespace Ryujinx.Core.OsHle.Svc namespace Ryujinx.Core.OsHle.Svc
{ {
partial class SvcHandler partial class SvcHandler
@ -11,7 +13,7 @@ namespace Ryujinx.Core.OsHle.Svc
long MutexAddress = (long)ThreadState.X1; long MutexAddress = (long)ThreadState.X1;
int RequestingThreadHandle = (int)ThreadState.X2; int RequestingThreadHandle = (int)ThreadState.X2;
HThread RequestingThread = Ns.Os.Handles.GetData<HThread>(RequestingThreadHandle); HThread RequestingThread = Process.HandleTable.GetData<HThread>(RequestingThreadHandle);
Mutex M = new Mutex(Process, MutexAddress, OwnerThreadHandle); Mutex M = new Mutex(Process, MutexAddress, OwnerThreadHandle);
@ -19,7 +21,7 @@ namespace Ryujinx.Core.OsHle.Svc
M.WaitForLock(RequestingThread, RequestingThreadHandle); M.WaitForLock(RequestingThread, RequestingThreadHandle);
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
private void SvcArbitrateUnlock(AThreadState ThreadState) private void SvcArbitrateUnlock(AThreadState ThreadState)
@ -31,7 +33,7 @@ namespace Ryujinx.Core.OsHle.Svc
M.Unlock(); M.Unlock();
} }
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState) private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState)
@ -41,7 +43,7 @@ namespace Ryujinx.Core.OsHle.Svc
int ThreadHandle = (int)ThreadState.X2; int ThreadHandle = (int)ThreadState.X2;
long Timeout = (long)ThreadState.X3; long Timeout = (long)ThreadState.X3;
HThread Thread = Ns.Os.Handles.GetData<HThread>(ThreadHandle); HThread Thread = Process.HandleTable.GetData<HThread>(ThreadHandle);
Mutex M = new Mutex(Process, MutexAddress, ThreadHandle); Mutex M = new Mutex(Process, MutexAddress, ThreadHandle);
@ -53,11 +55,16 @@ namespace Ryujinx.Core.OsHle.Svc
Cv = Ns.Os.CondVars.GetOrAdd(CondVarAddress, Cv); Cv = Ns.Os.CondVars.GetOrAdd(CondVarAddress, Cv);
Cv.WaitForSignal(Thread); if (!Cv.WaitForSignal(Thread))
{
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
return;
}
M.WaitForLock(Thread, ThreadHandle); M.WaitForLock(Thread, ThreadHandle);
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
private void SvcSignalProcessWideKey(AThreadState ThreadState) private void SvcSignalProcessWideKey(AThreadState ThreadState)
@ -72,7 +79,7 @@ namespace Ryujinx.Core.OsHle.Svc
Cv.SetSignal(CurrThread, Count); Cv.SetSignal(CurrThread, Count);
} }
ThreadState.X0 = (int)SvcResult.Success; ThreadState.X0 = 0;
} }
} }
} }

View file

@ -0,0 +1,7 @@
namespace Ryujinx.Core.OsHle.Utilities
{
static class EndianSwap
{
public static short Swap16(short Value) => (short)(((Value >> 8) & 0xff) | (Value << 8));
}
}

View file

@ -1,53 +0,0 @@
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Utilities
{
class IdPool
{
private HashSet<int> Ids;
private int CurrId;
private int MinId;
private int MaxId;
public IdPool(int Min, int Max)
{
Ids = new HashSet<int>();
CurrId = Min;
MinId = Min;
MaxId = Max;
}
public IdPool() : this(1, int.MaxValue) { }
public int GenerateId()
{
lock (Ids)
{
for (int Cnt = MinId; Cnt < MaxId; Cnt++)
{
if (Ids.Add(CurrId))
{
return CurrId;
}
if (CurrId++ == MaxId)
{
CurrId = MinId;
}
}
return -1;
}
}
public bool DeleteId(int Id)
{
lock (Ids)
{
return Ids.Remove(Id);
}
}
}
}

View file

@ -0,0 +1,84 @@
using System.Diagnostics;
using System.Timers;
namespace Ryujinx.Core
{
public class PerformanceStatistics
{
Stopwatch ExecutionTime = new Stopwatch();
Timer ResetTimer = new Timer(1000);
long CurrentGameFrameEnded;
long CurrentSystemFrameEnded;
long CurrentSystemFrameStart;
long LastGameFrameEnded;
long LastSystemFrameEnded;
double AccumulatedGameFrameTime;
double AccumulatedSystemFrameTime;
double CurrentGameFrameTime;
double CurrentSystemFrameTime;
double PreviousGameFrameTime;
double PreviousSystemFrameTime;
public double GameFrameRate { get; private set; }
public double SystemFrameRate { get; private set; }
public long SystemFramesRendered;
public long GameFramesRendered;
public long ElapsedMilliseconds => ExecutionTime.ElapsedMilliseconds;
public long ElapsedMicroseconds => (long)
(((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000);
public long ElapsedNanoseconds => (long)
(((double)ExecutionTime.ElapsedTicks / Stopwatch.Frequency) * 1000000000);
public PerformanceStatistics()
{
ExecutionTime.Start();
ResetTimer.Elapsed += ResetTimerElapsed;
ResetTimer.AutoReset = true;
ResetTimer.Start();
}
private void ResetTimerElapsed(object sender, ElapsedEventArgs e)
{
ResetStatistics();
}
public void StartSystemFrame()
{
PreviousSystemFrameTime = CurrentSystemFrameTime;
LastSystemFrameEnded = CurrentSystemFrameEnded;
CurrentSystemFrameStart = ElapsedMicroseconds;
}
public void EndSystemFrame()
{
CurrentSystemFrameEnded = ElapsedMicroseconds;
CurrentSystemFrameTime = CurrentSystemFrameEnded - CurrentSystemFrameStart;
AccumulatedSystemFrameTime += CurrentSystemFrameTime;
SystemFramesRendered++;
}
public void RecordGameFrameTime()
{
CurrentGameFrameEnded = ElapsedMicroseconds;
CurrentGameFrameTime = CurrentGameFrameEnded - LastGameFrameEnded;
PreviousGameFrameTime = CurrentGameFrameTime;
LastGameFrameEnded = CurrentGameFrameEnded;
AccumulatedGameFrameTime += CurrentGameFrameTime;
GameFramesRendered++;
}
public void ResetStatistics()
{
GameFrameRate = 1000 / ((AccumulatedGameFrameTime / GameFramesRendered) / 1000);
GameFrameRate = double.IsNaN(GameFrameRate) ? 0 : GameFrameRate;
SystemFrameRate = 1000 / ((AccumulatedSystemFrameTime / SystemFramesRendered) / 1000);
SystemFrameRate = double.IsNaN(SystemFrameRate) ? 0 : SystemFrameRate;
GameFramesRendered = 0;
SystemFramesRendered = 0;
AccumulatedGameFrameTime = 0;
AccumulatedSystemFrameTime = 0;
}
}
}

View file

@ -1,42 +1,40 @@
using ChocolArm64.Memory;
using Ryujinx.Core.Input; using Ryujinx.Core.Input;
using Ryujinx.Core.OsHle; using Ryujinx.Core.OsHle;
using Ryujinx.Core.Settings; using Ryujinx.Core.Settings;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using System; using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Core namespace Ryujinx.Core
{ {
public class Switch : IDisposable public class Switch : IDisposable
{ {
public IntPtr Ram {get; private set; }
internal NsGpu Gpu { get; private set; } internal NsGpu Gpu { get; private set; }
internal Horizon Os { get; private set; } internal Horizon Os { get; private set; }
internal VirtualFs VFs { get; private set; } internal VirtualFs VFs { get; private set; }
public Hid Hid { get; private set; } public Hid Hid { get; private set; }
public SetSys Settings { get; private set; } public SetSys Settings { get; private set; }
public PerformanceStatistics Statistics { get; private set; }
public event EventHandler Finish; public event EventHandler Finish;
public Switch(IGalRenderer Renderer) public Switch(IGalRenderer Renderer)
{ {
Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize);
Gpu = new NsGpu(Renderer); Gpu = new NsGpu(Renderer);
VFs = new VirtualFs(); VFs = new VirtualFs();
Hid = new Hid(this); Hid = new Hid();
Os = new Horizon(this);
Settings = new SetSys();
}
public void FinalizeAllProcesses() Statistics = new PerformanceStatistics();
{
Os.FinalizeAllProcesses(); Os = new Horizon(this);
Os.HidSharedMem.MemoryMapped += Hid.ShMemMap;
Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap;
Settings = new SetSys();
} }
public void LoadCart(string ExeFsDir, string RomFsFile = null) public void LoadCart(string ExeFsDir, string RomFsFile = null)
@ -59,14 +57,13 @@ namespace Ryujinx.Core
Dispose(true); Dispose(true);
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool Disposing)
{ {
if (disposing) if (Disposing)
{ {
Os.Dispose();
VFs.Dispose(); VFs.Dispose();
} }
Marshal.FreeHGlobal(Ram);
} }
} }
} }

View file

@ -18,10 +18,18 @@ namespace Ryujinx.Core
public string GetFullPath(string BasePath, string FileName) public string GetFullPath(string BasePath, string FileName)
{ {
if (FileName.StartsWith('/')) if (FileName.StartsWith("//"))
{
FileName = FileName.Substring(2);
}
else if (FileName.StartsWith('/'))
{ {
FileName = FileName.Substring(1); FileName = FileName.Substring(1);
} }
else
{
return null;
}
string FullPath = Path.GetFullPath(Path.Combine(BasePath, FileName)); string FullPath = Path.GetFullPath(Path.Combine(BasePath, FileName));

View file

@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gal
void RunActions(); void RunActions();
void InitializeFrameBuffer(); void InitializeFrameBuffer();
void ResetFrameBuffer();
void Render(); void Render();
void SetWindowSize(int Width, int Height); void SetWindowSize(int Width, int Height);
void SetFrameBuffer( void SetFrameBuffer(

View file

@ -24,6 +24,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private byte* FbPtr; private byte* FbPtr;
private object FbPtrLock;
public FrameBuffer(int Width, int Height) public FrameBuffer(int Width, int Height)
{ {
if (Width < 0) if (Width < 0)
@ -36,6 +38,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new ArgumentOutOfRangeException(nameof(Height)); throw new ArgumentOutOfRangeException(nameof(Height));
} }
FbPtrLock = new object();
TexWidth = Width; TexWidth = Width;
TexHeight = Height; TexHeight = Height;
@ -152,7 +156,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
throw new ArgumentOutOfRangeException(nameof(Height)); throw new ArgumentOutOfRangeException(nameof(Height));
} }
FbPtr = Fb; lock (FbPtrLock)
{
FbPtr = Fb;
}
if (Width != TexWidth || if (Width != TexWidth ||
Height != TexHeight) Height != TexHeight)
@ -178,17 +185,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.Uniform2(OffsetUniformLocation, Offs); GL.Uniform2(OffsetUniformLocation, Offs);
} }
public void Reset()
{
lock (FbPtrLock)
{
FbPtr = null;
}
}
public void Render() public void Render()
{ {
if (FbPtr == null) lock (FbPtrLock)
{ {
return; if (FbPtr == null)
} {
return;
}
for (int Y = 0; Y < TexHeight; Y++) for (int Y = 0; Y < TexHeight; Y++)
for (int X = 0; X < TexWidth; X++) for (int X = 0; X < TexWidth; X++)
{ {
Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y))); Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y)));
}
} }
GL.BindTexture(TextureTarget.Texture2D, TexHandle); GL.BindTexture(TextureTarget.Texture2D, TexHandle);

View file

@ -43,6 +43,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
FbRenderer = new FrameBuffer(1280, 720); FbRenderer = new FrameBuffer(1280, 720);
} }
public void ResetFrameBuffer()
{
FbRenderer.Reset();
}
public void QueueAction(Action ActionMthd) public void QueueAction(Action ActionMthd)
{ {
ActionsQueue.Enqueue(ActionMthd); ActionsQueue.Enqueue(ActionMthd);

View file

@ -2,8 +2,6 @@ using ChocolArm64;
using ChocolArm64.Memory; using ChocolArm64.Memory;
using ChocolArm64.State; using ChocolArm64.State;
using NUnit.Framework; using NUnit.Framework;
using System;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
namespace Ryujinx.Tests.Cpu namespace Ryujinx.Tests.Cpu
@ -16,7 +14,6 @@ namespace Ryujinx.Tests.Cpu
private long EntryPoint; private long EntryPoint;
private IntPtr Ram;
private AMemory Memory; private AMemory Memory;
private AThread Thread; private AThread Thread;
@ -28,19 +25,18 @@ namespace Ryujinx.Tests.Cpu
EntryPoint = Position; EntryPoint = Position;
Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize);
ATranslator Translator = new ATranslator(); ATranslator Translator = new ATranslator();
Memory = new AMemory(Ram); Memory = new AMemory();
Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute); Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute);
Thread = new AThread(Translator, Memory, ThreadPriority.Normal, EntryPoint); Thread = new AThread(Translator, Memory, EntryPoint);
} }
[TearDown] [TearDown]
public void Teardown() public void Teardown()
{ {
Memory.Dispose();
Thread = null; Thread = null;
Memory = null; Memory = null;
Marshal.FreeHGlobal(Ram);
} }
protected void Reset() protected void Reset()
@ -51,7 +47,7 @@ namespace Ryujinx.Tests.Cpu
protected void Opcode(uint Opcode) protected void Opcode(uint Opcode)
{ {
Thread.Memory.WriteUInt32(Position, Opcode); Thread.Memory.WriteUInt32Unchecked(Position, Opcode);
Position += 4; Position += 4;
} }

View file

@ -29,6 +29,10 @@ namespace Ryujinx
{ {
this.Ns = Ns; this.Ns = Ns;
this.Renderer = Renderer; this.Renderer = Renderer;
Location = new Point(
(DisplayDevice.Default.Width / 2) - (Width / 2),
(DisplayDevice.Default.Height / 2) - (Height / 2));
} }
protected override void OnLoad(EventArgs e) protected override void OnLoad(EventArgs e)
@ -162,9 +166,12 @@ namespace Ryujinx
protected override void OnRenderFrame(FrameEventArgs e) protected override void OnRenderFrame(FrameEventArgs e)
{ {
Ns.Statistics.StartSystemFrame();
GL.Viewport(0, 0, Width, Height); GL.Viewport(0, 0, Width, Height);
Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {1f / e.Time:0})"; Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " +
$"{Ns.Statistics.GameFrameRate:0})";
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
@ -172,6 +179,8 @@ namespace Ryujinx
Renderer.Render(); Renderer.Render();
SwapBuffers(); SwapBuffers();
Ns.Statistics.EndSystemFrame();
} }
protected override void OnResize(EventArgs e) protected override void OnResize(EventArgs e)

View file

@ -10,6 +10,8 @@ namespace Ryujinx
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
AOptimizations.DisableMemoryChecks = true;
Config.Read(); Config.Read();
Console.Title = "Ryujinx Console"; Console.Title = "Ryujinx Console";