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("x1111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg));
|
||||||
Set("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem));
|
Set("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem));
|
||||||
Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu));
|
Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu));
|
||||||
|
Set("x0011010110xxxxx010000xxxxxxxxxx", AInstEmit.Crc32b, typeof(AOpCodeAluRs));
|
||||||
|
Set("x0011010110xxxxx010001xxxxxxxxxx", AInstEmit.Crc32h, typeof(AOpCodeAluRs));
|
||||||
|
Set("x0011010110xxxxx010010xxxxxxxxxx", AInstEmit.Crc32w, typeof(AOpCodeAluRs));
|
||||||
|
Set("x0011010110xxxxx010011xxxxxxxxxx", AInstEmit.Crc32x, typeof(AOpCodeAluRs));
|
||||||
|
Set("x0011010110xxxxx010100xxxxxxxxxx", AInstEmit.Crc32cb, typeof(AOpCodeAluRs));
|
||||||
|
Set("x0011010110xxxxx010101xxxxxxxxxx", AInstEmit.Crc32ch, typeof(AOpCodeAluRs));
|
||||||
|
Set("x0011010110xxxxx010110xxxxxxxxxx", AInstEmit.Crc32cw, typeof(AOpCodeAluRs));
|
||||||
|
Set("x0011010110xxxxx010111xxxxxxxxxx", AInstEmit.Crc32cx, typeof(AOpCodeAluRs));
|
||||||
Set("x0011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csel, typeof(AOpCodeCsel));
|
Set("x0011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csel, typeof(AOpCodeCsel));
|
||||||
Set("x0011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csinc, typeof(AOpCodeCsel));
|
Set("x0011010100xxxxxxxxx01xxxxxxxxxx", AInstEmit.Csinc, typeof(AOpCodeCsel));
|
||||||
Set("x1011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csinv, typeof(AOpCodeCsel));
|
Set("x1011010100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Csinv, typeof(AOpCodeCsel));
|
||||||
|
@ -161,8 +169,10 @@ namespace ChocolArm64
|
||||||
Set("000111100x10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd));
|
Set("000111100x10001xx10000xxxxxxxxxx", AInstEmit.Fcvt_S, typeof(AOpCodeSimd));
|
||||||
Set("x00111100x100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt));
|
Set("x00111100x100100000000xxxxxxxxxx", AInstEmit.Fcvtas_Gp, typeof(AOpCodeSimdCvt));
|
||||||
Set("x00111100x100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt));
|
Set("x00111100x100101000000xxxxxxxxxx", AInstEmit.Fcvtau_Gp, typeof(AOpCodeSimdCvt));
|
||||||
|
Set("0x0011100x100001011110xxxxxxxxxx", AInstEmit.Fcvtl_V, typeof(AOpCodeSimd));
|
||||||
Set("x00111100x110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt));
|
Set("x00111100x110000000000xxxxxxxxxx", AInstEmit.Fcvtms_Gp, typeof(AOpCodeSimdCvt));
|
||||||
Set("x00111100x110001000000xxxxxxxxxx", AInstEmit.Fcvtmu_Gp, typeof(AOpCodeSimdCvt));
|
Set("x00111100x110001000000xxxxxxxxxx", AInstEmit.Fcvtmu_Gp, typeof(AOpCodeSimdCvt));
|
||||||
|
Set("0x0011100x100001011010xxxxxxxxxx", AInstEmit.Fcvtn_V, typeof(AOpCodeSimd));
|
||||||
Set("x00111100x101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt));
|
Set("x00111100x101000000000xxxxxxxxxx", AInstEmit.Fcvtps_Gp, typeof(AOpCodeSimdCvt));
|
||||||
Set("x00111100x101001000000xxxxxxxxxx", AInstEmit.Fcvtpu_Gp, typeof(AOpCodeSimdCvt));
|
Set("x00111100x101001000000xxxxxxxxxx", AInstEmit.Fcvtpu_Gp, typeof(AOpCodeSimdCvt));
|
||||||
Set("x00111100x111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt));
|
Set("x00111100x111000000000xxxxxxxxxx", AInstEmit.Fcvtzs_Gp, typeof(AOpCodeSimdCvt));
|
||||||
|
@ -181,7 +191,7 @@ namespace ChocolArm64
|
||||||
Set("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg));
|
Set("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg));
|
||||||
Set("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg));
|
Set("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg));
|
||||||
Set("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg));
|
Set("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElem));
|
Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElemF));
|
||||||
Set("000111100x100000010000xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimd));
|
Set("000111100x100000010000xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimd));
|
||||||
Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov));
|
Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov));
|
||||||
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
|
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
|
||||||
|
@ -192,12 +202,13 @@ namespace ChocolArm64
|
||||||
Set("000111110x0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
|
Set("000111110x0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
|
||||||
Set("000111100x1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, typeof(AOpCodeSimdReg));
|
Set("000111100x1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, typeof(AOpCodeSimdReg));
|
||||||
Set("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
|
Set("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElem));
|
Set("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElemF));
|
||||||
Set("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimdReg));
|
Set("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimdReg));
|
||||||
Set("000111110x1xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fnmsub_S, typeof(AOpCodeSimdReg));
|
Set("000111110x1xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fnmsub_S, typeof(AOpCodeSimdReg));
|
||||||
Set("000111100x1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg));
|
Set("000111100x1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg));
|
||||||
Set("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
|
Set("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
|
||||||
Set("000111100x100101010000xxxxxxxxxx", AInstEmit.Frintm_S, typeof(AOpCodeSimd));
|
Set("000111100x100101010000xxxxxxxxxx", AInstEmit.Frintm_S, typeof(AOpCodeSimd));
|
||||||
|
Set("0>0011100<100001100110xxxxxxxxxx", AInstEmit.Frintm_V, typeof(AOpCodeSimd));
|
||||||
Set("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S, typeof(AOpCodeSimd));
|
Set("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S, typeof(AOpCodeSimd));
|
||||||
Set("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S, typeof(AOpCodeSimd));
|
Set("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S, typeof(AOpCodeSimd));
|
||||||
Set("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd));
|
Set("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd));
|
||||||
|
@ -223,6 +234,7 @@ namespace ChocolArm64
|
||||||
Set("0x00111100000xxx110x01xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
Set("0x00111100000xxx110x01xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
||||||
Set("0xx0111100000xxx111001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
Set("0xx0111100000xxx111001xxxxxxxxxx", AInstEmit.Movi_V, typeof(AOpCodeSimdImm));
|
||||||
Set("0x001110<<1xxxxx100111xxxxxxxxxx", AInstEmit.Mul_V, typeof(AOpCodeSimdReg));
|
Set("0x001110<<1xxxxx100111xxxxxxxxxx", AInstEmit.Mul_V, typeof(AOpCodeSimdReg));
|
||||||
|
Set("0x001111xxxxxxxx1000x0xxxxxxxxxx", AInstEmit.Mul_Ve, typeof(AOpCodeSimdRegElem));
|
||||||
Set("0x10111100000xxx0xx001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
|
Set("0x10111100000xxx0xx001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
|
||||||
Set("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
|
Set("0x10111100000xxx10x001xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
|
||||||
Set("0x10111100000xxx110x01xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
|
Set("0x10111100000xxx110x01xxxxxxxxxx", AInstEmit.Mvni_V, typeof(AOpCodeSimdImm));
|
||||||
|
@ -237,14 +249,18 @@ namespace ChocolArm64
|
||||||
Set("0x0011100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_V, typeof(AOpCodeSimd));
|
Set("0x0011100x100001110110xxxxxxxxxx", AInstEmit.Scvtf_V, typeof(AOpCodeSimd));
|
||||||
Set("010111110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm));
|
Set("010111110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_S, typeof(AOpCodeSimdShImm));
|
||||||
Set("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm));
|
Set("0x0011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Shl_V, typeof(AOpCodeSimdShImm));
|
||||||
|
Set("0x101110<<100001001110xxxxxxxxxx", AInstEmit.Shll_V, typeof(AOpCodeSimd));
|
||||||
Set("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm));
|
Set("0x00111100>>>xxx100001xxxxxxxxxx", AInstEmit.Shrn_V, typeof(AOpCodeSimdShImm));
|
||||||
|
Set("0x1011110>>>>xxx010101xxxxxxxxxx", AInstEmit.Sli_V, typeof(AOpCodeSimdShImm));
|
||||||
Set("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg));
|
Set("0x001110<<1xxxxx011001xxxxxxxxxx", AInstEmit.Smax_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg));
|
Set("0x001110<<1xxxxx011011xxxxxxxxxx", AInstEmit.Smin_V, typeof(AOpCodeSimdReg));
|
||||||
|
Set("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg));
|
Set("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg));
|
Set("0>001110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Sshl_V, typeof(AOpCodeSimdReg));
|
||||||
Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm));
|
Set("0x00111100>>>xxx101001xxxxxxxxxx", AInstEmit.Sshll_V, typeof(AOpCodeSimdShImm));
|
||||||
Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm));
|
Set("010111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_S, typeof(AOpCodeSimdShImm));
|
||||||
Set("0x0011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm));
|
Set("0x0011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Sshr_V, typeof(AOpCodeSimdShImm));
|
||||||
|
Set("0x0011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm));
|
||||||
Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
|
Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
|
||||||
Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
|
Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
|
||||||
Set("0x00110100000000xx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
|
Set("0x00110100000000xx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
public static class AOptimizations
|
public static class AOptimizations
|
||||||
{
|
{
|
||||||
|
public static bool DisableMemoryChecks = false;
|
||||||
}
|
}
|
|
@ -10,47 +10,34 @@ namespace ChocolArm64
|
||||||
public AThreadState ThreadState { get; private set; }
|
public AThreadState ThreadState { get; private set; }
|
||||||
public AMemory Memory { get; private set; }
|
public AMemory Memory { get; private set; }
|
||||||
|
|
||||||
public long EntryPoint { get; private set; }
|
private long EntryPoint;
|
||||||
|
|
||||||
private ATranslator Translator;
|
private ATranslator Translator;
|
||||||
|
|
||||||
private ThreadPriority Priority;
|
|
||||||
|
|
||||||
private Thread Work;
|
private Thread Work;
|
||||||
|
|
||||||
public event EventHandler WorkFinished;
|
public event EventHandler WorkFinished;
|
||||||
|
|
||||||
public int ThreadId => ThreadState.ThreadId;
|
public int ThreadId => ThreadState.ThreadId;
|
||||||
|
|
||||||
public bool IsAlive => Work.IsAlive;
|
private int IsExecuting;
|
||||||
|
|
||||||
private bool IsExecuting;
|
public AThread(ATranslator Translator, AMemory Memory, long EntryPoint)
|
||||||
|
|
||||||
private object ExecuteLock;
|
|
||||||
|
|
||||||
public AThread(ATranslator Translator, AMemory Memory, ThreadPriority Priority, long EntryPoint)
|
|
||||||
{
|
{
|
||||||
this.Translator = Translator;
|
this.Translator = Translator;
|
||||||
this.Memory = Memory;
|
this.Memory = Memory;
|
||||||
this.Priority = Priority;
|
|
||||||
this.EntryPoint = EntryPoint;
|
this.EntryPoint = EntryPoint;
|
||||||
|
|
||||||
ThreadState = new AThreadState();
|
ThreadState = new AThreadState();
|
||||||
ExecuteLock = new object();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StopExecution() => Translator.StopExecution();
|
ThreadState.Running = true;
|
||||||
|
}
|
||||||
|
|
||||||
public bool Execute()
|
public bool Execute()
|
||||||
{
|
{
|
||||||
lock (ExecuteLock)
|
if (Interlocked.Exchange(ref IsExecuting, 1) == 1)
|
||||||
{
|
{
|
||||||
if (IsExecuting)
|
return false;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
IsExecuting = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Work = new Thread(delegate()
|
Work = new Thread(delegate()
|
||||||
|
@ -62,11 +49,11 @@ namespace ChocolArm64
|
||||||
WorkFinished?.Invoke(this, EventArgs.Empty);
|
WorkFinished?.Invoke(this, EventArgs.Empty);
|
||||||
});
|
});
|
||||||
|
|
||||||
Work.Priority = Priority;
|
|
||||||
|
|
||||||
Work.Start();
|
Work.Start();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void StopExecution() => ThreadState.Running = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,6 @@ using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace ChocolArm64
|
namespace ChocolArm64
|
||||||
{
|
{
|
||||||
|
@ -23,8 +22,6 @@ namespace ChocolArm64
|
||||||
|
|
||||||
public bool EnableCpuTrace { get; set; }
|
public bool EnableCpuTrace { get; set; }
|
||||||
|
|
||||||
private bool KeepRunning;
|
|
||||||
|
|
||||||
public ATranslator(IReadOnlyDictionary<long, string> SymbolTable = null)
|
public ATranslator(IReadOnlyDictionary<long, string> SymbolTable = null)
|
||||||
{
|
{
|
||||||
SubBlocks = new HashSet<long>();
|
SubBlocks = new HashSet<long>();
|
||||||
|
@ -39,12 +36,8 @@ namespace ChocolArm64
|
||||||
{
|
{
|
||||||
this.SymbolTable = new ConcurrentDictionary<long, string>();
|
this.SymbolTable = new ConcurrentDictionary<long, string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
KeepRunning = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void StopExecution() => KeepRunning = false;
|
|
||||||
|
|
||||||
internal void ExecuteSubroutine(AThread Thread, long Position)
|
internal void ExecuteSubroutine(AThread Thread, long Position)
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
|
@ -71,7 +64,7 @@ namespace ChocolArm64
|
||||||
|
|
||||||
Position = Sub.Execute(Thread.ThreadState, Thread.Memory);
|
Position = Sub.Execute(Thread.ThreadState, Thread.Memory);
|
||||||
}
|
}
|
||||||
while (Position != 0 && KeepRunning);
|
while (Position != 0 && Thread.ThreadState.Running);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub)
|
internal bool TryGetCachedSub(AOpCode OpCode, out ATranslatedSub Sub)
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace ChocolArm64.Decoder
|
||||||
|
|
||||||
public AOpCodeSimdExt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
public AOpCodeSimdExt(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||||
{
|
{
|
||||||
int Imm4 = (OpCode >> 11) & 0xf;
|
Imm4 = (OpCode >> 11) & 0xf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -88,7 +88,14 @@ namespace ChocolArm64.Decoder
|
||||||
|
|
||||||
private static long ShlOnes(long Value, int Shift)
|
private static long ShlOnes(long Value, int Shift)
|
||||||
{
|
{
|
||||||
return Value << Shift | (long)(ulong.MaxValue >> (64 - Shift));
|
if (Shift != 0)
|
||||||
|
{
|
||||||
|
return Value << Shift | (long)(ulong.MaxValue >> (64 - Shift));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,9 +4,9 @@ namespace ChocolArm64.Decoder
|
||||||
{
|
{
|
||||||
class AOpCodeSimdReg : AOpCodeSimd
|
class AOpCodeSimdReg : AOpCodeSimd
|
||||||
{
|
{
|
||||||
public bool Bit3 { get; private set; }
|
public bool Bit3 { get; private set; }
|
||||||
public int Ra { get; private set; }
|
public int Ra { get; private set; }
|
||||||
public int Rm { get; private set; }
|
public int Rm { get; protected set; }
|
||||||
|
|
||||||
public AOpCodeSimdReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
public AOpCodeSimdReg(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,14 +8,24 @@ namespace ChocolArm64.Decoder
|
||||||
|
|
||||||
public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
public AOpCodeSimdRegElem(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode)
|
||||||
{
|
{
|
||||||
if ((Size & 1) != 0)
|
switch (Size)
|
||||||
{
|
{
|
||||||
Index = (OpCode >> 11) & 1;
|
case 1:
|
||||||
}
|
Index = (OpCode >> 21) & 1 |
|
||||||
else
|
(OpCode >> 10) & 2 |
|
||||||
{
|
(OpCode >> 18) & 4;
|
||||||
Index = (OpCode >> 21) & 1 |
|
|
||||||
(OpCode >> 10) & 2;
|
Rm &= 0xf;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
Index = (OpCode >> 21) & 1 |
|
||||||
|
(OpCode >> 10) & 2;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: Emitter = AInstEmit.Und; return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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);
|
Context.EmitCall(MthdInfo);
|
||||||
|
|
||||||
|
//Check if the thread should still be running, if it isn't then we return 0
|
||||||
|
//to force a return to the dispatcher and then exit the thread.
|
||||||
|
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
|
||||||
|
|
||||||
|
Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Running));
|
||||||
|
|
||||||
|
AILLabel LblEnd = new AILLabel();
|
||||||
|
|
||||||
|
Context.Emit(OpCodes.Brtrue_S, LblEnd);
|
||||||
|
|
||||||
|
Context.EmitLdc_I8(0);
|
||||||
|
|
||||||
|
Context.Emit(OpCodes.Ret);
|
||||||
|
|
||||||
|
Context.MarkLabel(LblEnd);
|
||||||
|
|
||||||
if (Context.CurrBlock.Next != null)
|
if (Context.CurrBlock.Next != null)
|
||||||
{
|
{
|
||||||
Context.EmitLoadState(Context.CurrBlock.Next);
|
Context.EmitLoadState(Context.CurrBlock.Next);
|
||||||
|
|
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)
|
switch (Size)
|
||||||
{
|
{
|
||||||
case 0: Name = nameof(AMemory.ReadVector8); break;
|
case 0: Name = AOptimizations.DisableMemoryChecks
|
||||||
case 1: Name = nameof(AMemory.ReadVector16); break;
|
? nameof(AMemory.ReadVector8Unchecked)
|
||||||
case 2: Name = nameof(AMemory.ReadVector32); break;
|
: nameof(AMemory.ReadVector8); break;
|
||||||
case 3: Name = nameof(AMemory.ReadVector64); break;
|
|
||||||
case 4: Name = nameof(AMemory.ReadVector128); break;
|
case 1: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.ReadVector16Unchecked)
|
||||||
|
: nameof(AMemory.ReadVector16); break;
|
||||||
|
|
||||||
|
case 2: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.ReadVector32Unchecked)
|
||||||
|
: nameof(AMemory.ReadVector32); break;
|
||||||
|
|
||||||
|
case 3: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.ReadVector64Unchecked)
|
||||||
|
: nameof(AMemory.ReadVector64); break;
|
||||||
|
|
||||||
|
case 4: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.ReadVector128Unchecked)
|
||||||
|
: nameof(AMemory.ReadVector128); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (Size)
|
switch (Size)
|
||||||
{
|
{
|
||||||
case 0: Name = nameof(AMemory.ReadByte); break;
|
case 0: Name = AOptimizations.DisableMemoryChecks
|
||||||
case 1: Name = nameof(AMemory.ReadUInt16); break;
|
? nameof(AMemory.ReadByteUnchecked)
|
||||||
case 2: Name = nameof(AMemory.ReadUInt32); break;
|
: nameof(AMemory.ReadByte); break;
|
||||||
case 3: Name = nameof(AMemory.ReadUInt64); break;
|
|
||||||
|
case 1: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.ReadUInt16Unchecked)
|
||||||
|
: nameof(AMemory.ReadUInt16); break;
|
||||||
|
|
||||||
|
case 2: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.ReadUInt32Unchecked)
|
||||||
|
: nameof(AMemory.ReadUInt32); break;
|
||||||
|
|
||||||
|
case 3: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.ReadUInt64Unchecked)
|
||||||
|
: nameof(AMemory.ReadUInt64); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,21 +132,46 @@ namespace ChocolArm64.Instruction
|
||||||
{
|
{
|
||||||
switch (Size)
|
switch (Size)
|
||||||
{
|
{
|
||||||
case 0: Name = nameof(AMemory.WriteVector8); break;
|
case 0: Name = AOptimizations.DisableMemoryChecks
|
||||||
case 1: Name = nameof(AMemory.WriteVector16); break;
|
? nameof(AMemory.WriteVector8Unchecked)
|
||||||
case 2: Name = nameof(AMemory.WriteVector32); break;
|
: nameof(AMemory.WriteVector8); break;
|
||||||
case 3: Name = nameof(AMemory.WriteVector64); break;
|
|
||||||
case 4: Name = nameof(AMemory.WriteVector128); break;
|
case 1: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.WriteVector16Unchecked)
|
||||||
|
: nameof(AMemory.WriteVector16); break;
|
||||||
|
|
||||||
|
case 2: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.WriteVector32Unchecked)
|
||||||
|
: nameof(AMemory.WriteVector32); break;
|
||||||
|
|
||||||
|
case 3: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.WriteVector64Unchecked)
|
||||||
|
: nameof(AMemory.WriteVector64); break;
|
||||||
|
|
||||||
|
case 4: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.WriteVector128Unchecked)
|
||||||
|
: nameof(AMemory.WriteVector128); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (Size)
|
switch (Size)
|
||||||
{
|
{
|
||||||
case 0: Name = nameof(AMemory.WriteByte); break;
|
case 0: Name = AOptimizations.DisableMemoryChecks
|
||||||
case 1: Name = nameof(AMemory.WriteUInt16); break;
|
? nameof(AMemory.WriteByteUnchecked)
|
||||||
case 2: Name = nameof(AMemory.WriteUInt32); break;
|
: nameof(AMemory.WriteByte); break;
|
||||||
case 3: Name = nameof(AMemory.WriteUInt64); break;
|
|
||||||
|
case 1: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.WriteUInt16Unchecked)
|
||||||
|
: nameof(AMemory.WriteUInt16); break;
|
||||||
|
|
||||||
|
case 2: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.WriteUInt32Unchecked)
|
||||||
|
: nameof(AMemory.WriteUInt32); break;
|
||||||
|
|
||||||
|
case 3: Name = AOptimizations.DisableMemoryChecks
|
||||||
|
? nameof(AMemory.WriteUInt64Unchecked)
|
||||||
|
: nameof(AMemory.WriteUInt64); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -267,6 +267,14 @@ namespace ChocolArm64.Instruction
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Frintm_V(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
EmitVectorUnaryOpF(Context, () =>
|
||||||
|
{
|
||||||
|
EmitUnaryMathCall(Context, nameof(Math.Floor));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void Frintp_S(AILEmitterCtx Context)
|
public static void Frintp_S(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitScalarUnaryOpF(Context, () =>
|
EmitScalarUnaryOpF(Context, () =>
|
||||||
|
@ -341,6 +349,11 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));
|
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Mul_Ve(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
EmitVectorBinaryOpByElemZx(Context, () => Context.Emit(OpCodes.Mul));
|
||||||
|
}
|
||||||
|
|
||||||
public static void Neg_V(AILEmitterCtx Context)
|
public static void Neg_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
|
EmitVectorUnaryOpSx(Context, () => Context.Emit(OpCodes.Neg));
|
||||||
|
@ -369,6 +382,15 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo));
|
EmitVectorBinaryOpSx(Context, () => Context.EmitCall(MthdInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Smlal_V(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
EmitVectorWidenRnRmTernaryOpSx(Context, () =>
|
||||||
|
{
|
||||||
|
Context.Emit(OpCodes.Mul);
|
||||||
|
Context.Emit(OpCodes.Add);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static void Smull_V(AILEmitterCtx Context)
|
public static void Smull_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul));
|
EmitVectorWidenRnRmBinaryOpSx(Context, () => Context.Emit(OpCodes.Mul));
|
||||||
|
|
|
@ -31,6 +31,36 @@ namespace ChocolArm64.Instruction
|
||||||
EmitFcvt_u_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero));
|
EmitFcvt_u_Gp(Context, () => EmitRoundMathCall(Context, MidpointRounding.AwayFromZero));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Fcvtl_V(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||||
|
|
||||||
|
int SizeF = Op.Size & 1;
|
||||||
|
|
||||||
|
int Elems = 4 >> SizeF;
|
||||||
|
|
||||||
|
int Part = Context.CurrOp.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
|
||||||
|
|
||||||
|
for (int Index = 0; Index < Elems; Index++)
|
||||||
|
{
|
||||||
|
if (SizeF == 0)
|
||||||
|
{
|
||||||
|
//TODO: This need the half precision floating point type,
|
||||||
|
//that is not yet supported on .NET. We should probably
|
||||||
|
//do our own implementation on the meantime.
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
else /* if (SizeF == 1) */
|
||||||
|
{
|
||||||
|
EmitVectorExtractF(Context, Op.Rn, Part + Index, 0);
|
||||||
|
|
||||||
|
Context.Emit(OpCodes.Conv_R8);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Fcvtms_Gp(AILEmitterCtx Context)
|
public static void Fcvtms_Gp(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor)));
|
EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor)));
|
||||||
|
@ -41,6 +71,41 @@ namespace ChocolArm64.Instruction
|
||||||
EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor)));
|
EmitFcvt_u_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Floor)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Fcvtn_V(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||||
|
|
||||||
|
int SizeF = Op.Size & 1;
|
||||||
|
|
||||||
|
int Elems = 4 >> SizeF;
|
||||||
|
|
||||||
|
int Part = Context.CurrOp.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
|
||||||
|
|
||||||
|
for (int Index = 0; Index < Elems; Index++)
|
||||||
|
{
|
||||||
|
EmitVectorExtractF(Context, Op.Rd, Index, SizeF);
|
||||||
|
|
||||||
|
if (SizeF == 0)
|
||||||
|
{
|
||||||
|
//TODO: This need the half precision floating point type,
|
||||||
|
//that is not yet supported on .NET. We should probably
|
||||||
|
//do our own implementation on the meantime.
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
else /* if (SizeF == 1) */
|
||||||
|
{
|
||||||
|
Context.Emit(OpCodes.Conv_R4);
|
||||||
|
|
||||||
|
EmitVectorInsertF(Context, Op.Rd, Part + Index, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||||
|
{
|
||||||
|
EmitVectorZeroUpper(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Fcvtps_Gp(AILEmitterCtx Context)
|
public static void Fcvtps_Gp(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling)));
|
EmitFcvt_s_Gp(Context, () => EmitUnaryMathCall(Context, nameof(Math.Ceiling)));
|
||||||
|
|
|
@ -190,6 +190,11 @@ namespace ChocolArm64.Instruction
|
||||||
EmitScalarSetF(Context, Op.Rd, SizeF);
|
EmitScalarSetF(Context, Op.Rd, SizeF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void EmitVectorUnaryOpF(AILEmitterCtx Context, Action Emit)
|
||||||
|
{
|
||||||
|
EmitVectorOpF(Context, Emit, OperFlags.Rn);
|
||||||
|
}
|
||||||
|
|
||||||
public static void EmitVectorBinaryOpF(AILEmitterCtx Context, Action Emit)
|
public static void EmitVectorBinaryOpF(AILEmitterCtx Context, Action Emit)
|
||||||
{
|
{
|
||||||
EmitVectorOpF(Context, Emit, OperFlags.RnRm);
|
EmitVectorOpF(Context, Emit, OperFlags.RnRm);
|
||||||
|
@ -200,23 +205,9 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorOpF(Context, Emit, OperFlags.RdRnRm);
|
EmitVectorOpF(Context, Emit, OperFlags.RdRnRm);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit)
|
|
||||||
{
|
|
||||||
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
|
|
||||||
|
|
||||||
EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit)
|
|
||||||
{
|
|
||||||
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
|
|
||||||
|
|
||||||
EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers)
|
public static void EmitVectorOpF(AILEmitterCtx Context, Action Emit, OperFlags Opers)
|
||||||
{
|
{
|
||||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||||
|
|
||||||
int SizeF = Op.Size & 1;
|
int SizeF = Op.Size & 1;
|
||||||
|
|
||||||
|
@ -236,7 +227,7 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
if (Opers.HasFlag(OperFlags.Rm))
|
if (Opers.HasFlag(OperFlags.Rm))
|
||||||
{
|
{
|
||||||
EmitVectorExtractF(Context, Op.Rm, Index, SizeF);
|
EmitVectorExtractF(Context, ((AOpCodeSimdReg)Op).Rm, Index, SizeF);
|
||||||
}
|
}
|
||||||
|
|
||||||
Emit();
|
Emit();
|
||||||
|
@ -250,6 +241,20 @@ namespace ChocolArm64.Instruction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void EmitVectorBinaryOpByElemF(AILEmitterCtx Context, Action Emit)
|
||||||
|
{
|
||||||
|
AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp;
|
||||||
|
|
||||||
|
EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EmitVectorTernaryOpByElemF(AILEmitterCtx Context, Action Emit)
|
||||||
|
{
|
||||||
|
AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp;
|
||||||
|
|
||||||
|
EmitVectorOpByElemF(Context, Emit, Op.Index, Ternary: true);
|
||||||
|
}
|
||||||
|
|
||||||
public static void EmitVectorOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary)
|
public static void EmitVectorOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary)
|
||||||
{
|
{
|
||||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
|
@ -341,6 +346,54 @@ namespace ChocolArm64.Instruction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void EmitVectorBinaryOpByElemSx(AILEmitterCtx Context, Action Emit)
|
||||||
|
{
|
||||||
|
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
|
||||||
|
|
||||||
|
EmitVectorOpByElem(Context, Emit, Op.Index, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EmitVectorBinaryOpByElemZx(AILEmitterCtx Context, Action Emit)
|
||||||
|
{
|
||||||
|
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
|
||||||
|
|
||||||
|
EmitVectorOpByElem(Context, Emit, Op.Index, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EmitVectorTernaryOpByElemZx(AILEmitterCtx Context, Action Emit)
|
||||||
|
{
|
||||||
|
AOpCodeSimdRegElem Op = (AOpCodeSimdRegElem)Context.CurrOp;
|
||||||
|
|
||||||
|
EmitVectorOpByElem(Context, Emit, Op.Index, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EmitVectorOpByElem(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary, bool Signed)
|
||||||
|
{
|
||||||
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
|
|
||||||
|
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||||
|
|
||||||
|
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
||||||
|
{
|
||||||
|
if (Ternary)
|
||||||
|
{
|
||||||
|
EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
|
||||||
|
EmitVectorExtract(Context, Op.Rm, Index, Op.Size, Signed);
|
||||||
|
|
||||||
|
Emit();
|
||||||
|
|
||||||
|
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||||
|
{
|
||||||
|
EmitVectorZeroUpper(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void EmitVectorImmUnaryOp(AILEmitterCtx Context, Action Emit)
|
public static void EmitVectorImmUnaryOp(AILEmitterCtx Context, Action Emit)
|
||||||
{
|
{
|
||||||
EmitVectorImmOp(Context, Emit, false);
|
EmitVectorImmOp(Context, Emit, false);
|
||||||
|
@ -411,15 +464,25 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
public static void EmitVectorWidenRnRmBinaryOpSx(AILEmitterCtx Context, Action Emit)
|
public static void EmitVectorWidenRnRmBinaryOpSx(AILEmitterCtx Context, Action Emit)
|
||||||
{
|
{
|
||||||
EmitVectorWidenRnRmBinaryOp(Context, Emit, true);
|
EmitVectorWidenRnRmOp(Context, Emit, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitVectorWidenRnRmBinaryOpZx(AILEmitterCtx Context, Action Emit)
|
public static void EmitVectorWidenRnRmBinaryOpZx(AILEmitterCtx Context, Action Emit)
|
||||||
{
|
{
|
||||||
EmitVectorWidenRnRmBinaryOp(Context, Emit, false);
|
EmitVectorWidenRnRmOp(Context, Emit, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EmitVectorWidenRnRmBinaryOp(AILEmitterCtx Context, Action Emit, bool Signed)
|
public static void EmitVectorWidenRnRmTernaryOpSx(AILEmitterCtx Context, Action Emit)
|
||||||
|
{
|
||||||
|
EmitVectorWidenRnRmOp(Context, Emit, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EmitVectorWidenRnRmTernaryOpZx(AILEmitterCtx Context, Action Emit)
|
||||||
|
{
|
||||||
|
EmitVectorWidenRnRmOp(Context, Emit, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EmitVectorWidenRnRmOp(AILEmitterCtx Context, Action Emit, bool Ternary, bool Signed)
|
||||||
{
|
{
|
||||||
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
|
||||||
|
|
||||||
|
@ -429,6 +492,11 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
for (int Index = 0; Index < Elems; Index++)
|
for (int Index = 0; Index < Elems; Index++)
|
||||||
{
|
{
|
||||||
|
if (Ternary)
|
||||||
|
{
|
||||||
|
EmitVectorExtract(Context, Op.Rd, Index, Op.Size + 1, Signed);
|
||||||
|
}
|
||||||
|
|
||||||
EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed);
|
EmitVectorExtract(Context, Op.Rn, Part + Index, Op.Size, Signed);
|
||||||
EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed);
|
EmitVectorExtract(Context, Op.Rm, Part + Index, Op.Size, Signed);
|
||||||
|
|
||||||
|
|
|
@ -63,15 +63,18 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||||
|
|
||||||
|
int Position = Op.Imm4;
|
||||||
|
|
||||||
for (int Index = 0; Index < Bytes; Index++)
|
for (int Index = 0; Index < Bytes; Index++)
|
||||||
{
|
{
|
||||||
int Position = Op.Imm4 + Index;
|
int Reg = Op.Imm4 + Index < Bytes ? Op.Rn : Op.Rm;
|
||||||
|
|
||||||
int Reg = Position < Bytes ? Op.Rn : Op.Rm;
|
if (Position == Bytes)
|
||||||
|
{
|
||||||
|
Position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
Position &= Bytes - 1;
|
EmitVectorExtractZx(Context, Reg, Position++, 0);
|
||||||
|
|
||||||
EmitVectorExtractZx(Context, Reg, Position, 0);
|
|
||||||
|
|
||||||
EmitVectorInsert(Context, Op.Rd, Index, 0);
|
EmitVectorInsert(Context, Op.Rd, Index, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,15 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift);
|
EmitVectorShImmBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Shll_V(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||||
|
|
||||||
|
int Shift = 8 << Op.Size;
|
||||||
|
|
||||||
|
EmitVectorShImmWidenBinaryZx(Context, () => Context.Emit(OpCodes.Shl), Shift);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Shrn_V(AILEmitterCtx Context)
|
public static void Shrn_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||||
|
@ -41,6 +50,40 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorShImmNarrowBinaryZx(Context, () => Context.Emit(OpCodes.Shr_Un), Shift);
|
EmitVectorShImmNarrowBinaryZx(Context, () => Context.Emit(OpCodes.Shr_Un), Shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Sli_V(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||||
|
|
||||||
|
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||||
|
|
||||||
|
int Shift = Op.Imm - (8 << Op.Size);
|
||||||
|
|
||||||
|
ulong Mask = Shift != 0 ? ulong.MaxValue >> (64 - Shift) : 0;
|
||||||
|
|
||||||
|
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
||||||
|
{
|
||||||
|
EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size);
|
||||||
|
|
||||||
|
Context.EmitLdc_I4(Shift);
|
||||||
|
|
||||||
|
Context.Emit(OpCodes.Shl);
|
||||||
|
|
||||||
|
EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size);
|
||||||
|
|
||||||
|
Context.EmitLdc_I8((long)Mask);
|
||||||
|
|
||||||
|
Context.Emit(OpCodes.And);
|
||||||
|
Context.Emit(OpCodes.Or);
|
||||||
|
|
||||||
|
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Op.RegisterSize == ARegisterSize.SIMD64)
|
||||||
|
{
|
||||||
|
EmitVectorZeroUpper(Context, Op.Rd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Sshl_V(AILEmitterCtx Context)
|
public static void Sshl_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorShl(Context, Signed: true);
|
EmitVectorShl(Context, Signed: true);
|
||||||
|
@ -77,6 +120,21 @@ namespace ChocolArm64.Instruction
|
||||||
EmitVectorShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift);
|
EmitVectorShImmBinarySx(Context, () => Context.Emit(OpCodes.Shr), Shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Ssra_V(AILEmitterCtx Context)
|
||||||
|
{
|
||||||
|
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
||||||
|
|
||||||
|
int Shift = (8 << (Op.Size + 1)) - Op.Imm;
|
||||||
|
|
||||||
|
Action Emit = () =>
|
||||||
|
{
|
||||||
|
Context.Emit(OpCodes.Shr);
|
||||||
|
Context.Emit(OpCodes.Add);
|
||||||
|
};
|
||||||
|
|
||||||
|
EmitVectorShImmTernarySx(Context, Emit, Shift);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Ushl_V(AILEmitterCtx Context)
|
public static void Ushl_V(AILEmitterCtx Context)
|
||||||
{
|
{
|
||||||
EmitVectorShl(Context, Signed: false);
|
EmitVectorShl(Context, Signed: false);
|
||||||
|
@ -195,22 +253,32 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm)
|
private static void EmitVectorShImmBinarySx(AILEmitterCtx Context, Action Emit, int Imm)
|
||||||
{
|
{
|
||||||
EmitVectorShImmBinaryOp(Context, Emit, Imm, true);
|
EmitVectorShImmOp(Context, Emit, Imm, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitVectorShImmTernarySx(AILEmitterCtx Context, Action Emit, int Imm)
|
||||||
|
{
|
||||||
|
EmitVectorShImmOp(Context, Emit, Imm, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm)
|
private static void EmitVectorShImmBinaryZx(AILEmitterCtx Context, Action Emit, int Imm)
|
||||||
{
|
{
|
||||||
EmitVectorShImmBinaryOp(Context, Emit, Imm, false);
|
EmitVectorShImmOp(Context, Emit, Imm, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitVectorShImmBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
|
private static void EmitVectorShImmOp(AILEmitterCtx Context, Action Emit, int Imm, bool Ternary, bool Signed)
|
||||||
{
|
{
|
||||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||||
|
|
||||||
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
|
||||||
|
|
||||||
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
for (int Index = 0; Index < (Bytes >> Op.Size); Index++)
|
||||||
{
|
{
|
||||||
|
if (Ternary)
|
||||||
|
{
|
||||||
|
EmitVectorExtract(Context, Op.Rd, Index, Op.Size, Signed);
|
||||||
|
}
|
||||||
|
|
||||||
EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
|
EmitVectorExtract(Context, Op.Rn, Index, Op.Size, Signed);
|
||||||
|
|
||||||
Context.EmitLdc_I4(Imm);
|
Context.EmitLdc_I4(Imm);
|
||||||
|
@ -238,7 +306,7 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
private static void EmitVectorShImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
|
private static void EmitVectorShImmNarrowBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
|
||||||
{
|
{
|
||||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||||
|
|
||||||
int Elems = 8 >> Op.Size;
|
int Elems = 8 >> Op.Size;
|
||||||
|
|
||||||
|
@ -273,7 +341,7 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
private static void EmitVectorShImmWidenBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
|
private static void EmitVectorShImmWidenBinaryOp(AILEmitterCtx Context, Action Emit, int Imm, bool Signed)
|
||||||
{
|
{
|
||||||
AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp;
|
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
|
||||||
|
|
||||||
int Elems = 8 >> Op.Size;
|
int Elems = 8 >> Op.Size;
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ namespace ChocolArm64.Instruction
|
||||||
case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break;
|
case 0b11_011_0100_0100_001: PropName = nameof(AThreadState.Fpsr); break;
|
||||||
case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break;
|
case 0b11_011_1101_0000_010: PropName = nameof(AThreadState.TpidrEl0); break;
|
||||||
case 0b11_011_1101_0000_011: PropName = nameof(AThreadState.Tpidr); break;
|
case 0b11_011_1101_0000_011: PropName = nameof(AThreadState.Tpidr); break;
|
||||||
|
case 0b11_011_1110_0000_000: PropName = nameof(AThreadState.CntfrqEl0); break;
|
||||||
case 0b11_011_1110_0000_001: PropName = nameof(AThreadState.CntpctEl0); break;
|
case 0b11_011_1110_0000_001: PropName = nameof(AThreadState.CntpctEl0); break;
|
||||||
|
|
||||||
default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}");
|
default: throw new NotImplementedException($"Unknown MRS at {Op.Position:x16}");
|
||||||
|
@ -97,7 +98,7 @@ namespace ChocolArm64.Instruction
|
||||||
for (int Offs = 0; Offs < (4 << AThreadState.DczSizeLog2); Offs += 8)
|
for (int Offs = 0; Offs < (4 << AThreadState.DczSizeLog2); Offs += 8)
|
||||||
{
|
{
|
||||||
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
Context.EmitLdarg(ATranslatedSub.MemoryArgIdx);
|
||||||
Context.EmitLdint(Op.Rt);
|
Context.EmitLdintzr(Op.Rt);
|
||||||
Context.EmitLdc_I(Offs);
|
Context.EmitLdc_I(Offs);
|
||||||
|
|
||||||
Context.Emit(OpCodes.Add);
|
Context.Emit(OpCodes.Add);
|
||||||
|
@ -106,8 +107,13 @@ namespace ChocolArm64.Instruction
|
||||||
|
|
||||||
AInstEmitMemoryHelper.EmitWriteCall(Context, 3);
|
AInstEmitMemoryHelper.EmitWriteCall(Context, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//No-op
|
||||||
|
case 0b11_011_0111_1110_001: //DC CIVAC
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,65 @@ namespace ChocolArm64.Instruction
|
||||||
return (ulong)Size;
|
return (ulong)Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const uint Crc32RevPoly = 0xedb88320;
|
||||||
|
private const uint Crc32cRevPoly = 0x82f63b78;
|
||||||
|
|
||||||
|
public static uint Crc32b(uint Crc, byte Val) => Crc32 (Crc, Crc32RevPoly, Val);
|
||||||
|
public static uint Crc32h(uint Crc, byte Val) => Crc32h(Crc, Crc32RevPoly, Val);
|
||||||
|
public static uint Crc32w(uint Crc, byte Val) => Crc32w(Crc, Crc32RevPoly, Val);
|
||||||
|
public static uint Crc32x(uint Crc, byte Val) => Crc32x(Crc, Crc32RevPoly, Val);
|
||||||
|
|
||||||
|
public static uint Crc32cb(uint Crc, byte Val) => Crc32 (Crc, Crc32cRevPoly, Val);
|
||||||
|
public static uint Crc32ch(uint Crc, byte Val) => Crc32h(Crc, Crc32cRevPoly, Val);
|
||||||
|
public static uint Crc32cw(uint Crc, byte Val) => Crc32w(Crc, Crc32cRevPoly, Val);
|
||||||
|
public static uint Crc32cx(uint Crc, byte Val) => Crc32x(Crc, Crc32cRevPoly, Val);
|
||||||
|
|
||||||
|
private static uint Crc32h(uint Crc, uint Poly, ushort Val)
|
||||||
|
{
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 0));
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 8));
|
||||||
|
|
||||||
|
return Crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint Crc32w(uint Crc, uint Poly, uint Val)
|
||||||
|
{
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 0));
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 8));
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 16));
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 24));
|
||||||
|
|
||||||
|
return Crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint Crc32x(uint Crc, uint Poly, ulong Val)
|
||||||
|
{
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 0));
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 8));
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 16));
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 24));
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 32));
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 40));
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 48));
|
||||||
|
Crc = Crc32(Crc, Poly, (byte)(Val >> 56));
|
||||||
|
|
||||||
|
return Crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint Crc32(uint Crc, uint Poly, byte Val)
|
||||||
|
{
|
||||||
|
Crc ^= Val;
|
||||||
|
|
||||||
|
for (int Bit = 7; Bit >= 0; Bit--)
|
||||||
|
{
|
||||||
|
uint Mask = (uint)(-(int)(Crc & 1));
|
||||||
|
|
||||||
|
Crc = (Crc >> 1) ^ (Poly & Mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Crc;
|
||||||
|
}
|
||||||
|
|
||||||
public static uint ReverseBits32(uint Value)
|
public static uint ReverseBits32(uint Value)
|
||||||
{
|
{
|
||||||
Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1);
|
Value = ((Value & 0xaaaaaaaa) >> 1) | ((Value & 0x55555555) << 1);
|
||||||
|
|
|
@ -2,11 +2,11 @@ using ChocolArm64.Exceptions;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ChocolArm64.Memory
|
namespace ChocolArm64.Memory
|
||||||
{
|
{
|
||||||
public unsafe class AMemory
|
public unsafe class AMemory : IDisposable
|
||||||
{
|
{
|
||||||
private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1;
|
private const long ErgMask = (4 << AThreadState.ErgSizeLog2) - 1;
|
||||||
|
|
||||||
|
@ -39,9 +39,11 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
private HashSet<long> ExAddrs;
|
private HashSet<long> ExAddrs;
|
||||||
|
|
||||||
|
public IntPtr Ram { get; private set; }
|
||||||
|
|
||||||
private byte* RamPtr;
|
private byte* RamPtr;
|
||||||
|
|
||||||
public AMemory(IntPtr Ram)
|
public AMemory()
|
||||||
{
|
{
|
||||||
Manager = new AMemoryMgr();
|
Manager = new AMemoryMgr();
|
||||||
|
|
||||||
|
@ -49,6 +51,8 @@ namespace ChocolArm64.Memory
|
||||||
|
|
||||||
ExAddrs = new HashSet<long>();
|
ExAddrs = new HashSet<long>();
|
||||||
|
|
||||||
|
Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize + AMemoryMgr.PageSize);
|
||||||
|
|
||||||
RamPtr = (byte*)Ram;
|
RamPtr = (byte*)Ram;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,98 +138,79 @@ namespace ChocolArm64.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sbyte ReadSByte(long Position) => (sbyte)ReadByte (Position);
|
public sbyte ReadSByte(long Position)
|
||||||
public short ReadInt16(long Position) => (short)ReadUInt16(Position);
|
{
|
||||||
public int ReadInt32(long Position) => (int)ReadUInt32(Position);
|
return (sbyte)ReadByte(Position);
|
||||||
public long ReadInt64(long Position) => (long)ReadUInt64(Position);
|
}
|
||||||
|
|
||||||
|
public short ReadInt16(long Position)
|
||||||
|
{
|
||||||
|
return (short)ReadUInt16(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ReadInt32(long Position)
|
||||||
|
{
|
||||||
|
return (int)ReadUInt32(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long ReadInt64(long Position)
|
||||||
|
{
|
||||||
|
return (long)ReadUInt64(Position);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public byte ReadByte(long Position)
|
public byte ReadByte(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
||||||
#endif
|
|
||||||
|
|
||||||
return *((byte*)(RamPtr + (uint)Position));
|
return ReadByteUnchecked(Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ushort ReadUInt16(long Position)
|
public ushort ReadUInt16(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
EnsureAccessIsValid(Position + 1, AMemoryPerm.Read);
|
||||||
#endif
|
|
||||||
|
|
||||||
return *((ushort*)(RamPtr + (uint)Position));
|
return ReadUInt16Unchecked(Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public uint ReadUInt32(long Position)
|
public uint ReadUInt32(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
EnsureAccessIsValid(Position + 3, AMemoryPerm.Read);
|
||||||
#endif
|
|
||||||
|
|
||||||
return *((uint*)(RamPtr + (uint)Position));
|
return ReadUInt32Unchecked(Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public ulong ReadUInt64(long Position)
|
public ulong ReadUInt64(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
EnsureAccessIsValid(Position + 0, AMemoryPerm.Read);
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
EnsureAccessIsValid(Position + 7, AMemoryPerm.Read);
|
||||||
#endif
|
|
||||||
|
|
||||||
return *((ulong*)(RamPtr + (uint)Position));
|
return ReadUInt64Unchecked(Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public AVec ReadVector8(long Position)
|
public AVec ReadVector8(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return new AVec() { B0 = ReadByte(Position) };
|
return new AVec() { B0 = ReadByte(Position) };
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public AVec ReadVector16(long Position)
|
public AVec ReadVector16(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return new AVec() { H0 = ReadUInt16(Position) };
|
return new AVec() { H0 = ReadUInt16(Position) };
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public AVec ReadVector32(long Position)
|
public AVec ReadVector32(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return new AVec() { W0 = ReadUInt32(Position) };
|
return new AVec() { W0 = ReadUInt32(Position) };
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public AVec ReadVector64(long Position)
|
public AVec ReadVector64(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return new AVec() { X0 = ReadUInt64(Position) };
|
return new AVec() { X0 = ReadUInt64(Position) };
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public AVec ReadVector128(long Position)
|
public AVec ReadVector128(long Position)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Read);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return new AVec()
|
return new AVec()
|
||||||
{
|
{
|
||||||
X0 = ReadUInt64(Position + 0),
|
X0 = ReadUInt64(Position + 0),
|
||||||
|
@ -233,102 +218,218 @@ namespace ChocolArm64.Memory
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteSByte(long Position, sbyte Value) => WriteByte (Position, (byte)Value);
|
public sbyte ReadSByteUnchecked(long Position)
|
||||||
public void WriteInt16(long Position, short Value) => WriteUInt16(Position, (ushort)Value);
|
{
|
||||||
public void WriteInt32(long Position, int Value) => WriteUInt32(Position, (uint)Value);
|
return (sbyte)ReadByteUnchecked(Position);
|
||||||
public void WriteInt64(long Position, long Value) => WriteUInt64(Position, (ulong)Value);
|
}
|
||||||
|
|
||||||
|
public short ReadInt16Unchecked(long Position)
|
||||||
|
{
|
||||||
|
return (short)ReadUInt16Unchecked(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ReadInt32Unchecked(long Position)
|
||||||
|
{
|
||||||
|
return (int)ReadUInt32Unchecked(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long ReadInt64Unchecked(long Position)
|
||||||
|
{
|
||||||
|
return (long)ReadUInt64Unchecked(Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte ReadByteUnchecked(long Position)
|
||||||
|
{
|
||||||
|
return *((byte*)(RamPtr + (uint)Position));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ushort ReadUInt16Unchecked(long Position)
|
||||||
|
{
|
||||||
|
return *((ushort*)(RamPtr + (uint)Position));
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint ReadUInt32Unchecked(long Position)
|
||||||
|
{
|
||||||
|
return *((uint*)(RamPtr + (uint)Position));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong ReadUInt64Unchecked(long Position)
|
||||||
|
{
|
||||||
|
return *((ulong*)(RamPtr + (uint)Position));
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVec ReadVector8Unchecked(long Position)
|
||||||
|
{
|
||||||
|
return new AVec() { B0 = ReadByteUnchecked(Position) };
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVec ReadVector16Unchecked(long Position)
|
||||||
|
{
|
||||||
|
return new AVec() { H0 = ReadUInt16Unchecked(Position) };
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVec ReadVector32Unchecked(long Position)
|
||||||
|
{
|
||||||
|
return new AVec() { W0 = ReadUInt32Unchecked(Position) };
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVec ReadVector64Unchecked(long Position)
|
||||||
|
{
|
||||||
|
return new AVec() { X0 = ReadUInt64Unchecked(Position) };
|
||||||
|
}
|
||||||
|
|
||||||
|
public AVec ReadVector128Unchecked(long Position)
|
||||||
|
{
|
||||||
|
return new AVec()
|
||||||
|
{
|
||||||
|
X0 = ReadUInt64Unchecked(Position + 0),
|
||||||
|
X1 = ReadUInt64Unchecked(Position + 8)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteSByte(long Position, sbyte Value)
|
||||||
|
{
|
||||||
|
WriteByte(Position, (byte)Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteInt16(long Position, short Value)
|
||||||
|
{
|
||||||
|
WriteUInt16(Position, (ushort)Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteInt32(long Position, int Value)
|
||||||
|
{
|
||||||
|
WriteUInt32(Position, (uint)Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteInt64(long Position, long Value)
|
||||||
|
{
|
||||||
|
WriteUInt64(Position, (ulong)Value);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void WriteByte(long Position, byte Value)
|
public void WriteByte(long Position, byte Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
||||||
#endif
|
|
||||||
|
|
||||||
*((byte*)(RamPtr + (uint)Position)) = Value;
|
WriteByteUnchecked(Position, Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void WriteUInt16(long Position, ushort Value)
|
public void WriteUInt16(long Position, ushort Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
EnsureAccessIsValid(Position + 1, AMemoryPerm.Write);
|
||||||
#endif
|
|
||||||
|
|
||||||
*((ushort*)(RamPtr + (uint)Position)) = Value;
|
WriteUInt16Unchecked(Position, Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void WriteUInt32(long Position, uint Value)
|
public void WriteUInt32(long Position, uint Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
EnsureAccessIsValid(Position + 3, AMemoryPerm.Write);
|
||||||
#endif
|
|
||||||
|
|
||||||
*((uint*)(RamPtr + (uint)Position)) = Value;
|
WriteUInt32Unchecked(Position, Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void WriteUInt64(long Position, ulong Value)
|
public void WriteUInt64(long Position, ulong Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
EnsureAccessIsValid(Position + 0, AMemoryPerm.Write);
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
EnsureAccessIsValid(Position + 7, AMemoryPerm.Write);
|
||||||
#endif
|
|
||||||
|
|
||||||
*((ulong*)(RamPtr + (uint)Position)) = Value;
|
WriteUInt64Unchecked(Position, Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void WriteVector8(long Position, AVec Value)
|
public void WriteVector8(long Position, AVec Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
WriteByte(Position, Value.B0);
|
WriteByte(Position, Value.B0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void WriteVector16(long Position, AVec Value)
|
public void WriteVector16(long Position, AVec Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
WriteUInt16(Position, Value.H0);
|
WriteUInt16(Position, Value.H0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void WriteVector32(long Position, AVec Value)
|
public void WriteVector32(long Position, AVec Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
WriteUInt32(Position, Value.W0);
|
WriteUInt32(Position, Value.W0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void WriteVector64(long Position, AVec Value)
|
public void WriteVector64(long Position, AVec Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
WriteUInt64(Position, Value.X0);
|
WriteUInt64(Position, Value.X0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void WriteVector128(long Position, AVec Value)
|
public void WriteVector128(long Position, AVec Value)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
EnsureAccessIsValid(Position, AMemoryPerm.Write);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
WriteUInt64(Position + 0, Value.X0);
|
WriteUInt64(Position + 0, Value.X0);
|
||||||
WriteUInt64(Position + 8, Value.X1);
|
WriteUInt64(Position + 8, Value.X1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void WriteSByteUnchecked(long Position, sbyte Value)
|
||||||
|
{
|
||||||
|
WriteByteUnchecked(Position, (byte)Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteInt16Unchecked(long Position, short Value)
|
||||||
|
{
|
||||||
|
WriteUInt16Unchecked(Position, (ushort)Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteInt32Unchecked(long Position, int Value)
|
||||||
|
{
|
||||||
|
WriteUInt32Unchecked(Position, (uint)Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteInt64Unchecked(long Position, long Value)
|
||||||
|
{
|
||||||
|
WriteUInt64Unchecked(Position, (ulong)Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteByteUnchecked(long Position, byte Value)
|
||||||
|
{
|
||||||
|
*((byte*)(RamPtr + (uint)Position)) = Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteUInt16Unchecked(long Position, ushort Value)
|
||||||
|
{
|
||||||
|
*((ushort*)(RamPtr + (uint)Position)) = Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteUInt32Unchecked(long Position, uint Value)
|
||||||
|
{
|
||||||
|
*((uint*)(RamPtr + (uint)Position)) = Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteUInt64Unchecked(long Position, ulong Value)
|
||||||
|
{
|
||||||
|
*((ulong*)(RamPtr + (uint)Position)) = Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteVector8Unchecked(long Position, AVec Value)
|
||||||
|
{
|
||||||
|
WriteByteUnchecked(Position, Value.B0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteVector16Unchecked(long Position, AVec Value)
|
||||||
|
{
|
||||||
|
WriteUInt16Unchecked(Position, Value.H0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteVector32Unchecked(long Position, AVec Value)
|
||||||
|
{
|
||||||
|
WriteUInt32Unchecked(Position, Value.W0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteVector64Unchecked(long Position, AVec Value)
|
||||||
|
{
|
||||||
|
WriteUInt64Unchecked(Position, Value.X0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteVector128Unchecked(long Position, AVec Value)
|
||||||
|
{
|
||||||
|
WriteUInt64Unchecked(Position + 0, Value.X0);
|
||||||
|
WriteUInt64Unchecked(Position + 8, Value.X1);
|
||||||
|
}
|
||||||
|
|
||||||
private void EnsureAccessIsValid(long Position, AMemoryPerm Perm)
|
private void EnsureAccessIsValid(long Position, AMemoryPerm Perm)
|
||||||
{
|
{
|
||||||
if (!Manager.IsMapped(Position))
|
if (!Manager.IsMapped(Position))
|
||||||
|
@ -341,5 +442,20 @@ namespace ChocolArm64.Memory
|
||||||
throw new VmmAccessViolationException(Position, Perm);
|
throw new VmmAccessViolationException(Position, Perm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (Ram != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(Ram);
|
||||||
|
|
||||||
|
Ram = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,8 +4,8 @@ namespace ChocolArm64.Memory
|
||||||
{
|
{
|
||||||
public class AMemoryMgr
|
public class AMemoryMgr
|
||||||
{
|
{
|
||||||
public const long AddrSize = RamSize;
|
|
||||||
public const long RamSize = 4L * 1024 * 1024 * 1024;
|
public const long RamSize = 4L * 1024 * 1024 * 1024;
|
||||||
|
public const long AddrSize = RamSize;
|
||||||
|
|
||||||
private const int PTLvl0Bits = 10;
|
private const int PTLvl0Bits = 10;
|
||||||
private const int PTLvl1Bits = 10;
|
private const int PTLvl1Bits = 10;
|
||||||
|
@ -19,8 +19,8 @@ namespace ChocolArm64.Memory
|
||||||
private const int PTLvl1Mask = PTLvl1Size - 1;
|
private const int PTLvl1Mask = PTLvl1Size - 1;
|
||||||
public const int PageMask = PageSize - 1;
|
public const int PageMask = PageSize - 1;
|
||||||
|
|
||||||
private const int PTLvl0Bit = PTPageBits + PTLvl1Bits;
|
private const int PTLvl0Bit = PTPageBits + PTLvl1Bits;
|
||||||
private const int PTLvl1Bit = PTPageBits;
|
private const int PTLvl1Bit = PTPageBits;
|
||||||
|
|
||||||
private enum PTMap
|
private enum PTMap
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using ChocolArm64.Events;
|
using ChocolArm64.Events;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace ChocolArm64.State
|
namespace ChocolArm64.State
|
||||||
{
|
{
|
||||||
|
@ -29,6 +30,8 @@ namespace ChocolArm64.State
|
||||||
public int ProcessId;
|
public int ProcessId;
|
||||||
public int ThreadId;
|
public int ThreadId;
|
||||||
|
|
||||||
|
public bool Running { get; set; }
|
||||||
|
|
||||||
public long TpidrEl0 { get; set; }
|
public long TpidrEl0 { get; set; }
|
||||||
public long Tpidr { get; set; }
|
public long Tpidr { get; set; }
|
||||||
|
|
||||||
|
@ -38,15 +41,34 @@ namespace ChocolArm64.State
|
||||||
public uint CtrEl0 => 0x8444c004;
|
public uint CtrEl0 => 0x8444c004;
|
||||||
public uint DczidEl0 => 0x00000004;
|
public uint DczidEl0 => 0x00000004;
|
||||||
|
|
||||||
private const long TicksPerS = 19_200_000;
|
public ulong CntfrqEl0 { get; set; }
|
||||||
private const long TicksPerMS = TicksPerS / 1_000;
|
public ulong CntpctEl0
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
double Ticks = TickCounter.ElapsedTicks * HostTickFreq;
|
||||||
|
|
||||||
public long CntpctEl0 => Environment.TickCount * TicksPerMS;
|
return (ulong)(Ticks * CntfrqEl0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public event EventHandler<AInstExceptionEventArgs> Break;
|
public event EventHandler<AInstExceptionEventArgs> Break;
|
||||||
public event EventHandler<AInstExceptionEventArgs> SvcCall;
|
public event EventHandler<AInstExceptionEventArgs> SvcCall;
|
||||||
public event EventHandler<AInstUndefinedEventArgs> Undefined;
|
public event EventHandler<AInstUndefinedEventArgs> Undefined;
|
||||||
|
|
||||||
|
private static Stopwatch TickCounter;
|
||||||
|
|
||||||
|
private static double HostTickFreq;
|
||||||
|
|
||||||
|
static AThreadState()
|
||||||
|
{
|
||||||
|
HostTickFreq = 1.0 / Stopwatch.Frequency;
|
||||||
|
|
||||||
|
TickCounter = new Stopwatch();
|
||||||
|
|
||||||
|
TickCounter.Start();
|
||||||
|
}
|
||||||
|
|
||||||
internal void OnBreak(int Imm)
|
internal void OnBreak(int Imm)
|
||||||
{
|
{
|
||||||
Break?.Invoke(this, new AInstExceptionEventArgs(Imm));
|
Break?.Invoke(this, new AInstExceptionEventArgs(Imm));
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using System.Diagnostics;
|
using Ryujinx.Core.OsHle.Handles;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Core.Input
|
namespace Ryujinx.Core.Input
|
||||||
{
|
{
|
||||||
|
@ -60,33 +61,63 @@ namespace Ryujinx.Core.Input
|
||||||
|
|
||||||
private const int HidEntryCount = 17;
|
private const int HidEntryCount = 17;
|
||||||
|
|
||||||
private long SharedMemOffset;
|
private object ShMemLock;
|
||||||
|
|
||||||
private Switch Ns;
|
private (AMemory, long)[] ShMemPositions;
|
||||||
|
|
||||||
public Hid(Switch Ns)
|
public Hid()
|
||||||
{
|
{
|
||||||
this.Ns = Ns;
|
ShMemLock = new object();
|
||||||
|
|
||||||
|
ShMemPositions = new (AMemory, long)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Init(long HidOffset)
|
internal void ShMemMap(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
SharedMemOffset = HidOffset;
|
HSharedMem SharedMem = (HSharedMem)sender;
|
||||||
|
|
||||||
|
lock (ShMemLock)
|
||||||
|
{
|
||||||
|
ShMemPositions = SharedMem.GetVirtualPositions();
|
||||||
|
|
||||||
|
(AMemory Memory, long Position) ShMem = ShMemPositions[ShMemPositions.Length - 1];
|
||||||
|
|
||||||
|
Logging.Info($"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!");
|
||||||
|
|
||||||
|
Init(ShMem.Memory, ShMem.Position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ShMemUnmap(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
HSharedMem SharedMem = (HSharedMem)sender;
|
||||||
|
|
||||||
|
lock (ShMemLock)
|
||||||
|
{
|
||||||
|
ShMemPositions = SharedMem.GetVirtualPositions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Init(AMemory Memory, long Position)
|
||||||
|
{
|
||||||
InitializeJoyconPair(
|
InitializeJoyconPair(
|
||||||
|
Memory,
|
||||||
|
Position,
|
||||||
JoyConColor.Body_Neon_Red,
|
JoyConColor.Body_Neon_Red,
|
||||||
JoyConColor.Buttons_Neon_Red,
|
JoyConColor.Buttons_Neon_Red,
|
||||||
JoyConColor.Body_Neon_Blue,
|
JoyConColor.Body_Neon_Blue,
|
||||||
JoyConColor.Buttons_Neon_Blue);
|
JoyConColor.Buttons_Neon_Blue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitializeJoyconPair(
|
private void InitializeJoyconPair(
|
||||||
|
AMemory Memory,
|
||||||
|
long Position,
|
||||||
JoyConColor LeftColorBody,
|
JoyConColor LeftColorBody,
|
||||||
JoyConColor LeftColorButtons,
|
JoyConColor LeftColorButtons,
|
||||||
JoyConColor RightColorBody,
|
JoyConColor RightColorBody,
|
||||||
JoyConColor RightColorButtons)
|
JoyConColor RightColorButtons)
|
||||||
{
|
{
|
||||||
long BaseControllerOffset = HidControllersOffset + 8 * HidControllerSize;
|
long BaseControllerOffset = Position + HidControllersOffset + 8 * HidControllerSize;
|
||||||
|
|
||||||
HidControllerType Type =
|
HidControllerType Type =
|
||||||
HidControllerType.ControllerType_Handheld |
|
HidControllerType.ControllerType_Handheld |
|
||||||
|
@ -102,20 +133,20 @@ namespace Ryujinx.Core.Input
|
||||||
|
|
||||||
HidControllerColorDesc SplitColorDesc = 0;
|
HidControllerColorDesc SplitColorDesc = 0;
|
||||||
|
|
||||||
WriteInt32(BaseControllerOffset + 0x0, (int)Type);
|
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x0, (int)Type);
|
||||||
|
|
||||||
WriteInt32(BaseControllerOffset + 0x4, IsHalf ? 1 : 0);
|
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x4, IsHalf ? 1 : 0);
|
||||||
|
|
||||||
WriteInt32(BaseControllerOffset + 0x8, (int)SingleColorDesc);
|
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x8, (int)SingleColorDesc);
|
||||||
WriteInt32(BaseControllerOffset + 0xc, (int)SingleColorBody);
|
Memory.WriteInt32Unchecked(BaseControllerOffset + 0xc, (int)SingleColorBody);
|
||||||
WriteInt32(BaseControllerOffset + 0x10, (int)SingleColorButtons);
|
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x10, (int)SingleColorButtons);
|
||||||
WriteInt32(BaseControllerOffset + 0x14, (int)SplitColorDesc);
|
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x14, (int)SplitColorDesc);
|
||||||
|
|
||||||
WriteInt32(BaseControllerOffset + 0x18, (int)LeftColorBody);
|
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x18, (int)LeftColorBody);
|
||||||
WriteInt32(BaseControllerOffset + 0x1c, (int)LeftColorButtons);
|
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x1c, (int)LeftColorButtons);
|
||||||
|
|
||||||
WriteInt32(BaseControllerOffset + 0x20, (int)RightColorBody);
|
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x20, (int)RightColorBody);
|
||||||
WriteInt32(BaseControllerOffset + 0x24, (int)RightColorButtons);
|
Memory.WriteInt32Unchecked(BaseControllerOffset + 0x24, (int)RightColorButtons);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetJoyconButton(
|
public void SetJoyconButton(
|
||||||
|
@ -125,115 +156,109 @@ namespace Ryujinx.Core.Input
|
||||||
HidJoystickPosition LeftStick,
|
HidJoystickPosition LeftStick,
|
||||||
HidJoystickPosition RightStick)
|
HidJoystickPosition RightStick)
|
||||||
{
|
{
|
||||||
long ControllerOffset = HidControllersOffset + (int)ControllerId * HidControllerSize;
|
lock (ShMemLock)
|
||||||
|
{
|
||||||
|
foreach ((AMemory Memory, long Position) in ShMemPositions)
|
||||||
|
{
|
||||||
|
long ControllerOffset = Position + HidControllersOffset;
|
||||||
|
|
||||||
ControllerOffset += HidControllerHeaderSize;
|
ControllerOffset += (int)ControllerId * HidControllerSize;
|
||||||
|
|
||||||
ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize;
|
ControllerOffset += HidControllerHeaderSize;
|
||||||
|
|
||||||
long LastEntry = ReadInt64(ControllerOffset + 0x10);
|
ControllerOffset += (int)ControllerLayout * HidControllerLayoutsSize;
|
||||||
|
|
||||||
long CurrEntry = (LastEntry + 1) % HidEntryCount;
|
long LastEntry = Memory.ReadInt64Unchecked(ControllerOffset + 0x10);
|
||||||
|
|
||||||
long Timestamp = Stopwatch.GetTimestamp();
|
long CurrEntry = (LastEntry + 1) % HidEntryCount;
|
||||||
|
|
||||||
WriteInt64(ControllerOffset + 0x0, Timestamp);
|
long Timestamp = GetTimestamp();
|
||||||
WriteInt64(ControllerOffset + 0x8, HidEntryCount);
|
|
||||||
WriteInt64(ControllerOffset + 0x10, CurrEntry);
|
|
||||||
WriteInt64(ControllerOffset + 0x18, HidEntryCount - 1);
|
|
||||||
|
|
||||||
ControllerOffset += HidControllersLayoutHeaderSize;
|
Memory.WriteInt64Unchecked(ControllerOffset + 0x0, Timestamp);
|
||||||
|
Memory.WriteInt64Unchecked(ControllerOffset + 0x8, HidEntryCount);
|
||||||
|
Memory.WriteInt64Unchecked(ControllerOffset + 0x10, CurrEntry);
|
||||||
|
Memory.WriteInt64Unchecked(ControllerOffset + 0x18, HidEntryCount - 1);
|
||||||
|
|
||||||
ControllerOffset += CurrEntry * HidControllersInputEntrySize;
|
ControllerOffset += HidControllersLayoutHeaderSize;
|
||||||
|
|
||||||
WriteInt64(ControllerOffset + 0x0, Timestamp);
|
ControllerOffset += CurrEntry * HidControllersInputEntrySize;
|
||||||
WriteInt64(ControllerOffset + 0x8, Timestamp);
|
|
||||||
|
|
||||||
WriteInt64(ControllerOffset + 0x10, (uint)Buttons);
|
Memory.WriteInt64Unchecked(ControllerOffset + 0x0, Timestamp);
|
||||||
|
Memory.WriteInt64Unchecked(ControllerOffset + 0x8, Timestamp);
|
||||||
|
|
||||||
WriteInt32(ControllerOffset + 0x18, LeftStick.DX);
|
Memory.WriteInt64Unchecked(ControllerOffset + 0x10, (uint)Buttons);
|
||||||
WriteInt32(ControllerOffset + 0x1c, LeftStick.DY);
|
|
||||||
|
|
||||||
WriteInt64(ControllerOffset + 0x20, RightStick.DX);
|
Memory.WriteInt32Unchecked(ControllerOffset + 0x18, LeftStick.DX);
|
||||||
WriteInt64(ControllerOffset + 0x24, RightStick.DY);
|
Memory.WriteInt32Unchecked(ControllerOffset + 0x1c, LeftStick.DY);
|
||||||
|
|
||||||
WriteInt64(ControllerOffset + 0x28,
|
Memory.WriteInt64Unchecked(ControllerOffset + 0x20, RightStick.DX);
|
||||||
(uint)HidControllerConnState.Controller_State_Connected |
|
Memory.WriteInt64Unchecked(ControllerOffset + 0x24, RightStick.DY);
|
||||||
(uint)HidControllerConnState.Controller_State_Wired);
|
|
||||||
|
Memory.WriteInt64Unchecked(ControllerOffset + 0x28,
|
||||||
|
(uint)HidControllerConnState.Controller_State_Connected |
|
||||||
|
(uint)HidControllerConnState.Controller_State_Wired);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTouchPoints(params HidTouchPoint[] Points)
|
public void SetTouchPoints(params HidTouchPoint[] Points)
|
||||||
{
|
{
|
||||||
long LastEntry = ReadInt64(HidTouchScreenOffset + 0x10);
|
lock (ShMemLock)
|
||||||
|
|
||||||
long CurrEntry = (LastEntry + 1) % HidEntryCount;
|
|
||||||
|
|
||||||
long Timestamp = Stopwatch.GetTimestamp();
|
|
||||||
|
|
||||||
WriteInt64(HidTouchScreenOffset + 0x0, Timestamp);
|
|
||||||
WriteInt64(HidTouchScreenOffset + 0x8, HidEntryCount);
|
|
||||||
WriteInt64(HidTouchScreenOffset + 0x10, CurrEntry);
|
|
||||||
WriteInt64(HidTouchScreenOffset + 0x18, HidEntryCount - 1);
|
|
||||||
WriteInt64(HidTouchScreenOffset + 0x20, Timestamp);
|
|
||||||
|
|
||||||
long TouchEntryOffset = HidTouchScreenOffset + HidTouchHeaderSize;
|
|
||||||
|
|
||||||
long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize;
|
|
||||||
|
|
||||||
long LastTimestamp = ReadInt64(LastEntryOffset);
|
|
||||||
|
|
||||||
TouchEntryOffset += CurrEntry * HidTouchEntrySize;
|
|
||||||
|
|
||||||
WriteInt64(TouchEntryOffset + 0x0, LastTimestamp + 1);
|
|
||||||
WriteInt64(TouchEntryOffset + 0x8, Points.Length);
|
|
||||||
|
|
||||||
TouchEntryOffset += HidTouchEntryHeaderSize;
|
|
||||||
|
|
||||||
const int Padding = 0;
|
|
||||||
|
|
||||||
int Index = 0;
|
|
||||||
|
|
||||||
foreach (HidTouchPoint Point in Points)
|
|
||||||
{
|
{
|
||||||
WriteInt64(TouchEntryOffset + 0x0, Timestamp);
|
foreach ((AMemory Memory, long Position) in ShMemPositions)
|
||||||
WriteInt32(TouchEntryOffset + 0x8, Padding);
|
{
|
||||||
WriteInt32(TouchEntryOffset + 0xc, Index++);
|
long TouchScreenOffset = Position + HidTouchScreenOffset;
|
||||||
WriteInt32(TouchEntryOffset + 0x10, Point.X);
|
|
||||||
WriteInt32(TouchEntryOffset + 0x14, Point.Y);
|
|
||||||
WriteInt32(TouchEntryOffset + 0x18, Point.DiameterX);
|
|
||||||
WriteInt32(TouchEntryOffset + 0x1c, Point.DiameterY);
|
|
||||||
WriteInt32(TouchEntryOffset + 0x20, Point.Angle);
|
|
||||||
WriteInt32(TouchEntryOffset + 0x24, Padding);
|
|
||||||
|
|
||||||
TouchEntryOffset += HidTouchEntryTouchSize;
|
long LastEntry = Memory.ReadInt64Unchecked(TouchScreenOffset + 0x10);
|
||||||
|
|
||||||
|
long CurrEntry = (LastEntry + 1) % HidEntryCount;
|
||||||
|
|
||||||
|
long Timestamp = GetTimestamp();
|
||||||
|
|
||||||
|
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x0, Timestamp);
|
||||||
|
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x8, HidEntryCount);
|
||||||
|
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x10, CurrEntry);
|
||||||
|
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x18, HidEntryCount - 1);
|
||||||
|
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp);
|
||||||
|
|
||||||
|
long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize;
|
||||||
|
|
||||||
|
long LastEntryOffset = TouchEntryOffset + LastEntry * HidTouchEntrySize;
|
||||||
|
|
||||||
|
long SampleCounter = Memory.ReadInt64Unchecked(LastEntryOffset) + 1;
|
||||||
|
|
||||||
|
TouchEntryOffset += CurrEntry * HidTouchEntrySize;
|
||||||
|
|
||||||
|
Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, SampleCounter);
|
||||||
|
Memory.WriteInt64Unchecked(TouchEntryOffset + 0x8, Points.Length);
|
||||||
|
|
||||||
|
TouchEntryOffset += HidTouchEntryHeaderSize;
|
||||||
|
|
||||||
|
const int Padding = 0;
|
||||||
|
|
||||||
|
int Index = 0;
|
||||||
|
|
||||||
|
foreach (HidTouchPoint Point in Points)
|
||||||
|
{
|
||||||
|
Memory.WriteInt64Unchecked(TouchEntryOffset + 0x0, Timestamp);
|
||||||
|
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x8, Padding);
|
||||||
|
Memory.WriteInt32Unchecked(TouchEntryOffset + 0xc, Index++);
|
||||||
|
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x10, Point.X);
|
||||||
|
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x14, Point.Y);
|
||||||
|
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x18, Point.DiameterX);
|
||||||
|
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x1c, Point.DiameterY);
|
||||||
|
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x20, Point.Angle);
|
||||||
|
Memory.WriteInt32Unchecked(TouchEntryOffset + 0x24, Padding);
|
||||||
|
|
||||||
|
TouchEntryOffset += HidTouchEntryTouchSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe long ReadInt64(long Position)
|
private static long GetTimestamp()
|
||||||
{
|
{
|
||||||
Position += SharedMemOffset;
|
return (long)((ulong)Environment.TickCount * 19_200);
|
||||||
|
|
||||||
if ((ulong)Position + 8 > AMemoryMgr.AddrSize) return 0;
|
|
||||||
|
|
||||||
return *((long*)((byte*)Ns.Ram + Position));
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe void WriteInt32(long Position, int Value)
|
|
||||||
{
|
|
||||||
Position += SharedMemOffset;
|
|
||||||
|
|
||||||
if ((ulong)Position + 4 > AMemoryMgr.AddrSize) return;
|
|
||||||
|
|
||||||
*((int*)((byte*)Ns.Ram + Position)) = Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe void WriteInt64(long Position, long Value)
|
|
||||||
{
|
|
||||||
Position += SharedMemOffset;
|
|
||||||
|
|
||||||
if ((ulong)Position + 8 > AMemoryMgr.AddrSize) return;
|
|
||||||
|
|
||||||
*((long*)((byte*)Ns.Ram + Position)) = Value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -34,7 +34,7 @@ namespace Ryujinx.Core.Loaders
|
||||||
|
|
||||||
if (Exe.Mod0Offset == 0)
|
if (Exe.Mod0Offset == 0)
|
||||||
{
|
{
|
||||||
int BssOffset = Exe.DataOffset + Exe.Data.Count;
|
int BssOffset = Exe.DataOffset + Exe.Data.Length;
|
||||||
int BssSize = Exe.BssSize;
|
int BssSize = Exe.BssSize;
|
||||||
|
|
||||||
MapBss(ImageBase + BssOffset, BssSize);
|
MapBss(ImageBase + BssOffset, BssSize);
|
||||||
|
@ -92,18 +92,15 @@ namespace Ryujinx.Core.Loaders
|
||||||
|
|
||||||
private void WriteData(
|
private void WriteData(
|
||||||
long Position,
|
long Position,
|
||||||
IList<byte> Data,
|
byte[] Data,
|
||||||
MemoryType Type,
|
MemoryType Type,
|
||||||
AMemoryPerm Perm)
|
AMemoryPerm Perm)
|
||||||
{
|
{
|
||||||
Memory.Manager.Map(Position, Data.Count, (int)Type, AMemoryPerm.Write);
|
Memory.Manager.Map(Position, Data.Length, (int)Type, AMemoryPerm.Write);
|
||||||
|
|
||||||
for (int Index = 0; Index < Data.Count; Index++)
|
AMemoryHelper.WriteBytes(Memory, Position, Data);
|
||||||
{
|
|
||||||
Memory.WriteByte(Position + Index, Data[Index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Memory.Manager.Reprotect(Position, Data.Count, Perm);
|
Memory.Manager.Reprotect(Position, Data.Length, Perm);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MapBss(long Position, long Size)
|
private void MapBss(long Position, long Size)
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
|
|
||||||
namespace Ryujinx.Core.Loaders.Executables
|
namespace Ryujinx.Core.Loaders.Executables
|
||||||
{
|
{
|
||||||
public interface IExecutable
|
public interface IExecutable
|
||||||
{
|
{
|
||||||
ReadOnlyCollection<byte> Text { get; }
|
byte[] Text { get; }
|
||||||
ReadOnlyCollection<byte> RO { get; }
|
byte[] RO { get; }
|
||||||
ReadOnlyCollection<byte> Data { get; }
|
byte[] Data { get; }
|
||||||
|
|
||||||
int Mod0Offset { get; }
|
int Mod0Offset { get; }
|
||||||
int TextOffset { get; }
|
int TextOffset { get; }
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
using System;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.Core.Loaders.Executables
|
namespace Ryujinx.Core.Loaders.Executables
|
||||||
{
|
{
|
||||||
class Nro : IExecutable
|
class Nro : IExecutable
|
||||||
{
|
{
|
||||||
private byte[] m_Text;
|
public byte[] Text { get; private set; }
|
||||||
private byte[] m_RO;
|
public byte[] RO { get; private set; }
|
||||||
private byte[] m_Data;
|
public byte[] Data { get; private set; }
|
||||||
|
|
||||||
public ReadOnlyCollection<byte> Text => Array.AsReadOnly(m_Text);
|
|
||||||
public ReadOnlyCollection<byte> RO => Array.AsReadOnly(m_RO);
|
|
||||||
public ReadOnlyCollection<byte> Data => Array.AsReadOnly(m_Data);
|
|
||||||
|
|
||||||
public int Mod0Offset { get; private set; }
|
public int Mod0Offset { get; private set; }
|
||||||
public int TextOffset { get; private set; }
|
public int TextOffset { get; private set; }
|
||||||
|
@ -54,9 +48,9 @@ namespace Ryujinx.Core.Loaders.Executables
|
||||||
return Reader.ReadBytes(Size);
|
return Reader.ReadBytes(Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Text = Read(TextOffset, TextSize);
|
Text = Read(TextOffset, TextSize);
|
||||||
m_RO = Read(ROOffset, ROSize);
|
RO = Read(ROOffset, ROSize);
|
||||||
m_Data = Read(DataOffset, DataSize);
|
Data = Read(DataOffset, DataSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,19 +1,14 @@
|
||||||
using Ryujinx.Core.Loaders.Compression;
|
using Ryujinx.Core.Loaders.Compression;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.Core.Loaders.Executables
|
namespace Ryujinx.Core.Loaders.Executables
|
||||||
{
|
{
|
||||||
class Nso : IExecutable
|
class Nso : IExecutable
|
||||||
{
|
{
|
||||||
private byte[] m_Text;
|
public byte[] Text { get; private set; }
|
||||||
private byte[] m_RO;
|
public byte[] RO { get; private set; }
|
||||||
private byte[] m_Data;
|
public byte[] Data { get; private set; }
|
||||||
|
|
||||||
public ReadOnlyCollection<byte> Text => Array.AsReadOnly(m_Text);
|
|
||||||
public ReadOnlyCollection<byte> RO => Array.AsReadOnly(m_RO);
|
|
||||||
public ReadOnlyCollection<byte> Data => Array.AsReadOnly(m_Data);
|
|
||||||
|
|
||||||
public int Mod0Offset { get; private set; }
|
public int Mod0Offset { get; private set; }
|
||||||
public int TextOffset { get; private set; }
|
public int TextOffset { get; private set; }
|
||||||
|
@ -57,9 +52,9 @@ namespace Ryujinx.Core.Loaders.Executables
|
||||||
|
|
||||||
byte[] BuildId = Reader.ReadBytes(0x20);
|
byte[] BuildId = Reader.ReadBytes(0x20);
|
||||||
|
|
||||||
int TextSize = Reader.ReadInt32();
|
int TextSize = Reader.ReadInt32();
|
||||||
int ROSize = Reader.ReadInt32();
|
int ROSize = Reader.ReadInt32();
|
||||||
int DataSize = Reader.ReadInt32();
|
int DataSize = Reader.ReadInt32();
|
||||||
|
|
||||||
Input.Seek(0x24, SeekOrigin.Current);
|
Input.Seek(0x24, SeekOrigin.Current);
|
||||||
|
|
||||||
|
@ -82,38 +77,38 @@ namespace Ryujinx.Core.Loaders.Executables
|
||||||
//Text segment
|
//Text segment
|
||||||
Input.Seek(TextOffset, SeekOrigin.Begin);
|
Input.Seek(TextOffset, SeekOrigin.Begin);
|
||||||
|
|
||||||
m_Text = Reader.ReadBytes(TextSize);
|
Text = Reader.ReadBytes(TextSize);
|
||||||
|
|
||||||
if (Flags.HasFlag(NsoFlags.IsTextCompressed) || true)
|
if (Flags.HasFlag(NsoFlags.IsTextCompressed) || true)
|
||||||
{
|
{
|
||||||
m_Text = Lz4.Decompress(m_Text, TextDecSize);
|
Text = Lz4.Decompress(Text, TextDecSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Read-only data segment
|
//Read-only data segment
|
||||||
Input.Seek(ROOffset, SeekOrigin.Begin);
|
Input.Seek(ROOffset, SeekOrigin.Begin);
|
||||||
|
|
||||||
m_RO = Reader.ReadBytes(ROSize);
|
RO = Reader.ReadBytes(ROSize);
|
||||||
|
|
||||||
if (Flags.HasFlag(NsoFlags.IsROCompressed) || true)
|
if (Flags.HasFlag(NsoFlags.IsROCompressed) || true)
|
||||||
{
|
{
|
||||||
m_RO = Lz4.Decompress(m_RO, RODecSize);
|
RO = Lz4.Decompress(RO, RODecSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Data segment
|
//Data segment
|
||||||
Input.Seek(DataOffset, SeekOrigin.Begin);
|
Input.Seek(DataOffset, SeekOrigin.Begin);
|
||||||
|
|
||||||
m_Data = Reader.ReadBytes(DataSize);
|
Data = Reader.ReadBytes(DataSize);
|
||||||
|
|
||||||
if (Flags.HasFlag(NsoFlags.IsDataCompressed) || true)
|
if (Flags.HasFlag(NsoFlags.IsDataCompressed) || true)
|
||||||
{
|
{
|
||||||
m_Data = Lz4.Decompress(m_Data, DataDecSize);
|
Data = Lz4.Decompress(Data, DataDecSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (MemoryStream Text = new MemoryStream(m_Text))
|
using (MemoryStream TextMS = new MemoryStream(Text))
|
||||||
{
|
{
|
||||||
BinaryReader TextReader = new BinaryReader(Text);
|
BinaryReader TextReader = new BinaryReader(TextMS);
|
||||||
|
|
||||||
Text.Seek(4, SeekOrigin.Begin);
|
TextMS.Seek(4, SeekOrigin.Begin);
|
||||||
|
|
||||||
Mod0Offset = TextReader.ReadInt32();
|
Mod0Offset = TextReader.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@ namespace Ryujinx.Core
|
||||||
{
|
{
|
||||||
public static class Logging
|
public static class Logging
|
||||||
{
|
{
|
||||||
private static Stopwatch ExecutionTime = new Stopwatch();
|
private static Stopwatch ExecutionTime;
|
||||||
|
|
||||||
private const string LogFileName = "Ryujinx.log";
|
private const string LogFileName = "Ryujinx.log";
|
||||||
|
|
||||||
private static bool EnableInfo = Config.LoggingEnableInfo;
|
private static bool EnableInfo = Config.LoggingEnableInfo;
|
||||||
|
@ -22,9 +23,11 @@ namespace Ryujinx.Core
|
||||||
|
|
||||||
static Logging()
|
static Logging()
|
||||||
{
|
{
|
||||||
ExecutionTime.Start();
|
|
||||||
|
|
||||||
if (File.Exists(LogFileName)) File.Delete(LogFileName);
|
if (File.Exists(LogFileName)) File.Delete(LogFileName);
|
||||||
|
|
||||||
|
ExecutionTime = new Stopwatch();
|
||||||
|
|
||||||
|
ExecutionTime.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetExecutionTime()
|
public static string GetExecutionTime()
|
||||||
|
|
|
@ -4,7 +4,7 @@ using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle
|
namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
public class CondVar
|
class CondVar
|
||||||
{
|
{
|
||||||
private Process Process;
|
private Process Process;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ namespace Ryujinx.Core.OsHle
|
||||||
WaitingThreads = new List<HThread>();
|
WaitingThreads = new List<HThread>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WaitForSignal(HThread Thread)
|
public bool WaitForSignal(HThread Thread)
|
||||||
{
|
{
|
||||||
int Count = Process.Memory.ReadInt32(CondVarAddress);
|
int Count = Process.Memory.ReadInt32(CondVarAddress);
|
||||||
|
|
||||||
|
@ -41,12 +41,14 @@ namespace Ryujinx.Core.OsHle
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000));
|
bool Result = Process.Scheduler.WaitForSignal(Thread, (int)(Timeout / 1000000));
|
||||||
|
|
||||||
lock (WaitingThreads)
|
lock (WaitingThreads)
|
||||||
{
|
{
|
||||||
WaitingThreads.Remove(Thread);
|
WaitingThreads.Remove(Thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +62,8 @@ namespace Ryujinx.Core.OsHle
|
||||||
}
|
}
|
||||||
|
|
||||||
ReleaseCondVarValue();
|
ReleaseCondVarValue();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSignal(HThread Thread, int Count)
|
public void SetSignal(HThread Thread, int Count)
|
||||||
|
|
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
|
enum ErrorModule
|
||||||
{
|
{
|
|
@ -1,58 +1,48 @@
|
||||||
using Ryujinx.Core.OsHle.Utilities;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Handles
|
namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
class HDomain : HSession
|
class HDomain : HSession, IDisposable
|
||||||
{
|
{
|
||||||
private Dictionary<int, object> Objects;
|
private IdDictionary Objects;
|
||||||
|
|
||||||
private IdPool ObjIds;
|
|
||||||
|
|
||||||
public HDomain(HSession Session) : base(Session)
|
public HDomain(HSession Session) : base(Session)
|
||||||
{
|
{
|
||||||
Objects = new Dictionary<int, object>();
|
Objects = new IdDictionary();
|
||||||
|
|
||||||
ObjIds = new IdPool();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GenerateObjectId(object Obj)
|
public int Add(object Obj)
|
||||||
{
|
{
|
||||||
int Id = ObjIds.GenerateId();
|
return Objects.Add(Obj);
|
||||||
|
|
||||||
if (Id == -1)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
Objects.Add(Id, Obj);
|
|
||||||
|
|
||||||
return Id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteObject(int Id)
|
public bool Delete(int Id)
|
||||||
{
|
{
|
||||||
if (Objects.TryGetValue(Id, out object Obj))
|
return Objects.Delete(Id);
|
||||||
{
|
|
||||||
if (Obj is IDisposable DisposableObj)
|
|
||||||
{
|
|
||||||
DisposableObj.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjIds.DeleteId(Id);
|
|
||||||
Objects.Remove(Id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public object GetObject(int Id)
|
public object GetObject(int Id)
|
||||||
{
|
{
|
||||||
if (Objects.TryGetValue(Id, out object Obj))
|
return Objects.GetData(Id);
|
||||||
{
|
}
|
||||||
return Obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool Disposing)
|
||||||
|
{
|
||||||
|
if (Disposing)
|
||||||
|
{
|
||||||
|
foreach (object Obj in Objects)
|
||||||
|
{
|
||||||
|
if (Obj != this && Obj is IDisposable DisposableObj)
|
||||||
|
{
|
||||||
|
DisposableObj.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
@ -5,66 +6,39 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
class HSharedMem
|
class HSharedMem
|
||||||
{
|
{
|
||||||
private List<long> Positions;
|
private List<(AMemory, long)> Positions;
|
||||||
|
|
||||||
public int PositionsCount => Positions.Count;
|
|
||||||
|
|
||||||
public EventHandler<EventArgs> MemoryMapped;
|
public EventHandler<EventArgs> MemoryMapped;
|
||||||
public EventHandler<EventArgs> MemoryUnmapped;
|
public EventHandler<EventArgs> MemoryUnmapped;
|
||||||
|
|
||||||
public HSharedMem()
|
public HSharedMem()
|
||||||
{
|
{
|
||||||
Positions = new List<long>();
|
Positions = new List<(AMemory, long)>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddVirtualPosition(long Position)
|
public void AddVirtualPosition(AMemory Memory, long Position)
|
||||||
{
|
{
|
||||||
lock (Positions)
|
lock (Positions)
|
||||||
{
|
{
|
||||||
Positions.Add(Position);
|
Positions.Add((Memory, Position));
|
||||||
|
|
||||||
MemoryMapped?.Invoke(this, EventArgs.Empty);
|
MemoryMapped?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveVirtualPosition(long Position)
|
public void RemoveVirtualPosition(AMemory Memory, long Position)
|
||||||
{
|
{
|
||||||
lock (Positions)
|
lock (Positions)
|
||||||
{
|
{
|
||||||
Positions.Remove(Position);
|
Positions.Remove((Memory, Position));
|
||||||
|
|
||||||
MemoryUnmapped?.Invoke(this, EventArgs.Empty);
|
MemoryUnmapped?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetVirtualPosition(int Index)
|
public (AMemory, long)[] GetVirtualPositions()
|
||||||
{
|
{
|
||||||
lock (Positions)
|
return Positions.ToArray();
|
||||||
{
|
|
||||||
if (Index < 0 || Index >= Positions.Count)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(Index));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Positions[Index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetLastVirtualPosition(out long Position)
|
|
||||||
{
|
|
||||||
lock (Positions)
|
|
||||||
{
|
|
||||||
if (Positions.Count > 0)
|
|
||||||
{
|
|
||||||
Position = Positions[Positions.Count - 1];
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Position = 0;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
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);
|
TryResumingExecution(SchedThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WaitForSignal(HThread Thread, int Timeout = -1)
|
public bool WaitForSignal(HThread Thread, int Timeout = -1)
|
||||||
{
|
{
|
||||||
SchedulerThread SchedThread;
|
SchedulerThread SchedThread;
|
||||||
|
|
||||||
|
@ -206,22 +206,26 @@ namespace Ryujinx.Core.OsHle.Handles
|
||||||
{
|
{
|
||||||
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
|
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
|
||||||
|
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Result;
|
||||||
|
|
||||||
if (Timeout >= 0)
|
if (Timeout >= 0)
|
||||||
{
|
{
|
||||||
Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
|
Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
|
||||||
|
|
||||||
SchedThread.WaitEvent.WaitOne(Timeout);
|
Result = SchedThread.WaitEvent.WaitOne(Timeout);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SchedThread.WaitEvent.WaitOne();
|
Result = SchedThread.WaitEvent.WaitOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
TryResumingExecution(SchedThread);
|
TryResumingExecution(SchedThread);
|
||||||
|
|
||||||
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryResumingExecution(SchedulerThread SchedThread)
|
private void TryResumingExecution(SchedulerThread SchedThread)
|
||||||
|
|
|
@ -37,5 +37,33 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
Position += 0x18;
|
Position += 0x18;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string ReadHbAbiNextLoadPath(AMemory Memory, long Position)
|
||||||
|
{
|
||||||
|
string FileName = null;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
long Key = Memory.ReadInt64(Position);
|
||||||
|
|
||||||
|
if (Key == 2)
|
||||||
|
{
|
||||||
|
long Value0 = Memory.ReadInt64(Position + 0x08);
|
||||||
|
long Value1 = Memory.ReadInt64(Position + 0x10);
|
||||||
|
|
||||||
|
FileName = AMemoryHelper.ReadAsciiString(Memory, Value0, (int)(Value1 - Value0));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (Key == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Position += 0x18;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FileName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,23 @@
|
||||||
using Ryujinx.Core.Loaders.Executables;
|
using Ryujinx.Core.Loaders.Executables;
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
using Ryujinx.Core.OsHle.Handles;
|
||||||
using Ryujinx.Core.OsHle.Utilities;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle
|
namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
public class Horizon
|
public class Horizon : IDisposable
|
||||||
{
|
{
|
||||||
internal const int HidSize = 0x40000;
|
internal const int HidSize = 0x40000;
|
||||||
internal const int FontSize = 0x50;
|
internal const int FontSize = 0x50;
|
||||||
|
|
||||||
internal int HidHandle { get; private set; }
|
internal ConcurrentDictionary<long, Mutex> Mutexes { get; private set; }
|
||||||
internal int FontHandle { get; private set; }
|
internal ConcurrentDictionary<long, CondVar> CondVars { get; private set; }
|
||||||
|
|
||||||
internal IdPool IdGen { get; private set; }
|
|
||||||
internal IdPool NvMapIds { get; private set; }
|
|
||||||
|
|
||||||
internal IdPoolWithObj Handles { get; private set; }
|
|
||||||
internal IdPoolWithObj Fds { get; private set; }
|
|
||||||
internal IdPoolWithObj Displays { get; private set; }
|
|
||||||
|
|
||||||
public ConcurrentDictionary<long, Mutex> Mutexes { get; private set; }
|
|
||||||
public ConcurrentDictionary<long, CondVar> CondVars { get; private set; }
|
|
||||||
|
|
||||||
private ConcurrentDictionary<int, Process> Processes;
|
private ConcurrentDictionary<int, Process> Processes;
|
||||||
|
|
||||||
private HSharedMem HidSharedMem;
|
internal HSharedMem HidSharedMem;
|
||||||
|
internal HSharedMem FontSharedMem;
|
||||||
|
|
||||||
private Switch Ns;
|
private Switch Ns;
|
||||||
|
|
||||||
|
@ -35,25 +25,13 @@ namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
this.Ns = Ns;
|
this.Ns = Ns;
|
||||||
|
|
||||||
IdGen = new IdPool();
|
|
||||||
NvMapIds = new IdPool();
|
|
||||||
|
|
||||||
Handles = new IdPoolWithObj();
|
|
||||||
Fds = new IdPoolWithObj();
|
|
||||||
Displays = new IdPoolWithObj();
|
|
||||||
|
|
||||||
Mutexes = new ConcurrentDictionary<long, Mutex>();
|
Mutexes = new ConcurrentDictionary<long, Mutex>();
|
||||||
CondVars = new ConcurrentDictionary<long, CondVar>();
|
CondVars = new ConcurrentDictionary<long, CondVar>();
|
||||||
|
|
||||||
Processes = new ConcurrentDictionary<int, Process>();
|
Processes = new ConcurrentDictionary<int, Process>();
|
||||||
|
|
||||||
HidSharedMem = new HSharedMem();
|
HidSharedMem = new HSharedMem();
|
||||||
|
FontSharedMem = new HSharedMem();
|
||||||
HidSharedMem.MemoryMapped += HidInit;
|
|
||||||
|
|
||||||
HidHandle = Handles.GenerateId(HidSharedMem);
|
|
||||||
|
|
||||||
FontHandle = Handles.GenerateId(new HSharedMem());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
||||||
|
@ -63,9 +41,7 @@ namespace Ryujinx.Core.OsHle
|
||||||
Ns.VFs.LoadRomFs(RomFsFile);
|
Ns.VFs.LoadRomFs(RomFsFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ProcessId = IdGen.GenerateId();
|
Process MainProcess = MakeProcess();
|
||||||
|
|
||||||
Process MainProcess = new Process(Ns, ProcessId);
|
|
||||||
|
|
||||||
void LoadNso(string FileName)
|
void LoadNso(string FileName)
|
||||||
{
|
{
|
||||||
|
@ -96,17 +72,13 @@ namespace Ryujinx.Core.OsHle
|
||||||
LoadNso("sdk");
|
LoadNso("sdk");
|
||||||
|
|
||||||
MainProcess.Run();
|
MainProcess.Run();
|
||||||
|
|
||||||
Processes.TryAdd(ProcessId, MainProcess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadProgram(string FileName)
|
public void LoadProgram(string FileName)
|
||||||
{
|
{
|
||||||
bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
|
bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
|
||||||
|
|
||||||
int ProcessId = IdGen.GenerateId();
|
Process MainProcess = MakeProcess();
|
||||||
|
|
||||||
Process MainProcess = new Process(Ns, ProcessId);
|
|
||||||
|
|
||||||
using (FileStream Input = new FileStream(FileName, FileMode.Open))
|
using (FileStream Input = new FileStream(FileName, FileMode.Open))
|
||||||
{
|
{
|
||||||
|
@ -117,34 +89,67 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
MainProcess.SetEmptyArgs();
|
MainProcess.SetEmptyArgs();
|
||||||
MainProcess.Run(IsNro);
|
MainProcess.Run(IsNro);
|
||||||
|
|
||||||
Processes.TryAdd(ProcessId, MainProcess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FinalizeAllProcesses()
|
private Process MakeProcess()
|
||||||
{
|
{
|
||||||
foreach (Process Process in Processes.Values)
|
Process Process;
|
||||||
|
|
||||||
|
lock (Processes)
|
||||||
{
|
{
|
||||||
Process.StopAllThreads();
|
int ProcessId = 0;
|
||||||
|
|
||||||
|
while (Processes.ContainsKey(ProcessId))
|
||||||
|
{
|
||||||
|
ProcessId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Process = new Process(Ns, ProcessId);
|
||||||
|
|
||||||
|
Processes.TryAdd(ProcessId, Process);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Process;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ExitProcess(int ProcessId)
|
||||||
|
{
|
||||||
|
if (Processes.TryGetValue(ProcessId, out Process Process) && Process.NeedsHbAbi)
|
||||||
|
{
|
||||||
|
string NextNro = Homebrew.ReadHbAbiNextLoadPath(Process.Memory, Process.HbAbiDataPosition);
|
||||||
|
|
||||||
|
Logging.Info($"HbAbi NextLoadPath {NextNro}");
|
||||||
|
|
||||||
|
if (NextNro == string.Empty)
|
||||||
|
{
|
||||||
|
NextNro = "sdmc:/hbmenu.nro";
|
||||||
|
}
|
||||||
|
|
||||||
|
NextNro = NextNro.Replace("sdmc:", string.Empty);
|
||||||
|
|
||||||
|
NextNro = Ns.VFs.GetFullPath(Ns.VFs.GetSdCardPath(), NextNro);
|
||||||
|
|
||||||
|
if (File.Exists(NextNro))
|
||||||
|
{
|
||||||
|
//TODO: Those dictionaries shouldn't even exist,
|
||||||
|
//the Mutex and CondVar helper classes should be static.
|
||||||
|
Mutexes.Clear();
|
||||||
|
CondVars.Clear();
|
||||||
|
|
||||||
|
LoadProgram(NextNro);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Processes.TryRemove(ProcessId, out Process))
|
||||||
|
{
|
||||||
|
Process.StopAllThreadsAsync();
|
||||||
Process.Dispose();
|
Process.Dispose();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool ExitProcess(int ProcessId)
|
if (Processes.Count == 0)
|
||||||
{
|
{
|
||||||
bool Success = Processes.TryRemove(ProcessId, out Process Process);
|
Ns.OnFinish(EventArgs.Empty);
|
||||||
|
}
|
||||||
if (Success)
|
|
||||||
{
|
|
||||||
Process.StopAllThreads();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Processes.Count == 0)
|
|
||||||
{
|
|
||||||
Ns.OnFinish(EventArgs.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool TryGetProcess(int ProcessId, out Process Process)
|
internal bool TryGetProcess(int ProcessId, out Process Process)
|
||||||
|
@ -152,30 +157,20 @@ namespace Ryujinx.Core.OsHle
|
||||||
return Processes.TryGetValue(ProcessId, out Process);
|
return Processes.TryGetValue(ProcessId, out Process);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void CloseHandle(int Handle)
|
public void Dispose()
|
||||||
{
|
{
|
||||||
object HndData = Handles.GetData<object>(Handle);
|
Dispose(true);
|
||||||
|
|
||||||
if (HndData is HTransferMem TransferMem)
|
|
||||||
{
|
|
||||||
TransferMem.Memory.Manager.Reprotect(
|
|
||||||
TransferMem.Position,
|
|
||||||
TransferMem.Size,
|
|
||||||
TransferMem.Perm);
|
|
||||||
}
|
|
||||||
|
|
||||||
Handles.Delete(Handle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HidInit(object sender, EventArgs e)
|
protected virtual void Dispose(bool Disposing)
|
||||||
{
|
{
|
||||||
HSharedMem SharedMem = (HSharedMem)sender;
|
if (Disposing)
|
||||||
|
|
||||||
if (SharedMem.TryGetLastVirtualPosition(out long Position))
|
|
||||||
{
|
{
|
||||||
Logging.Info($"HID shared memory successfully mapped to 0x{Position:x16}!");
|
foreach (Process Process in Processes.Values)
|
||||||
|
{
|
||||||
Ns.Hid.Init(Position);
|
Process.StopAllThreadsAsync();
|
||||||
|
Process.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,31 +3,40 @@ using System.Collections;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Utilities
|
namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
class IdPoolWithObj : IEnumerable<KeyValuePair<int, object>>
|
class IdDictionary : IEnumerable<object>
|
||||||
{
|
{
|
||||||
private IdPool Ids;
|
|
||||||
|
|
||||||
private ConcurrentDictionary<int, object> Objs;
|
private ConcurrentDictionary<int, object> Objs;
|
||||||
|
|
||||||
public IdPoolWithObj()
|
private int FreeIdHint = 1;
|
||||||
{
|
|
||||||
Ids = new IdPool();
|
|
||||||
|
|
||||||
|
public IdDictionary()
|
||||||
|
{
|
||||||
Objs = new ConcurrentDictionary<int, object>();
|
Objs = new ConcurrentDictionary<int, object>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GenerateId(object Data)
|
public int Add(object Data)
|
||||||
{
|
{
|
||||||
int Id = Ids.GenerateId();
|
if (Objs.TryAdd(FreeIdHint, Data))
|
||||||
|
|
||||||
if (Id == -1 || !Objs.TryAdd(Id, Data))
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException();
|
return FreeIdHint++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Id;
|
return AddSlow(Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int AddSlow(object Data)
|
||||||
|
{
|
||||||
|
for (int Id = 1; Id < int.MaxValue; Id++)
|
||||||
|
{
|
||||||
|
if (Objs.TryAdd(Id, Data))
|
||||||
|
{
|
||||||
|
return Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ReplaceData(int Id, object Data)
|
public bool ReplaceData(int Id, object Data)
|
||||||
|
@ -42,6 +51,16 @@ namespace Ryujinx.Core.OsHle.Utilities
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public object GetData(int Id)
|
||||||
|
{
|
||||||
|
if (Objs.TryGetValue(Id, out object Data))
|
||||||
|
{
|
||||||
|
return Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public T GetData<T>(int Id)
|
public T GetData<T>(int Id)
|
||||||
{
|
{
|
||||||
if (Objs.TryGetValue(Id, out object Data) && Data is T)
|
if (Objs.TryGetValue(Id, out object Data) && Data is T)
|
||||||
|
@ -52,7 +71,7 @@ namespace Ryujinx.Core.OsHle.Utilities
|
||||||
return default(T);
|
return default(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Delete(int Id)
|
public bool Delete(int Id)
|
||||||
{
|
{
|
||||||
if (Objs.TryRemove(Id, out object Obj))
|
if (Objs.TryRemove(Id, out object Obj))
|
||||||
{
|
{
|
||||||
|
@ -61,18 +80,22 @@ namespace Ryujinx.Core.OsHle.Utilities
|
||||||
DisposableObj.Dispose();
|
DisposableObj.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ids.DeleteId(Id);
|
FreeIdHint = Id;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator<KeyValuePair<int, object>> IEnumerable<KeyValuePair<int, object>>.GetEnumerator()
|
IEnumerator<object> IEnumerable<object>.GetEnumerator()
|
||||||
{
|
{
|
||||||
return Objs.GetEnumerator();
|
return Objs.Values.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
{
|
{
|
||||||
return Objs.GetEnumerator();
|
return Objs.Values.GetEnumerator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,6 +13,7 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
|
|
||||||
public static void IpcCall(
|
public static void IpcCall(
|
||||||
Switch Ns,
|
Switch Ns,
|
||||||
|
Process Process,
|
||||||
AMemory Memory,
|
AMemory Memory,
|
||||||
HSession Session,
|
HSession Session,
|
||||||
IpcMessage Request,
|
IpcMessage Request,
|
||||||
|
@ -60,7 +61,7 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
}
|
}
|
||||||
else if (Request.DomCmd == IpcDomCmd.DeleteObj)
|
else if (Request.DomCmd == IpcDomCmd.DeleteObj)
|
||||||
{
|
{
|
||||||
Dom.DeleteObject(Request.DomObjId);
|
Dom.Delete(Request.DomObjId);
|
||||||
|
|
||||||
Response = FillResponse(Response, 0);
|
Response = FillResponse(Response, 0);
|
||||||
|
|
||||||
|
@ -100,6 +101,7 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
|
|
||||||
ServiceCtx Context = new ServiceCtx(
|
ServiceCtx Context = new ServiceCtx(
|
||||||
Ns,
|
Ns,
|
||||||
|
Process,
|
||||||
Memory,
|
Memory,
|
||||||
Session,
|
Session,
|
||||||
Request,
|
Request,
|
||||||
|
@ -124,15 +126,43 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
|
|
||||||
switch (CmdId)
|
switch (CmdId)
|
||||||
{
|
{
|
||||||
case 0: Request = IpcConvertSessionToDomain(Ns, Session, Response, HndId); break;
|
case 0:
|
||||||
case 3: Request = IpcQueryBufferPointerSize(Response); break;
|
{
|
||||||
case 2: //IpcDuplicateSession, differences is unknown.
|
HDomain Dom = new HDomain(Session);
|
||||||
case 4: Request = IpcDuplicateSessionEx(Ns, Session, Response, ReqReader); break;
|
|
||||||
|
Process.HandleTable.ReplaceData(HndId, Dom);
|
||||||
|
|
||||||
|
Request = FillResponse(Response, 0, Dom.Add(Dom));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
Request = FillResponse(Response, 0, 0x500);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Whats the difference between IpcDuplicateSession/Ex?
|
||||||
|
case 2:
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
int Unknown = ReqReader.ReadInt32();
|
||||||
|
|
||||||
|
int Handle = Process.HandleTable.OpenHandle(Session);
|
||||||
|
|
||||||
|
Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
|
||||||
|
|
||||||
|
Request = FillResponse(Response, 0);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: throw new NotImplementedException(CmdId.ToString());
|
default: throw new NotImplementedException(CmdId.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Request.Type == IpcMessageType.Unknown2)
|
else if (Request.Type == IpcMessageType.CloseSession)
|
||||||
{
|
{
|
||||||
//TODO
|
//TODO
|
||||||
}
|
}
|
||||||
|
@ -145,39 +175,6 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IpcMessage IpcConvertSessionToDomain(
|
|
||||||
Switch Ns,
|
|
||||||
HSession Session,
|
|
||||||
IpcMessage Response,
|
|
||||||
int HndId)
|
|
||||||
{
|
|
||||||
HDomain Dom = new HDomain(Session);
|
|
||||||
|
|
||||||
Ns.Os.Handles.ReplaceData(HndId, Dom);
|
|
||||||
|
|
||||||
return FillResponse(Response, 0, Dom.GenerateObjectId(Dom));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IpcMessage IpcDuplicateSessionEx(
|
|
||||||
Switch Ns,
|
|
||||||
HSession Session,
|
|
||||||
IpcMessage Response,
|
|
||||||
BinaryReader ReqReader)
|
|
||||||
{
|
|
||||||
int Unknown = ReqReader.ReadInt32();
|
|
||||||
|
|
||||||
int Handle = Ns.Os.Handles.GenerateId(Session);
|
|
||||||
|
|
||||||
Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
|
|
||||||
|
|
||||||
return FillResponse(Response, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IpcMessage IpcQueryBufferPointerSize(IpcMessage Response)
|
|
||||||
{
|
|
||||||
return FillResponse(Response, 0, 0x500);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values)
|
private static IpcMessage FillResponse(IpcMessage Response, long Result, params int[] Values)
|
||||||
{
|
{
|
||||||
using (MemoryStream MS = new MemoryStream())
|
using (MemoryStream MS = new MemoryStream())
|
||||||
|
|
|
@ -217,16 +217,26 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
|
|
||||||
public long GetSendBuffPtr()
|
public long GetSendBuffPtr()
|
||||||
{
|
{
|
||||||
if (SendBuff.Count > 0 && SendBuff[0].Position != 0)
|
if (SendBuff.Count > 0 && SendBuff[0].Size != 0)
|
||||||
{
|
{
|
||||||
return SendBuff[0].Position;
|
return SendBuff[0].Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PtrBuff.Count > 0 && PtrBuff[0].Position != 0)
|
if (PtrBuff.Count > 0 && PtrBuff[0].Size != 0)
|
||||||
{
|
{
|
||||||
return PtrBuff[0].Position;
|
return PtrBuff[0].Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ReceiveBuff.Count > 0 && ReceiveBuff[0].Size != 0)
|
||||||
|
{
|
||||||
|
return ReceiveBuff[0].Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RecvListBuff.Count > 0 && RecvListBuff[0].Size != 0)
|
||||||
|
{
|
||||||
|
return RecvListBuff[0].Position;
|
||||||
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@ namespace Ryujinx.Core.OsHle.Ipc
|
||||||
{
|
{
|
||||||
enum IpcMessageType
|
enum IpcMessageType
|
||||||
{
|
{
|
||||||
Response = 0,
|
Response = 0,
|
||||||
Unknown2 = 2,
|
CloseSession = 2,
|
||||||
Request = 4,
|
Request = 4,
|
||||||
Control = 5
|
Control = 5
|
||||||
}
|
}
|
||||||
}
|
}
|
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 AddrSpaceStart = 0x08000000;
|
||||||
|
|
||||||
public const long MapRegionAddress = 0x10000000;
|
public const long MapRegionAddress = 0x10000000;
|
||||||
public const long MapRegionSize = 0x10000000;
|
public const long MapRegionSize = 0x20000000;
|
||||||
|
|
||||||
|
public const long HeapRegionAddress = MapRegionAddress + MapRegionSize;
|
||||||
|
public const long HeapRegionSize = TlsPagesAddress - HeapRegionAddress;
|
||||||
|
|
||||||
public const long MainStackSize = 0x100000;
|
public const long MainStackSize = 0x100000;
|
||||||
|
|
||||||
|
@ -17,8 +20,6 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
public const long TlsPagesAddress = MainStackAddress - TlsPagesSize;
|
public const long TlsPagesAddress = MainStackAddress - TlsPagesSize;
|
||||||
|
|
||||||
public const long HeapRegionAddress = MapRegionAddress + MapRegionSize;
|
|
||||||
|
|
||||||
public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize;
|
public const long TotalMemoryUsed = HeapRegionAddress + TlsPagesSize + MainStackSize;
|
||||||
|
|
||||||
public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart;
|
public const long TotalMemoryAvailable = AMemoryMgr.RamSize - AddrSpaceStart;
|
||||||
|
|
|
@ -4,7 +4,7 @@ using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle
|
namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
public class Mutex
|
class Mutex
|
||||||
{
|
{
|
||||||
private const int MutexHasListenersMask = 0x40000000;
|
private const int MutexHasListenersMask = 0x40000000;
|
||||||
|
|
||||||
|
|
|
@ -9,25 +9,34 @@ using Ryujinx.Core.OsHle.Svc;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle
|
namespace Ryujinx.Core.OsHle
|
||||||
{
|
{
|
||||||
public class Process : IDisposable
|
class Process : IDisposable
|
||||||
{
|
{
|
||||||
private const int TlsSize = 0x200;
|
private const int TlsSize = 0x200;
|
||||||
private const int TotalTlsSlots = 32;
|
private const int TotalTlsSlots = 32;
|
||||||
|
|
||||||
|
private const int TickFreq = 19_200_000;
|
||||||
|
|
||||||
private Switch Ns;
|
private Switch Ns;
|
||||||
|
|
||||||
|
public bool NeedsHbAbi { get; private set; }
|
||||||
|
|
||||||
|
public long HbAbiDataPosition { get; private set; }
|
||||||
|
|
||||||
public int ProcessId { get; private set; }
|
public int ProcessId { get; private set; }
|
||||||
|
|
||||||
private ATranslator Translator;
|
private ATranslator Translator;
|
||||||
|
|
||||||
public AMemory Memory { get; private set; }
|
public AMemory Memory { get; private set; }
|
||||||
|
|
||||||
|
public ServiceMgr Services { get; private set; }
|
||||||
|
|
||||||
public KProcessScheduler Scheduler { get; private set; }
|
public KProcessScheduler Scheduler { get; private set; }
|
||||||
|
|
||||||
|
public KProcessHandleTable HandleTable { get; private set; }
|
||||||
|
|
||||||
private SvcHandler SvcHandler;
|
private SvcHandler SvcHandler;
|
||||||
|
|
||||||
private ConcurrentDictionary<int, AThread> TlsSlots;
|
private ConcurrentDictionary<int, AThread> TlsSlots;
|
||||||
|
@ -40,14 +49,22 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
private long ImageBase;
|
private long ImageBase;
|
||||||
|
|
||||||
|
private bool ShouldDispose;
|
||||||
|
|
||||||
|
private bool Disposed;
|
||||||
|
|
||||||
public Process(Switch Ns, int ProcessId)
|
public Process(Switch Ns, int ProcessId)
|
||||||
{
|
{
|
||||||
this.Ns = Ns;
|
this.Ns = Ns;
|
||||||
this.ProcessId = ProcessId;
|
this.ProcessId = ProcessId;
|
||||||
|
|
||||||
Memory = new AMemory(Ns.Ram);
|
Memory = new AMemory();
|
||||||
|
|
||||||
Scheduler = new KProcessScheduler();
|
Services = new ServiceMgr();
|
||||||
|
|
||||||
|
HandleTable = new KProcessHandleTable();
|
||||||
|
|
||||||
|
Scheduler = new KProcessScheduler();
|
||||||
|
|
||||||
SvcHandler = new SvcHandler(Ns, this);
|
SvcHandler = new SvcHandler(Ns, this);
|
||||||
|
|
||||||
|
@ -67,6 +84,11 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
public void LoadProgram(IExecutable Program)
|
public void LoadProgram(IExecutable Program)
|
||||||
{
|
{
|
||||||
|
if (Disposed)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(Process));
|
||||||
|
}
|
||||||
|
|
||||||
Logging.Info($"Image base at 0x{ImageBase:x16}.");
|
Logging.Info($"Image base at 0x{ImageBase:x16}.");
|
||||||
|
|
||||||
Executable Executable = new Executable(Program, Memory, ImageBase);
|
Executable Executable = new Executable(Program, Memory, ImageBase);
|
||||||
|
@ -78,11 +100,19 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
public void SetEmptyArgs()
|
public void SetEmptyArgs()
|
||||||
{
|
{
|
||||||
|
//TODO: This should be part of Run.
|
||||||
ImageBase += AMemoryMgr.PageSize;
|
ImageBase += AMemoryMgr.PageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Run(bool UseHbAbi = false)
|
public bool Run(bool NeedsHbAbi = false)
|
||||||
{
|
{
|
||||||
|
if (Disposed)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(Process));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.NeedsHbAbi = NeedsHbAbi;
|
||||||
|
|
||||||
if (Executables.Count == 0)
|
if (Executables.Count == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -102,11 +132,11 @@ namespace Ryujinx.Core.OsHle
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
MainThread = Ns.Os.Handles.GetData<HThread>(Handle);
|
MainThread = HandleTable.GetData<HThread>(Handle);
|
||||||
|
|
||||||
if (UseHbAbi)
|
if (NeedsHbAbi)
|
||||||
{
|
{
|
||||||
long HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd);
|
HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd);
|
||||||
|
|
||||||
Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle);
|
Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle);
|
||||||
|
|
||||||
|
@ -124,22 +154,21 @@ namespace Ryujinx.Core.OsHle
|
||||||
Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW);
|
Memory.Manager.Map(Position, Size, (int)Type, AMemoryPerm.RW);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopAllThreads()
|
public void StopAllThreadsAsync()
|
||||||
{
|
{
|
||||||
|
if (Disposed)
|
||||||
|
{
|
||||||
|
throw new ObjectDisposedException(nameof(Process));
|
||||||
|
}
|
||||||
|
|
||||||
if (MainThread != null)
|
if (MainThread != null)
|
||||||
{
|
{
|
||||||
while (MainThread.Thread.IsAlive)
|
MainThread.Thread.StopExecution();
|
||||||
{
|
|
||||||
MainThread.Thread.StopExecution();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (AThread Thread in TlsSlots.Values)
|
foreach (AThread Thread in TlsSlots.Values)
|
||||||
{
|
{
|
||||||
while (Thread.IsAlive)
|
Thread.StopExecution();
|
||||||
{
|
|
||||||
Thread.StopExecution();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,49 +179,27 @@ namespace Ryujinx.Core.OsHle
|
||||||
int Priority,
|
int Priority,
|
||||||
int ProcessorId)
|
int ProcessorId)
|
||||||
{
|
{
|
||||||
ThreadPriority ThreadPrio;
|
if (Disposed)
|
||||||
|
|
||||||
if (Priority < 12)
|
|
||||||
{
|
{
|
||||||
ThreadPrio = ThreadPriority.Highest;
|
throw new ObjectDisposedException(nameof(Process));
|
||||||
}
|
|
||||||
else if (Priority < 24)
|
|
||||||
{
|
|
||||||
ThreadPrio = ThreadPriority.AboveNormal;
|
|
||||||
}
|
|
||||||
else if (Priority < 36)
|
|
||||||
{
|
|
||||||
ThreadPrio = ThreadPriority.Normal;
|
|
||||||
}
|
|
||||||
else if (Priority < 48)
|
|
||||||
{
|
|
||||||
ThreadPrio = ThreadPriority.BelowNormal;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ThreadPrio = ThreadPriority.Lowest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AThread Thread = new AThread(GetTranslator(), Memory, ThreadPrio, EntryPoint);
|
AThread Thread = new AThread(GetTranslator(), Memory, EntryPoint);
|
||||||
|
|
||||||
HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority);
|
HThread ThreadHnd = new HThread(Thread, ProcessorId, Priority);
|
||||||
|
|
||||||
int Handle = Ns.Os.Handles.GenerateId(ThreadHnd);
|
int Handle = HandleTable.OpenHandle(ThreadHnd);
|
||||||
|
|
||||||
int TlsSlot = GetFreeTlsSlot(Thread);
|
int ThreadId = GetFreeTlsSlot(Thread);
|
||||||
|
|
||||||
if (TlsSlot == -1 || Handle == -1)
|
long Tpidr = MemoryRegions.TlsPagesAddress + ThreadId * TlsSize;
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
long Tpidr = MemoryRegions.TlsPagesAddress + TlsSlot * TlsSize;
|
|
||||||
|
|
||||||
Thread.ThreadState.Break += BreakHandler;
|
Thread.ThreadState.Break += BreakHandler;
|
||||||
Thread.ThreadState.SvcCall += SvcHandler.SvcCall;
|
Thread.ThreadState.SvcCall += SvcHandler.SvcCall;
|
||||||
Thread.ThreadState.Undefined += UndefinedHandler;
|
Thread.ThreadState.Undefined += UndefinedHandler;
|
||||||
Thread.ThreadState.ProcessId = ProcessId;
|
Thread.ThreadState.ProcessId = ProcessId;
|
||||||
Thread.ThreadState.ThreadId = Ns.Os.IdGen.GenerateId();
|
Thread.ThreadState.ThreadId = ThreadId;
|
||||||
|
Thread.ThreadState.CntfrqEl0 = TickFreq;
|
||||||
Thread.ThreadState.Tpidr = Tpidr;
|
Thread.ThreadState.Tpidr = Tpidr;
|
||||||
Thread.ThreadState.X0 = (ulong)ArgsPtr;
|
Thread.ThreadState.X0 = (ulong)ArgsPtr;
|
||||||
Thread.ThreadState.X1 = (ulong)Handle;
|
Thread.ThreadState.X1 = (ulong)Handle;
|
||||||
|
@ -224,7 +231,7 @@ namespace Ryujinx.Core.OsHle
|
||||||
foreach (Executable Exe in Executables)
|
foreach (Executable Exe in Executables)
|
||||||
{
|
{
|
||||||
foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
|
foreach (KeyValuePair<long, string> KV in Exe.SymbolTable)
|
||||||
{
|
{
|
||||||
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
|
SymbolTable.TryAdd(Exe.ImageBase + KV.Key, KV.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,16 +281,28 @@ namespace Ryujinx.Core.OsHle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThreadFinished(object sender, EventArgs e)
|
private void ThreadFinished(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is AThread Thread)
|
if (sender is AThread Thread)
|
||||||
{
|
{
|
||||||
TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
|
Logging.Info($"Thread {Thread.ThreadId} exiting...");
|
||||||
|
|
||||||
Ns.Os.IdGen.DeleteId(Thread.ThreadId);
|
TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TlsSlots.Count == 0)
|
||||||
|
{
|
||||||
|
if (ShouldDispose)
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Logging.Info($"No threads running, now exiting Process {ProcessId}...");
|
||||||
|
|
||||||
|
Ns.Os.ExitProcess(ProcessId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,9 +328,30 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
protected virtual void Dispose(bool Disposing)
|
protected virtual void Dispose(bool Disposing)
|
||||||
{
|
{
|
||||||
if (Disposing)
|
if (Disposing && !Disposed)
|
||||||
{
|
{
|
||||||
|
//If there is still some thread running, disposing the objects is not
|
||||||
|
//safe as the thread may try to access those resources. Instead, we set
|
||||||
|
//the flag to have the Process disposed when all threads finishes.
|
||||||
|
//Note: This may not happen if the guest code gets stuck on a infinite loop.
|
||||||
|
if (TlsSlots.Count > 0)
|
||||||
|
{
|
||||||
|
ShouldDispose = true;
|
||||||
|
|
||||||
|
Logging.Info($"Process {ProcessId} waiting all threads terminate...");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Disposed = true;
|
||||||
|
|
||||||
|
Services.Dispose();
|
||||||
|
HandleTable.Dispose();
|
||||||
Scheduler.Dispose();
|
Scheduler.Dispose();
|
||||||
|
SvcHandler.Dispose();
|
||||||
|
Memory.Dispose();
|
||||||
|
|
||||||
|
Logging.Info($"Process {ProcessId} exiting...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace Ryujinx.Core.OsHle
|
||||||
class ServiceCtx
|
class ServiceCtx
|
||||||
{
|
{
|
||||||
public Switch Ns { get; private set; }
|
public Switch Ns { get; private set; }
|
||||||
|
public Process Process { get; private set; }
|
||||||
public AMemory Memory { get; private set; }
|
public AMemory Memory { get; private set; }
|
||||||
public HSession Session { get; private set; }
|
public HSession Session { get; private set; }
|
||||||
public IpcMessage Request { get; private set; }
|
public IpcMessage Request { get; private set; }
|
||||||
|
@ -17,6 +18,7 @@ namespace Ryujinx.Core.OsHle
|
||||||
|
|
||||||
public ServiceCtx(
|
public ServiceCtx(
|
||||||
Switch Ns,
|
Switch Ns,
|
||||||
|
Process Process,
|
||||||
AMemory Memory,
|
AMemory Memory,
|
||||||
HSession Session,
|
HSession Session,
|
||||||
IpcMessage Request,
|
IpcMessage Request,
|
||||||
|
@ -25,6 +27,7 @@ namespace Ryujinx.Core.OsHle
|
||||||
BinaryWriter ResponseData)
|
BinaryWriter ResponseData)
|
||||||
{
|
{
|
||||||
this.Ns = Ns;
|
this.Ns = Ns;
|
||||||
|
this.Process = Process;
|
||||||
this.Memory = Memory;
|
this.Memory = Memory;
|
||||||
this.Session = Session;
|
this.Session = Session;
|
||||||
this.Request = Request;
|
this.Request = Request;
|
||||||
|
|
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)
|
public long RegisterBufferEvent(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
|
int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
|
||||||
|
|
||||||
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
|
||||||
|
|
||||||
public long QuerySystemEvent(ServiceCtx Context)
|
public long QuerySystemEvent(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
|
int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
|
||||||
|
|
||||||
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
|
||||||
{
|
{
|
||||||
{ 0, OpenAudioRenderer },
|
{ 0, OpenAudioRenderer },
|
||||||
{ 1, GetAudioRendererWorkBufferSize },
|
{ 1, GetAudioRendererWorkBufferSize },
|
||||||
|
{ 2, GetAudioDevice }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,5 +48,14 @@ namespace Ryujinx.Core.OsHle.IpcServices.Aud
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long GetAudioDevice(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
long UserId = Context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
MakeObject(Context, new IAudioDevice());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,64 @@
|
||||||
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
|
using Ryujinx.Core.OsHle.Utilities;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.IpcServices.Bsd
|
namespace Ryujinx.Core.OsHle.IpcServices.Bsd
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//bsd_errno == (SocketException.ErrorCode - 10000)
|
||||||
|
//https://github.com/freebsd/freebsd/blob/master/sys/sys/errno.h
|
||||||
|
public enum BsdError
|
||||||
|
{
|
||||||
|
ENOTSOCK = 38, /* Socket operation on non-socket */
|
||||||
|
EDESTADDRREQ = 39, /* Destination address required */
|
||||||
|
EMSGSIZE = 40, /* Message too long */
|
||||||
|
EPROTOTYPE = 41, /* Protocol wrong type for socket */
|
||||||
|
ENOPROTOOPT = 42, /* Protocol not available */
|
||||||
|
EPROTONOSUPPORT = 43, /* Protocol not supported */
|
||||||
|
ESOCKTNOSUPPORT = 44, /* Socket type not supported */
|
||||||
|
EOPNOTSUPP = 45, /* Operation not supported */
|
||||||
|
EPFNOSUPPORT = 46, /* Protocol family not supported */
|
||||||
|
EAFNOSUPPORT = 47, /* Address family not supported by protocol family */
|
||||||
|
EADDRINUSE = 48, /* Address already in use */
|
||||||
|
EADDRNOTAVAIL = 49, /* Can't assign requested address */
|
||||||
|
ENETDOWN = 50, /* Network is down */
|
||||||
|
ENETUNREACH = 51, /* Network is unreachable */
|
||||||
|
ENETRESET = 52, /* Network dropped connection on reset */
|
||||||
|
ECONNABORTED = 53, /* Software caused connection abort */
|
||||||
|
ECONNRESET = 54, /* Connection reset by peer */
|
||||||
|
ENOBUFS = 55, /* No buffer space available */
|
||||||
|
EISCONN = 56, /* Socket is already connected */
|
||||||
|
ENOTCONN = 57, /* Socket is not connected */
|
||||||
|
ESHUTDOWN = 58, /* Can't send after socket shutdown */
|
||||||
|
ETOOMANYREFS = 59, /* Too many references: can't splice */
|
||||||
|
ETIMEDOUT = 60, /* Operation timed out */
|
||||||
|
ECONNREFUSED = 61 /* Connection refused */
|
||||||
|
}
|
||||||
|
|
||||||
|
class SocketBsd
|
||||||
|
{
|
||||||
|
public int Family;
|
||||||
|
public int Type;
|
||||||
|
public int Protocol;
|
||||||
|
public IPAddress IpAddress;
|
||||||
|
public IPEndPoint RemoteEP;
|
||||||
|
public Socket Handle;
|
||||||
|
}
|
||||||
|
|
||||||
class ServiceBsd : IIpcService
|
class ServiceBsd : IIpcService
|
||||||
{
|
{
|
||||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
|
private List<SocketBsd> Sockets = new List<SocketBsd>();
|
||||||
|
|
||||||
public ServiceBsd()
|
public ServiceBsd()
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
|
@ -16,26 +66,31 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
|
||||||
{ 0, Initialize },
|
{ 0, Initialize },
|
||||||
{ 1, StartMonitoring },
|
{ 1, StartMonitoring },
|
||||||
{ 2, Socket },
|
{ 2, Socket },
|
||||||
|
{ 6, Poll },
|
||||||
|
{ 8, Recv },
|
||||||
{ 10, Send },
|
{ 10, Send },
|
||||||
{ 14, Connect }
|
{ 11, SendTo },
|
||||||
|
{ 12, Accept },
|
||||||
|
{ 13, Bind },
|
||||||
|
{ 14, Connect },
|
||||||
|
{ 18, Listen },
|
||||||
|
{ 21, SetSockOpt },
|
||||||
|
{ 26, Close }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
//Initialize(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno
|
//(u32, u32, u32, u32, u32, u32, u32, u32, u64 pid, u64 transferMemorySize, pid, KObject) -> u32 bsd_errno
|
||||||
public long Initialize(ServiceCtx Context)
|
public long Initialize(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
typedef struct {
|
typedef struct {
|
||||||
u32 version; // Observed 1 on 2.0 LibAppletWeb, 2 on 3.0.
|
u32 version; // Observed 1 on 2.0 LibAppletWeb, 2 on 3.0.
|
||||||
|
|
||||||
u32 tcp_tx_buf_size; // Size of the TCP transfer (send) buffer (initial or fixed).
|
u32 tcp_tx_buf_size; // Size of the TCP transfer (send) buffer (initial or fixed).
|
||||||
u32 tcp_rx_buf_size; // Size of the TCP recieve buffer (initial or fixed).
|
u32 tcp_rx_buf_size; // Size of the TCP recieve buffer (initial or fixed).
|
||||||
u32 tcp_tx_buf_max_size; // Maximum size of the TCP transfer (send) buffer. If it is 0, the size of the buffer is fixed to its initial value.
|
u32 tcp_tx_buf_max_size; // Maximum size of the TCP transfer (send) buffer. If it is 0, the size of the buffer is fixed to its initial value.
|
||||||
u32 tcp_rx_buf_max_size; // Maximum size of the TCP receive buffer. If it is 0, the size of the buffer is fixed to its initial value.
|
u32 tcp_rx_buf_max_size; // Maximum size of the TCP receive buffer. If it is 0, the size of the buffer is fixed to its initial value.
|
||||||
|
|
||||||
u32 udp_tx_buf_size; // Size of the UDP transfer (send) buffer (typically 0x2400 bytes).
|
u32 udp_tx_buf_size; // Size of the UDP transfer (send) buffer (typically 0x2400 bytes).
|
||||||
u32 udp_rx_buf_size; // Size of the UDP receive buffer (typically 0xA500 bytes).
|
u32 udp_rx_buf_size; // Size of the UDP receive buffer (typically 0xA500 bytes).
|
||||||
|
|
||||||
u32 sb_efficiency; // Number of buffers for each socket (standard values range from 1 to 8).
|
u32 sb_efficiency; // Number of buffers for each socket (standard values range from 1 to 8).
|
||||||
} BsdBufferConfig;
|
} BsdBufferConfig;
|
||||||
*/
|
*/
|
||||||
|
@ -52,7 +107,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//StartMonitoring(u64, pid)
|
//(u64, pid)
|
||||||
public long StartMonitoring(ServiceCtx Context)
|
public long StartMonitoring(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
//Todo: Stub
|
//Todo: Stub
|
||||||
|
@ -60,37 +115,378 @@ namespace Ryujinx.Core.OsHle.IpcServices.Bsd
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Socket(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno)
|
//(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno)
|
||||||
public long Socket(ServiceCtx Context)
|
public long Socket(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
Context.ResponseData.Write(0);
|
SocketBsd NewBSDSocket = new SocketBsd
|
||||||
Context.ResponseData.Write(0);
|
{
|
||||||
|
Family = Context.RequestData.ReadInt32(),
|
||||||
|
Type = Context.RequestData.ReadInt32(),
|
||||||
|
Protocol = Context.RequestData.ReadInt32()
|
||||||
|
};
|
||||||
|
|
||||||
//Todo: Stub
|
Sockets.Add(NewBSDSocket);
|
||||||
|
|
||||||
|
Sockets[Sockets.Count - 1].Handle = new Socket((AddressFamily)Sockets[Sockets.Count - 1].Family,
|
||||||
|
(SocketType)Sockets[Sockets.Count - 1].Type,
|
||||||
|
(ProtocolType)Sockets[Sockets.Count - 1].Protocol);
|
||||||
|
|
||||||
|
Context.ResponseData.Write(Sockets.Count - 1);
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Connect(u32 socket, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
|
//(u32, u32, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno, buffer<unknown, 0x22, 0>)
|
||||||
public long Connect(ServiceCtx Context)
|
public long Poll(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
Context.ResponseData.Write(0);
|
int PollCount = Context.RequestData.ReadInt32();
|
||||||
Context.ResponseData.Write(0);
|
int TimeOut = Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
//Todo: Stub
|
//https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h
|
||||||
|
//https://msdn.microsoft.com/fr-fr/library/system.net.sockets.socket.poll(v=vs.110).aspx
|
||||||
|
//https://github.com/switchbrew/libnx/blob/e0457c4534b3c37426d83e1a620f82cb28c3b528/nx/source/services/bsd.c#L343
|
||||||
|
//https://github.com/TuxSH/ftpd/blob/switch_pr/source/ftp.c#L1634
|
||||||
|
//https://linux.die.net/man/2/poll
|
||||||
|
|
||||||
|
byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
|
||||||
|
Context.Request.SendBuff[0].Position,
|
||||||
|
(int)Context.Request.SendBuff[0].Size);
|
||||||
|
int SocketId = Get32(SentBuffer, 0);
|
||||||
|
short RequestedEvents = (short)Get16(SentBuffer, 4);
|
||||||
|
short ReturnedEvents = (short)Get16(SentBuffer, 6);
|
||||||
|
|
||||||
|
//Todo: Stub - Need to implemented the Type-22 buffer.
|
||||||
|
|
||||||
|
Context.ResponseData.Write(1);
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Send(u32 socket, u32 flags, buffer<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
|
//(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, buffer<i8, 0x22, 0> message)
|
||||||
|
public long Recv(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int SocketId = Context.RequestData.ReadInt32();
|
||||||
|
int SocketFlags = Context.RequestData.ReadInt32();
|
||||||
|
byte[] ReceivedBuffer = new byte[Context.Request.ReceiveBuff[0].Size];
|
||||||
|
int ReadedBytes = Sockets[SocketId].Handle.Receive(ReceivedBuffer);
|
||||||
|
|
||||||
|
//Logging.Debug("Received Buffer:" + Environment.NewLine + Logging.HexDump(ReceivedBuffer));
|
||||||
|
|
||||||
|
AMemoryHelper.WriteBytes(Context.Memory, Context.Request.ReceiveBuff[0].Position, ReceivedBuffer);
|
||||||
|
|
||||||
|
Context.ResponseData.Write(ReadedBytes);
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
}
|
||||||
|
catch (SocketException Ex)
|
||||||
|
{
|
||||||
|
Context.ResponseData.Write(-1);
|
||||||
|
Context.ResponseData.Write(Ex.ErrorCode - 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//(u32 socket, u32 flags, buffer<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
|
||||||
public long Send(ServiceCtx Context)
|
public long Send(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
Context.ResponseData.Write(0);
|
int SocketId = Context.RequestData.ReadInt32();
|
||||||
Context.ResponseData.Write(0);
|
int SocketFlags = Context.RequestData.ReadInt32();
|
||||||
|
byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
|
||||||
|
Context.Request.SendBuff[0].Position,
|
||||||
|
(int)Context.Request.SendBuff[0].Size);
|
||||||
|
|
||||||
//Todo: Stub
|
try
|
||||||
|
{
|
||||||
|
//Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer));
|
||||||
|
|
||||||
|
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
|
||||||
|
|
||||||
|
Context.ResponseData.Write(BytesSent);
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
}
|
||||||
|
catch (SocketException Ex)
|
||||||
|
{
|
||||||
|
Context.ResponseData.Write(-1);
|
||||||
|
Context.ResponseData.Write(Ex.ErrorCode - 10000);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//(u32 socket, u32 flags, buffer<i8, 0x21, 0>, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
|
||||||
|
public long SendTo(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
int SocketId = Context.RequestData.ReadInt32();
|
||||||
|
int SocketFlags = Context.RequestData.ReadInt32();
|
||||||
|
byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
|
||||||
|
Context.Request.SendBuff[0].Position,
|
||||||
|
(int)Context.Request.SendBuff[0].Size);
|
||||||
|
byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
|
||||||
|
Context.Request.SendBuff[1].Position,
|
||||||
|
(int)Context.Request.SendBuff[1].Size);
|
||||||
|
|
||||||
|
if (!Sockets[SocketId].Handle.Connected)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ParseAddrBuffer(SocketId, AddressBuffer);
|
||||||
|
|
||||||
|
Sockets[SocketId].Handle.Connect(Sockets[SocketId].RemoteEP);
|
||||||
|
}
|
||||||
|
catch (SocketException Ex)
|
||||||
|
{
|
||||||
|
Context.ResponseData.Write(-1);
|
||||||
|
Context.ResponseData.Write(Ex.ErrorCode - 10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer));
|
||||||
|
|
||||||
|
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
|
||||||
|
|
||||||
|
Context.ResponseData.Write(BytesSent);
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
}
|
||||||
|
catch (SocketException Ex)
|
||||||
|
{
|
||||||
|
Context.ResponseData.Write(-1);
|
||||||
|
Context.ResponseData.Write(Ex.ErrorCode - 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<sockaddr, 0x22, 0> addr)
|
||||||
|
public long Accept(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
int SocketId = Context.RequestData.ReadInt32();
|
||||||
|
long AddrBufferPtr = Context.Request.ReceiveBuff[0].Position;
|
||||||
|
|
||||||
|
Socket HandleAccept = null;
|
||||||
|
|
||||||
|
var TimeOut = Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HandleAccept = Sockets[SocketId].Handle.Accept();
|
||||||
|
}
|
||||||
|
catch (SocketException Ex)
|
||||||
|
{
|
||||||
|
Context.ResponseData.Write(-1);
|
||||||
|
Context.ResponseData.Write(Ex.ErrorCode - 10000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TimeOut.Wait(10000);
|
||||||
|
|
||||||
|
if (HandleAccept != null)
|
||||||
|
{
|
||||||
|
SocketBsd NewBSDSocket = new SocketBsd
|
||||||
|
{
|
||||||
|
IpAddress = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint).Address,
|
||||||
|
RemoteEP = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint),
|
||||||
|
Handle = HandleAccept
|
||||||
|
};
|
||||||
|
|
||||||
|
Sockets.Add(NewBSDSocket);
|
||||||
|
|
||||||
|
using (MemoryStream MS = new MemoryStream())
|
||||||
|
{
|
||||||
|
BinaryWriter Writer = new BinaryWriter(MS);
|
||||||
|
|
||||||
|
Writer.Write((byte)0);
|
||||||
|
Writer.Write((byte)Sockets[Sockets.Count - 1].Handle.AddressFamily);
|
||||||
|
Writer.Write((Int16)((IPEndPoint)Sockets[Sockets.Count - 1].Handle.LocalEndPoint).Port);
|
||||||
|
|
||||||
|
string[] IpAdress = Sockets[Sockets.Count - 1].IpAddress.ToString().Split('.');
|
||||||
|
Writer.Write(byte.Parse(IpAdress[0]));
|
||||||
|
Writer.Write(byte.Parse(IpAdress[1]));
|
||||||
|
Writer.Write(byte.Parse(IpAdress[2]));
|
||||||
|
Writer.Write(byte.Parse(IpAdress[3]));
|
||||||
|
|
||||||
|
AMemoryHelper.WriteBytes(Context.Memory, AddrBufferPtr, MS.ToArray());
|
||||||
|
|
||||||
|
Context.ResponseData.Write(Sockets.Count - 1);
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
Context.ResponseData.Write(MS.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Context.ResponseData.Write(-1);
|
||||||
|
Context.ResponseData.Write((int)BsdError.ETIMEDOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//(u32 socket, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
|
||||||
|
public long Bind(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
int SocketId = Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
|
||||||
|
Context.Request.SendBuff[0].Position,
|
||||||
|
(int)Context.Request.SendBuff[0].Size);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ParseAddrBuffer(SocketId, AddressBuffer);
|
||||||
|
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
}
|
||||||
|
catch (SocketException Ex)
|
||||||
|
{
|
||||||
|
Context.ResponseData.Write(-1);
|
||||||
|
Context.ResponseData.Write(Ex.ErrorCode - 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//(u32 socket, buffer<sockaddr, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
|
||||||
|
public long Connect(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
int SocketId = Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
|
||||||
|
Context.Request.SendBuff[0].Position,
|
||||||
|
(int)Context.Request.SendBuff[0].Size);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ParseAddrBuffer(SocketId, AddressBuffer);
|
||||||
|
|
||||||
|
Sockets[SocketId].Handle.Connect(Sockets[SocketId].RemoteEP);
|
||||||
|
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
}
|
||||||
|
catch (SocketException Ex)
|
||||||
|
{
|
||||||
|
Context.ResponseData.Write(-1);
|
||||||
|
Context.ResponseData.Write(Ex.ErrorCode - 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//(u32 socket, u32 backlog) -> (i32 ret, u32 bsd_errno)
|
||||||
|
public long Listen(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
int SocketId = Context.RequestData.ReadInt32();
|
||||||
|
int BackLog = Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Sockets[SocketId].Handle.Bind(Sockets[SocketId].RemoteEP);
|
||||||
|
Sockets[SocketId].Handle.Listen(BackLog);
|
||||||
|
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
}
|
||||||
|
catch (SocketException Ex)
|
||||||
|
{
|
||||||
|
Context.ResponseData.Write(-1);
|
||||||
|
Context.ResponseData.Write(Ex.ErrorCode - 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//(u32 socket, u32 level, u32 option_name, buffer<unknown, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
|
||||||
|
public long SetSockOpt(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
int SocketId = Context.RequestData.ReadInt32();
|
||||||
|
int SocketLevel = Context.RequestData.ReadInt32();
|
||||||
|
int SocketOptionName = Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
byte[] SocketOptionValue = AMemoryHelper.ReadBytes(Context.Memory,
|
||||||
|
Context.Request.PtrBuff[0].Position,
|
||||||
|
Context.Request.PtrBuff[0].Size);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Sockets[SocketId].Handle.SetSocketOption((SocketOptionLevel)SocketLevel,
|
||||||
|
(SocketOptionName)SocketOptionName,
|
||||||
|
Get32(SocketOptionValue, 0));
|
||||||
|
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
}
|
||||||
|
catch (SocketException Ex)
|
||||||
|
{
|
||||||
|
Context.ResponseData.Write(-1);
|
||||||
|
Context.ResponseData.Write(Ex.ErrorCode - 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//(u32 socket) -> (i32 ret, u32 bsd_errno)
|
||||||
|
public long Close(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
int SocketId = Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Sockets[SocketId].Handle.Close();
|
||||||
|
Sockets[SocketId] = null;
|
||||||
|
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
Context.ResponseData.Write(0);
|
||||||
|
}
|
||||||
|
catch (SocketException Ex)
|
||||||
|
{
|
||||||
|
Context.ResponseData.Write(-1);
|
||||||
|
Context.ResponseData.Write(Ex.ErrorCode - 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ParseAddrBuffer(int SocketId, byte[] AddrBuffer)
|
||||||
|
{
|
||||||
|
using (MemoryStream MS = new MemoryStream(AddrBuffer))
|
||||||
|
{
|
||||||
|
BinaryReader Reader = new BinaryReader(MS);
|
||||||
|
|
||||||
|
int Size = Reader.ReadByte();
|
||||||
|
int Family = Reader.ReadByte();
|
||||||
|
int Port = EndianSwap.Swap16(Reader.ReadInt16());
|
||||||
|
string IpAddress = Reader.ReadByte().ToString() +
|
||||||
|
"." + Reader.ReadByte().ToString() +
|
||||||
|
"." + Reader.ReadByte().ToString() +
|
||||||
|
"." + Reader.ReadByte().ToString();
|
||||||
|
|
||||||
|
Logging.Debug($"Try to connect to {IpAddress}:{Port}");
|
||||||
|
|
||||||
|
Sockets[SocketId].IpAddress = IPAddress.Parse(IpAddress);
|
||||||
|
Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int Get16(byte[] Data, int Address)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
Data[Address + 0] << 0 |
|
||||||
|
Data[Address + 1] << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int Get32(byte[] Data, int Address)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
Data[Address + 0] << 0 |
|
||||||
|
Data[Address + 1] << 8 |
|
||||||
|
Data[Address + 2] << 16 |
|
||||||
|
Data[Address + 3] << 24;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
using static Ryujinx.Core.OsHle.IpcServices.ErrorCode;
|
using static Ryujinx.Core.OsHle.ErrorCode;
|
||||||
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
|
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
|
namespace Ryujinx.Core.OsHle.IpcServices.FspSrv
|
||||||
|
|
|
@ -10,21 +10,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
private HSharedMem Handle;
|
private HSharedMem HidSharedMem;
|
||||||
|
|
||||||
public IAppletResource(HSharedMem Handle)
|
public IAppletResource(HSharedMem HidSharedMem)
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
{
|
{
|
||||||
{ 0, GetSharedMemoryHandle }
|
{ 0, GetSharedMemoryHandle }
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Handle = Handle;
|
this.HidSharedMem = HidSharedMem;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long GetSharedMemoryHandle(ServiceCtx Context)
|
public long GetSharedMemoryHandle(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.HidHandle);
|
int Handle = Context.Process.HandleTable.OpenHandle(HidSharedMem);
|
||||||
|
|
||||||
|
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Ryujinx.Core.Input;
|
||||||
|
|
||||||
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
|
using static Ryujinx.Core.OsHle.IpcServices.ObjHelper;
|
||||||
|
|
||||||
|
@ -16,25 +16,27 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
{
|
{
|
||||||
{ 0, CreateAppletResource },
|
{ 0, CreateAppletResource },
|
||||||
{ 11, ActivateTouchScreen },
|
{ 11, ActivateTouchScreen },
|
||||||
{ 100, SetSupportedNpadStyleSet },
|
{ 66, StartSixAxisSensor },
|
||||||
{ 101, GetSupportedNpadStyleSet },
|
{ 100, SetSupportedNpadStyleSet },
|
||||||
{ 102, SetSupportedNpadIdType },
|
{ 101, GetSupportedNpadStyleSet },
|
||||||
{ 103, ActivateNpad },
|
{ 102, SetSupportedNpadIdType },
|
||||||
{ 120, SetNpadJoyHoldType },
|
{ 103, ActivateNpad },
|
||||||
{ 121, GetNpadJoyHoldType },
|
{ 120, SetNpadJoyHoldType },
|
||||||
{ 200, GetVibrationDeviceInfo },
|
{ 122, SetNpadJoyAssignmentModeSingleByDefault },
|
||||||
{ 203, CreateActiveVibrationDeviceList },
|
{ 123, SetNpadJoyAssignmentModeSingle },
|
||||||
{ 206, SendVibrationValues }
|
{ 124, SetNpadJoyAssignmentModeDual },
|
||||||
|
{ 125, MergeSingleJoyAsDualJoy },
|
||||||
|
{ 200, GetVibrationDeviceInfo },
|
||||||
|
{ 203, CreateActiveVibrationDeviceList },
|
||||||
|
{ 206, SendVibrationValues }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public long CreateAppletResource(ServiceCtx Context)
|
public long CreateAppletResource(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
HSharedMem HidHndData = Context.Ns.Os.Handles.GetData<HSharedMem>(Context.Ns.Os.HidHandle);
|
MakeObject(Context, new IAppletResource(Context.Ns.Os.HidSharedMem));
|
||||||
|
|
||||||
MakeObject(Context, new IAppletResource(HidHndData));
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -46,6 +48,15 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long StartSixAxisSensor(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
int Handle = Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
long AppletResourceUserId = Context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public long GetSupportedNpadStyleSet(ServiceCtx Context)
|
public long GetSupportedNpadStyleSet(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
Context.ResponseData.Write(0);
|
Context.ResponseData.Write(0);
|
||||||
|
@ -90,6 +101,40 @@ namespace Ryujinx.Core.OsHle.IpcServices.Hid
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32();
|
||||||
|
long AppletUserResourseId = Context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SetNpadJoyAssignmentModeSingle(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32();
|
||||||
|
long AppletUserResourseId = Context.RequestData.ReadInt64();
|
||||||
|
long NpadJoyDeviceType = Context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long SetNpadJoyAssignmentModeDual(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32();
|
||||||
|
long AppletUserResourseId = Context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long MergeSingleJoyAsDualJoy(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
long Unknown0 = Context.RequestData.ReadInt32();
|
||||||
|
long Unknown8 = Context.RequestData.ReadInt32();
|
||||||
|
long AppletUserResourseId = Context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public long GetVibrationDeviceInfo(ServiceCtx Context)
|
public long GetVibrationDeviceInfo(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
int VibrationDeviceHandle = Context.RequestData.ReadInt32();
|
int VibrationDeviceHandle = Context.RequestData.ReadInt32();
|
||||||
|
|
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 ChocolArm64.Memory;
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using Ryujinx.Core.OsHle.Utilities;
|
using Ryujinx.Core.OsHle.Utilities;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
|
@ -12,40 +11,17 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
{
|
{
|
||||||
private delegate long ServiceProcessIoctl(ServiceCtx Context);
|
private delegate long ServiceProcessIoctl(ServiceCtx Context);
|
||||||
|
|
||||||
private static Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds =
|
|
||||||
new Dictionary<(string, int), ServiceProcessIoctl>()
|
|
||||||
{
|
|
||||||
{ ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel },
|
|
||||||
{ ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace },
|
|
||||||
{ ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx },
|
|
||||||
{ ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions },
|
|
||||||
{ ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx },
|
|
||||||
{ ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig },
|
|
||||||
{ ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait },
|
|
||||||
{ ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize },
|
|
||||||
{ ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo },
|
|
||||||
{ ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics },
|
|
||||||
{ ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks },
|
|
||||||
{ ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask },
|
|
||||||
{ ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData },
|
|
||||||
{ ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap },
|
|
||||||
{ ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo },
|
|
||||||
{ ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx },
|
|
||||||
{ ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind },
|
|
||||||
{ ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier },
|
|
||||||
{ ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority },
|
|
||||||
{ ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 },
|
|
||||||
{ ("/dev/nvmap", 0x0101), NvMapIocCreate },
|
|
||||||
{ ("/dev/nvmap", 0x0103), NvMapIocFromId },
|
|
||||||
{ ("/dev/nvmap", 0x0104), NvMapIocAlloc },
|
|
||||||
{ ("/dev/nvmap", 0x0109), NvMapIocParam },
|
|
||||||
{ ("/dev/nvmap", 0x010e), NvMapIocGetId },
|
|
||||||
};
|
|
||||||
|
|
||||||
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
private Dictionary<int, ServiceProcessRequest> m_Commands;
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
|
private Dictionary<(string, int), ServiceProcessIoctl> IoctlCmds;
|
||||||
|
|
||||||
|
private IdDictionary Fds;
|
||||||
|
|
||||||
|
private IdDictionary NvMaps;
|
||||||
|
private IdDictionary NvMapsById;
|
||||||
|
|
||||||
public ServiceNvDrv()
|
public ServiceNvDrv()
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
|
@ -57,15 +33,50 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
{ 4, QueryEvent },
|
{ 4, QueryEvent },
|
||||||
{ 8, SetClientPid },
|
{ 8, SetClientPid },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
IoctlCmds = new Dictionary<(string, int), ServiceProcessIoctl>()
|
||||||
|
{
|
||||||
|
{ ("/dev/nvhost-as-gpu", 0x4101), NvGpuAsIoctlBindChannel },
|
||||||
|
{ ("/dev/nvhost-as-gpu", 0x4102), NvGpuAsIoctlAllocSpace },
|
||||||
|
{ ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx },
|
||||||
|
{ ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions },
|
||||||
|
{ ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx },
|
||||||
|
{ ("/dev/nvhost-ctrl", 0x001b), NvHostIoctlCtrlGetConfig },
|
||||||
|
{ ("/dev/nvhost-ctrl", 0x001d), NvHostIoctlCtrlEventWait },
|
||||||
|
{ ("/dev/nvhost-ctrl-gpu", 0x4701), NvGpuIoctlZcullGetCtxSize },
|
||||||
|
{ ("/dev/nvhost-ctrl-gpu", 0x4702), NvGpuIoctlZcullGetInfo },
|
||||||
|
{ ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics },
|
||||||
|
{ ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks },
|
||||||
|
{ ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask },
|
||||||
|
{ ("/dev/nvhost-gpu", 0x4714), NvMapIoctlChannelSetUserData },
|
||||||
|
{ ("/dev/nvhost-gpu", 0x4801), NvMapIoctlChannelSetNvMap },
|
||||||
|
{ ("/dev/nvhost-gpu", 0x4808), NvMapIoctlChannelSubmitGpFifo },
|
||||||
|
{ ("/dev/nvhost-gpu", 0x4809), NvMapIoctlChannelAllocObjCtx },
|
||||||
|
{ ("/dev/nvhost-gpu", 0x480b), NvMapIoctlChannelZcullBind },
|
||||||
|
{ ("/dev/nvhost-gpu", 0x480c), NvMapIoctlChannelSetErrorNotifier },
|
||||||
|
{ ("/dev/nvhost-gpu", 0x480d), NvMapIoctlChannelSetPriority },
|
||||||
|
{ ("/dev/nvhost-gpu", 0x481a), NvMapIoctlChannelAllocGpFifoEx2 },
|
||||||
|
{ ("/dev/nvmap", 0x0101), NvMapIocCreate },
|
||||||
|
{ ("/dev/nvmap", 0x0103), NvMapIocFromId },
|
||||||
|
{ ("/dev/nvmap", 0x0104), NvMapIocAlloc },
|
||||||
|
{ ("/dev/nvmap", 0x0105), NvMapIocFree },
|
||||||
|
{ ("/dev/nvmap", 0x0109), NvMapIocParam },
|
||||||
|
{ ("/dev/nvmap", 0x010e), NvMapIocGetId },
|
||||||
|
};
|
||||||
|
|
||||||
|
Fds = new IdDictionary();
|
||||||
|
|
||||||
|
NvMaps = new IdDictionary();
|
||||||
|
NvMapsById = new IdDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long Open(ServiceCtx Context)
|
public long Open(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long NamePtr = Context.Request.SendBuff[0].Position;
|
long NamePtr = Context.Request.SendBuff[0].Position;
|
||||||
|
|
||||||
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr);
|
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, NamePtr);
|
||||||
|
|
||||||
int Fd = Context.Ns.Os.Fds.GenerateId(new FileDesc(Name));
|
int Fd = Fds.Add(new NvFd(Name));
|
||||||
|
|
||||||
Context.ResponseData.Write(Fd);
|
Context.ResponseData.Write(Fd);
|
||||||
Context.ResponseData.Write(0);
|
Context.ResponseData.Write(0);
|
||||||
|
@ -73,14 +84,14 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long Ioctl(ServiceCtx Context)
|
public long Ioctl(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
int Fd = Context.RequestData.ReadInt32();
|
int Fd = Context.RequestData.ReadInt32();
|
||||||
int Cmd = Context.RequestData.ReadInt32() & 0xffff;
|
int Cmd = Context.RequestData.ReadInt32() & 0xffff;
|
||||||
|
|
||||||
FileDesc FdData = Context.Ns.Os.Fds.GetData<FileDesc>(Fd);
|
NvFd FdData = Fds.GetData<NvFd>(Fd);
|
||||||
|
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
Context.ResponseData.Write(0);
|
Context.ResponseData.Write(0);
|
||||||
|
|
||||||
|
@ -94,18 +105,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long Close(ServiceCtx Context)
|
public long Close(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
int Fd = Context.RequestData.ReadInt32();
|
int Fd = Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
Context.Ns.Os.Fds.Delete(Fd);
|
Fds.Delete(Fd);
|
||||||
|
|
||||||
Context.ResponseData.Write(0);
|
Context.ResponseData.Write(0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long Initialize(ServiceCtx Context)
|
public long Initialize(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long TransferMemSize = Context.RequestData.ReadInt64();
|
long TransferMemSize = Context.RequestData.ReadInt64();
|
||||||
int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0];
|
int TransferMemHandle = Context.Request.HandleDesc.ToCopy[0];
|
||||||
|
@ -115,7 +126,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long QueryEvent(ServiceCtx Context)
|
public long QueryEvent(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
int Fd = Context.RequestData.ReadInt32();
|
int Fd = Context.RequestData.ReadInt32();
|
||||||
int EventId = Context.RequestData.ReadInt32();
|
int EventId = Context.RequestData.ReadInt32();
|
||||||
|
@ -127,7 +138,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long SetClientPid(ServiceCtx Context)
|
public long SetClientPid(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Pid = Context.RequestData.ReadInt64();
|
long Pid = Context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
@ -136,18 +147,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvGpuAsIoctlBindChannel(ServiceCtx Context)
|
private long NvGpuAsIoctlBindChannel(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
int Fd = Context.Memory.ReadInt32(Position);
|
int Fd = Context.Memory.ReadInt32(Position);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvGpuAsIoctlAllocSpace(ServiceCtx Context)
|
private long NvGpuAsIoctlAllocSpace(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
|
|
||||||
|
@ -171,9 +182,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvGpuAsIoctlMapBufferEx(ServiceCtx Context)
|
private long NvGpuAsIoctlMapBufferEx(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
|
|
||||||
|
@ -185,18 +196,29 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
long MapSize = Reader.ReadInt64();
|
long MapSize = Reader.ReadInt64();
|
||||||
long Offset = Reader.ReadInt64();
|
long Offset = Reader.ReadInt64();
|
||||||
|
|
||||||
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
|
if (Handle == 0)
|
||||||
|
|
||||||
if (NvMap != null)
|
|
||||||
{
|
{
|
||||||
if ((Flags & 1) != 0)
|
//Handle 0 is valid here, but it refers to something else.
|
||||||
{
|
//TODO: Figure out what, for now just return success.
|
||||||
Offset = Context.Ns.Gpu.MapMemory(NvMap.Address, Offset, NvMap.Size);
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
NvMap Map = NvMaps.GetData<NvMap>(Handle);
|
||||||
Offset = Context.Ns.Gpu.MapMemory(NvMap.Address, NvMap.Size);
|
|
||||||
}
|
if (Map == null)
|
||||||
|
{
|
||||||
|
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
|
||||||
|
|
||||||
|
return -1; //TODO: Corrent error code.
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Flags & 1) != 0)
|
||||||
|
{
|
||||||
|
Offset = Context.Ns.Gpu.MapMemory(Map.Address, Offset, Map.Size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Offset = Context.Ns.Gpu.MapMemory(Map.Address, Map.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.Memory.WriteInt64(Position + 0x20, Offset);
|
Context.Memory.WriteInt64(Position + 0x20, Offset);
|
||||||
|
@ -204,9 +226,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvGpuAsIoctlGetVaRegions(ServiceCtx Context)
|
private long NvGpuAsIoctlGetVaRegions(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
MemWriter Writer = new MemWriter(Context.Memory, Position);
|
MemWriter Writer = new MemWriter(Context.Memory, Position);
|
||||||
|
@ -234,9 +256,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvGpuAsIoctlInitializeEx(ServiceCtx Context)
|
private long NvGpuAsIoctlInitializeEx(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
|
|
||||||
|
@ -251,9 +273,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvHostIoctlCtrlGetConfig(ServiceCtx Context)
|
private long NvHostIoctlCtrlGetConfig(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
MemWriter Writer = new MemWriter(Context.Memory, Position + 0x82);
|
MemWriter Writer = new MemWriter(Context.Memory, Position + 0x82);
|
||||||
|
@ -266,9 +288,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvHostIoctlCtrlEventWait(ServiceCtx Context)
|
private long NvHostIoctlCtrlEventWait(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
|
|
||||||
|
@ -282,18 +304,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context)
|
private long NvGpuIoctlZcullGetCtxSize(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
Context.Memory.WriteInt32(Position, 1);
|
Context.Memory.WriteInt32(Position, 1);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvGpuIoctlZcullGetInfo(ServiceCtx Context)
|
private long NvGpuIoctlZcullGetInfo(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemWriter Writer = new MemWriter(Context.Memory, Position);
|
MemWriter Writer = new MemWriter(Context.Memory, Position);
|
||||||
|
|
||||||
|
@ -311,9 +333,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvGpuIoctlGetCharacteristics(ServiceCtx Context)
|
private long NvGpuIoctlGetCharacteristics(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
MemWriter Writer = new MemWriter(Context.Memory, Position);
|
MemWriter Writer = new MemWriter(Context.Memory, Position);
|
||||||
|
@ -373,9 +395,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvGpuIoctlGetTpcMasks(ServiceCtx Context)
|
private long NvGpuIoctlGetTpcMasks(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
|
|
||||||
|
@ -387,9 +409,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context)
|
private long NvGpuIoctlZbcGetActiveSlotMask(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
Context.Memory.WriteInt32(Position + 0, 7);
|
Context.Memory.WriteInt32(Position + 0, 7);
|
||||||
Context.Memory.WriteInt32(Position + 4, 1);
|
Context.Memory.WriteInt32(Position + 4, 1);
|
||||||
|
@ -397,25 +419,25 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIoctlChannelSetUserData(ServiceCtx Context)
|
private long NvMapIoctlChannelSetUserData(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIoctlChannelSetNvMap(ServiceCtx Context)
|
private long NvMapIoctlChannelSetNvMap(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
int Fd = Context.Memory.ReadInt32(Position);
|
int Fd = Context.Memory.ReadInt32(Position);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context)
|
private long NvMapIoctlChannelSubmitGpFifo(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
MemWriter Writer = new MemWriter(Context.Memory, Position + 0x10);
|
MemWriter Writer = new MemWriter(Context.Memory, Position + 0x10);
|
||||||
|
@ -452,9 +474,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context)
|
private long NvMapIoctlChannelAllocObjCtx(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
int ClassNum = Context.Memory.ReadInt32(Position + 0);
|
int ClassNum = Context.Memory.ReadInt32(Position + 0);
|
||||||
int Flags = Context.Memory.ReadInt32(Position + 4);
|
int Flags = Context.Memory.ReadInt32(Position + 4);
|
||||||
|
@ -464,9 +486,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIoctlChannelZcullBind(ServiceCtx Context)
|
private long NvMapIoctlChannelZcullBind(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
|
|
||||||
|
@ -477,9 +499,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context)
|
private long NvMapIoctlChannelSetErrorNotifier(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
|
|
||||||
|
@ -491,18 +513,18 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIoctlChannelSetPriority(ServiceCtx Context)
|
private long NvMapIoctlChannelSetPriority(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
int Priority = Context.Memory.ReadInt32(Position);
|
int Priority = Context.Memory.ReadInt32(Position);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context)
|
private long NvMapIoctlChannelAllocGpFifoEx2(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.PtrBuff[0].Position;
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
MemReader Reader = new MemReader(Context.Memory, Position);
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
MemWriter Writer = new MemWriter(Context.Memory, Position + 0xc);
|
MemWriter Writer = new MemWriter(Context.Memory, Position + 0xc);
|
||||||
|
@ -520,47 +542,46 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIocCreate(ServiceCtx Context)
|
private long NvMapIocCreate(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.GetSendBuffPtr();
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
int Size = Context.Memory.ReadInt32(Position);
|
int Size = Context.Memory.ReadInt32(Position);
|
||||||
|
|
||||||
int Id = Context.Ns.Os.NvMapIds.GenerateId();
|
NvMap Map = new NvMap() { Size = Size };
|
||||||
|
|
||||||
int Handle = Context.Ns.Os.Handles.GenerateId(new HNvMap(Id, Size));
|
Map.Handle = NvMaps.Add(Map);
|
||||||
|
|
||||||
Context.Memory.WriteInt32(Position + 4, Handle);
|
Map.Id = NvMapsById.Add(Map);
|
||||||
|
|
||||||
Logging.Info($"NvMap {Id} created with size {Size:x8}!");
|
Context.Memory.WriteInt32(Position + 4, Map.Handle);
|
||||||
|
|
||||||
|
Logging.Info($"NvMap {Map.Id} created with size {Size:x8}!");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIocFromId(ServiceCtx Context)
|
private long NvMapIocFromId(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.GetSendBuffPtr();
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
int Id = Context.Memory.ReadInt32(Position);
|
int Id = Context.Memory.ReadInt32(Position);
|
||||||
|
|
||||||
int Handle = -1;
|
NvMap Map = NvMapsById.GetData<NvMap>(Id);
|
||||||
|
|
||||||
foreach (KeyValuePair<int, object> KV in Context.Ns.Os.Handles)
|
if (Map == null)
|
||||||
{
|
{
|
||||||
if (KV.Value is HNvMap NvMap && NvMap.Id == Id)
|
Logging.Warn($"Trying to use invalid NvMap Id {Id}!");
|
||||||
{
|
|
||||||
Handle = KV.Key;
|
return -1; //TODO: Corrent error code.
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.Memory.WriteInt32(Position + 4, Handle);
|
Context.Memory.WriteInt32(Position + 4, Map.Handle);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIocAlloc(ServiceCtx Context)
|
private long NvMapIocAlloc(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.GetSendBuffPtr();
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
|
@ -573,19 +594,49 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
byte Kind = (byte)Reader.ReadInt64();
|
byte Kind = (byte)Reader.ReadInt64();
|
||||||
long Addr = Reader.ReadInt64();
|
long Addr = Reader.ReadInt64();
|
||||||
|
|
||||||
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
|
NvMap Map = NvMaps.GetData<NvMap>(Handle);
|
||||||
|
|
||||||
if (NvMap != null)
|
if (Map == null)
|
||||||
{
|
{
|
||||||
NvMap.Address = Addr;
|
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
|
||||||
NvMap.Align = Align;
|
|
||||||
NvMap.Kind = Kind;
|
return -1; //TODO: Corrent error code.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map.Address = Addr;
|
||||||
|
Map.Align = Align;
|
||||||
|
Map.Kind = Kind;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIocParam(ServiceCtx Context)
|
private long NvMapIocFree(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
|
MemReader Reader = new MemReader(Context.Memory, Position);
|
||||||
|
MemWriter Writer = new MemWriter(Context.Memory, Position + 8);
|
||||||
|
|
||||||
|
int Handle = Reader.ReadInt32();
|
||||||
|
int Padding = Reader.ReadInt32();
|
||||||
|
|
||||||
|
NvMap Map = NvMaps.GetData<NvMap>(Handle);
|
||||||
|
|
||||||
|
if (Map == null)
|
||||||
|
{
|
||||||
|
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
|
||||||
|
|
||||||
|
return -1; //TODO: Corrent error code.
|
||||||
|
}
|
||||||
|
|
||||||
|
Writer.WriteInt64(0);
|
||||||
|
Writer.WriteInt32(Map.Size);
|
||||||
|
Writer.WriteInt32(0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long NvMapIocParam(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.GetSendBuffPtr();
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
|
@ -594,16 +645,23 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
int Handle = Reader.ReadInt32();
|
int Handle = Reader.ReadInt32();
|
||||||
int Param = Reader.ReadInt32();
|
int Param = Reader.ReadInt32();
|
||||||
|
|
||||||
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
|
NvMap Map = NvMaps.GetData<NvMap>(Handle);
|
||||||
|
|
||||||
|
if (Map == null)
|
||||||
|
{
|
||||||
|
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
|
||||||
|
|
||||||
|
return -1; //TODO: Corrent error code.
|
||||||
|
}
|
||||||
|
|
||||||
int Response = 0;
|
int Response = 0;
|
||||||
|
|
||||||
switch (Param)
|
switch (Param)
|
||||||
{
|
{
|
||||||
case 1: Response = NvMap.Size; break;
|
case 1: Response = Map.Size; break;
|
||||||
case 2: Response = NvMap.Align; break;
|
case 2: Response = Map.Align; break;
|
||||||
case 4: Response = 0x40000000; break;
|
case 4: Response = 0x40000000; break;
|
||||||
case 5: Response = NvMap.Kind; break;
|
case 5: Response = Map.Kind; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.Memory.WriteInt32(Position + 8, Response);
|
Context.Memory.WriteInt32(Position + 8, Response);
|
||||||
|
@ -611,17 +669,29 @@ namespace Ryujinx.Core.OsHle.IpcServices.NvServices
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long NvMapIocGetId(ServiceCtx Context)
|
private long NvMapIocGetId(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long Position = Context.Request.GetSendBuffPtr();
|
long Position = Context.Request.GetSendBuffPtr();
|
||||||
|
|
||||||
int Handle = Context.Memory.ReadInt32(Position + 4);
|
int Handle = Context.Memory.ReadInt32(Position + 4);
|
||||||
|
|
||||||
HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle);
|
NvMap Map = NvMaps.GetData<NvMap>(Handle);
|
||||||
|
|
||||||
Context.Memory.WriteInt32(Position, NvMap.Id);
|
if (Map == null)
|
||||||
|
{
|
||||||
|
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
|
||||||
|
|
||||||
|
return -1; //TODO: Corrent error code.
|
||||||
|
}
|
||||||
|
|
||||||
|
Context.Memory.WriteInt32(Position, Map.Id);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NvMap GetNvMap(int Handle)
|
||||||
|
{
|
||||||
|
return NvMaps.GetData<NvMap>(Handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,13 +9,13 @@ namespace Ryujinx.Core.OsHle.IpcServices
|
||||||
{
|
{
|
||||||
if (Context.Session is HDomain Dom)
|
if (Context.Session is HDomain Dom)
|
||||||
{
|
{
|
||||||
Context.Response.ResponseObjIds.Add(Dom.GenerateObjectId(Obj));
|
Context.Response.ResponseObjIds.Add(Dom.Add(Obj));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
HSessionObj HndData = new HSessionObj(Context.Session, Obj);
|
HSessionObj HndData = new HSessionObj(Context.Session, Obj);
|
||||||
|
|
||||||
int VHandle = Context.Ns.Os.Handles.GenerateId(HndData);
|
int VHandle = Context.Process.HandleTable.OpenHandle(HndData);
|
||||||
|
|
||||||
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(VHandle);
|
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(VHandle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Pl
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
{
|
{
|
||||||
|
{ 0, RequestLoad },
|
||||||
{ 1, GetLoadState },
|
{ 1, GetLoadState },
|
||||||
{ 2, GetFontSize },
|
{ 2, GetFontSize },
|
||||||
{ 3, GetSharedMemoryAddressOffset },
|
{ 3, GetSharedMemoryAddressOffset },
|
||||||
|
@ -20,30 +21,39 @@ namespace Ryujinx.Core.OsHle.IpcServices.Pl
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long GetLoadState(ServiceCtx Context)
|
public long RequestLoad(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
SharedFontType FontType = (SharedFontType)Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long GetLoadState(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
Context.ResponseData.Write(1); //Loaded
|
Context.ResponseData.Write(1); //Loaded
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long GetFontSize(ServiceCtx Context)
|
public long GetFontSize(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
Context.ResponseData.Write(Horizon.FontSize);
|
Context.ResponseData.Write(Horizon.FontSize);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long GetSharedMemoryAddressOffset(ServiceCtx Context)
|
public long GetSharedMemoryAddressOffset(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
Context.ResponseData.Write(0);
|
Context.ResponseData.Write(0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long GetSharedMemoryNativeHandle(ServiceCtx Context)
|
public long GetSharedMemoryNativeHandle(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Context.Ns.Os.FontHandle);
|
int Handle = Context.Process.HandleTable.OpenHandle(Context.Ns.Os.FontSharedMem);
|
||||||
|
|
||||||
|
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
HSession Session = new HSession(ServiceFactory.MakeService(Name));
|
HSession Session = new HSession(Context.Process.Services.GetService(Name));
|
||||||
|
|
||||||
int Handle = Context.Ns.Os.Handles.GenerateId(Session);
|
int Handle = Context.Process.HandleTable.OpenHandle(Session);
|
||||||
|
|
||||||
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
|
Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Ryujinx.Core.OsHle
|
namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
{
|
{
|
||||||
class Display
|
class Display
|
||||||
{
|
{
|
|
@ -15,6 +15,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
|
|
||||||
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
|
||||||
|
|
||||||
|
private IdDictionary Displays;
|
||||||
|
|
||||||
public IApplicationDisplayService()
|
public IApplicationDisplayService()
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
|
@ -24,16 +26,21 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
{ 102, GetManagerDisplayService },
|
{ 102, GetManagerDisplayService },
|
||||||
{ 103, GetIndirectDisplayTransactionService },
|
{ 103, GetIndirectDisplayTransactionService },
|
||||||
{ 1010, OpenDisplay },
|
{ 1010, OpenDisplay },
|
||||||
|
{ 1020, CloseDisplay },
|
||||||
{ 2020, OpenLayer },
|
{ 2020, OpenLayer },
|
||||||
|
{ 2021, CloseLayer },
|
||||||
{ 2030, CreateStrayLayer },
|
{ 2030, CreateStrayLayer },
|
||||||
|
{ 2031, DestroyStrayLayer },
|
||||||
{ 2101, SetLayerScalingMode },
|
{ 2101, SetLayerScalingMode },
|
||||||
{ 5202, GetDisplayVSyncEvent }
|
{ 5202, GetDisplayVSyncEvent }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Displays = new IdDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetRelayService(ServiceCtx Context)
|
public long GetRelayService(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
MakeObject(Context, new IHOSBinderDriver());
|
MakeObject(Context, new IHOSBinderDriver(Context.Ns.Gpu.Renderer));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -54,7 +61,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
|
|
||||||
public long GetIndirectDisplayTransactionService(ServiceCtx Context)
|
public long GetIndirectDisplayTransactionService(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
MakeObject(Context, new IHOSBinderDriver());
|
MakeObject(Context, new IHOSBinderDriver(Context.Ns.Gpu.Renderer));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -63,13 +70,22 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
{
|
{
|
||||||
string Name = GetDisplayName(Context);
|
string Name = GetDisplayName(Context);
|
||||||
|
|
||||||
long DisplayId = Context.Ns.Os.Displays.GenerateId(new Display(Name));
|
long DisplayId = Displays.Add(new Display(Name));
|
||||||
|
|
||||||
Context.ResponseData.Write(DisplayId);
|
Context.ResponseData.Write(DisplayId);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long CloseDisplay(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
int DisplayId = Context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
Displays.Delete(DisplayId);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public long OpenLayer(ServiceCtx Context)
|
public long OpenLayer(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long LayerId = Context.RequestData.ReadInt64();
|
long LayerId = Context.RequestData.ReadInt64();
|
||||||
|
@ -86,6 +102,13 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long CloseLayer(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
long LayerId = Context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public long CreateStrayLayer(ServiceCtx Context)
|
public long CreateStrayLayer(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
long LayerFlags = Context.RequestData.ReadInt64();
|
long LayerFlags = Context.RequestData.ReadInt64();
|
||||||
|
@ -93,7 +116,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
|
|
||||||
long ParcelPtr = Context.Request.ReceiveBuff[0].Position;
|
long ParcelPtr = Context.Request.ReceiveBuff[0].Position;
|
||||||
|
|
||||||
Display Disp = Context.Ns.Os.Displays.GetData<Display>((int)DisplayId);
|
Display Disp = Displays.GetData<Display>((int)DisplayId);
|
||||||
|
|
||||||
byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr);
|
byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr);
|
||||||
|
|
||||||
|
@ -105,6 +128,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long DestroyStrayLayer(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public long SetLayerScalingMode(ServiceCtx Context)
|
public long SetLayerScalingMode(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
int ScalingMode = Context.RequestData.ReadInt32();
|
int ScalingMode = Context.RequestData.ReadInt32();
|
||||||
|
@ -117,7 +145,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
{
|
{
|
||||||
string Name = GetDisplayName(Context);
|
string Name = GetDisplayName(Context);
|
||||||
|
|
||||||
int Handle = Context.Ns.Os.Handles.GenerateId(new HEvent());
|
int Handle = Context.Process.HandleTable.OpenHandle(new HEvent());
|
||||||
|
|
||||||
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using Ryujinx.Core.OsHle.IpcServices.Android;
|
using Ryujinx.Core.OsHle.IpcServices.Android;
|
||||||
|
using Ryujinx.Graphics.Gal;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
|
|
||||||
private NvFlinger Flinger;
|
private NvFlinger Flinger;
|
||||||
|
|
||||||
public IHOSBinderDriver()
|
public IHOSBinderDriver(IGalRenderer Renderer)
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
{
|
{
|
||||||
|
@ -23,7 +24,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
{ 2, GetNativeHandle }
|
{ 2, GetNativeHandle }
|
||||||
};
|
};
|
||||||
|
|
||||||
Flinger = new NvFlinger();
|
Flinger = new NvFlinger(Renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long TransactParcel(ServiceCtx Context)
|
public long TransactParcel(ServiceCtx Context)
|
||||||
|
|
|
@ -13,8 +13,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
{
|
{
|
||||||
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
m_Commands = new Dictionary<int, ServiceProcessRequest>()
|
||||||
{
|
{
|
||||||
{ 2010, CreateManagedLayer },
|
{ 2010, CreateManagedLayer },
|
||||||
{ 6000, AddToLayerStack }
|
{ 2011, DestroyManagedLayer },
|
||||||
|
{ 6000, AddToLayerStack }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +26,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Vi
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long DestroyManagedLayer(ServiceCtx Context)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
public static long AddToLayerStack(ServiceCtx Context)
|
public static long AddToLayerStack(ServiceCtx Context)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
using Ryujinx.Core.OsHle.IpcServices.NvServices;
|
||||||
|
using Ryujinx.Graphics.Gal;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -54,29 +55,43 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
|
||||||
public GbpBuffer Data;
|
public GbpBuffer Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IGalRenderer Renderer;
|
||||||
|
|
||||||
private BufferEntry[] BufferQueue;
|
private BufferEntry[] BufferQueue;
|
||||||
|
|
||||||
private ManualResetEvent WaitBufferFree;
|
private ManualResetEvent WaitBufferFree;
|
||||||
|
|
||||||
|
private object RenderQueueLock;
|
||||||
|
|
||||||
|
private int RenderQueueCount;
|
||||||
|
|
||||||
|
private bool NvFlingerDisposed;
|
||||||
|
|
||||||
private bool KeepRunning;
|
private bool KeepRunning;
|
||||||
|
|
||||||
public NvFlinger()
|
public NvFlinger(IGalRenderer Renderer)
|
||||||
{
|
{
|
||||||
Commands = new Dictionary<(string, int), ServiceProcessParcel>()
|
Commands = new Dictionary<(string, int), ServiceProcessParcel>()
|
||||||
{
|
{
|
||||||
{ ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer },
|
{ ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer },
|
||||||
{ ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer },
|
{ ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer },
|
||||||
|
{ ("android.gui.IGraphicBufferProducer", 0x4), GbpDetachBuffer },
|
||||||
{ ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer },
|
{ ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer },
|
||||||
{ ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer },
|
{ ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer },
|
||||||
{ ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery },
|
{ ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery },
|
||||||
{ ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect },
|
{ ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect },
|
||||||
|
{ ("android.gui.IGraphicBufferProducer", 0xb), GbpDisconnect },
|
||||||
{ ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
|
{ ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.Renderer = Renderer;
|
||||||
|
|
||||||
BufferQueue = new BufferEntry[0x40];
|
BufferQueue = new BufferEntry[0x40];
|
||||||
|
|
||||||
WaitBufferFree = new ManualResetEvent(false);
|
WaitBufferFree = new ManualResetEvent(false);
|
||||||
|
|
||||||
|
RenderQueueLock = new object();
|
||||||
|
|
||||||
KeepRunning = true;
|
KeepRunning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,6 +169,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
|
||||||
|
|
||||||
private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
||||||
{
|
{
|
||||||
|
Context.Ns.Statistics.RecordGameFrameTime();
|
||||||
|
|
||||||
//TODO: Errors.
|
//TODO: Errors.
|
||||||
int Slot = ParcelReader.ReadInt32();
|
int Slot = ParcelReader.ReadInt32();
|
||||||
int Unknown4 = ParcelReader.ReadInt32();
|
int Unknown4 = ParcelReader.ReadInt32();
|
||||||
|
@ -190,6 +207,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
|
||||||
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
|
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long GbpDetachBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
||||||
|
{
|
||||||
|
return MakeReplyParcel(Context, 0);
|
||||||
|
}
|
||||||
|
|
||||||
private long GbpCancelBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
private long GbpCancelBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
||||||
{
|
{
|
||||||
//TODO: Errors.
|
//TODO: Errors.
|
||||||
|
@ -210,6 +232,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
|
||||||
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
|
return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long GbpDisconnect(ServiceCtx Context, BinaryReader ParcelReader)
|
||||||
|
{
|
||||||
|
return MakeReplyParcel(Context, 0);
|
||||||
|
}
|
||||||
|
|
||||||
private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader)
|
||||||
{
|
{
|
||||||
int Slot = ParcelReader.ReadInt32();
|
int Slot = ParcelReader.ReadInt32();
|
||||||
|
@ -256,11 +283,11 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
|
||||||
int FbWidth = BufferQueue[Slot].Data.Width;
|
int FbWidth = BufferQueue[Slot].Data.Width;
|
||||||
int FbHeight = BufferQueue[Slot].Data.Height;
|
int FbHeight = BufferQueue[Slot].Data.Height;
|
||||||
|
|
||||||
int FbSize = FbWidth * FbHeight * 4;
|
long FbSize = (uint)FbWidth * FbHeight * 4;
|
||||||
|
|
||||||
HNvMap NvMap = GetNvMap(Context, Slot);
|
NvMap NvMap = GetNvMap(Context, Slot);
|
||||||
|
|
||||||
if (FbSize < 0 || NvMap.Address < 0 || NvMap.Address + FbSize > AMemoryMgr.AddrSize)
|
if ((ulong)(NvMap.Address + FbSize) > AMemoryMgr.AddrSize)
|
||||||
{
|
{
|
||||||
Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!");
|
Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!");
|
||||||
|
|
||||||
|
@ -278,45 +305,61 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
|
||||||
int RealWidth = FbWidth;
|
int RealWidth = FbWidth;
|
||||||
int RealHeight = FbHeight;
|
int RealHeight = FbHeight;
|
||||||
|
|
||||||
|
float XSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX) ? -1 : 1;
|
||||||
|
float YSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY) ? -1 : 1;
|
||||||
|
|
||||||
float ScaleX = 1;
|
float ScaleX = 1;
|
||||||
float ScaleY = 1;
|
float ScaleY = 1;
|
||||||
|
|
||||||
float OffsX = 0;
|
float OffsX = 0;
|
||||||
float OffsY = 0;
|
float OffsY = 0;
|
||||||
|
|
||||||
if (Crop.Right != 0 &&
|
if (Crop.Right != 0 &&
|
||||||
Crop.Bottom != 0)
|
Crop.Bottom != 0)
|
||||||
{
|
{
|
||||||
|
//Who knows if this is right, I was never good with math...
|
||||||
RealWidth = Crop.Right - Crop.Left;
|
RealWidth = Crop.Right - Crop.Left;
|
||||||
RealHeight = Crop.Bottom - Crop.Top;
|
RealHeight = Crop.Bottom - Crop.Top;
|
||||||
|
|
||||||
ScaleX = (float)FbWidth / RealWidth;
|
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90))
|
||||||
ScaleY = (float)FbHeight / RealHeight;
|
{
|
||||||
|
ScaleY = (float)FbHeight / RealHeight;
|
||||||
|
ScaleX = (float)FbWidth / RealWidth;
|
||||||
|
|
||||||
OffsX = -(float)Crop.Left / Crop.Right;
|
OffsY = ((-(float)Crop.Left / Crop.Right) + ScaleX - 1) * -XSign;
|
||||||
OffsY = -(float)Crop.Top / Crop.Bottom;
|
OffsX = ((-(float)Crop.Top / Crop.Bottom) + ScaleY - 1) * -YSign;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ScaleX = (float)FbWidth / RealWidth;
|
||||||
|
ScaleY = (float)FbHeight / RealHeight;
|
||||||
|
|
||||||
OffsX += ScaleX - 1;
|
OffsX = ((-(float)Crop.Left / Crop.Right) + ScaleX - 1) * XSign;
|
||||||
OffsY += ScaleY - 1;
|
OffsY = ((-(float)Crop.Top / Crop.Bottom) + ScaleY - 1) * -YSign;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScaleX *= XSign;
|
||||||
|
ScaleY *= YSign;
|
||||||
|
|
||||||
float Rotate = 0;
|
float Rotate = 0;
|
||||||
|
|
||||||
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX))
|
|
||||||
{
|
|
||||||
ScaleX = -ScaleX;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY))
|
|
||||||
{
|
|
||||||
ScaleY = -ScaleY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90))
|
if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90))
|
||||||
{
|
{
|
||||||
Rotate = -MathF.PI * 0.5f;
|
Rotate = -MathF.PI * 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte* Fb = (byte*)Context.Ns.Ram + NvMap.Address;
|
lock (RenderQueueLock)
|
||||||
|
{
|
||||||
|
if (NvFlingerDisposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Interlocked.Increment(ref RenderQueueCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte* Fb = (byte*)Context.Memory.Ram + NvMap.Address;
|
||||||
|
|
||||||
Context.Ns.Gpu.Renderer.QueueAction(delegate()
|
Context.Ns.Gpu.Renderer.QueueAction(delegate()
|
||||||
{
|
{
|
||||||
|
@ -332,6 +375,8 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
|
||||||
|
|
||||||
BufferQueue[Slot].State = BufferState.Free;
|
BufferQueue[Slot].State = BufferState.Free;
|
||||||
|
|
||||||
|
Interlocked.Decrement(ref RenderQueueCount);
|
||||||
|
|
||||||
lock (WaitBufferFree)
|
lock (WaitBufferFree)
|
||||||
{
|
{
|
||||||
WaitBufferFree.Set();
|
WaitBufferFree.Set();
|
||||||
|
@ -339,7 +384,7 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private HNvMap GetNvMap(ServiceCtx Context, int Slot)
|
private NvMap GetNvMap(ServiceCtx Context, int Slot)
|
||||||
{
|
{
|
||||||
int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c);
|
int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c);
|
||||||
|
|
||||||
|
@ -352,7 +397,9 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
|
||||||
NvMapHandle = BitConverter.ToInt32(RawValue, 0);
|
NvMapHandle = BitConverter.ToInt32(RawValue, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Context.Ns.Os.Handles.GetData<HNvMap>(NvMapHandle);
|
ServiceNvDrv NvDrv = (ServiceNvDrv)Context.Process.Services.GetService("nvdrv");
|
||||||
|
|
||||||
|
return NvDrv.GetNvMap(NvMapHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetFreeSlotBlocking(int Width, int Height)
|
private int GetFreeSlotBlocking(int Width, int Height)
|
||||||
|
@ -418,10 +465,24 @@ namespace Ryujinx.Core.OsHle.IpcServices.Android
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool Disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (Disposing && !NvFlingerDisposed)
|
||||||
{
|
{
|
||||||
|
lock (RenderQueueLock)
|
||||||
|
{
|
||||||
|
NvFlingerDisposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Ensure that all pending actions was sent before
|
||||||
|
//we can safely assume that the class was disposed.
|
||||||
|
while (RenderQueueCount > 0)
|
||||||
|
{
|
||||||
|
Thread.Yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
Renderer.ResetFrameBuffer();
|
||||||
|
|
||||||
lock (WaitBufferFree)
|
lock (WaitBufferFree)
|
||||||
{
|
{
|
||||||
KeepRunning = false;
|
KeepRunning = false;
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
using ChocolArm64.Events;
|
using ChocolArm64.Events;
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
|
using Ryujinx.Core.OsHle.Handles;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Svc
|
namespace Ryujinx.Core.OsHle.Svc
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler : IDisposable
|
||||||
{
|
{
|
||||||
private delegate void SvcFunc(AThreadState ThreadState);
|
private delegate void SvcFunc(AThreadState ThreadState);
|
||||||
|
|
||||||
|
@ -16,10 +17,12 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
private Process Process;
|
private Process Process;
|
||||||
private AMemory Memory;
|
private AMemory Memory;
|
||||||
|
|
||||||
private static Random Rng;
|
private HashSet<(HSharedMem, long)> MappedSharedMems;
|
||||||
|
|
||||||
private ulong CurrentHeapSize;
|
private ulong CurrentHeapSize;
|
||||||
|
|
||||||
|
private static Random Rng;
|
||||||
|
|
||||||
public SvcHandler(Switch Ns, Process Process)
|
public SvcHandler(Switch Ns, Process Process)
|
||||||
{
|
{
|
||||||
SvcFuncs = new Dictionary<int, SvcFunc>()
|
SvcFuncs = new Dictionary<int, SvcFunc>()
|
||||||
|
@ -32,10 +35,12 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
{ 0x07, SvcExitProcess },
|
{ 0x07, SvcExitProcess },
|
||||||
{ 0x08, SvcCreateThread },
|
{ 0x08, SvcCreateThread },
|
||||||
{ 0x09, SvcStartThread },
|
{ 0x09, SvcStartThread },
|
||||||
|
{ 0x0a, SvcExitThread },
|
||||||
{ 0x0b, SvcSleepThread },
|
{ 0x0b, SvcSleepThread },
|
||||||
{ 0x0c, SvcGetThreadPriority },
|
{ 0x0c, SvcGetThreadPriority },
|
||||||
{ 0x0d, SvcSetThreadPriority },
|
{ 0x0d, SvcSetThreadPriority },
|
||||||
{ 0x0f, SvcSetThreadCoreMask },
|
{ 0x0f, SvcSetThreadCoreMask },
|
||||||
|
{ 0x12, SvcClearEvent },
|
||||||
{ 0x13, SvcMapSharedMemory },
|
{ 0x13, SvcMapSharedMemory },
|
||||||
{ 0x14, SvcUnmapSharedMemory },
|
{ 0x14, SvcUnmapSharedMemory },
|
||||||
{ 0x15, SvcCreateTransferMemory },
|
{ 0x15, SvcCreateTransferMemory },
|
||||||
|
@ -59,6 +64,8 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
this.Ns = Ns;
|
this.Ns = Ns;
|
||||||
this.Process = Process;
|
this.Process = Process;
|
||||||
this.Memory = Process.Memory;
|
this.Memory = Process.Memory;
|
||||||
|
|
||||||
|
MappedSharedMems = new HashSet<(HSharedMem, long)>();
|
||||||
}
|
}
|
||||||
|
|
||||||
static SvcHandler()
|
static SvcHandler()
|
||||||
|
@ -83,5 +90,26 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
throw new NotImplementedException(e.Id.ToString("x4"));
|
throw new NotImplementedException(e.Id.ToString("x4"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool Disposing)
|
||||||
|
{
|
||||||
|
if (Disposing)
|
||||||
|
{
|
||||||
|
lock (MappedSharedMems)
|
||||||
|
{
|
||||||
|
foreach ((HSharedMem SharedMem, long Position) in MappedSharedMems)
|
||||||
|
{
|
||||||
|
SharedMem.RemoveVirtualPosition(Memory, Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
MappedSharedMems.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,8 @@ using ChocolArm64.Memory;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
using Ryujinx.Core.OsHle.Handles;
|
||||||
|
|
||||||
|
using static Ryujinx.Core.OsHle.ErrorCode;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Svc
|
namespace Ryujinx.Core.OsHle.Svc
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler
|
||||||
|
@ -23,7 +25,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
CurrentHeapSize = Size;
|
CurrentHeapSize = Size;
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
ThreadState.X1 = (ulong)Position;
|
ThreadState.X1 = (ulong)Position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +46,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
Memory.Manager.SetAttrBit(Position, Size, 3);
|
Memory.Manager.SetAttrBit(Position, Size, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcMapMemory(AThreadState ThreadState)
|
private void SvcMapMemory(AThreadState ThreadState)
|
||||||
|
@ -53,6 +55,24 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
long Src = (long)ThreadState.X1;
|
long Src = (long)ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
|
|
||||||
|
if (!IsValidPosition(Src))
|
||||||
|
{
|
||||||
|
Logging.Warn($"Tried to map Memory at invalid src address {Src:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsValidMapPosition(Dst))
|
||||||
|
{
|
||||||
|
Logging.Warn($"Tried to map Memory at invalid dst address {Dst:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src);
|
AMemoryMapInfo SrcInfo = Memory.Manager.GetMapInfo(Src);
|
||||||
|
|
||||||
Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm);
|
Memory.Manager.Map(Dst, Size, (int)MemoryType.MappedMemory, SrcInfo.Perm);
|
||||||
|
@ -61,7 +81,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
Memory.Manager.SetAttrBit(Src, Size, 0);
|
Memory.Manager.SetAttrBit(Src, Size, 0);
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcUnmapMemory(AThreadState ThreadState)
|
private void SvcUnmapMemory(AThreadState ThreadState)
|
||||||
|
@ -70,6 +90,24 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
long Src = (long)ThreadState.X1;
|
long Src = (long)ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
|
|
||||||
|
if (!IsValidPosition(Src))
|
||||||
|
{
|
||||||
|
Logging.Warn($"Tried to unmap Memory at invalid src address {Src:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsValidMapPosition(Dst))
|
||||||
|
{
|
||||||
|
Logging.Warn($"Tried to unmap Memory at invalid dst address {Dst:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst);
|
AMemoryMapInfo DstInfo = Memory.Manager.GetMapInfo(Dst);
|
||||||
|
|
||||||
Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory);
|
Memory.Manager.Unmap(Dst, Size, (int)MemoryType.MappedMemory);
|
||||||
|
@ -78,7 +116,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
Memory.Manager.ClearAttrBit(Src, Size, 0);
|
Memory.Manager.ClearAttrBit(Src, Size, 0);
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcQueryMemory(AThreadState ThreadState)
|
private void SvcQueryMemory(AThreadState ThreadState)
|
||||||
|
@ -90,10 +128,11 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
if (MapInfo == null)
|
if (MapInfo == null)
|
||||||
{
|
{
|
||||||
//TODO: Correct error code.
|
long AddrSpaceEnd = MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
|
||||||
ThreadState.X0 = ulong.MaxValue;
|
|
||||||
|
|
||||||
return;
|
long ReservedSize = (long)(ulong.MaxValue - (ulong)AddrSpaceEnd) + 1;
|
||||||
|
|
||||||
|
MapInfo = new AMemoryMapInfo(AddrSpaceEnd, ReservedSize, (int)MemoryType.Reserved, 0, AMemoryPerm.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position);
|
Memory.WriteInt64(InfoPtr + 0x00, MapInfo.Position);
|
||||||
|
@ -106,7 +145,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
Memory.WriteInt32(InfoPtr + 0x24, 0);
|
Memory.WriteInt32(InfoPtr + 0x24, 0);
|
||||||
//TODO: X1.
|
//TODO: X1.
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
ThreadState.X1 = 0;
|
ThreadState.X1 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,17 +156,33 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
long Size = (long)ThreadState.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
int Perm = (int)ThreadState.X3;
|
int Perm = (int)ThreadState.X3;
|
||||||
|
|
||||||
HSharedMem SharedMem = Ns.Os.Handles.GetData<HSharedMem>(Handle);
|
if (!IsValidPosition(Src))
|
||||||
|
{
|
||||||
|
Logging.Warn($"Tried to map SharedMemory at invalid address {Src:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
|
||||||
|
|
||||||
if (SharedMem != null)
|
if (SharedMem != null)
|
||||||
{
|
{
|
||||||
|
Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, AMemoryPerm.Write);
|
||||||
|
|
||||||
AMemoryHelper.FillWithZeros(Memory, Src, (int)Size);
|
AMemoryHelper.FillWithZeros(Memory, Src, (int)Size);
|
||||||
|
|
||||||
SharedMem.AddVirtualPosition(Src);
|
Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
|
||||||
|
|
||||||
Memory.Manager.Map(Src, Size, (int)MemoryType.SharedMemory, (AMemoryPerm)Perm);
|
lock (MappedSharedMems)
|
||||||
|
{
|
||||||
|
MappedSharedMems.Add((SharedMem, Src));
|
||||||
|
}
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
SharedMem.AddVirtualPosition(Memory, Src);
|
||||||
|
|
||||||
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
//TODO: Error codes.
|
||||||
|
@ -135,15 +190,33 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
private void SvcUnmapSharedMemory(AThreadState ThreadState)
|
private void SvcUnmapSharedMemory(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
long Position = (long)ThreadState.X1;
|
long Src = (long)ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
|
|
||||||
HSharedMem HndData = Ns.Os.Handles.GetData<HSharedMem>(Handle);
|
if (!IsValidPosition(Src))
|
||||||
|
|
||||||
if (HndData != null)
|
|
||||||
{
|
{
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
Logging.Warn($"Tried to unmap SharedMemory at invalid address {Src:x16}!");
|
||||||
|
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HSharedMem SharedMem = Process.HandleTable.GetData<HSharedMem>(Handle);
|
||||||
|
|
||||||
|
if (SharedMem != null)
|
||||||
|
{
|
||||||
|
Memory.Manager.Unmap(Src, Size, (int)MemoryType.SharedMemory);
|
||||||
|
|
||||||
|
SharedMem.RemoveVirtualPosition(Memory, Src);
|
||||||
|
|
||||||
|
lock (MappedSharedMems)
|
||||||
|
{
|
||||||
|
MappedSharedMems.Remove((SharedMem, Src));
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
//TODO: Error codes.
|
||||||
|
@ -151,20 +224,41 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
private void SvcCreateTransferMemory(AThreadState ThreadState)
|
private void SvcCreateTransferMemory(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
long Position = (long)ThreadState.X1;
|
long Src = (long)ThreadState.X1;
|
||||||
long Size = (long)ThreadState.X2;
|
long Size = (long)ThreadState.X2;
|
||||||
int Perm = (int)ThreadState.X3;
|
int Perm = (int)ThreadState.X3;
|
||||||
|
|
||||||
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Position);
|
if (!IsValidPosition(Src))
|
||||||
|
{
|
||||||
|
Logging.Warn($"Tried to create TransferMemory at invalid address {Src:x16}!");
|
||||||
|
|
||||||
Memory.Manager.Reprotect(Position, Size, (AMemoryPerm)Perm);
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
|
||||||
|
|
||||||
HTransferMem HndData = new HTransferMem(Memory, MapInfo.Perm, Position, Size);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int Handle = Ns.Os.Handles.GenerateId(HndData);
|
AMemoryMapInfo MapInfo = Memory.Manager.GetMapInfo(Src);
|
||||||
|
|
||||||
ThreadState.X1 = (ulong)Handle;
|
Memory.Manager.Reprotect(Src, Size, (AMemoryPerm)Perm);
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
|
||||||
|
HTransferMem TMem = new HTransferMem(Memory, MapInfo.Perm, Src, Size);
|
||||||
|
|
||||||
|
ulong Handle = (ulong)Process.HandleTable.OpenHandle(TMem);
|
||||||
|
|
||||||
|
ThreadState.X0 = 0;
|
||||||
|
ThreadState.X1 = Handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsValidPosition(long Position)
|
||||||
|
{
|
||||||
|
return Position >= MemoryRegions.AddrSpaceStart &&
|
||||||
|
Position < MemoryRegions.AddrSpaceStart + MemoryRegions.AddrSpaceSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsValidMapPosition(long Position)
|
||||||
|
{
|
||||||
|
return Position >= MemoryRegions.MapRegionAddress &&
|
||||||
|
Position < MemoryRegions.MapRegionAddress + MemoryRegions.MapRegionSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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.Exceptions;
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
using Ryujinx.Core.OsHle.Handles;
|
||||||
using Ryujinx.Core.OsHle.Ipc;
|
using Ryujinx.Core.OsHle.Ipc;
|
||||||
using Ryujinx.Core.OsHle.IpcServices;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
using static Ryujinx.Core.OsHle.ErrorCode;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Svc
|
namespace Ryujinx.Core.OsHle.Svc
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler
|
||||||
|
@ -20,13 +21,22 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
Ns.Os.ExitProcess(ThreadState.ProcessId);
|
Ns.Os.ExitProcess(ThreadState.ProcessId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SvcClearEvent(AThreadState ThreadState)
|
||||||
|
{
|
||||||
|
int Handle = (int)ThreadState.X0;
|
||||||
|
|
||||||
|
//TODO: Implement events.
|
||||||
|
|
||||||
|
ThreadState.X0 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
private void SvcCloseHandle(AThreadState ThreadState)
|
private void SvcCloseHandle(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
|
|
||||||
Ns.Os.CloseHandle(Handle);
|
Process.HandleTable.CloseHandle(Handle);
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcResetSignal(AThreadState ThreadState)
|
private void SvcResetSignal(AThreadState ThreadState)
|
||||||
|
@ -35,7 +45,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
//TODO: Implement events.
|
//TODO: Implement events.
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcWaitSynchronization(AThreadState ThreadState)
|
private void SvcWaitSynchronization(AThreadState ThreadState)
|
||||||
|
@ -51,12 +61,12 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
Process.Scheduler.Suspend(CurrThread.ProcessorId);
|
Process.Scheduler.Suspend(CurrThread.ProcessorId);
|
||||||
Process.Scheduler.Resume(CurrThread);
|
Process.Scheduler.Resume(CurrThread);
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcGetSystemTick(AThreadState ThreadState)
|
private void SvcGetSystemTick(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
ThreadState.X0 = (ulong)ThreadState.CntpctEl0;
|
ThreadState.X0 = ThreadState.CntpctEl0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcConnectToNamedPort(AThreadState ThreadState)
|
private void SvcConnectToNamedPort(AThreadState ThreadState)
|
||||||
|
@ -69,10 +79,12 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
//TODO: Validate that app has perms to access the service, and that the service
|
//TODO: Validate that app has perms to access the service, and that the service
|
||||||
//actually exists, return error codes otherwise.
|
//actually exists, return error codes otherwise.
|
||||||
|
|
||||||
HSession Session = new HSession(ServiceFactory.MakeService(Name));
|
HSession Session = new HSession(Process.Services.GetService(Name));
|
||||||
|
|
||||||
ThreadState.X1 = (ulong)Ns.Os.Handles.GenerateId(Session);
|
ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session);
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
|
||||||
|
ThreadState.X0 = 0;
|
||||||
|
ThreadState.X1 = Handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcSendSyncRequest(AThreadState ThreadState)
|
private void SvcSendSyncRequest(AThreadState ThreadState)
|
||||||
|
@ -108,21 +120,29 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
|
byte[] CmdData = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
|
||||||
|
|
||||||
HSession Session = Ns.Os.Handles.GetData<HSession>(Handle);
|
HSession Session = Process.HandleTable.GetData<HSession>(Handle);
|
||||||
|
|
||||||
IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr, Session is HDomain);
|
IpcMessage Cmd = new IpcMessage(CmdData, CmdPtr, Session is HDomain);
|
||||||
|
|
||||||
if (Session != null)
|
if (Session != null)
|
||||||
{
|
{
|
||||||
IpcHandler.IpcCall(Ns, Memory, Session, Cmd, ThreadState.ThreadId, CmdPtr, Handle);
|
IpcHandler.IpcCall(
|
||||||
|
Ns,
|
||||||
|
Process,
|
||||||
|
Memory,
|
||||||
|
Session,
|
||||||
|
Cmd,
|
||||||
|
ThreadState.ThreadId,
|
||||||
|
CmdPtr,
|
||||||
|
Handle);
|
||||||
|
|
||||||
byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
|
byte[] Response = AMemoryHelper.ReadBytes(Memory, CmdPtr, (int)Size);
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ThreadState.X0 = (int)SvcResult.ErrBadIpcReq;
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidIpcReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.Yield();
|
Thread.Yield();
|
||||||
|
@ -148,7 +168,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
Logging.Info($"SvcOutputDebugString: {Str}");
|
Logging.Info($"SvcOutputDebugString: {Str}");
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcGetInfo(AThreadState ThreadState)
|
private void SvcGetInfo(AThreadState ThreadState)
|
||||||
|
@ -162,7 +182,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
if (InfoType == 18 ||
|
if (InfoType == 18 ||
|
||||||
InfoType == 19)
|
InfoType == 19)
|
||||||
{
|
{
|
||||||
ThreadState.X0 = (int)SvcResult.ErrBadInfo;
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -186,7 +206,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5:
|
case 5:
|
||||||
ThreadState.X1 = CurrentHeapSize;
|
ThreadState.X1 = MemoryRegions.HeapRegionSize;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
|
@ -224,7 +244,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}");
|
default: throw new NotImplementedException($"SvcGetInfo: {InfoType} {Handle} {InfoId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
Priority,
|
Priority,
|
||||||
ProcessorId);
|
ProcessorId);
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
ThreadState.X1 = (ulong)Handle;
|
ThreadState.X1 = (ulong)Handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,18 +39,25 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
|
|
||||||
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle);
|
HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
|
||||||
|
|
||||||
if (Thread != null)
|
if (Thread != null)
|
||||||
{
|
{
|
||||||
Process.Scheduler.StartThread(Thread);
|
Process.Scheduler.StartThread(Thread);
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
//TODO: Error codes.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SvcExitThread(AThreadState ThreadState)
|
||||||
|
{
|
||||||
|
HThread CurrThread = Process.GetThread(ThreadState.Tpidr);
|
||||||
|
|
||||||
|
CurrThread.Thread.StopExecution();
|
||||||
|
}
|
||||||
|
|
||||||
private void SvcSleepThread(AThreadState ThreadState)
|
private void SvcSleepThread(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
ulong NanoSecs = ThreadState.X0;
|
ulong NanoSecs = ThreadState.X0;
|
||||||
|
@ -71,12 +78,12 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X1;
|
int Handle = (int)ThreadState.X1;
|
||||||
|
|
||||||
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle);
|
HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
|
||||||
|
|
||||||
if (Thread != null)
|
if (Thread != null)
|
||||||
{
|
{
|
||||||
|
ThreadState.X0 = 0;
|
||||||
ThreadState.X1 = (ulong)Thread.Priority;
|
ThreadState.X1 = (ulong)Thread.Priority;
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
//TODO: Error codes.
|
||||||
|
@ -87,13 +94,13 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
int Handle = (int)ThreadState.X1;
|
int Handle = (int)ThreadState.X1;
|
||||||
int Prio = (int)ThreadState.X0;
|
int Prio = (int)ThreadState.X0;
|
||||||
|
|
||||||
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle);
|
HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
|
||||||
|
|
||||||
if (Thread != null)
|
if (Thread != null)
|
||||||
{
|
{
|
||||||
Thread.Priority = Prio;
|
Thread.Priority = Prio;
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
//TODO: Error codes.
|
||||||
|
@ -101,7 +108,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
private void SvcSetThreadCoreMask(AThreadState ThreadState)
|
private void SvcSetThreadCoreMask(AThreadState ThreadState)
|
||||||
{
|
{
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
|
|
||||||
//TODO: Error codes.
|
//TODO: Error codes.
|
||||||
}
|
}
|
||||||
|
@ -110,12 +117,12 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
{
|
{
|
||||||
int Handle = (int)ThreadState.X0;
|
int Handle = (int)ThreadState.X0;
|
||||||
|
|
||||||
HThread Thread = Ns.Os.Handles.GetData<HThread>(Handle);
|
HThread Thread = Process.HandleTable.GetData<HThread>(Handle);
|
||||||
|
|
||||||
if (Thread != null)
|
if (Thread != null)
|
||||||
{
|
{
|
||||||
|
ThreadState.X0 = 0;
|
||||||
ThreadState.X1 = (ulong)Thread.ThreadId;
|
ThreadState.X1 = (ulong)Thread.ThreadId;
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Error codes.
|
//TODO: Error codes.
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using Ryujinx.Core.OsHle.Handles;
|
using Ryujinx.Core.OsHle.Handles;
|
||||||
|
|
||||||
|
using static Ryujinx.Core.OsHle.ErrorCode;
|
||||||
|
|
||||||
namespace Ryujinx.Core.OsHle.Svc
|
namespace Ryujinx.Core.OsHle.Svc
|
||||||
{
|
{
|
||||||
partial class SvcHandler
|
partial class SvcHandler
|
||||||
|
@ -11,7 +13,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
long MutexAddress = (long)ThreadState.X1;
|
long MutexAddress = (long)ThreadState.X1;
|
||||||
int RequestingThreadHandle = (int)ThreadState.X2;
|
int RequestingThreadHandle = (int)ThreadState.X2;
|
||||||
|
|
||||||
HThread RequestingThread = Ns.Os.Handles.GetData<HThread>(RequestingThreadHandle);
|
HThread RequestingThread = Process.HandleTable.GetData<HThread>(RequestingThreadHandle);
|
||||||
|
|
||||||
Mutex M = new Mutex(Process, MutexAddress, OwnerThreadHandle);
|
Mutex M = new Mutex(Process, MutexAddress, OwnerThreadHandle);
|
||||||
|
|
||||||
|
@ -19,7 +21,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
M.WaitForLock(RequestingThread, RequestingThreadHandle);
|
M.WaitForLock(RequestingThread, RequestingThreadHandle);
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcArbitrateUnlock(AThreadState ThreadState)
|
private void SvcArbitrateUnlock(AThreadState ThreadState)
|
||||||
|
@ -31,7 +33,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
M.Unlock();
|
M.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState)
|
private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState)
|
||||||
|
@ -41,7 +43,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
int ThreadHandle = (int)ThreadState.X2;
|
int ThreadHandle = (int)ThreadState.X2;
|
||||||
long Timeout = (long)ThreadState.X3;
|
long Timeout = (long)ThreadState.X3;
|
||||||
|
|
||||||
HThread Thread = Ns.Os.Handles.GetData<HThread>(ThreadHandle);
|
HThread Thread = Process.HandleTable.GetData<HThread>(ThreadHandle);
|
||||||
|
|
||||||
Mutex M = new Mutex(Process, MutexAddress, ThreadHandle);
|
Mutex M = new Mutex(Process, MutexAddress, ThreadHandle);
|
||||||
|
|
||||||
|
@ -53,11 +55,16 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
|
|
||||||
Cv = Ns.Os.CondVars.GetOrAdd(CondVarAddress, Cv);
|
Cv = Ns.Os.CondVars.GetOrAdd(CondVarAddress, Cv);
|
||||||
|
|
||||||
Cv.WaitForSignal(Thread);
|
if (!Cv.WaitForSignal(Thread))
|
||||||
|
{
|
||||||
|
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
M.WaitForLock(Thread, ThreadHandle);
|
M.WaitForLock(Thread, ThreadHandle);
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SvcSignalProcessWideKey(AThreadState ThreadState)
|
private void SvcSignalProcessWideKey(AThreadState ThreadState)
|
||||||
|
@ -72,7 +79,7 @@ namespace Ryujinx.Core.OsHle.Svc
|
||||||
Cv.SetSignal(CurrThread, Count);
|
Cv.SetSignal(CurrThread, Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadState.X0 = (int)SvcResult.Success;
|
ThreadState.X0 = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
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.Input;
|
||||||
using Ryujinx.Core.OsHle;
|
using Ryujinx.Core.OsHle;
|
||||||
using Ryujinx.Core.Settings;
|
using Ryujinx.Core.Settings;
|
||||||
using Ryujinx.Graphics.Gal;
|
using Ryujinx.Graphics.Gal;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Ryujinx.Core
|
namespace Ryujinx.Core
|
||||||
{
|
{
|
||||||
public class Switch : IDisposable
|
public class Switch : IDisposable
|
||||||
{
|
{
|
||||||
public IntPtr Ram {get; private set; }
|
|
||||||
|
|
||||||
internal NsGpu Gpu { get; private set; }
|
internal NsGpu Gpu { get; private set; }
|
||||||
internal Horizon Os { get; private set; }
|
internal Horizon Os { get; private set; }
|
||||||
internal VirtualFs VFs { get; private set; }
|
internal VirtualFs VFs { get; private set; }
|
||||||
|
|
||||||
public Hid Hid { get; private set; }
|
public Hid Hid { get; private set; }
|
||||||
public SetSys Settings { get; private set; }
|
public SetSys Settings { get; private set; }
|
||||||
|
public PerformanceStatistics Statistics { get; private set; }
|
||||||
|
|
||||||
public event EventHandler Finish;
|
public event EventHandler Finish;
|
||||||
|
|
||||||
public Switch(IGalRenderer Renderer)
|
public Switch(IGalRenderer Renderer)
|
||||||
{
|
{
|
||||||
Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize);
|
|
||||||
|
|
||||||
Gpu = new NsGpu(Renderer);
|
Gpu = new NsGpu(Renderer);
|
||||||
|
|
||||||
VFs = new VirtualFs();
|
VFs = new VirtualFs();
|
||||||
|
|
||||||
Hid = new Hid(this);
|
Hid = new Hid();
|
||||||
Os = new Horizon(this);
|
|
||||||
Settings = new SetSys();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FinalizeAllProcesses()
|
Statistics = new PerformanceStatistics();
|
||||||
{
|
|
||||||
Os.FinalizeAllProcesses();
|
Os = new Horizon(this);
|
||||||
|
|
||||||
|
Os.HidSharedMem.MemoryMapped += Hid.ShMemMap;
|
||||||
|
Os.HidSharedMem.MemoryUnmapped += Hid.ShMemUnmap;
|
||||||
|
|
||||||
|
Settings = new SetSys();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
public void LoadCart(string ExeFsDir, string RomFsFile = null)
|
||||||
|
@ -59,14 +57,13 @@ namespace Ryujinx.Core
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool Disposing)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (Disposing)
|
||||||
{
|
{
|
||||||
|
Os.Dispose();
|
||||||
VFs.Dispose();
|
VFs.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Marshal.FreeHGlobal(Ram);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,10 +18,18 @@ namespace Ryujinx.Core
|
||||||
|
|
||||||
public string GetFullPath(string BasePath, string FileName)
|
public string GetFullPath(string BasePath, string FileName)
|
||||||
{
|
{
|
||||||
if (FileName.StartsWith('/'))
|
if (FileName.StartsWith("//"))
|
||||||
|
{
|
||||||
|
FileName = FileName.Substring(2);
|
||||||
|
}
|
||||||
|
else if (FileName.StartsWith('/'))
|
||||||
{
|
{
|
||||||
FileName = FileName.Substring(1);
|
FileName = FileName.Substring(1);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
string FullPath = Path.GetFullPath(Path.Combine(BasePath, FileName));
|
string FullPath = Path.GetFullPath(Path.Combine(BasePath, FileName));
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gal
|
||||||
void RunActions();
|
void RunActions();
|
||||||
|
|
||||||
void InitializeFrameBuffer();
|
void InitializeFrameBuffer();
|
||||||
|
void ResetFrameBuffer();
|
||||||
void Render();
|
void Render();
|
||||||
void SetWindowSize(int Width, int Height);
|
void SetWindowSize(int Width, int Height);
|
||||||
void SetFrameBuffer(
|
void SetFrameBuffer(
|
||||||
|
|
|
@ -24,6 +24,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
|
|
||||||
private byte* FbPtr;
|
private byte* FbPtr;
|
||||||
|
|
||||||
|
private object FbPtrLock;
|
||||||
|
|
||||||
public FrameBuffer(int Width, int Height)
|
public FrameBuffer(int Width, int Height)
|
||||||
{
|
{
|
||||||
if (Width < 0)
|
if (Width < 0)
|
||||||
|
@ -36,6 +38,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
throw new ArgumentOutOfRangeException(nameof(Height));
|
throw new ArgumentOutOfRangeException(nameof(Height));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FbPtrLock = new object();
|
||||||
|
|
||||||
TexWidth = Width;
|
TexWidth = Width;
|
||||||
TexHeight = Height;
|
TexHeight = Height;
|
||||||
|
|
||||||
|
@ -152,7 +156,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
throw new ArgumentOutOfRangeException(nameof(Height));
|
throw new ArgumentOutOfRangeException(nameof(Height));
|
||||||
}
|
}
|
||||||
|
|
||||||
FbPtr = Fb;
|
lock (FbPtrLock)
|
||||||
|
{
|
||||||
|
FbPtr = Fb;
|
||||||
|
}
|
||||||
|
|
||||||
if (Width != TexWidth ||
|
if (Width != TexWidth ||
|
||||||
Height != TexHeight)
|
Height != TexHeight)
|
||||||
|
@ -178,17 +185,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
GL.Uniform2(OffsetUniformLocation, Offs);
|
GL.Uniform2(OffsetUniformLocation, Offs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
lock (FbPtrLock)
|
||||||
|
{
|
||||||
|
FbPtr = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Render()
|
public void Render()
|
||||||
{
|
{
|
||||||
if (FbPtr == null)
|
lock (FbPtrLock)
|
||||||
{
|
{
|
||||||
return;
|
if (FbPtr == null)
|
||||||
}
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int Y = 0; Y < TexHeight; Y++)
|
for (int Y = 0; Y < TexHeight; Y++)
|
||||||
for (int X = 0; X < TexWidth; X++)
|
for (int X = 0; X < TexWidth; X++)
|
||||||
{
|
{
|
||||||
Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y)));
|
Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GL.BindTexture(TextureTarget.Texture2D, TexHandle);
|
GL.BindTexture(TextureTarget.Texture2D, TexHandle);
|
||||||
|
|
|
@ -43,6 +43,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
|
||||||
FbRenderer = new FrameBuffer(1280, 720);
|
FbRenderer = new FrameBuffer(1280, 720);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ResetFrameBuffer()
|
||||||
|
{
|
||||||
|
FbRenderer.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
public void QueueAction(Action ActionMthd)
|
public void QueueAction(Action ActionMthd)
|
||||||
{
|
{
|
||||||
ActionsQueue.Enqueue(ActionMthd);
|
ActionsQueue.Enqueue(ActionMthd);
|
||||||
|
|
|
@ -2,8 +2,6 @@ using ChocolArm64;
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
using ChocolArm64.State;
|
using ChocolArm64.State;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Tests.Cpu
|
namespace Ryujinx.Tests.Cpu
|
||||||
|
@ -16,7 +14,6 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
private long EntryPoint;
|
private long EntryPoint;
|
||||||
|
|
||||||
private IntPtr Ram;
|
|
||||||
private AMemory Memory;
|
private AMemory Memory;
|
||||||
private AThread Thread;
|
private AThread Thread;
|
||||||
|
|
||||||
|
@ -28,19 +25,18 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
EntryPoint = Position;
|
EntryPoint = Position;
|
||||||
|
|
||||||
Ram = Marshal.AllocHGlobal((IntPtr)AMemoryMgr.RamSize);
|
|
||||||
ATranslator Translator = new ATranslator();
|
ATranslator Translator = new ATranslator();
|
||||||
Memory = new AMemory(Ram);
|
Memory = new AMemory();
|
||||||
Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute);
|
Memory.Manager.Map(Position, Size, 2, AMemoryPerm.Read | AMemoryPerm.Write | AMemoryPerm.Execute);
|
||||||
Thread = new AThread(Translator, Memory, ThreadPriority.Normal, EntryPoint);
|
Thread = new AThread(Translator, Memory, EntryPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TearDown]
|
[TearDown]
|
||||||
public void Teardown()
|
public void Teardown()
|
||||||
{
|
{
|
||||||
|
Memory.Dispose();
|
||||||
Thread = null;
|
Thread = null;
|
||||||
Memory = null;
|
Memory = null;
|
||||||
Marshal.FreeHGlobal(Ram);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Reset()
|
protected void Reset()
|
||||||
|
@ -51,7 +47,7 @@ namespace Ryujinx.Tests.Cpu
|
||||||
|
|
||||||
protected void Opcode(uint Opcode)
|
protected void Opcode(uint Opcode)
|
||||||
{
|
{
|
||||||
Thread.Memory.WriteUInt32(Position, Opcode);
|
Thread.Memory.WriteUInt32Unchecked(Position, Opcode);
|
||||||
Position += 4;
|
Position += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,10 @@ namespace Ryujinx
|
||||||
{
|
{
|
||||||
this.Ns = Ns;
|
this.Ns = Ns;
|
||||||
this.Renderer = Renderer;
|
this.Renderer = Renderer;
|
||||||
|
|
||||||
|
Location = new Point(
|
||||||
|
(DisplayDevice.Default.Width / 2) - (Width / 2),
|
||||||
|
(DisplayDevice.Default.Height / 2) - (Height / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnLoad(EventArgs e)
|
protected override void OnLoad(EventArgs e)
|
||||||
|
@ -162,9 +166,12 @@ namespace Ryujinx
|
||||||
|
|
||||||
protected override void OnRenderFrame(FrameEventArgs e)
|
protected override void OnRenderFrame(FrameEventArgs e)
|
||||||
{
|
{
|
||||||
|
Ns.Statistics.StartSystemFrame();
|
||||||
|
|
||||||
GL.Viewport(0, 0, Width, Height);
|
GL.Viewport(0, 0, Width, Height);
|
||||||
|
|
||||||
Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {1f / e.Time:0})";
|
Title = $"Ryujinx Screen - (Vsync: {VSync} - FPS: {Ns.Statistics.SystemFrameRate:0} - Guest FPS: " +
|
||||||
|
$"{Ns.Statistics.GameFrameRate:0})";
|
||||||
|
|
||||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||||
|
|
||||||
|
@ -172,6 +179,8 @@ namespace Ryujinx
|
||||||
Renderer.Render();
|
Renderer.Render();
|
||||||
|
|
||||||
SwapBuffers();
|
SwapBuffers();
|
||||||
|
|
||||||
|
Ns.Statistics.EndSystemFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnResize(EventArgs e)
|
protected override void OnResize(EventArgs e)
|
||||||
|
|
|
@ -10,6 +10,8 @@ namespace Ryujinx
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
AOptimizations.DisableMemoryChecks = true;
|
||||||
|
|
||||||
Config.Read();
|
Config.Read();
|
||||||
|
|
||||||
Console.Title = "Ryujinx Console";
|
Console.Title = "Ryujinx Console";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue