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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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);
//Check if the thread should still be running, if it isn't then we return 0
//to force a return to the dispatcher and then exit the thread.
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Running));
AILLabel LblEnd = new AILLabel();
Context.Emit(OpCodes.Brtrue_S, LblEnd);
Context.EmitLdc_I8(0);
Context.Emit(OpCodes.Ret);
Context.MarkLabel(LblEnd);
if (Context.CurrBlock.Next != null)
{
Context.EmitLoadState(Context.CurrBlock.Next);

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

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)
{
EmitScalarUnaryOpF(Context, () =>
@ -341,6 +349,11 @@ namespace ChocolArm64.Instruction
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));
}
public static void Mul_Ve(AILEmitterCtx Context)
{
EmitVectorBinaryOpByElemZx(Context, () => Context.Emit(OpCodes.Mul));
}
public static void Neg_V(AILEmitterCtx Context)
{
EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
@ -369,6 +382,15 @@ namespace ChocolArm64.Instruction
EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo));
}
public static void Smlal_V(AILEmitterCtx Context)
{
EmitVectorWidenRnRmTernaryOpSx(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Add);
});
}
public static void Smull_V(AILEmitterCtx Context)
{
EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul));

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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
{

View file

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

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

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

View file

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

View file

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

View file

@ -3,31 +3,40 @@ using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Utilities
namespace Ryujinx.Core.OsHle
{
class IdPoolWithObj : IEnumerable<KeyValuePair<int, object>>
class IdDictionary : IEnumerable<object>
{
private IdPool Ids;
private ConcurrentDictionary<int, object> Objs;
public IdPoolWithObj()
{
Ids = new IdPool();
private int FreeIdHint = 1;
public IdDictionary()
{
Objs = new ConcurrentDictionary<int, object>();
}
public int GenerateId(object Data)
public int Add(object Data)
{
int Id = Ids.GenerateId();
if (Id == -1 || !Objs.TryAdd(Id, Data))
if (Objs.TryAdd(FreeIdHint, Data))
{
throw new InvalidOperationException();
return FreeIdHint++;
}
return Id;
return AddSlow(Data);
}
private int AddSlow(object Data)
{
for (int Id = 1; Id < int.MaxValue; Id++)
{
if (Objs.TryAdd(Id, Data))
{
return Id;
}
}
throw new InvalidOperationException();
}
public bool ReplaceData(int Id, object Data)
@ -42,6 +51,16 @@ namespace Ryujinx.Core.OsHle.Utilities
return false;
}
public object GetData(int Id)
{
if (Objs.TryGetValue(Id, out object Data))
{
return Data;
}
return null;
}
public T GetData<T>(int Id)
{
if (Objs.TryGetValue(Id, out object Data) && Data is T)
@ -52,7 +71,7 @@ namespace Ryujinx.Core.OsHle.Utilities
return default(T);
}
public void Delete(int Id)
public bool Delete(int Id)
{
if (Objs.TryRemove(Id, out object Obj))
{
@ -61,18 +80,22 @@ namespace Ryujinx.Core.OsHle.Utilities
DisposableObj.Dispose();
}
Ids.DeleteId(Id);
FreeIdHint = Id;
return true;
}
return false;
}
IEnumerator<KeyValuePair<int, object>> IEnumerable<KeyValuePair<int, object>>.GetEnumerator()
IEnumerator<object> IEnumerable<object>.GetEnumerator()
{
return Objs.GetEnumerator();
return Objs.Values.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(
Switch Ns,
Process Process,
AMemory Memory,
HSession Session,
IpcMessage Request,
@ -60,7 +61,7 @@ namespace Ryujinx.Core.OsHle.Ipc
}
else if (Request.DomCmd == IpcDomCmd.DeleteObj)
{
Dom.DeleteObject(Request.DomObjId);
Dom.Delete(Request.DomObjId);
Response = FillResponse(Response, 0);
@ -100,6 +101,7 @@ namespace Ryujinx.Core.OsHle.Ipc
ServiceCtx Context = new ServiceCtx(
Ns,
Process,
Memory,
Session,
Request,
@ -124,15 +126,43 @@ namespace Ryujinx.Core.OsHle.Ipc
switch (CmdId)
{
case 0: Request = IpcConvertSessionToDomain(Ns, Session, Response, HndId); break;
case 3: Request = IpcQueryBufferPointerSize(Response); break;
case 2: //IpcDuplicateSession, differences is unknown.
case 4: Request = IpcDuplicateSessionEx(Ns, Session, Response, ReqReader); break;
case 0:
{
HDomain Dom = new HDomain(Session);
Process.HandleTable.ReplaceData(HndId, Dom);
Request = FillResponse(Response, 0, Dom.Add(Dom));
break;
}
case 3:
{
Request = FillResponse(Response, 0, 0x500);
break;
}
//TODO: Whats the difference between IpcDuplicateSession/Ex?
case 2:
case 4:
{
int Unknown = ReqReader.ReadInt32();
int Handle = Process.HandleTable.OpenHandle(Session);
Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
Request = FillResponse(Response, 0);
break;
}
default: throw new NotImplementedException(CmdId.ToString());
}
}
else if (Request.Type == IpcMessageType.Unknown2)
else if (Request.Type == IpcMessageType.CloseSession)
{
//TODO
}
@ -145,39 +175,6 @@ namespace Ryujinx.Core.OsHle.Ipc
}
}
private static IpcMessage IpcConvertSessionToDomain(
Switch Ns,
HSession Session,
IpcMessage Response,
int HndId)
{
HDomain Dom = new HDomain(Session);
Ns.Os.Handles.ReplaceData(HndId, Dom);
return FillResponse(Response, 0, Dom.GenerateObjectId(Dom));
}
private static IpcMessage IpcDuplicateSessionEx(
Switch Ns,
HSession Session,
IpcMessage Response,
BinaryReader ReqReader)
{
int Unknown = ReqReader.ReadInt32();
int Handle = Ns.Os.Handles.GenerateId(Session);
Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
return FillResponse(Response, 0);
}
private static IpcMessage IpcQueryBufferPointerSize(IpcMessage Response)
{
return FillResponse(Response, 0, 0x500);
}
private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values)
{
using (MemoryStream MS = new MemoryStream())

View file

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

View file

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

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

View file

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

View file

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

View file

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

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)
{
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);

View file

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

View file

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

View file

@ -1,14 +1,64 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace Ryujinx.Core.OsHle.IpcServices.Bsd
{
//bsd_errno == (SocketException.ErrorCode - 10000)
//https://github.com/freebsd/freebsd/blob/master/sys/sys/errno.h
public enum BsdError
{
ENOTSOCK = 38, /* Socket operation on non-socket */
EDESTADDRREQ = 39, /* Destination address required */
EMSGSIZE = 40, /* Message too long */
EPROTOTYPE = 41, /* Protocol wrong type for socket */
ENOPROTOOPT = 42, /* Protocol not available */
EPROTONOSUPPORT = 43, /* Protocol not supported */
ESOCKTNOSUPPORT = 44, /* Socket type not supported */
EOPNOTSUPP = 45, /* Operation not supported */
EPFNOSUPPORT = 46, /* Protocol family not supported */
EAFNOSUPPORT = 47, /* Address family not supported by protocol family */
EADDRINUSE = 48, /* Address already in use */
EADDRNOTAVAIL = 49, /* Can't assign requested address */
ENETDOWN = 50, /* Network is down */
ENETUNREACH = 51, /* Network is unreachable */
ENETRESET = 52, /* Network dropped connection on reset */
ECONNABORTED = 53, /* Software caused connection abort */
ECONNRESET = 54, /* Connection reset by peer */
ENOBUFS = 55, /* No buffer space available */
EISCONN = 56, /* Socket is already connected */
ENOTCONN = 57, /* Socket is not connected */
ESHUTDOWN = 58, /* Can't send after socket shutdown */
ETOOMANYREFS = 59, /* Too many references: can't splice */
ETIMEDOUT = 60, /* Operation timed out */
ECONNREFUSED = 61 /* Connection refused */
}
class SocketBsd
{
public int Family;
public int Type;
public int Protocol;
public IPAddress IpAddress;
public IPEndPoint RemoteEP;
public Socket Handle;
}
class ServiceBsd : IIpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private List<SocketBsd> Sockets = new List<SocketBsd>();
public ServiceBsd()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
@ -16,26 +66,31 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
{ 0, Initialize },
{ 1, StartMonitoring },
{ 2, Socket },
{ 6, Poll },
{ 8, Recv },
{ 10, Send },
{ 14, Connect }
{ 11, SendTo },
{ 12, Accept },
{ 13, Bind },
{ 14, Connect },
{ 18, Listen },
{ 21, SetSockOpt },
{ 26, Close }
};
}
//Initialize(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno
//(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno
public long Initialize(ServiceCtx Context)
{
/*
typedef struct {
u32 version; // Observed 1 on 2.0 LibAppletWeb, 2 on 3.0.
u32 tcp_tx_buf_size; // Size of the TCP transfer (send) buffer (initial or fixed).
u32 tcp_rx_buf_size; // Size of the TCP recieve buffer (initial or fixed).
u32 tcp_tx_buf_max_size; // Maximum size of the TCP transfer (send) buffer. If it is 0, the size of the buffer is fixed to its initial value.
u32 tcp_rx_buf_max_size; // Maximum size of the TCP receive buffer. If it is 0, the size of the buffer is fixed to its initial value.
u32 udp_tx_buf_size; // Size of the UDP transfer (send) buffer (typically 0x2400 bytes).
u32 udp_rx_buf_size; // Size of the UDP receive buffer (typically 0xA500 bytes).
u32 sb_efficiency; // Number of buffers for each socket (standard values range from 1 to 8).
} BsdBufferConfig;
*/
@ -52,7 +107,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
return 0;
}
//StartMonitoring(u64, pid)
//(u64, pid)
public long StartMonitoring(ServiceCtx Context)
{
//Todo: Stub
@ -60,37 +115,378 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
return 0;
}
//Socket(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno)
//(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno)
public long Socket(ServiceCtx Context)
{
Context.ResponseData.Write(0);
Context.ResponseData.Write(0);
SocketBsd NewBSDSocket = new SocketBsd
{
Family = Context.RequestData.ReadInt32(),
Type = Context.RequestData.ReadInt32(),
Protocol = Context.RequestData.ReadInt32()
};
//Todo: Stub
Sockets.Add(NewBSDSocket);
Sockets[Sockets.Count - 1].Handle = new Socket((AddressFamily)Sockets[Sockets.Count - 1].Family,
(SocketType)Sockets[Sockets.Count - 1].Type,
(ProtocolType)Sockets[Sockets.Count - 1].Protocol);
Context.ResponseData.Write(Sockets.Count - 1);
Context.ResponseData.Write(0);
return 0;
}
//Connect(u32 socket, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
public long Connect(ServiceCtx Context)
//(u32, u32, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>)
public long Poll(ServiceCtx Context)
{
Context.ResponseData.Write(0);
Context.ResponseData.Write(0);
int PollCount = Context.RequestData.ReadInt32();
int TimeOut = Context.RequestData.ReadInt32();
//Todo: Stub
//https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h
//https://msdn.microsoft.com/fr-fr/library/system.net.sockets.socket.poll(v=vs.110).aspx
//https://github.com/switchbrew/libnx/blob/e0457c4534b3c37426d83e1a620f82cb28c3b528/nx/source/services/bsd.c#L343
//https://github.com/TuxSH/ftpd/blob/switch_pr/source/ftp.c#L1634
//https://linux.die.net/man/2/poll
byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
(int)Context.Request.SendBuff[0].Size);
int SocketId = Get32(SentBuffer, 0);
short RequestedEvents = (short)Get16(SentBuffer, 4);
short ReturnedEvents = (short)Get16(SentBuffer, 6);
//Todo: Stub - Need to implemented the Type-22 buffer.
Context.ResponseData.Write(1);
Context.ResponseData.Write(0);
return 0;
}
//Send(u32 socket, u32 flags, buffer<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)
{
Context.ResponseData.Write(0);
Context.ResponseData.Write(0);
int SocketId = Context.RequestData.ReadInt32();
int SocketFlags = Context.RequestData.ReadInt32();
byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
(int)Context.Request.SendBuff[0].Size);
//Todo: Stub
try
{
//Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer));
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
Context.ResponseData.Write(BytesSent);
Context.ResponseData.Write(0);
}
catch (SocketException Ex)
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write(Ex.ErrorCode - 10000);
}
return 0;
}
//(u32 socket, u32 flags, buffer<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.Text;
using static Ryujinx.Core.OsHle.IpcServices.ErrorCode;
using static Ryujinx.Core.OsHle.ErrorCode;
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
namespace Ryujinx.Core.OsHle.IpcServices.FspSrv

View file

@ -10,21 +10,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
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>()
{
{ 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;
}

View file

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

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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