Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
71de819ba1
87 changed files with 2830 additions and 1096 deletions
|
@ -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));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
public static class AOptimizations
|
||||
{
|
||||
|
||||
public static bool DisableMemoryChecks = false;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
22
ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs
Normal file
22
ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
73
ChocolArm64/Instruction/AInstEmitHash.cs
Normal file
73
ChocolArm64/Instruction/AInstEmitHash.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
10
Ryujinx.Core/OsHle/ErrorCode.cs
Normal file
10
Ryujinx.Core/OsHle/ErrorCode.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.Core.OsHle.IpcServices
|
||||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
enum ErrorModule
|
||||
{
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
63
Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs
Normal file
63
Ryujinx.Core/OsHle/Handles/KProcessHandleTable.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
11
Ryujinx.Core/OsHle/KernelErr.cs
Normal file
11
Ryujinx.Core/OsHle/KernelErr.cs
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -4,7 +4,7 @@ using System.Threading;
|
|||
|
||||
namespace Ryujinx.Core.OsHle
|
||||
{
|
||||
public class Mutex
|
||||
class Mutex
|
||||
{
|
||||
private const int MutexHasListenersMask = 0x40000000;
|
||||
|
||||
|
|
|
@ -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...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
110
Ryujinx.Core/OsHle/ServiceMgr.cs
Normal file
110
Ryujinx.Core/OsHle/ServiceMgr.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
63
Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs
Normal file
63
Ryujinx.Core/OsHle/Services/Aud/IAudioDevice.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
12
Ryujinx.Core/OsHle/Services/Nv/NvFd.cs
Normal file
12
Ryujinx.Core/OsHle/Services/Nv/NvFd.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Core/OsHle/Services/Nv/NvMap.cs
Normal file
12
Ryujinx.Core/OsHle/Services/Nv/NvMap.cs
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
12
Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs
Normal file
12
Ryujinx.Core/OsHle/Services/Pl/SharedFontType.cs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
namespace Ryujinx.Core.OsHle
|
||||
namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||
{
|
||||
class Display
|
||||
{
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
namespace Ryujinx.Core.OsHle.Svc
|
||||
{
|
||||
enum SvcResult
|
||||
{
|
||||
Success = 0,
|
||||
ErrBadHandle = 0xe401,
|
||||
ErrTimeout = 0xea01,
|
||||
ErrBadInfo = 0xf001,
|
||||
ErrBadIpcReq = 0xf601
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
7
Ryujinx.Core/OsHle/Utilities/EndianSwap.cs
Normal file
7
Ryujinx.Core/OsHle/Utilities/EndianSwap.cs
Normal 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));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
84
Ryujinx.Core/PerformanceStatistics.cs
Normal file
84
Ryujinx.Core/PerformanceStatistics.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gal
|
|||
void RunActions();
|
||||
|
||||
void InitializeFrameBuffer();
|
||||
void ResetFrameBuffer();
|
||||
void Render();
|
||||
void SetWindowSize(int Width, int Height);
|
||||
void SetFrameBuffer(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -10,6 +10,8 @@ namespace Ryujinx
|
|||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
AOptimizations.DisableMemoryChecks = true;
|
||||
|
||||
Config.Read();
|
||||
|
||||
Console.Title = "Ryujinx Console";
|
||||
|
|
Loading…
Add table
Reference in a new issue