merge + add fmax/min vector and fix + add fmin/max test

This commit is contained in:
unknown 2018-04-18 20:00:59 +02:00
parent b78c62e80d
commit da03846628
166 changed files with 8515 additions and 1923 deletions

View file

@ -8,7 +8,7 @@ namespace ChocolArm64
{
static AOpCodeTable()
{
#region "OpCode Table"
#region "OpCode Table"
//Integer
Set("x0011010000xxxxx000000xxxxxxxxxx", AInstEmit.Adc, typeof(AOpCodeAluRs));
Set("x0111010000xxxxx000000xxxxxxxxxx", AInstEmit.Adcs, typeof(AOpCodeAluRs));
@ -41,6 +41,7 @@ namespace ChocolArm64
Set("x1111010010xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpImm));
Set("x1111010010xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ccmp, typeof(AOpCodeCcmpReg));
Set("11010101000000110011xxxx01011111", AInstEmit.Clrex, typeof(AOpCodeSystem));
Set("x101101011000000000101xxxxxxxxxx", AInstEmit.Cls, typeof(AOpCodeAlu));
Set("x101101011000000000100xxxxxxxxxx", AInstEmit.Clz, typeof(AOpCodeAlu));
Set("x0011010110xxxxx010000xxxxxxxxxx", AInstEmit.Crc32b, typeof(AOpCodeAluRs));
Set("x0011010110xxxxx010001xxxxxxxxxx", AInstEmit.Crc32h, typeof(AOpCodeAluRs));
@ -68,7 +69,7 @@ namespace ChocolArm64
Set("xx111000010xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm));
Set("xx11100101xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemImm));
Set("xx111000011xxxxxxxxx10xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeMemReg));
Set("xx011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeMemLit));
Set("xx011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.LdrLit, typeof(AOpCodeMemLit));
Set("0x1110001x0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm));
Set("0x1110011xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm));
Set("10111000100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldrs, typeof(AOpCodeMemImm));
@ -91,6 +92,7 @@ namespace ChocolArm64
Set("x01100100xxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluImm));
Set("x0101010xx0xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Orr, typeof(AOpCodeAluRs));
Set("1111100110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm));
Set("11111000100xxxxxxxxx00xxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemImm));
Set("11011000xxxxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Pfrm, typeof(AOpCodeMemLit));
Set("x101101011000000000000xxxxxxxxxx", AInstEmit.Rbit, typeof(AOpCodeAlu));
Set("11010110010xxxxx000000xxxxxxxxxx", AInstEmit.Ret, typeof(AOpCodeBReg));
@ -140,6 +142,7 @@ namespace ChocolArm64
Set("0x001110011xxxxx000111xxxxxxxxxx", AInstEmit.Bic_V, typeof(AOpCodeSimdReg));
Set("0x10111100000xxx<<x101xxxxxxxxxx", AInstEmit.Bic_Vi, typeof(AOpCodeSimdImm));
Set("0x101110111xxxxx000111xxxxxxxxxx", AInstEmit.Bif_V, typeof(AOpCodeSimdReg));
Set("0x101110101xxxxx000111xxxxxxxxxx", AInstEmit.Bit_V, typeof(AOpCodeSimdReg));
Set("0x101110011xxxxx000111xxxxxxxxxx", AInstEmit.Bsl_V, typeof(AOpCodeSimdReg));
Set("0>101110<<1xxxxx100011xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimdReg));
Set("0>001110<<100000100110xxxxxxxxxx", AInstEmit.Cmeq_V, typeof(AOpCodeSimd));
@ -162,8 +165,25 @@ namespace ChocolArm64
Set("000111100x100000110000xxxxxxxxxx", AInstEmit.Fabs_S, typeof(AOpCodeSimd));
Set("000111100x1xxxxx001010xxxxxxxxxx", AInstEmit.Fadd_S, typeof(AOpCodeSimdReg));
Set("0>0011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Fadd_V, typeof(AOpCodeSimdReg));
Set("0>1011100<1xxxxx110101xxxxxxxxxx", AInstEmit.Faddp_V, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxxxxxx01xxxxx0xxxx", AInstEmit.Fccmp_S, typeof(AOpCodeSimdFcond));
Set("000111100x1xxxxxxxxx01xxxxx1xxxx", AInstEmit.Fccmpe_S, typeof(AOpCodeSimdFcond));
Set("010111100x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmeq_S, typeof(AOpCodeSimdReg));
Set("0>0011100<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmeq_V, typeof(AOpCodeSimdReg));
Set("010111101x100000110110xxxxxxxxxx", AInstEmit.Fcmeq_S, typeof(AOpCodeSimd));
Set("0>0011101<100000110110xxxxxxxxxx", AInstEmit.Fcmeq_V, typeof(AOpCodeSimd));
Set("011111100x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmge_S, typeof(AOpCodeSimdReg));
Set("0>1011100<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmge_V, typeof(AOpCodeSimdReg));
Set("011111101x100000110010xxxxxxxxxx", AInstEmit.Fcmge_S, typeof(AOpCodeSimd));
Set("0>1011101<100000110010xxxxxxxxxx", AInstEmit.Fcmge_V, typeof(AOpCodeSimd));
Set("011111101x1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmgt_S, typeof(AOpCodeSimdReg));
Set("0>1011101<1xxxxx111001xxxxxxxxxx", AInstEmit.Fcmgt_V, typeof(AOpCodeSimdReg));
Set("010111101x100000110010xxxxxxxxxx", AInstEmit.Fcmgt_S, typeof(AOpCodeSimd));
Set("0>0011101<100000110010xxxxxxxxxx", AInstEmit.Fcmgt_V, typeof(AOpCodeSimd));
Set("011111101x100000110110xxxxxxxxxx", AInstEmit.Fcmle_S, typeof(AOpCodeSimd));
Set("0>1011101<100000110110xxxxxxxxxx", AInstEmit.Fcmle_V, typeof(AOpCodeSimd));
Set("010111101x100000111010xxxxxxxxxx", AInstEmit.Fcmlt_S, typeof(AOpCodeSimd));
Set("0>0011101<100000111010xxxxxxxxxx", AInstEmit.Fcmlt_V, typeof(AOpCodeSimd));
Set("000111100x1xxxxx001000xxxxx0x000", AInstEmit.Fcmp_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx001000xxxxx1x000", AInstEmit.Fcmpe_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxxxxxx11xxxxxxxxxx", AInstEmit.Fcsel_S, typeof(AOpCodeSimdFcond));
@ -183,16 +203,20 @@ namespace ChocolArm64
Set("x00111100x111001000000xxxxxxxxxx", AInstEmit.Fcvtzu_Gp, typeof(AOpCodeSimdCvt));
Set("x00111100x011001xxxxxxxxxxxxxxxx", AInstEmit.Fcvtzu_Gp_Fix, typeof(AOpCodeSimdCvt));
Set("0>1011101<100001101110xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimd));
Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm));
Set("0x1011110>>xxxxx111111xxxxxxxxxx", AInstEmit.Fcvtzu_V, typeof(AOpCodeSimdShImm));
Set("000111100x1xxxxx000110xxxxxxxxxx", AInstEmit.Fdiv_S, typeof(AOpCodeSimdReg));
Set("0>1011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Fdiv_V, typeof(AOpCodeSimdReg));
Set("000111110x0xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fmadd_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx010010xxxxxxxxxx", AInstEmit.Fmax_S, typeof(AOpCodeSimdReg));
Set("0x0011100x1xxxxx111101xxxxxxxxxx", AInstEmit.Fmax_V, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg));
Set("0x0011101x1xxxxx111101xxxxxxxxxx", AInstEmit.Fmin_V, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg));
Set("0>0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg));
Set("0x0011111<<xxxxx0001x0xxxxxxxxxx", AInstEmit.Fmla_Ve, typeof(AOpCodeSimdRegElemF));
Set("0>0011101<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmls_V, typeof(AOpCodeSimdReg));
Set("0x0011111<<xxxxx0101x0xxxxxxxxxx", AInstEmit.Fmls_Ve, typeof(AOpCodeSimdRegElemF));
Set("000111100x100000010000xxxxxxxxxx", AInstEmit.Fmov_S, typeof(AOpCodeSimd));
Set("00011110xx1xxxxxxxx100xxxxxxxxxx", AInstEmit.Fmov_Si, typeof(AOpCodeSimdFmov));
Set("0xx0111100000xxx111101xxxxxxxxxx", AInstEmit.Fmov_V, typeof(AOpCodeSimdImm));
@ -202,11 +226,18 @@ namespace ChocolArm64
Set("1001111010101111000000xxxxxxxxxx", AInstEmit.Fmov_Itof1, typeof(AOpCodeSimdCvt));
Set("000111110x0xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fmsub_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx000010xxxxxxxxxx", AInstEmit.Fmul_S, typeof(AOpCodeSimdReg));
Set("010111111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Se, typeof(AOpCodeSimdRegElemF));
Set("0>1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg));
Set("0x0011111<<xxxxx1001x0xxxxxxxxxx", AInstEmit.Fmul_Ve, typeof(AOpCodeSimdRegElemF));
Set("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimdReg));
Set("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimd));
Set("0>1011101<100000111110xxxxxxxxxx", AInstEmit.Fneg_V, typeof(AOpCodeSimd));
Set("000111110x1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fnmadd_S, typeof(AOpCodeSimdReg));
Set("000111110x1xxxxx1xxxxxxxxxxxxxxx", AInstEmit.Fnmsub_S, typeof(AOpCodeSimdReg));
Set("000111100x1xxxxx100010xxxxxxxxxx", AInstEmit.Fnmul_S, typeof(AOpCodeSimdReg));
Set("010111101x100001110110xxxxxxxxxx", AInstEmit.Frecpe_S, typeof(AOpCodeSimd));
Set("0>0011101<100001110110xxxxxxxxxx", AInstEmit.Frecpe_V, typeof(AOpCodeSimd));
Set("010111100x1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_S, typeof(AOpCodeSimdReg));
Set("0>0011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_V, typeof(AOpCodeSimdReg));
Set("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd));
Set("0>1011100<100001100010xxxxxxxxxx", AInstEmit.Frinta_V, typeof(AOpCodeSimd));
Set("000111100x100111110000xxxxxxxxxx", AInstEmit.Frinti_S, typeof(AOpCodeSimd));
@ -218,7 +249,11 @@ namespace ChocolArm64
Set("000111100x100100110000xxxxxxxxxx", AInstEmit.Frintp_S, typeof(AOpCodeSimd));
Set("0>0011101<100001100010xxxxxxxxxx", AInstEmit.Frintp_V, typeof(AOpCodeSimd));
Set("000111100x100111010000xxxxxxxxxx", AInstEmit.Frintx_S, typeof(AOpCodeSimd));
Set("0>1011100<100001100110xxxxxxxxxx", AInstEmit.Frintx_V, typeof(AOpCodeSimd));
Set("0>1011100<100001100110xxxxxxxxxx", AInstEmit.Frintx_V, typeof(AOpCodeSimd));
Set("011111101x100001110110xxxxxxxxxx", AInstEmit.Frsqrte_S, typeof(AOpCodeSimd));
Set("0>1011101<100001110110xxxxxxxxxx", AInstEmit.Frsqrte_V, typeof(AOpCodeSimd));
Set("010111101x1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_S, typeof(AOpCodeSimdReg));
Set("0>0011101<1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_V, typeof(AOpCodeSimdReg));
Set("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd));
Set("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg));
Set("0>0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, typeof(AOpCodeSimdReg));
@ -226,8 +261,8 @@ namespace ChocolArm64
Set("01101110000xxxxx0xxxx1xxxxxxxxxx", AInstEmit.Ins_V, typeof(AOpCodeSimdIns));
Set("0x00110001000000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs));
Set("0x001100110xxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vms, typeof(AOpCodeSimdMemMs));
Set("0x00110101000000xx0xxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs));
Set("0x001101110xxxxxxx0xxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs));
Set("0x00110101x00000xxxxxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs));
Set("0x00110111xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ld__Vss, typeof(AOpCodeSimdMemSs));
Set("xx10110xx1xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Ldp, typeof(AOpCodeSimdMemPair));
Set("xx111100x10xxxxxxxxx00xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm));
Set("xx111100x10xxxxxxxxx01xxxxxxxxxx", AInstEmit.Ldr, typeof(AOpCodeSimdMemImm));
@ -272,8 +307,8 @@ namespace ChocolArm64
Set("0x0011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Ssra_V, typeof(AOpCodeSimdShImm));
Set("0x00110000000000xxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
Set("0x001100100xxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vms, typeof(AOpCodeSimdMemMs));
Set("0x00110100000000xx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
Set("0x001101100xxxxxxx0xxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
Set("0x00110100x00000xxxxxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
Set("0x00110110xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.St__Vss, typeof(AOpCodeSimdMemSs));
Set("xx10110xx0xxxxxxxxxxxxxxxxxxxxxx", AInstEmit.Stp, typeof(AOpCodeSimdMemPair));
Set("xx111100x00xxxxxxxxx00xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
Set("xx111100x00xxxxxxxxx01xxxxxxxxxx", AInstEmit.Str, typeof(AOpCodeSimdMemImm));
@ -283,12 +318,18 @@ namespace ChocolArm64
Set("01111110xx1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_S, typeof(AOpCodeSimdReg));
Set("0>101110<<1xxxxx100001xxxxxxxxxx", AInstEmit.Sub_V, typeof(AOpCodeSimdReg));
Set("0x001110000xxxxx0xx000xxxxxxxxxx", AInstEmit.Tbl_V, typeof(AOpCodeSimdTbl));
Set("0>001110<<0xxxxx001010xxxxxxxxxx", AInstEmit.Trn1_V, typeof(AOpCodeSimdReg));
Set("0>001110<<0xxxxx011010xxxxxxxxxx", AInstEmit.Trn2_V, typeof(AOpCodeSimdReg));
Set("0x101110<<1xxxxx011101xxxxxxxxxx", AInstEmit.Uabd_V, typeof(AOpCodeSimdReg));
Set("0x101110<<1xxxxx011100xxxxxxxxxx", AInstEmit.Uabdl_V, typeof(AOpCodeSimdReg));
Set("0x101110<<1xxxxx000000xxxxxxxxxx", AInstEmit.Uaddl_V, typeof(AOpCodeSimdReg));
Set("001011100x110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd));
Set("01101110<<110000001110xxxxxxxxxx", AInstEmit.Uaddlv_V, typeof(AOpCodeSimd));
Set("0x101110<<1xxxxx000100xxxxxxxxxx", AInstEmit.Uaddw_V, typeof(AOpCodeSimdReg));
Set("x0011110xx100011000000xxxxxxxxxx", AInstEmit.Ucvtf_Gp, typeof(AOpCodeSimdCvt));
Set("011111100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_S, typeof(AOpCodeSimd));
Set("0x1011100x100001110110xxxxxxxxxx", AInstEmit.Ucvtf_V, typeof(AOpCodeSimd));
Set("0x101110<<1xxxxx000001xxxxxxxxxx", AInstEmit.Uhadd_V, typeof(AOpCodeSimdReg));
Set("0x001110000xxxxx001111xxxxxxxxxx", AInstEmit.Umov_S, typeof(AOpCodeSimdIns));
Set("0x101110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Umull_V, typeof(AOpCodeSimdReg));
Set("0>101110<<1xxxxx010001xxxxxxxxxx", AInstEmit.Ushl_V, typeof(AOpCodeSimdReg));
@ -296,11 +337,11 @@ namespace ChocolArm64
Set("011111110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_S, typeof(AOpCodeSimdShImm));
Set("0x1011110>>>>xxx000001xxxxxxxxxx", AInstEmit.Ushr_V, typeof(AOpCodeSimdShImm));
Set("0x1011110>>>>xxx000101xxxxxxxxxx", AInstEmit.Usra_V, typeof(AOpCodeSimdShImm));
Set("0x001110xx0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg));
Set("0x001110xx0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg));
Set("0>001110<<0xxxxx000110xxxxxxxxxx", AInstEmit.Uzp1_V, typeof(AOpCodeSimdReg));
Set("0>001110<<0xxxxx010110xxxxxxxxxx", AInstEmit.Uzp2_V, typeof(AOpCodeSimdReg));
Set("0x001110<<100001001010xxxxxxxxxx", AInstEmit.Xtn_V, typeof(AOpCodeSimd));
Set("0x001110xx0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V, typeof(AOpCodeSimdReg));
Set("0x001110xx0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V, typeof(AOpCodeSimdReg));
Set("0>001110<<0xxxxx001110xxxxxxxxxx", AInstEmit.Zip1_V, typeof(AOpCodeSimdReg));
Set("0>001110<<0xxxxx011110xxxxxxxxxx", AInstEmit.Zip2_V, typeof(AOpCodeSimdReg));
#endregion
}
@ -442,4 +483,4 @@ namespace ChocolArm64
return AInst.Undefined;
}
}
}
}

View file

@ -3,6 +3,7 @@ using ChocolArm64.State;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
@ -23,7 +24,7 @@ namespace ChocolArm64
public ReadOnlyCollection<ARegister> Params { get; private set; }
private HashSet<long> Callees;
private HashSet<long> Callers;
private ATranslatedSubType Type;
@ -33,7 +34,7 @@ namespace ChocolArm64
private int MinCallCountForReJit = 250;
public ATranslatedSub(DynamicMethod Method, List<ARegister> Params, HashSet<long> Callees)
public ATranslatedSub(DynamicMethod Method, List<ARegister> Params)
{
if (Method == null)
{
@ -45,14 +46,10 @@ namespace ChocolArm64
throw new ArgumentNullException(nameof(Params));
}
if (Callees == null)
{
throw new ArgumentNullException(nameof(Callees));
}
this.Method = Method;
this.Params = Params.AsReadOnly();
this.Callees = Callees;
Callers = new HashSet<long>();
PrepareDelegate();
}
@ -107,17 +104,14 @@ namespace ChocolArm64
public bool ShouldReJit()
{
if (Type == ATranslatedSubType.SubTier0)
if (NeedsReJit && CallCount < MinCallCountForReJit)
{
if (CallCount < MinCallCountForReJit)
{
CallCount++;
}
CallCount++;
return CallCount == MinCallCountForReJit;
return false;
}
return Type == ATranslatedSubType.SubTier1 && NeedsReJit;
return NeedsReJit;
}
public long Execute(AThreadState ThreadState, AMemory Memory)
@ -125,10 +119,32 @@ namespace ChocolArm64
return ExecDelegate(ThreadState, Memory);
}
public void SetType(ATranslatedSubType Type) => this.Type = Type;
public void AddCaller(long Position)
{
lock (Callers)
{
Callers.Add(Position);
}
}
public bool HasCallee(long Position) => Callees.Contains(Position);
public long[] GetCallerPositions()
{
lock (Callers)
{
return Callers.ToArray();
}
}
public void MarkForReJit() => NeedsReJit = true;
public void SetType(ATranslatedSubType Type)
{
this.Type = Type;
if (Type == ATranslatedSubType.SubTier0)
{
NeedsReJit = true;
}
}
public void MarkForReJit() => NeedsReJit = true;
}
}

View file

@ -107,25 +107,31 @@ namespace ChocolArm64
ATranslatedSub Subroutine = Context.GetSubroutine();
if (SubBlocks.Contains(Position))
lock (SubBlocks)
{
SubBlocks.Remove(Position);
if (SubBlocks.Contains(Position))
{
SubBlocks.Remove(Position);
Subroutine.SetType(ATranslatedSubType.SubBlock);
}
else
{
Subroutine.SetType(ATranslatedSubType.SubTier0);
Subroutine.SetType(ATranslatedSubType.SubBlock);
}
else
{
Subroutine.SetType(ATranslatedSubType.SubTier0);
}
}
CachedSubs.AddOrUpdate(Position, Subroutine, (Key, OldVal) => Subroutine);
AOpCode LastOp = Block.GetLastOp();
if (LastOp.Emitter != AInstEmit.Ret &&
LastOp.Emitter != AInstEmit.Br)
lock (SubBlocks)
{
SubBlocks.Add(LastOp.Position + 4);
if (LastOp.Emitter != AInstEmit.Ret &&
LastOp.Emitter != AInstEmit.Br)
{
SubBlocks.Add(LastOp.Position + 4);
}
}
return Subroutine;
@ -154,11 +160,14 @@ namespace ChocolArm64
//Mark all methods that calls this method for ReJiting,
//since we can now call it directly which is faster.
foreach (ATranslatedSub TS in CachedSubs.Values)
if (CachedSubs.TryGetValue(Position, out ATranslatedSub OldSub))
{
if (TS.HasCallee(Position))
foreach (long CallerPos in OldSub.GetCallerPositions())
{
TS.MarkForReJit();
if (CachedSubs.TryGetValue(Position, out ATranslatedSub CallerSub))
{
CallerSub.MarkForReJit();
}
}
}

View file

@ -1,4 +1,5 @@
using ChocolArm64.Instruction;
using ChocolArm64.State;
namespace ChocolArm64.Decoder
{
@ -11,6 +12,10 @@ namespace ChocolArm64.Decoder
Rt = OpCode & 0x1f;
Imm = Position + ADecoderHelper.DecodeImmS19_2(OpCode);
RegisterSize = (OpCode >> 31) != 0
? ARegisterSize.Int64
: ARegisterSize.Int32;
}
}
}

View file

@ -25,8 +25,8 @@ namespace ChocolArm64.Decoder
default: Inst = AInst.Undefined; return;
}
Size = (OpCode >> 10) & 0x3;
WBack = ((OpCode >> 23) & 0x1) != 0;
Size = (OpCode >> 10) & 3;
WBack = ((OpCode >> 23) & 1) != 0;
bool Q = ((OpCode >> 30) & 1) != 0;

View file

@ -18,7 +18,7 @@ namespace ChocolArm64.Decoder
int Scale = (OpCode >> 14) & 3;
int L = (OpCode >> 22) & 1;
int Q = (OpCode >> 30) & 1;
SElems |= (OpCode >> 21) & 1;
SElems++;
@ -88,7 +88,7 @@ namespace ChocolArm64.Decoder
Extend64 = false;
WBack = ((OpCode >> 23) & 0x1) != 0;
WBack = ((OpCode >> 23) & 1) != 0;
RegisterSize = Q != 0
? ARegisterSize.SIMD128

View file

@ -100,6 +100,24 @@ namespace ChocolArm64.Instruction
EmitDataStore(Context, SetFlags);
}
public static void Cls(AILEmitterCtx Context)
{
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
Context.EmitLdintzr(Op.Rn);
if (Op.RegisterSize == ARegisterSize.Int32)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingSigns32));
}
else
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingSigns64));
}
Context.EmitStintzr(Op.Rd);
}
public static void Clz(AILEmitterCtx Context)
{
AOpCodeAlu Op = (AOpCodeAlu)Context.CurrOp;
@ -383,4 +401,4 @@ namespace ChocolArm64.Instruction
Context.EmitStflg((int)APState.CBit);
}
}
}
}

View file

@ -129,6 +129,38 @@ namespace ChocolArm64.Instruction
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Add));
}
public static void Faddp_V(AILEmitterCtx Context)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
int Elems = Bytes >> SizeF + 2;
int Half = Elems >> 1;
for (int Index = 0; Index < Elems; Index++)
{
int Elem = (Index & (Half - 1)) << 1;
EmitVectorExtractF(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, SizeF);
EmitVectorExtractF(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, SizeF);
Context.Emit(OpCodes.Add);
EmitVectorInsertTmpF(Context, Index, SizeF);
}
Context.EmitLdvectmp();
Context.EmitStvec(Op.Rd);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
public static void Fdiv_S(AILEmitterCtx Context)
{
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div));
@ -150,17 +182,84 @@ namespace ChocolArm64.Instruction
public static void Fmax_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitScalarBinaryOpF(Context, () =>
{
EmitBinaryMathCall(Context, nameof(Math.Max));
if (Op.Size == 0)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CustomMaxF));
}
else if (Op.Size == 1)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CustomMax));
}
else
{
throw new InvalidOperationException();
}
});
}
public static void Fmax_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitVectorBinaryOpF(Context, () =>
{
if (Op.Size == 0)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CustomMaxF));
}
else if (Op.Size == 1)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CustomMax));
}
else
{
throw new InvalidOperationException();
}
});
}
public static void Fmin_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitScalarBinaryOpF(Context, () =>
{
EmitBinaryMathCall(Context, nameof(Math.Min));
if (Op.Size == 0)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CustomMinF));
}
else if (Op.Size == 1)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CustomMin));
}
else
{
throw new InvalidOperationException();
}
});
}
public static void Fmin_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitVectorBinaryOpF(Context, () =>
{
if (Op.Size == 2)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CustomMinF));
}
else if (Op.Size == 3)
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CustomMin));
}
else
{
throw new InvalidOperationException();
}
});
}
@ -192,6 +291,24 @@ namespace ChocolArm64.Instruction
});
}
public static void Fmls_V(AILEmitterCtx Context)
{
EmitVectorTernaryOpF(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Sub);
});
}
public static void Fmls_Ve(AILEmitterCtx Context)
{
EmitVectorTernaryOpByElemF(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Sub);
});
}
public static void Fmsub_S(AILEmitterCtx Context)
{
EmitScalarTernaryRaOpF(Context, () =>
@ -206,6 +323,11 @@ namespace ChocolArm64.Instruction
EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
}
public static void Fmul_Se(AILEmitterCtx Context)
{
EmitScalarBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul));
}
public static void Fmul_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul));
@ -221,13 +343,30 @@ namespace ChocolArm64.Instruction
EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg));
}
public static void Fnmul_S(AILEmitterCtx Context)
public static void Fneg_V(AILEmitterCtx Context)
{
EmitScalarBinaryOpF(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Neg);
});
EmitVectorUnaryOpF(Context, () => Context.Emit(OpCodes.Neg));
}
public static void Fnmadd_S(AILEmitterCtx Context)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
Context.Emit(OpCodes.Neg);
EmitVectorExtractF(Context, Op.Rm, 0, SizeF);
Context.Emit(OpCodes.Mul);
EmitVectorExtractF(Context, Op.Ra, 0, SizeF);
Context.Emit(OpCodes.Sub);
EmitScalarSetF(Context, Op.Rd, SizeF);
}
public static void Fnmsub_S(AILEmitterCtx Context)
@ -235,7 +374,7 @@ namespace ChocolArm64.Instruction
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
EmitVectorExtractF(Context, Op.Rm, 0, SizeF);
@ -248,6 +387,138 @@ namespace ChocolArm64.Instruction
EmitScalarSetF(Context, Op.Rd, SizeF);
}
public static void Fnmul_S(AILEmitterCtx Context)
{
EmitScalarBinaryOpF(Context, () =>
{
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Neg);
});
}
public static void Frecpe_S(AILEmitterCtx Context)
{
EmitFrecpe(Context, 0, Scalar: true);
}
public static void Frecpe_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
{
EmitFrecpe(Context, Index, Scalar: false);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitFrecpe(AILEmitterCtx Context, int Index, bool Scalar)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
if (SizeF == 0)
{
Context.EmitLdc_R4(1);
}
else /* if (SizeF == 1) */
{
Context.EmitLdc_R8(1);
}
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
Context.Emit(OpCodes.Div);
if (Scalar)
{
EmitVectorZeroAll(Context, Op.Rd);
}
EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
}
public static void Frecps_S(AILEmitterCtx Context)
{
EmitFrecps(Context, 0, Scalar: true);
}
public static void Frecps_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
{
EmitFrecps(Context, Index, Scalar: false);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitFrecps(AILEmitterCtx Context, int Index, bool Scalar)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
if (SizeF == 0)
{
Context.EmitLdc_R4(2);
}
else /* if (SizeF == 1) */
{
Context.EmitLdc_R8(2);
}
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
EmitVectorExtractF(Context, Op.Rm, Index, SizeF);
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Sub);
if (Scalar)
{
EmitVectorZeroAll(Context, Op.Rd);
}
EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
}
public static void Frinta_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
EmitRoundMathCall(Context, MidpointRounding.AwayFromZero);
EmitScalarSetF(Context, Op.Rd, Op.Size);
}
public static void Frinta_V(AILEmitterCtx Context)
{
EmitVectorUnaryOpF(Context, () =>
{
EmitRoundMathCall(Context, MidpointRounding.AwayFromZero);
});
}
public static void Frinti_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@ -276,7 +547,7 @@ namespace ChocolArm64.Instruction
public static void Frinti_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitVectorUnaryOpF(Context, () =>
{
Context.EmitLdarg(ATranslatedSub.StateArgIdx);
@ -284,11 +555,11 @@ namespace ChocolArm64.Instruction
Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr));
if (Op.Size == 2)
{
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.RoundF));
}
else if (Op.Size == 3)
{
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Round));
}
else
@ -298,25 +569,6 @@ namespace ChocolArm64.Instruction
});
}
public static void Frinta_S(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
EmitVectorExtractF(Context, Op.Rn, 0, Op.Size);
EmitRoundMathCall(Context, MidpointRounding.AwayFromZero);
EmitScalarSetF(Context, Op.Rd, Op.Size);
}
public static void Frinta_V(AILEmitterCtx Context)
{
EmitVectorUnaryOpF(Context, () =>
{
EmitRoundMathCall(Context, MidpointRounding.AwayFromZero);
});
}
public static void Frintm_S(AILEmitterCtx Context)
{
EmitScalarUnaryOpF(Context, () =>
@ -404,11 +656,11 @@ namespace ChocolArm64.Instruction
Context.EmitCallPropGet(typeof(AThreadState), nameof(AThreadState.Fpcr));
if (Op.Size == 0)
{
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.RoundF));
}
else if (Op.Size == 1)
{
{
ASoftFallback.EmitCall(Context, nameof(ASoftFallback.Round));
}
else
@ -418,6 +670,86 @@ namespace ChocolArm64.Instruction
});
}
public static void Frsqrte_S(AILEmitterCtx Context)
{
EmitScalarUnaryOpF(Context, () =>
{
EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.InvSqrtEstimate));
});
}
public static void Frsqrte_V(AILEmitterCtx Context)
{
EmitVectorUnaryOpF(Context, () =>
{
EmitUnarySoftFloatCall(Context, nameof(ASoftFloat.InvSqrtEstimate));
});
}
public static void Frsqrts_S(AILEmitterCtx Context)
{
EmitFrsqrts(Context, 0, Scalar: true);
}
public static void Frsqrts_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
{
EmitFrsqrts(Context, Index, Scalar: false);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitFrsqrts(AILEmitterCtx Context, int Index, bool Scalar)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
if (SizeF == 0)
{
Context.EmitLdc_R4(3);
}
else /* if (SizeF == 1) */
{
Context.EmitLdc_R8(3);
}
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
EmitVectorExtractF(Context, Op.Rm, Index, SizeF);
Context.Emit(OpCodes.Mul);
Context.Emit(OpCodes.Sub);
if (SizeF == 0)
{
Context.EmitLdc_R4(0.5f);
}
else /* if (SizeF == 1) */
{
Context.EmitLdc_R8(0.5);
}
Context.Emit(OpCodes.Mul);
if (Scalar)
{
EmitVectorZeroAll(Context, Op.Rd);
}
EmitVectorInsertF(Context, Op.Rd, Index, SizeF);
}
public static void Fsqrt_S(AILEmitterCtx Context)
{
EmitScalarUnaryOpF(Context, () =>
@ -525,6 +857,30 @@ namespace ChocolArm64.Instruction
EmitVectorBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub));
}
public static void Uabd_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpZx(Context, () => EmitAbd(Context));
}
public static void Uabdl_V(AILEmitterCtx Context)
{
EmitVectorWidenRnRmBinaryOpZx(Context, () => EmitAbd(Context));
}
private static void EmitAbd(AILEmitterCtx Context)
{
Context.Emit(OpCodes.Sub);
Type[] Types = new Type[] { typeof(long) };
Context.EmitCall(typeof(Math).GetMethod(nameof(Math.Abs), Types));
}
public static void Uaddl_V(AILEmitterCtx Context)
{
EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
}
public static void Uaddlv_V(AILEmitterCtx Context)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
@ -548,9 +904,21 @@ namespace ChocolArm64.Instruction
EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Add));
}
public static void Uhadd_V(AILEmitterCtx Context)
{
EmitVectorBinaryOpZx(Context, () =>
{
Context.Emit(OpCodes.Add);
Context.EmitLdc_I4(1);
Context.Emit(OpCodes.Shr_Un);
});
}
public static void Umull_V(AILEmitterCtx Context)
{
EmitVectorWidenRnRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Mul));
}
}
}
}

View file

@ -110,13 +110,64 @@ namespace ChocolArm64.Instruction
Fccmp_S(Context);
}
public static void Fcmeq_S(AILEmitterCtx Context)
{
EmitScalarFcmp(Context, OpCodes.Beq_S);
}
public static void Fcmeq_V(AILEmitterCtx Context)
{
EmitVectorFcmp(Context, OpCodes.Beq_S);
}
public static void Fcmge_S(AILEmitterCtx Context)
{
EmitScalarFcmp(Context, OpCodes.Bge_S);
}
public static void Fcmge_V(AILEmitterCtx Context)
{
EmitVectorFcmp(Context, OpCodes.Bge_S);
}
public static void Fcmgt_S(AILEmitterCtx Context)
{
EmitScalarFcmp(Context, OpCodes.Bgt_S);
}
public static void Fcmgt_V(AILEmitterCtx Context)
{
EmitVectorFcmp(Context, OpCodes.Bgt_S);
}
public static void Fcmle_S(AILEmitterCtx Context)
{
EmitScalarFcmp(Context, OpCodes.Ble_S);
}
public static void Fcmle_V(AILEmitterCtx Context)
{
EmitVectorFcmp(Context, OpCodes.Ble_S);
}
public static void Fcmlt_S(AILEmitterCtx Context)
{
EmitScalarFcmp(Context, OpCodes.Blt_S);
}
public static void Fcmlt_V(AILEmitterCtx Context)
{
EmitVectorFcmp(Context, OpCodes.Blt_S);
}
public static void Fcmp_S(AILEmitterCtx Context)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
bool CmpWithZero = !(Op is AOpCodeSimdFcond) ? Op.Bit3 : false;
//Handle NaN case. If any number is NaN, then NZCV = 0011.
//Handle NaN case.
//If any number is NaN, then NZCV = 0011.
if (CmpWithZero)
{
EmitNaNCheck(Context, Op.Rn);
@ -140,7 +191,14 @@ namespace ChocolArm64.Instruction
if (CmpWithZero)
{
EmitLdcImmF(Context, 0, Op.Size);
if (Op.Size == 0)
{
Context.EmitLdc_R4(0);
}
else /* if (SizeF == 1) */
{
Context.EmitLdc_R8(0);
}
}
else
{
@ -190,22 +248,6 @@ namespace ChocolArm64.Instruction
Fcmp_S(Context);
}
private static void EmitLdcImmF(AILEmitterCtx Context, double ImmF, int Size)
{
if (Size == 0)
{
Context.EmitLdc_R4((float)ImmF);
}
else if (Size == 1)
{
Context.EmitLdc_R8(ImmF);
}
else
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
}
private static void EmitNaNCheck(AILEmitterCtx Context, int Reg)
{
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
@ -268,5 +310,84 @@ namespace ChocolArm64.Instruction
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitScalarFcmp(AILEmitterCtx Context, OpCode ILOp)
{
EmitFcmp(Context, ILOp, 0, Scalar: true);
}
private static void EmitVectorFcmp(AILEmitterCtx Context, OpCode ILOp)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
for (int Index = 0; Index < Bytes >> SizeF + 2; Index++)
{
EmitFcmp(Context, ILOp, Index, Scalar: false);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitFcmp(AILEmitterCtx Context, OpCode ILOp, int Index, bool Scalar)
{
AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
ulong SzMask = ulong.MaxValue >> (64 - (32 << SizeF));
EmitVectorExtractF(Context, Op.Rn, Index, SizeF);
if (Op is AOpCodeSimdReg BinOp)
{
EmitVectorExtractF(Context, BinOp.Rm, Index, SizeF);
}
else if (SizeF == 0)
{
Context.EmitLdc_R4(0);
}
else /* if (SizeF == 1) */
{
Context.EmitLdc_R8(0);
}
AILLabel LblTrue = new AILLabel();
AILLabel LblEnd = new AILLabel();
Context.Emit(ILOp, LblTrue);
if (Scalar)
{
EmitVectorZeroAll(Context, Op.Rd);
}
else
{
EmitVectorInsert(Context, Op.Rd, Index, SizeF + 2, 0);
}
Context.Emit(OpCodes.Br_S, LblEnd);
Context.MarkLabel(LblTrue);
if (Scalar)
{
EmitVectorInsert(Context, Op.Rd, Index, 3, (long)SzMask);
EmitVectorZeroUpper(Context, Op.Rd);
}
else
{
EmitVectorInsert(Context, Op.Rd, Index, SizeF + 2, (long)SzMask);
}
Context.MarkLabel(LblEnd);
}
}
}

View file

@ -100,6 +100,52 @@ namespace ChocolArm64.Instruction
Context.EmitCall(MthdInfo);
}
public static void EmitUnarySoftFloatCall(AILEmitterCtx Context, string Name)
{
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
int SizeF = Op.Size & 1;
MethodInfo MthdInfo;
if (SizeF == 0)
{
MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(float) });
}
else /* if (SizeF == 1) */
{
MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(double) });
}
Context.EmitCall(MthdInfo);
}
public static void EmitScalarBinaryOpByElemF(AILEmitterCtx Context, Action Emit)
{
AOpCodeSimdRegElemF Op = (AOpCodeSimdRegElemF)Context.CurrOp;
EmitScalarOpByElemF(Context, Emit, Op.Index, Ternary: false);
}
public static void EmitScalarOpByElemF(AILEmitterCtx Context, Action Emit, int Elem, bool Ternary)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int SizeF = Op.Size & 1;
if (Ternary)
{
EmitVectorExtractF(Context, Op.Rd, 0, SizeF);
}
EmitVectorExtractF(Context, Op.Rn, 0, SizeF);
EmitVectorExtractF(Context, Op.Rm, Elem, SizeF);
Emit();
EmitScalarSetF(Context, Op.Rd, SizeF);
}
public static void EmitScalarUnaryOpSx(AILEmitterCtx Context, Action Emit)
{
EmitScalarOp(Context, Emit, OperFlags.Rn, true);
@ -447,6 +493,9 @@ namespace ChocolArm64.Instruction
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
Context.EmitLdvec(Op.Rd);
Context.EmitStvectmp();
int Elems = 8 >> Op.Size;
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
@ -489,6 +538,9 @@ namespace ChocolArm64.Instruction
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
Context.EmitLdvec(Op.Rd);
Context.EmitStvectmp();
int Elems = 8 >> Op.Size;
int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0;
@ -536,10 +588,7 @@ namespace ChocolArm64.Instruction
public static void EmitVectorExtract(AILEmitterCtx Context, int Reg, int Index, int Size, bool Signed)
{
if (Size < 0 || Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
ThrowIfInvalid(Index, Size);
IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp;
@ -554,6 +603,8 @@ namespace ChocolArm64.Instruction
public static void EmitVectorExtractF(AILEmitterCtx Context, int Reg, int Index, int Size)
{
ThrowIfInvalidF(Index, Size);
Context.EmitLdvec(Reg);
Context.EmitLdc_I4(Index);
@ -589,10 +640,7 @@ namespace ChocolArm64.Instruction
public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size)
{
if (Size < 0 || Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
ThrowIfInvalid(Index, Size);
Context.EmitLdvec(Reg);
Context.EmitLdc_I4(Index);
@ -605,10 +653,7 @@ namespace ChocolArm64.Instruction
public static void EmitVectorInsertTmp(AILEmitterCtx Context, int Index, int Size)
{
if (Size < 0 || Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
ThrowIfInvalid(Index, Size);
Context.EmitLdvectmp();
Context.EmitLdc_I4(Index);
@ -621,10 +666,7 @@ namespace ChocolArm64.Instruction
public static void EmitVectorInsert(AILEmitterCtx Context, int Reg, int Index, int Size, long Value)
{
if (Size < 0 || Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
ThrowIfInvalid(Index, Size);
Context.EmitLdc_I8(Value);
Context.EmitLdvec(Reg);
@ -638,6 +680,8 @@ namespace ChocolArm64.Instruction
public static void EmitVectorInsertF(AILEmitterCtx Context, int Reg, int Index, int Size)
{
ThrowIfInvalidF(Index, Size);
Context.EmitLdvec(Reg);
Context.EmitLdc_I4(Index);
@ -659,6 +703,8 @@ namespace ChocolArm64.Instruction
public static void EmitVectorInsertTmpF(AILEmitterCtx Context, int Index, int Size)
{
ThrowIfInvalidF(Index, Size);
Context.EmitLdvectmp();
Context.EmitLdc_I4(Index);
@ -677,5 +723,31 @@ namespace ChocolArm64.Instruction
Context.EmitStvectmp();
}
private static void ThrowIfInvalid(int Index, int Size)
{
if ((uint)Size > 3)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
if ((uint)Index >= 16 >> Size)
{
throw new ArgumentOutOfRangeException(nameof(Index));
}
}
private static void ThrowIfInvalidF(int Index, int Size)
{
if ((uint)Size > 1)
{
throw new ArgumentOutOfRangeException(nameof(Size));
}
if ((uint)Index >= 4 >> Size)
{
throw new ArgumentOutOfRangeException(nameof(Index));
}
}
}
}
}

View file

@ -33,6 +33,16 @@ namespace ChocolArm64.Instruction
}
public static void Bif_V(AILEmitterCtx Context)
{
EmitBitBif(Context, true);
}
public static void Bit_V(AILEmitterCtx Context)
{
EmitBitBif(Context, false);
}
public static void EmitBitBif(AILEmitterCtx Context, bool NotRm)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
@ -47,6 +57,11 @@ namespace ChocolArm64.Instruction
EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size);
if (NotRm)
{
Context.Emit(OpCodes.Not);
}
Context.Emit(OpCodes.And);
EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size);

View file

@ -61,6 +61,9 @@ namespace ChocolArm64.Instruction
{
AOpCodeSimdExt Op = (AOpCodeSimdExt)Context.CurrOp;
Context.EmitLdvec(Op.Rd);
Context.EmitStvectmp();
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
int Position = Op.Imm4;
@ -75,10 +78,12 @@ namespace ChocolArm64.Instruction
}
EmitVectorExtractZx(Context, Reg, Position++, 0);
EmitVectorInsert(Context, Op.Rd, Index, 0);
EmitVectorInsertTmp(Context, Index, 0);
}
Context.EmitLdvectmp();
Context.EmitStvec(Op.Rd);
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
@ -113,7 +118,7 @@ namespace ChocolArm64.Instruction
EmitVectorExtractZx(Context, Op.Rn, 0, 3);
EmitIntZeroHigherIfNeeded(Context);
EmitIntZeroUpperIfNeeded(Context);
Context.EmitStintzr(Op.Rd);
}
@ -124,7 +129,7 @@ namespace ChocolArm64.Instruction
EmitVectorExtractZx(Context, Op.Rn, 1, 3);
EmitIntZeroHigherIfNeeded(Context);
EmitIntZeroUpperIfNeeded(Context);
Context.EmitStintzr(Op.Rd);
}
@ -135,7 +140,7 @@ namespace ChocolArm64.Instruction
Context.EmitLdintzr(Op.Rn);
EmitIntZeroHigherIfNeeded(Context);
EmitIntZeroUpperIfNeeded(Context);
EmitScalarSet(Context, Op.Rd, 3);
}
@ -146,7 +151,7 @@ namespace ChocolArm64.Instruction
Context.EmitLdintzr(Op.Rn);
EmitIntZeroHigherIfNeeded(Context);
EmitIntZeroUpperIfNeeded(Context);
EmitVectorInsert(Context, Op.Rd, 1, 3);
}
@ -251,6 +256,16 @@ namespace ChocolArm64.Instruction
Context.EmitStvec(Op.Rd);
}
public static void Trn1_V(AILEmitterCtx Context)
{
EmitVectorTranspose(Context, Part: 0);
}
public static void Trn2_V(AILEmitterCtx Context)
{
EmitVectorTranspose(Context, Part: 1);
}
public static void Umov_S(AILEmitterCtx Context)
{
AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp;
@ -301,7 +316,7 @@ namespace ChocolArm64.Instruction
EmitVectorZip(Context, Part: 1);
}
private static void EmitIntZeroHigherIfNeeded(AILEmitterCtx Context)
private static void EmitIntZeroUpperIfNeeded(AILEmitterCtx Context)
{
if (Context.CurrOp.RegisterSize == ARegisterSize.Int32)
{
@ -310,6 +325,29 @@ namespace ChocolArm64.Instruction
}
}
private static void EmitVectorTranspose(AILEmitterCtx Context, int Part)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
int Bytes = Context.CurrOp.GetBitsCount() >> 3;
int Elems = Bytes >> Op.Size;
for (int Index = 0; Index < Elems; Index++)
{
int Elem = (Index & ~1) + Part;
EmitVectorExtractZx(Context, (Index & 1) == 0 ? Op.Rn : Op.Rm, Elem, Op.Size);
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
}
if (Op.RegisterSize == ARegisterSize.SIMD64)
{
EmitVectorZeroUpper(Context, Op.Rd);
}
}
private static void EmitVectorUnzip(AILEmitterCtx Context, int Part)
{
AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp;
@ -322,7 +360,7 @@ namespace ChocolArm64.Instruction
for (int Index = 0; Index < Elems; Index++)
{
int Elem = Part + ((Index & (Half - 1)) << 1);
EmitVectorExtractZx(Context, Index < Half ? Op.Rn : Op.Rm, Elem, Op.Size);
EmitVectorInsert(Context, Op.Rd, Index, Op.Size);
@ -358,4 +396,4 @@ namespace ChocolArm64.Instruction
}
}
}
}
}

View file

@ -20,6 +20,14 @@ namespace ChocolArm64.Instruction
Context.EmitCall(typeof(ASoftFallback), MthdName);
}
public static uint CountLeadingSigns32(uint Value) => (uint)CountLeadingSigns(Value, 32);
public static ulong CountLeadingSigns64(ulong Value) => (ulong)CountLeadingSigns(Value, 64);
private static ulong CountLeadingSigns(ulong Value, int Size)
{
return CountLeadingZeros((Value >> 1) ^ Value, Size - 1);
}
public static uint CountLeadingZeros32(uint Value) => (uint)CountLeadingZeros(Value, 32);
public static ulong CountLeadingZeros64(ulong Value) => (ulong)CountLeadingZeros(Value, 64);
@ -113,7 +121,7 @@ namespace ChocolArm64.Instruction
Value = ((Value & 0xcccccccccccccccc) >> 2) | ((Value & 0x3333333333333333) << 2);
Value = ((Value & 0xf0f0f0f0f0f0f0f0) >> 4) | ((Value & 0x0f0f0f0f0f0f0f0f) << 4);
Value = ((Value & 0xff00ff00ff00ff00) >> 8) | ((Value & 0x00ff00ff00ff00ff) << 8);
Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16);
Value = ((Value & 0xffff0000ffff0000) >> 16) | ((Value & 0x0000ffff0000ffff) << 16);
return (Value >> 32) | (Value << 32);
}
@ -242,10 +250,113 @@ namespace ChocolArm64.Instruction
public static int CountSetBits8(byte Value)
{
return (Value >> 0) & 1 + (Value >> 1) & 1 +
(Value >> 2) & 1 + (Value >> 3) & 1 +
(Value >> 4) & 1 + (Value >> 5) & 1 +
(Value >> 6) & 1 + (Value >> 7);
return ((Value >> 0) & 1) + ((Value >> 1) & 1) +
((Value >> 2) & 1) + ((Value >> 3) & 1) +
((Value >> 4) & 1) + ((Value >> 5) & 1) +
((Value >> 6) & 1) + (Value >> 7);
}
public static float CustomMaxF(float val1, float val2) {
if(val1 == 0.0 && val2 == 0.0)
{
if(BitConverter.GetBytes(val1)[3] == 0x80 && BitConverter.GetBytes(val2)[3] == 0x80)
return (float)-0.0;
if(BitConverter.GetBytes(val1)[3] == 0x80 || BitConverter.GetBytes(val2)[3] == 0x80)
return (float)0.0;
return (float)0.0;
}
if(val1 > val2)
return val1;
if(Single.IsNaN(val1))
return val1;
return val2;
}
public static double CustomMax(double val1, double val2) {
if(val1 == 0.0 && val2 == 0.0)
{
if(BitConverter.GetBytes(val1)[7] == 0x80 && BitConverter.GetBytes(val2)[7] == 0x80)
return -0.0;
if(BitConverter.GetBytes(val1)[7] == 0x80 || BitConverter.GetBytes(val2)[7] == 0x80)
return 0.0;
return 0.0;
}
if(val1 > val2)
return val1;
if(Double.IsNaN(val1))
return val1;
return val2;
}
public static float CustomMinF(float val1, float val2) {
if((val1 == 0.0 && val2 >= 0.0) || (val1 >= 0.0 && val2 == 0.0))
{
if(BitConverter.GetBytes(val1)[3] == 0x80 || BitConverter.GetBytes(val2)[3] == 0x80)
return (float)-0.0;
}
if(val1 == 0.0 && val2 == 0.0)
{
if(BitConverter.GetBytes(val1)[3] == 0x80 && BitConverter.GetBytes(val2)[3] == 0x80)
return (float)-0.0;
if(BitConverter.GetBytes(val1)[3] == 0x80 || BitConverter.GetBytes(val2)[3] == 0x80)
return (float)-0.0;
return (float)0.0;
}
if(val1 < val2)
return val1;
if(Single.IsNaN(val1))
return val1;
return val2;
}
public static double CustomMin(double val1, double val2) {
if((val1 == 0.0 && val2 >= 0.0) || (val1 >= 0.0 && val2 == 0.0))
{
if(BitConverter.GetBytes(val1)[7] == 0x80 || BitConverter.GetBytes(val2)[7] == 0x80)
return -0.0;
}
if(val1 == 0.0 && val2 == 0.0)
{
if(BitConverter.GetBytes(val1)[7] == 0x80 && BitConverter.GetBytes(val2)[7] == 0x80)
return -0.0;
if(BitConverter.GetBytes(val1)[7] == 0x80 || BitConverter.GetBytes(val2)[7] == 0x80)
return -0.0;
return 0.0;
}
if(val1 < val2)
return val1;
if(Double.IsNaN(val1))
return val1;
return val2;
}
public static float RoundF(float Value, int Fpcr)
@ -398,4 +509,4 @@ namespace ChocolArm64.Instruction
throw new ArgumentOutOfRangeException(nameof(Size));
}
}
}
}

View file

@ -0,0 +1,103 @@
using System;
namespace ChocolArm64.Instruction
{
static class ASoftFloat
{
static ASoftFloat()
{
InvSqrtEstimateTable = BuildInvSqrtEstimateTable();
}
private static readonly byte[] InvSqrtEstimateTable;
private static byte[] BuildInvSqrtEstimateTable()
{
byte[] Table = new byte[512];
for (ulong index = 128; index < 512; index++)
{
ulong a = index;
if (a < 256)
{
a = (a << 1) + 1;
}
else
{
a = (a | 1) << 1;
}
ulong b = 256;
while (a * (b + 1) * (b + 1) < (1ul << 28))
{
b++;
}
b = (b + 1) >> 1;
Table[index] = (byte)(b & 0xFF);
}
return Table;
}
public static float InvSqrtEstimate(float x)
{
return (float)InvSqrtEstimate((double)x);
}
public static double InvSqrtEstimate(double x)
{
ulong x_bits = (ulong)BitConverter.DoubleToInt64Bits(x);
ulong x_sign = x_bits & 0x8000000000000000;
long x_exp = (long)((x_bits >> 52) & 0x7FF);
ulong scaled = x_bits & ((1ul << 52) - 1);
if (x_exp == 0x7ff)
{
if (scaled == 0)
{
// Infinity -> Zero
return BitConverter.Int64BitsToDouble((long)x_sign);
}
// NaN
return BitConverter.Int64BitsToDouble((long)(x_bits | 0x0008000000000000));
}
if (x_exp == 0)
{
if (scaled == 0)
{
// Zero -> Infinity
return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7ff0000000000000));
}
// Denormal
while ((scaled & (1 << 51)) == 0)
{
scaled <<= 1;
x_exp--;
}
scaled <<= 1;
}
if (((ulong)x_exp & 1) == 1)
{
scaled >>= 45;
scaled &= 0xFF;
scaled |= 0x80;
}
else
{
scaled >>= 44;
scaled &= 0xFF;
scaled |= 0x100;
}
ulong result_exp = ((ulong)(3068 - x_exp) / 2) & 0x7FF;
ulong estimate = (ulong)InvSqrtEstimateTable[scaled];
ulong fraction = estimate << 44;
ulong result = x_sign | (result_exp << 52) | fraction;
return BitConverter.Int64BitsToDouble((long)result);
}
}
}

View file

@ -60,11 +60,11 @@ namespace ChocolArm64.Translation
public AILBlock GetILBlock(int Index) => ILBlocks[Index];
public ATranslatedSub GetSubroutine(HashSet<long> Callees)
public ATranslatedSub GetSubroutine()
{
LocalAlloc = new ALocalAlloc(ILBlocks, Root);
InitSubroutine(Callees);
InitSubroutine();
InitLocals();
foreach (AILBlock ILBlock in ILBlocks)
@ -75,7 +75,7 @@ namespace ChocolArm64.Translation
return Subroutine;
}
private void InitSubroutine(HashSet<long> Callees)
private void InitSubroutine()
{
List<ARegister> Params = new List<ARegister>();
@ -99,7 +99,7 @@ namespace ChocolArm64.Translation
Generator = Mthd.GetILGenerator();
Subroutine = new ATranslatedSub(Mthd, Params, Callees);
Subroutine = new ATranslatedSub(Mthd, Params);
}
private void InitLocals()
@ -115,7 +115,7 @@ namespace ChocolArm64.Translation
Generator.EmitLdarg(Index + ParamsStart);
Generator.EmitStloc(GetLocalIndex(Reg));
}
}
}
private Type[] GetParamTypes(IList<ARegister> Params)
{

View file

@ -12,8 +12,6 @@ namespace ChocolArm64.Translation
{
private ATranslator Translator;
private HashSet<long> Callees;
private Dictionary<long, AILLabel> Labels;
private int BlkIndex;
@ -66,13 +64,11 @@ namespace ChocolArm64.Translation
this.Graph = Graph;
this.Root = Root;
Callees = new HashSet<long>();
Labels = new Dictionary<long, AILLabel>();
Emitter = new AILEmitter(Graph, Root, SubName);
ILBlock = Emitter.GetILBlock(0);
ILBlock = Emitter.GetILBlock(0);
OpcIndex = -1;
@ -84,7 +80,7 @@ namespace ChocolArm64.Translation
public ATranslatedSub GetSubroutine()
{
return Emitter.GetSubroutine(Callees);
return Emitter.GetSubroutine();
}
public bool AdvanceOpCode()
@ -123,8 +119,6 @@ namespace ChocolArm64.Translation
public bool TryOptEmitSubroutineCall()
{
Callees.Add(((AOpCodeBImm)CurrOp).Imm);
if (CurrBlock.Next == null)
{
return false;
@ -152,6 +146,8 @@ namespace ChocolArm64.Translation
EmitCall(Sub.Method);
Sub.AddCaller(Root.Position);
return true;
}
@ -260,18 +256,24 @@ namespace ChocolArm64.Translation
case AIntType.Int64: Emit(OpCodes.Conv_I8); break;
}
if (IntType == AIntType.UInt64 ||
IntType == AIntType.Int64)
bool Sz64 = CurrOp.RegisterSize != ARegisterSize.Int32;
if (Sz64 == (IntType == AIntType.UInt64 ||
IntType == AIntType.Int64))
{
return;
}
if (CurrOp.RegisterSize != ARegisterSize.Int32)
if (Sz64)
{
Emit(IntType >= AIntType.Int8
? OpCodes.Conv_I8
: OpCodes.Conv_U8);
}
else
{
Emit(OpCodes.Conv_U4);
}
}
public void EmitLsl(int Amount) => EmitILShift(Amount, OpCodes.Shl);
@ -298,7 +300,7 @@ namespace ChocolArm64.Translation
EmitLdc_I4(Amount);
Emit(OpCodes.Shr_Un);
Ldloc(Tmp2Index, AIoType.Int);
EmitLdc_I4(CurrOp.GetBitsCount() - Amount);

View file

@ -48,6 +48,9 @@ https://openal.org/downloads/OpenAL11CoreSDK.zip
- Config File: `Ryujinx.conf` should be present in executable folder.
For more informations [you can go here](CONFIG.md).
- If you are a Windows user, you can configure your keys, the logs, install OpenAL, etc... with Ryujinx-Setting.
[Download it, right here](https://github.com/AcK77/Ryujinx-Settings)
**Help**

View file

@ -9,15 +9,18 @@ namespace Ryujinx.Core
{
public static class Config
{
public static bool EnableMemoryChecks { get; private set; }
public static bool LoggingEnableInfo { get; private set; }
public static bool LoggingEnableTrace { get; private set; }
public static bool LoggingEnableDebug { get; private set; }
public static bool LoggingEnableWarn { get; private set; }
public static bool LoggingEnableError { get; private set; }
public static bool LoggingEnableFatal { get; private set; }
public static bool LoggingEnableIpc { get; private set; }
public static bool LoggingEnableLogFile { get; private set; }
public static bool EnableMemoryChecks { get; private set; }
public static bool LoggingEnableInfo { get; private set; }
public static bool LoggingEnableTrace { get; private set; }
public static bool LoggingEnableDebug { get; private set; }
public static bool LoggingEnableWarn { get; private set; }
public static bool LoggingEnableError { get; private set; }
public static bool LoggingEnableFatal { get; private set; }
public static bool LoggingEnableIpc { get; private set; }
public static bool LoggingEnableStub { get; private set; }
public static bool LoggingEnableLogFile { get; private set; }
public static bool LoggingEnableFilter { get; private set; }
public static bool[] LoggingFilteredClasses { get; private set; }
public static JoyCon FakeJoyCon { get; private set; }
@ -27,15 +30,33 @@ namespace Ryujinx.Core
var iniPath = Path.Combine(iniFolder, "Ryujinx.conf");
IniParser Parser = new IniParser(iniPath);
EnableMemoryChecks = Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
LoggingEnableInfo = Convert.ToBoolean(Parser.Value("Logging_Enable_Info"));
LoggingEnableTrace = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace"));
LoggingEnableDebug = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"));
LoggingEnableWarn = Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"));
LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
LoggingEnableFatal = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
LoggingEnableIpc = Convert.ToBoolean(Parser.Value("Logging_Enable_Ipc"));
LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
EnableMemoryChecks = Convert.ToBoolean(Parser.Value("Enable_Memory_Checks"));
LoggingEnableInfo = Convert.ToBoolean(Parser.Value("Logging_Enable_Info"));
LoggingEnableTrace = Convert.ToBoolean(Parser.Value("Logging_Enable_Trace"));
LoggingEnableDebug = Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"));
LoggingEnableWarn = Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"));
LoggingEnableError = Convert.ToBoolean(Parser.Value("Logging_Enable_Error"));
LoggingEnableFatal = Convert.ToBoolean(Parser.Value("Logging_Enable_Fatal"));
LoggingEnableIpc = Convert.ToBoolean(Parser.Value("Logging_Enable_Ipc"));
LoggingEnableStub = Convert.ToBoolean(Parser.Value("Logging_Enable_Stub"));
LoggingEnableLogFile = Convert.ToBoolean(Parser.Value("Logging_Enable_LogFile"));
LoggingEnableFilter = Convert.ToBoolean(Parser.Value("Logging_Enable_Filter"));
LoggingFilteredClasses = new bool[(int)LogClass.Count];
string[] FilteredLogClasses = Parser.Value("Logging_Filtered_Classes", string.Empty).Split(',');
foreach (string LogClass in FilteredLogClasses)
{
if (!string.IsNullOrEmpty(LogClass.Trim()))
{
foreach (LogClass EnumItemName in Enum.GetValues(typeof(LogClass)))
{
if (EnumItemName.ToString().ToLower().Contains(LogClass.Trim().ToLower()))
{
LoggingFilteredClasses[(int)EnumItemName] = true;
}
}
}
}
FakeJoyCon = new JoyCon
{

View file

@ -82,7 +82,7 @@ namespace Ryujinx.Core.Input
(AMemory Memory, long Position) ShMem = ShMemPositions[ShMemPositions.Length - 1];
Logging.Info($"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!");
Logging.Info(LogClass.ServiceHid, $"HID shared memory successfully mapped to 0x{ShMem.Position:x16}!");
Init(ShMem.Memory, ShMem.Position);
}
@ -219,7 +219,7 @@ namespace Ryujinx.Core.Input
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x8, HidEntryCount);
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x10, CurrEntry);
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x18, HidEntryCount - 1);
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp);
Memory.WriteInt64Unchecked(TouchScreenOffset + 0x20, Timestamp);
long TouchEntryOffset = TouchScreenOffset + HidTouchHeaderSize;

36
Ryujinx.Core/LogClass.cs Normal file
View file

@ -0,0 +1,36 @@
namespace Ryujinx.Core
{
public enum LogClass
{
Audio,
CPU,
GPU,
Kernel,
KernelIpc,
KernelScheduler,
KernelSvc,
Loader,
Service,
ServiceAcc,
ServiceAm,
ServiceApm,
ServiceAudio,
ServiceBsd,
ServiceFriend,
ServiceFs,
ServiceHid,
ServiceLm,
ServiceNifm,
ServiceNs,
ServiceNv,
ServicePctl,
ServicePl,
ServiceSet,
ServiceSfdnsres,
ServiceSm,
ServiceSss,
ServiceTime,
ServiceVi,
Count,
}
}

View file

@ -2,7 +2,9 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
namespace Ryujinx.Core
{
@ -12,14 +14,28 @@ namespace Ryujinx.Core
private const string LogFileName = "Ryujinx.log";
private static bool EnableInfo = Config.LoggingEnableInfo;
private static bool EnableTrace = Config.LoggingEnableTrace;
private static bool EnableDebug = Config.LoggingEnableDebug;
private static bool EnableWarn = Config.LoggingEnableWarn;
private static bool EnableError = Config.LoggingEnableError;
private static bool EnableFatal = Config.LoggingEnableFatal;
private static bool EnableIpc = Config.LoggingEnableIpc;
private static bool EnableLogFile = Config.LoggingEnableLogFile;
private static bool EnableInfo = Config.LoggingEnableInfo;
private static bool EnableTrace = Config.LoggingEnableTrace;
private static bool EnableDebug = Config.LoggingEnableDebug;
private static bool EnableWarn = Config.LoggingEnableWarn;
private static bool EnableError = Config.LoggingEnableError;
private static bool EnableFatal = Config.LoggingEnableFatal;
private static bool EnableStub = Config.LoggingEnableIpc;
private static bool EnableIpc = Config.LoggingEnableIpc;
private static bool EnableFilter = Config.LoggingEnableFilter;
private static bool EnableLogFile = Config.LoggingEnableLogFile;
private static bool[] FilteredLogClasses = Config.LoggingFilteredClasses;
private enum LogLevel
{
Debug,
Error,
Fatal,
Info,
Stub,
Trace,
Warn
}
static Logging()
{
@ -30,14 +46,51 @@ namespace Ryujinx.Core
ExecutionTime.Start();
}
public static string GetExecutionTime()
{
return ExecutionTime.ElapsedMilliseconds.ToString().PadLeft(8, '0') + "ms";
}
public static string GetExecutionTime() => ExecutionTime.ElapsedMilliseconds.ToString().PadLeft(8, '0') + "ms";
private static string WhoCalledMe()
private static void LogMessage(LogEntry LogEntry)
{
return new StackTrace().GetFrame(2).GetMethod().Name;
if (EnableFilter)
if (!FilteredLogClasses[(int)LogEntry.LogClass])
return;
ConsoleColor consoleColor = ConsoleColor.White;
switch (LogEntry.LogLevel)
{
case LogLevel.Debug:
consoleColor = ConsoleColor.Gray;
break;
case LogLevel.Error:
consoleColor = ConsoleColor.Red;
break;
case LogLevel.Fatal:
consoleColor = ConsoleColor.Magenta;
break;
case LogLevel.Info:
consoleColor = ConsoleColor.White;
break;
case LogLevel.Stub:
consoleColor = ConsoleColor.DarkYellow;
break;
case LogLevel.Trace:
consoleColor = ConsoleColor.DarkGray;
break;
case LogLevel.Warn:
consoleColor = ConsoleColor.Yellow;
break;
}
LogEntry.ManagedThreadId = Thread.CurrentThread.ManagedThreadId;
string Text = $"{LogEntry.ExecutionTime} | {LogEntry.ManagedThreadId} > {LogEntry.LogClass} > " +
$"{LogEntry.LogLevel.ToString()} > {LogEntry.CallingMember} > {LogEntry.Message}";
Console.ForegroundColor = consoleColor;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
}
private static void LogFile(string Message)
@ -51,87 +104,108 @@ namespace Ryujinx.Core
}
}
public static void Info(string Message)
public static void Info(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableInfo)
{
string Text = $"{GetExecutionTime()} | INFO > {Message}";
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Info,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
public static void Trace(string Message)
public static void Trace(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableTrace)
{
string Text = $"{GetExecutionTime()} | TRACE > {WhoCalledMe()} - {Message}";
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Trace,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
public static void Debug(string Message)
public static void Stub(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableStub)
{
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Stub,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
public static void Debug(LogClass LogClass,string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableDebug)
{
string Text = $"{GetExecutionTime()} | DEBUG > {WhoCalledMe()} - {Message}";
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Debug,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
public static void Warn(string Message)
public static void Warn(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableWarn)
{
string Text = $"{GetExecutionTime()} | WARN > {WhoCalledMe()} - {Message}";
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Warn,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
public static void Error(string Message)
public static void Error(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableError)
{
string Text = $"{GetExecutionTime()} | ERROR > {WhoCalledMe()} - {Message}";
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Error,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
public static void Fatal(string Message)
public static void Fatal(LogClass LogClass, string Message, [CallerMemberName] string CallingMember = "")
{
if (EnableFatal)
{
string Text = $"{GetExecutionTime()} | FATAL > {WhoCalledMe()} - {Message}";
Console.ForegroundColor = ConsoleColor.Magenta;
Console.WriteLine(Text.PadLeft(Text.Length + 1, ' '));
Console.ResetColor();
LogFile(Text);
LogMessage(new LogEntry
{
CallingMember = CallingMember,
LogLevel = LogLevel.Fatal,
LogClass = LogClass,
Message = Message,
ExecutionTime = GetExecutionTime()
});
}
}
@ -160,7 +234,7 @@ namespace Ryujinx.Core
int firstCharColumn = firstHexColumn
+ bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space
+ (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th
+ 2; // 2 spaces
+ 2; // 2 spaces
int lineLength = firstCharColumn
+ bytesPerLine // - characters to show the ascii value
@ -208,5 +282,15 @@ namespace Ryujinx.Core
}
return result.ToString();
}
private struct LogEntry
{
public string CallingMember;
public string ExecutionTime;
public string Message;
public int ManagedThreadId;
public LogClass LogClass;
public LogLevel LogLevel;
}
}
}

View file

@ -136,13 +136,13 @@ namespace Ryujinx.Core.OsHle.Handles
Thread.Thread.Execute();
Logging.Debug($"{GetDbgThreadInfo(Thread)} running.");
Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} running.");
}
else
{
WaitingToRun[Thread.ProcessorId].Push(SchedThread);
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
}
}
}
@ -168,13 +168,13 @@ namespace Ryujinx.Core.OsHle.Handles
{
SchedulerThread SchedThread;
Logging.Debug($"{GetDbgThreadInfo(CurrThread)} entering ipc delay wait state.");
Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(CurrThread)} entering ipc delay wait state.");
lock (SchedLock)
{
if (!AllThreads.TryGetValue(CurrThread, out SchedThread))
{
Logging.Error($"{GetDbgThreadInfo(CurrThread)} was not found on the scheduler queue!");
Logging.Error(LogClass.KernelScheduler, $"{GetDbgThreadInfo(CurrThread)} was not found on the scheduler queue!");
return;
}
@ -187,7 +187,7 @@ namespace Ryujinx.Core.OsHle.Handles
{
SchedulerThread SchedThread;
Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state.");
Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} entering signal wait state.");
lock (SchedLock)
{
@ -204,7 +204,7 @@ namespace Ryujinx.Core.OsHle.Handles
if (!AllThreads.TryGetValue(Thread, out SchedThread))
{
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
Logging.Error(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
return false;
}
@ -214,7 +214,7 @@ namespace Ryujinx.Core.OsHle.Handles
if (Timeout >= 0)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} has wait timeout of {Timeout}ms.");
Result = SchedThread.WaitEvent.WaitOne(Timeout);
}
@ -236,7 +236,7 @@ namespace Ryujinx.Core.OsHle.Handles
{
if (ActiveProcessors.Add(Thread.ProcessorId))
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} resuming execution...");
return;
}
@ -246,14 +246,14 @@ namespace Ryujinx.Core.OsHle.Handles
SchedThread.WaitEvent.WaitOne();
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} resuming execution...");
}
public void Yield(KThread Thread)
{
SchedulerThread SchedThread;
Logging.Debug($"{GetDbgThreadInfo(Thread)} yielded execution.");
Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} yielded execution.");
lock (SchedLock)
{
@ -261,7 +261,7 @@ namespace Ryujinx.Core.OsHle.Handles
if (SchedThread == null)
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} resumed because theres nothing better to run.");
Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} resumed because theres nothing better to run.");
return;
}
@ -270,7 +270,7 @@ namespace Ryujinx.Core.OsHle.Handles
if (!AllThreads.TryGetValue(Thread, out SchedThread))
{
Logging.Error($"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
Logging.Error(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} was not found on the scheduler queue!");
return;
}
@ -280,7 +280,7 @@ namespace Ryujinx.Core.OsHle.Handles
SchedThread.WaitEvent.WaitOne();
Logging.Debug($"{GetDbgThreadInfo(Thread)} resuming execution...");
Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} resuming execution...");
}
private void RunThread(SchedulerThread SchedThread)
@ -291,7 +291,7 @@ namespace Ryujinx.Core.OsHle.Handles
}
else
{
Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} running.");
Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(SchedThread.Thread)} running.");
}
}
@ -305,7 +305,7 @@ namespace Ryujinx.Core.OsHle.Handles
{
if (!WaitingToRun[Thread.ProcessorId].HasThread(SchedThread))
{
Logging.Debug($"{GetDbgThreadInfo(Thread)} signaled.");
Logging.Debug(LogClass.KernelScheduler, $"{GetDbgThreadInfo(Thread)} signaled.");
SchedThread.WaitEvent.Set();
}

View file

@ -7,9 +7,12 @@ namespace Ryujinx.Core.OsHle.Handles
{
public IpcService Service { get; private set; }
public KSession(IpcService Service)
public string ServiceName { get; private set; }
public KSession(IpcService Service, string ServiceName)
{
this.Service = Service;
this.Service = Service;
this.ServiceName = ServiceName;
}
public void Dispose()

View file

@ -56,7 +56,7 @@ namespace Ryujinx.Core.OsHle
continue;
}
Logging.Info($"Loading {Path.GetFileNameWithoutExtension(File)}...");
Logging.Info(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}...");
using (FileStream Input = new FileStream(File, FileMode.Open))
{
@ -131,7 +131,7 @@ namespace Ryujinx.Core.OsHle
{
string NextNro = Homebrew.ReadHbAbiNextLoadPath(Process.Memory, Process.HbAbiDataPosition);
Logging.Info($"HbAbi NextLoadPath {NextNro}");
Logging.Info(LogClass.Loader, $"HbAbi NextLoadPath {NextNro}");
if (NextNro == string.Empty)
{

View file

@ -61,12 +61,12 @@ namespace Ryujinx.Core.OsHle.Ipc
case 3:
{
Request = FillResponse(Response, 0, 0x500);
break;
}
//TODO: Whats the difference between IpcDuplicateSession/Ex?
case 2:
//TODO: Whats the difference between IpcDuplicateSession/Ex?
case 2:
case 4:
{
int Unknown = ReqReader.ReadInt32();

View file

@ -16,7 +16,7 @@ namespace Ryujinx.Core.OsHle
public const long MainStackAddress = AMemoryMgr.AddrSize - MainStackSize;
public const long TlsPagesSize = 0x4000;
public const long TlsPagesSize = 0x20000;
public const long TlsPagesAddress = MainStackAddress - TlsPagesSize;

View file

@ -15,8 +15,9 @@ namespace Ryujinx.Core.OsHle
{
class Process : IDisposable
{
private const int TlsSize = 0x200;
private const int TotalTlsSlots = 32;
private const int TlsSize = 0x200;
private const int TotalTlsSlots = (int)MemoryRegions.TlsPagesSize / TlsSize;
private const int TickFreq = 19_200_000;
@ -90,7 +91,7 @@ namespace Ryujinx.Core.OsHle
throw new ObjectDisposedException(nameof(Process));
}
Logging.Info($"Image base at 0x{ImageBase:x16}.");
Logging.Info(LogClass.Loader, $"Image base at 0x{ImageBase:x16}.");
Executable Executable = new Executable(Program, Memory, ImageBase);
@ -123,7 +124,7 @@ namespace Ryujinx.Core.OsHle
MemoryRegions.MainStackAddress,
MemoryRegions.MainStackSize,
MemoryType.Normal);
long StackTop = MemoryRegions.MainStackAddress + MemoryRegions.MainStackSize;
int Handle = MakeThread(Executables[0].ImageBase, StackTop, 0, 0, 0);
@ -254,12 +255,12 @@ namespace Ryujinx.Core.OsHle
if (e.Position >= Executables[Index].ImageBase)
{
NsoName = $"{(e.Position - Executables[Index].ImageBase):x16}";
break;
}
}
Logging.Trace($"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
Logging.Trace(LogClass.Loader, $"Executing at 0x{e.Position:x16} {e.SubName} {NsoName}");
}
public void EnableCpuTracing()
@ -289,7 +290,7 @@ namespace Ryujinx.Core.OsHle
{
if (sender is AThread Thread)
{
Logging.Info($"Thread {Thread.ThreadId} exiting...");
Logging.Info(LogClass.KernelScheduler, $"Thread {Thread.ThreadId} exiting...");
TlsSlots.TryRemove(GetTlsSlot(Thread.ThreadState.Tpidr), out _);
}
@ -301,7 +302,7 @@ namespace Ryujinx.Core.OsHle
Dispose();
}
Logging.Info($"No threads running, now exiting Process {ProcessId}...");
Logging.Info(LogClass.KernelScheduler, $"No threads running, now exiting Process {ProcessId}...");
Ns.Os.ExitProcess(ProcessId);
}
@ -316,7 +317,7 @@ namespace Ryujinx.Core.OsHle
{
if (!ThreadsByTpidr.TryGetValue(Tpidr, out KThread Thread))
{
Logging.Error($"Thread with TPIDR 0x{Tpidr:x16} not found!");
Logging.Error(LogClass.KernelScheduler, $"Thread with TPIDR 0x{Tpidr:x16} not found!");
}
return Thread;
@ -339,7 +340,7 @@ namespace Ryujinx.Core.OsHle
{
ShouldDispose = true;
Logging.Info($"Process {ProcessId} waiting all threads terminate...");
Logging.Info(LogClass.KernelScheduler, $"Process {ProcessId} waiting all threads terminate...");
return;
}
@ -354,11 +355,11 @@ namespace Ryujinx.Core.OsHle
}
}
ServiceNvDrv.Fds.DeleteProcess(this);
INvDrvServices.Fds.DeleteProcess(this);
ServiceNvDrv.NvMaps .DeleteProcess(this);
ServiceNvDrv.NvMapsById.DeleteProcess(this);
ServiceNvDrv.NvMapsFb .DeleteProcess(this);
INvDrvServices.NvMaps .DeleteProcess(this);
INvDrvServices.NvMapsById.DeleteProcess(this);
INvDrvServices.NvMapsFb .DeleteProcess(this);
Scheduler.Dispose();
@ -368,7 +369,7 @@ namespace Ryujinx.Core.OsHle
Memory.Dispose();
Logging.Info($"Process {ProcessId} exiting...");
Logging.Info(LogClass.KernelScheduler, $"Process {ProcessId} exiting...");
}
}
}

View file

@ -3,16 +3,17 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Acc
{
class ServiceAcc : IpcService
class IAccountServiceForApplication : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceAcc()
public IAccountServiceForApplication()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, GetUserCount },
{ 3, ListOpenUsers },
{ 5, GetProfile },
{ 100, InitializeApplicationInfo },
@ -20,8 +21,19 @@ namespace Ryujinx.Core.OsHle.Services.Acc
};
}
public long GetUserCount(ServiceCtx Context)
{
Context.ResponseData.Write(0);
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
return 0;
}
public long ListOpenUsers(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
return 0;
}
@ -34,6 +46,8 @@ namespace Ryujinx.Core.OsHle.Services.Acc
public long InitializeApplicationInfo(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
return 0;
}
@ -44,4 +58,4 @@ namespace Ryujinx.Core.OsHle.Services.Acc
return 0;
}
}
}
}

View file

@ -19,12 +19,16 @@ namespace Ryujinx.Core.OsHle.Services.Acc
}
public long CheckAvailability(ServiceCtx Context)
{
{
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
return 0;
}
public long GetAccountId(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAcc, "AccountId = 0xcafeL");
Context.ResponseData.Write(0xcafeL);
return 0;

View file

@ -19,6 +19,8 @@ namespace Ryujinx.Core.OsHle.Services.Acc
public long GetBase(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAcc, "Stubbed");
Context.ResponseData.Write(0L);
Context.ResponseData.Write(0L);
Context.ResponseData.Write(0L);

View file

@ -37,6 +37,8 @@ namespace Ryujinx.Core.OsHle.Services.Am
long UIdLow = Context.RequestData.ReadInt64();
long UIdHigh = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceAm, $"UidLow = {UIdLow}, UidHigh = {UIdHigh}");
Context.ResponseData.Write(0L);
return 0;
@ -44,6 +46,8 @@ namespace Ryujinx.Core.OsHle.Services.Am
public long GetDesiredLanguage(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAm, "LanguageId = 1");
//This is an enumerator where each number is a differnet language.
//0 is Japanese and 1 is English, need to figure out the other codes.
Context.ResponseData.Write(1L);
@ -58,7 +62,7 @@ namespace Ryujinx.Core.OsHle.Services.Am
int Module = ErrorCode & 0xFF;
int Description = (ErrorCode >> 9) & 0xFFF;
Logging.Info($"({(ErrorModule)Module}){2000 + Module}-{Description}");
Logging.Info(LogClass.ServiceAm, $"({(ErrorModule)Module}){2000 + Module}-{Description}");
return 0;
}

View file

@ -3,13 +3,13 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Am
{
class ServiceAppletOE : IpcService
class IApplicationProxyService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceAppletOE()
public IApplicationProxyService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{

View file

@ -56,7 +56,7 @@ namespace Ryujinx.Core.OsHle.Services.Am
public long GetPerformanceMode(ServiceCtx Context)
{
Context.ResponseData.Write((byte)0);
Context.ResponseData.Write((byte)Apm.PerformanceMode.Handheld);
return 0;
}

View file

@ -13,7 +13,7 @@ namespace Ryujinx.Core.OsHle.Services.Am
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, Exit },
{ 1, LockExit },
{ 10, SetScreenShotPermission },
{ 11, SetOperationModeChangedNotification },
{ 12, SetPerformanceModeChangedNotification },
@ -23,7 +23,7 @@ namespace Ryujinx.Core.OsHle.Services.Am
};
}
public long Exit(ServiceCtx Context)
public long LockExit(ServiceCtx Context)
{
return 0;
}
@ -32,6 +32,8 @@ namespace Ryujinx.Core.OsHle.Services.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"ScreenShot Allowed = {Enable}");
return 0;
}
@ -39,6 +41,8 @@ namespace Ryujinx.Core.OsHle.Services.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"OperationMode Changed = {Enable}");
return 0;
}
@ -46,6 +50,8 @@ namespace Ryujinx.Core.OsHle.Services.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"PerformanceMode Changed = {Enable}");
return 0;
}
@ -55,6 +61,8 @@ namespace Ryujinx.Core.OsHle.Services.Am
bool Flag2 = Context.RequestData.ReadByte() != 0 ? true : false;
bool Flag3 = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"Focus Handling Mode Flags = {{{Flag1}|{Flag2}|{Flag3}}}");
return 0;
}
@ -62,6 +70,8 @@ namespace Ryujinx.Core.OsHle.Services.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"Restart Message Enabled = {Enable}");
return 0;
}
@ -69,6 +79,8 @@ namespace Ryujinx.Core.OsHle.Services.Am
{
bool Enable = Context.RequestData.ReadByte() != 0 ? true : false;
Logging.Stub(LogClass.ServiceAm, $"Out Of Focus Suspending Enabled = {Enable}");
return 0;
}
}

View file

@ -20,6 +20,8 @@ namespace Ryujinx.Core.OsHle.Services.Am
public long GetAppletResourceUserId(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAm, $"Applet Resource Id = 0");
Context.ResponseData.Write(0L);
return 0;
@ -27,6 +29,8 @@ namespace Ryujinx.Core.OsHle.Services.Am
public long AcquireForegroundRights(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAm, "Stubbed");
return 0;
}
}

View file

@ -3,13 +3,13 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Apm
{
class ServiceApm : IpcService
class IManager : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceApm()
public IManager()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{

View file

@ -13,14 +13,27 @@ namespace Ryujinx.Core.OsHle.Services.Apm
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, SetPerformanceConfiguration }
{ 0, SetPerformanceConfiguration },
{ 1, GetPerformanceConfiguration }
};
}
public long SetPerformanceConfiguration(ServiceCtx Context)
{
int PerfMode = Context.RequestData.ReadInt32();
int PerfConfig = Context.RequestData.ReadInt32();
PerformanceMode PerfMode = (PerformanceMode)Context.RequestData.ReadInt32();
PerformanceConfiguration PerfConfig = (PerformanceConfiguration)Context.RequestData.ReadInt32();
return 0;
}
public long GetPerformanceConfiguration(ServiceCtx Context)
{
PerformanceMode PerfMode = (PerformanceMode)Context.RequestData.ReadInt32();
Context.ResponseData.Write((uint)PerformanceConfiguration.PerformanceConfiguration1);
Logging.Stub(LogClass.ServiceApm, $"PerformanceMode = {PerfMode}, PerformanceConfiguration =" +
$" {PerformanceConfiguration.PerformanceConfiguration1}");
return 0;
}

View file

@ -0,0 +1,18 @@
namespace Ryujinx.Core.OsHle.Services.Apm
{
enum PerformanceConfiguration : uint
{
PerformanceConfiguration1 = 0x00010000,
PerformanceConfiguration2 = 0x00010001,
PerformanceConfiguration3 = 0x00010002,
PerformanceConfiguration4 = 0x00020000,
PerformanceConfiguration5 = 0x00020001,
PerformanceConfiguration6 = 0x00020002,
PerformanceConfiguration7 = 0x00020003,
PerformanceConfiguration8 = 0x00020004,
PerformanceConfiguration9 = 0x00020005,
PerformanceConfiguration10 = 0x00020006,
PerformanceConfiguration11 = 0x92220007,
PerformanceConfiguration12 = 0x92220008
}
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Core.OsHle.Services.Apm
{
enum PerformanceMode
{
Handheld = 0,
Docked = 1
}
}

View file

@ -5,13 +5,13 @@ using System.Text;
namespace Ryujinx.Core.OsHle.Services.Aud
{
class IAudioDevice : IpcService
class IAudioDeviceService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IAudioDevice()
public IAudioDeviceService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
@ -57,6 +57,8 @@ namespace Ryujinx.Core.OsHle.Services.Aud
string Name = AMemoryHelper.ReadAsciiString(Context.Memory, Position, Size);
Logging.Stub(LogClass.ServiceAudio, $"Volume = {Volume}, Position = {Position}, Size = {Size}");
return 0;
}
}

View file

@ -124,14 +124,14 @@ namespace Ryujinx.Core.OsHle.Services.Aud
public long AppendAudioOutBufferEx(ServiceCtx Context)
{
Logging.Warn("Not implemented!");
Logging.Stub(LogClass.ServiceAudio, "Stubbed");
return 0;
}
public long GetReleasedAudioOutBufferEx(ServiceCtx Context)
{
Logging.Warn("Not implemented!");
Logging.Stub(LogClass.ServiceAudio, "Stubbed");
return 0;
}

View file

@ -7,13 +7,13 @@ using System.Text;
namespace Ryujinx.Core.OsHle.Services.Aud
{
class ServiceAudOut : IpcService
class IAudioOutManager : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceAudOut()
public IAudioOutManager()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{

View file

@ -54,11 +54,15 @@ namespace Ryujinx.Core.OsHle.Services.Aud
public long StartAudioRenderer(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAudio, "Stubbed");
return 0;
}
public long StopAudioRenderer(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceAudio, "Stubbed");
return 0;
}

View file

@ -3,13 +3,13 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Aud
{
class ServiceAudRen : IpcService
class IAudioRendererManager : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceAudRen()
public IAudioRendererManager()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
@ -42,6 +42,8 @@ namespace Ryujinx.Core.OsHle.Services.Aud
int Unknown2c = Context.RequestData.ReadInt32();
int Rev1Magic = Context.RequestData.ReadInt32();
Logging.Stub(LogClass.ServiceAudio, "BufferSize = 0x400L");
Context.ResponseData.Write(0x400L);
return 0;
@ -51,7 +53,7 @@ namespace Ryujinx.Core.OsHle.Services.Aud
{
long UserId = Context.RequestData.ReadInt64();
MakeObject(Context, new IAudioDevice());
MakeObject(Context, new IAudioDeviceService());
return 0;
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Core.OsHle.Services.Bsd
{
//bsd_errno == (SocketException.ErrorCode - 10000)
public enum BsdError
{
Timeout = 60
}
}

View file

@ -0,0 +1,18 @@
using System.Net;
using System.Net.Sockets;
namespace Ryujinx.Core.OsHle.Services.Bsd
{
class BsdSocket
{
public int Family;
public int Type;
public int Protocol;
public IPAddress IpAddress;
public IPEndPoint RemoteEP;
public Socket Handle;
}
}

View file

@ -1,7 +1,6 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.OsHle.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
@ -10,56 +9,15 @@ using System.Threading.Tasks;
namespace Ryujinx.Core.OsHle.Services.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 : IpcService
class IClient : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private List<SocketBsd> Sockets = new List<SocketBsd>();
private List<BsdSocket> Sockets = new List<BsdSocket>();
public ServiceBsd()
public IClient()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
@ -95,11 +53,6 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
} BsdBufferConfig;
*/
long Pid = Context.RequestData.ReadInt64();
long TransferMemorySize = Context.RequestData.ReadInt64();
// Two other args are unknown!
Context.ResponseData.Write(0);
//Todo: Stub
@ -118,18 +71,18 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
//(u32 domain, u32 type, u32 protocol) -> (i32 ret, u32 bsd_errno)
public long Socket(ServiceCtx Context)
{
SocketBsd NewBSDSocket = new SocketBsd
BsdSocket NewBsdSocket = new BsdSocket
{
Family = Context.RequestData.ReadInt32(),
Type = Context.RequestData.ReadInt32(),
Protocol = Context.RequestData.ReadInt32()
};
Sockets.Add(NewBSDSocket);
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);
NewBsdSocket.Handle = new Socket((AddressFamily)NewBsdSocket.Family,
(SocketType)NewBsdSocket.Type,
(ProtocolType)NewBsdSocket.Protocol);
Context.ResponseData.Write(Sockets.Count - 1);
Context.ResponseData.Write(0);
@ -149,12 +102,13 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
//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,
Context.Request.SendBuff[0].Size);
int SocketId = Get32(SentBuffer, 0);
short RequestedEvents = (short)Get16(SentBuffer, 4);
short ReturnedEvents = (short)Get16(SentBuffer, 6);
byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
Context.Request.SendBuff[0].Size);
int SocketId = Get32(SentBuffer, 0);
int RequestedEvents = Get16(SentBuffer, 4);
int ReturnedEvents = Get16(SentBuffer, 6);
//Todo: Stub - Need to implemented the Type-22 buffer.
@ -167,18 +121,20 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
//(u32 socket, u32 flags) -> (i32 ret, u32 bsd_errno, buffer<i8, 0x22, 0> message)
public long Recv(ServiceCtx Context)
{
int SocketId = Context.RequestData.ReadInt32();
int SocketFlags = Context.RequestData.ReadInt32();
byte[] ReceivedBuffer = new byte[Context.Request.ReceiveBuff[0].Size];
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);
int BytesRead = 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(BytesRead);
Context.ResponseData.Write(0);
}
catch (SocketException Ex)
@ -193,15 +149,16 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
//(u32 socket, u32 flags, buffer<i8, 0x21, 0>) -> (i32 ret, u32 bsd_errno)
public long Send(ServiceCtx Context)
{
int SocketId = Context.RequestData.ReadInt32();
int SocketFlags = Context.RequestData.ReadInt32();
byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
int SocketId = Context.RequestData.ReadInt32();
int SocketFlags = Context.RequestData.ReadInt32();
byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
Context.Request.SendBuff[0].Size);
try
{
//Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer));
//Logging.Debug("Sent Buffer:" + Environment.NewLine + Logging.HexDump(SentBuffer));
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
@ -220,13 +177,15 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
//(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,
Context.Request.SendBuff[0].Size);
byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[1].Position,
int SocketId = Context.RequestData.ReadInt32();
int SocketFlags = Context.RequestData.ReadInt32();
byte[] SentBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
Context.Request.SendBuff[0].Size);
byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[1].Position,
Context.Request.SendBuff[1].Size);
if (!Sockets[SocketId].Handle.Connected)
@ -246,7 +205,7 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
try
{
//Logging.Debug("Sended Buffer:" + Environment.NewLine + Logging.HexDump(SendedBuffer));
//Logging.Debug("Sent Buffer:" + Environment.NewLine + Logging.HexDump(SentBuffer));
int BytesSent = Sockets[SocketId].Handle.Send(SentBuffer);
@ -265,12 +224,13 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
//(u32 socket) -> (i32 ret, u32 bsd_errno, u32 addrlen, buffer<sockaddr, 0x22, 0> addr)
public long Accept(ServiceCtx Context)
{
int SocketId = Context.RequestData.ReadInt32();
int SocketId = Context.RequestData.ReadInt32();
long AddrBufferPtr = Context.Request.ReceiveBuff[0].Position;
Socket HandleAccept = null;
var TimeOut = Task.Factory.StartNew(() =>
Task TimeOut = Task.Factory.StartNew(() =>
{
try
{
@ -287,28 +247,28 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
if (HandleAccept != null)
{
SocketBsd NewBSDSocket = new SocketBsd
BsdSocket NewBsdSocket = new BsdSocket
{
IpAddress = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint).Address,
RemoteEP = ((IPEndPoint)Sockets[SocketId].Handle.LocalEndPoint),
Handle = HandleAccept
};
Sockets.Add(NewBSDSocket);
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]));
Writer.Write((byte)NewBsdSocket.Handle.AddressFamily);
Writer.Write((short)((IPEndPoint)NewBsdSocket.Handle.LocalEndPoint).Port);
byte[] IpAddress = NewBsdSocket.IpAddress.GetAddressBytes();
Writer.Write(IpAddress);
AMemoryHelper.WriteBytes(Context.Memory, AddrBufferPtr, MS.ToArray());
@ -320,7 +280,7 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
else
{
Context.ResponseData.Write(-1);
Context.ResponseData.Write((int)BsdError.ETIMEDOUT);
Context.ResponseData.Write((int)BsdError.Timeout);
}
return 0;
@ -331,8 +291,8 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
{
int SocketId = Context.RequestData.ReadInt32();
byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
Context.Request.SendBuff[0].Size);
try
@ -356,8 +316,8 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
{
int SocketId = Context.RequestData.ReadInt32();
byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
byte[] AddressBuffer = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.SendBuff[0].Position,
Context.Request.SendBuff[0].Size);
try
@ -404,19 +364,20 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
//(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();
int SocketId = Context.RequestData.ReadInt32();
byte[] SocketOptionValue = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.PtrBuff[0].Position,
SocketOptionLevel SocketLevel = (SocketOptionLevel)Context.RequestData.ReadInt32();
SocketOptionName SocketOptionName = (SocketOptionName)Context.RequestData.ReadInt32();
byte[] SocketOptionValue = AMemoryHelper.ReadBytes(Context.Memory,
Context.Request.PtrBuff[0].Position,
Context.Request.PtrBuff[0].Size);
int OptionValue = Get32(SocketOptionValue, 0);
try
{
Sockets[SocketId].Handle.SetSocketOption((SocketOptionLevel)SocketLevel,
(SocketOptionName)SocketOptionName,
Get32(SocketOptionValue, 0));
Sockets[SocketId].Handle.SetSocketOption(SocketLevel, SocketOptionName, OptionValue);
Context.ResponseData.Write(0);
Context.ResponseData.Write(0);
@ -461,12 +422,13 @@ namespace Ryujinx.Core.OsHle.Services.Bsd
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}");
string IpAddress = Reader.ReadByte().ToString() + "." +
Reader.ReadByte().ToString() + "." +
Reader.ReadByte().ToString() + "." +
Reader.ReadByte().ToString();
Logging.Debug(LogClass.ServiceBsd, $"Try to connect to {IpAddress}:{Port}");
Sockets[SocketId].IpAddress = IPAddress.Parse(IpAddress);
Sockets[SocketId].RemoteEP = new IPEndPoint(Sockets[SocketId].IpAddress, Port);

View file

@ -3,13 +3,13 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Friend
{
class ServiceFriend : IpcService
class IServiceCreator : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceFriend()
public IServiceCreator()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{

View file

@ -3,38 +3,38 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.FspSrv
{
class ServiceFspSrv : IpcService
class IFileSystemProxy : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceFspSrv()
public IFileSystemProxy()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, Initialize },
{ 18, MountSdCard },
{ 51, MountSaveData },
{ 200, OpenDataStorageByCurrentProcess },
{ 203, OpenRomStorage },
{ 1005, GetGlobalAccessLogMode }
{ 1, SetCurrentProcess },
{ 18, OpenSdCardFileSystem },
{ 51, OpenSaveDataFileSystem },
{ 200, OpenDataStorageByCurrentProcess },
{ 203, OpenPatchDataStorageByCurrentProcess },
{ 1005, GetGlobalAccessLogMode }
};
}
public long Initialize(ServiceCtx Context)
public long SetCurrentProcess(ServiceCtx Context)
{
return 0;
}
public long MountSdCard(ServiceCtx Context)
public long OpenSdCardFileSystem(ServiceCtx Context)
{
MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetSdCardPath()));
return 0;
}
public long MountSaveData(ServiceCtx Context)
public long OpenSaveDataFileSystem(ServiceCtx Context)
{
MakeObject(Context, new IFileSystem(Context.Ns.VFs.GetGameSavesPath()));
@ -48,7 +48,7 @@ namespace Ryujinx.Core.OsHle.Services.FspSrv
return 0;
}
public long OpenRomStorage(ServiceCtx Context)
public long OpenPatchDataStorageByCurrentProcess(ServiceCtx Context)
{
MakeObject(Context, new IStorage(Context.Ns.VFs.RomFs));
@ -60,6 +60,6 @@ namespace Ryujinx.Core.OsHle.Services.FspSrv
Context.ResponseData.Write(0);
return 0;
}
}
}
}

View file

@ -4,29 +4,37 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Hid
{
class ServiceHid : IpcService
class IHidServer : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceHid()
public IHidServer()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, CreateAppletResource },
{ 1, ActivateDebugPad },
{ 11, ActivateTouchScreen },
{ 21, ActivateMouse },
{ 31, ActivateKeyboard },
{ 66, StartSixAxisSensor },
{ 79, SetGyroscopeZeroDriftMode },
{ 100, SetSupportedNpadStyleSet },
{ 101, GetSupportedNpadStyleSet },
{ 102, SetSupportedNpadIdType },
{ 103, ActivateNpad },
{ 108, GetPlayerLedPattern },
{ 120, SetNpadJoyHoldType },
{ 121, GetNpadJoyHoldType },
{ 122, SetNpadJoyAssignmentModeSingleByDefault },
{ 123, SetNpadJoyAssignmentModeSingle },
{ 124, SetNpadJoyAssignmentModeDual },
{ 125, MergeSingleJoyAsDualJoy },
{ 128, SetNpadHandheldActivationMode },
{ 200, GetVibrationDeviceInfo },
{ 201, SendVibrationValue },
{ 203, CreateActiveVibrationDeviceList },
{ 206, SendVibrationValues }
};
@ -39,9 +47,36 @@ namespace Ryujinx.Core.OsHle.Services.Hid
return 0;
}
public long ActivateDebugPad(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
public long ActivateTouchScreen(ServiceCtx Context)
{
long Unknown = Context.RequestData.ReadInt64();
long AppletResourceUserId = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
public long ActivateMouse(ServiceCtx Context)
{
long AppletResourceUserId = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
public long ActivateKeyboard(ServiceCtx Context)
{
long AppletResourceUserId = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
@ -52,6 +87,19 @@ namespace Ryujinx.Core.OsHle.Services.Hid
long AppletResourceUserId = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
public long SetGyroscopeZeroDriftMode(ServiceCtx Context)
{
int Handle = Context.RequestData.ReadInt32();
int Unknown = Context.RequestData.ReadInt32();
long AppletResourceUserId = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
@ -59,6 +107,8 @@ namespace Ryujinx.Core.OsHle.Services.Hid
{
Context.ResponseData.Write(0);
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
@ -67,6 +117,8 @@ namespace Ryujinx.Core.OsHle.Services.Hid
long Unknown0 = Context.RequestData.ReadInt64();
long Unknown8 = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
@ -74,6 +126,8 @@ namespace Ryujinx.Core.OsHle.Services.Hid
{
long Unknown = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
@ -81,6 +135,19 @@ namespace Ryujinx.Core.OsHle.Services.Hid
{
long Unknown = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
public long GetPlayerLedPattern(ServiceCtx Context)
{
long Unknown = Context.RequestData.ReadInt32();
Context.ResponseData.Write(0L);
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
@ -89,6 +156,8 @@ namespace Ryujinx.Core.OsHle.Services.Hid
long Unknown0 = Context.RequestData.ReadInt64();
long Unknown8 = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
@ -96,13 +165,17 @@ namespace Ryujinx.Core.OsHle.Services.Hid
{
Context.ResponseData.Write(0L);
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
public long SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx Context)
{
HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32();
long AppletUserResourseId = Context.RequestData.ReadInt64();
long AppletUserResourceId = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
@ -110,16 +183,20 @@ namespace Ryujinx.Core.OsHle.Services.Hid
public long SetNpadJoyAssignmentModeSingle(ServiceCtx Context)
{
HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32();
long AppletUserResourseId = Context.RequestData.ReadInt64();
long AppletUserResourceId = Context.RequestData.ReadInt64();
long NpadJoyDeviceType = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
public long SetNpadJoyAssignmentModeDual(ServiceCtx Context)
{
HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32();
long AppletUserResourseId = Context.RequestData.ReadInt64();
long AppletUserResourceId = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
@ -128,7 +205,19 @@ namespace Ryujinx.Core.OsHle.Services.Hid
{
long Unknown0 = Context.RequestData.ReadInt32();
long Unknown8 = Context.RequestData.ReadInt32();
long AppletUserResourseId = Context.RequestData.ReadInt64();
long AppletUserResourceId = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
public long SetNpadHandheldActivationMode(ServiceCtx Context)
{
long AppletUserResourceId = Context.RequestData.ReadInt64();
long Unknown = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
@ -137,11 +226,29 @@ namespace Ryujinx.Core.OsHle.Services.Hid
{
int VibrationDeviceHandle = Context.RequestData.ReadInt32();
Logging.Stub(LogClass.ServiceHid, $"VibrationDeviceHandle = {VibrationDeviceHandle}, VibrationDeviceInfo = 0");
Context.ResponseData.Write(0L); //VibrationDeviceInfoForIpc
return 0;
}
public long SendVibrationValue(ServiceCtx Context)
{
int VibrationDeviceHandle = Context.RequestData.ReadInt32();
int VibrationValue1 = Context.RequestData.ReadInt32();
int VibrationValue2 = Context.RequestData.ReadInt32();
int VibrationValue3 = Context.RequestData.ReadInt32();
int VibrationValue4 = Context.RequestData.ReadInt32();
long AppletUserResourceId = Context.RequestData.ReadInt64();
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
public long CreateActiveVibrationDeviceList(ServiceCtx Context)
{
MakeObject(Context, new IActiveApplicationDeviceList());
@ -151,7 +258,9 @@ namespace Ryujinx.Core.OsHle.Services.Hid
public long SendVibrationValues(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceHid, "Stubbed");
return 0;
}
}
}
}

View file

@ -14,7 +14,7 @@ namespace Ryujinx.Core.OsHle.Services
private int SelfId;
private bool IsDomain;
private bool IsDomain;
public IpcService()
{
@ -81,7 +81,7 @@ namespace Ryujinx.Core.OsHle.Services
{
Context.ResponseData.BaseStream.Seek(IsDomain ? 0x20 : 0x10, SeekOrigin.Begin);
Logging.Trace($"{Service.GetType().Name}: {ProcessRequest.Method.Name}");
Logging.Trace(LogClass.KernelIpc, $"{Service.GetType().Name}: {ProcessRequest.Method.Name}");
long Result = ProcessRequest(Context);
@ -104,7 +104,9 @@ namespace Ryujinx.Core.OsHle.Services
}
else
{
throw new NotImplementedException($"{Service.GetType().Name}: {CommandId}");
string DbgMessage = $"{Context.Session.ServiceName} {Service.GetType().Name}: {CommandId}";
throw new NotImplementedException(DbgMessage);
}
}
@ -118,7 +120,7 @@ namespace Ryujinx.Core.OsHle.Services
}
else
{
KSession Session = new KSession(Obj);
KSession Session = new KSession(Obj, Context.Session.ServiceName);
int Handle = Context.Process.HandleTable.OpenHandle(Session);

View file

@ -3,13 +3,13 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Lm
{
class ServiceLm : IpcService
class ILogService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceLm()
public ILogService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{

View file

@ -129,11 +129,11 @@ namespace Ryujinx.Core.OsHle.Services.Lm
switch((Severity)iSeverity)
{
case Severity.Trace: Logging.Trace(LogString); break;
case Severity.Info: Logging.Info(LogString); break;
case Severity.Warning: Logging.Warn(LogString); break;
case Severity.Error: Logging.Error(LogString); break;
case Severity.Critical: Logging.Fatal(LogString); break;
case Severity.Trace: Logging.Trace(LogClass.ServiceLm, LogString); break;
case Severity.Info: Logging.Info(LogClass.ServiceLm, LogString); break;
case Severity.Warning: Logging.Warn(LogClass.ServiceLm, LogString); break;
case Severity.Error: Logging.Error(LogClass.ServiceLm, LogString); break;
case Severity.Critical: Logging.Fatal(LogClass.ServiceLm, LogString); break;
}
return 0;

View file

@ -24,7 +24,7 @@ namespace Ryujinx.Core.OsHle.Services.Nifm
MakeObject(Context, new IRequest());
//Todo: Stub
Logging.Stub(LogClass.ServiceNifm, "Stubbed");
return 0;
}

View file

@ -19,7 +19,9 @@ namespace Ryujinx.Core.OsHle.Services.Nifm
{
{ 0, GetRequestState },
{ 1, GetResult },
{ 2, GetSystemEventReadableHandles }
{ 2, GetSystemEventReadableHandles },
{ 3, Cancel },
{ 4, Submit },
};
Event = new KEvent();
@ -29,14 +31,14 @@ namespace Ryujinx.Core.OsHle.Services.Nifm
{
Context.ResponseData.Write(0);
//Todo: Stub
Logging.Stub(LogClass.ServiceNifm, "Stubbed");
return 0;
}
public long GetResult(ServiceCtx Context)
{
//Todo: Stub
Logging.Stub(LogClass.ServiceNifm, "Stubbed");
return 0;
}
@ -52,6 +54,20 @@ namespace Ryujinx.Core.OsHle.Services.Nifm
return 0;
}
public long Cancel(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceNifm, "Stubbed");
return 0;
}
public long Submit(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceNifm, "Stubbed");
return 0;
}
public void Dispose()
{
Dispose(true);

View file

@ -3,13 +3,13 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Nifm
{
class ServiceNifm : IpcService
class IStaticService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceNifm()
public IStaticService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{

View file

@ -0,0 +1,41 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Ns
{
class IAddOnContentManager : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IAddOnContentManager()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 2, CountAddOnContent },
{ 3, ListAddOnContent }
};
}
public static long CountAddOnContent(ServiceCtx Context)
{
Context.ResponseData.Write(0);
Logging.Stub(LogClass.ServiceNs, "Stubbed");
return 0;
}
public static long ListAddOnContent(ServiceCtx Context)
{
Logging.Stub(LogClass.ServiceNs, "Stubbed");
//TODO: This is supposed to write a u32 array aswell.
//It's unknown what it contains.
Context.ResponseData.Write(0);
return 0;
}
}
}

View file

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

View file

@ -5,10 +5,11 @@ using Ryujinx.Core.OsHle.Utilities;
using Ryujinx.Graphics.Gpu;
using System;
using System.Collections.Generic;
using System.IO;
namespace Ryujinx.Core.OsHle.Services.Nv
{
class ServiceNvDrv : IpcService, IDisposable
class INvDrvServices : IpcService, IDisposable
{
private delegate long ServiceProcessIoctl(ServiceCtx Context);
@ -26,7 +27,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
private KEvent Event;
public ServiceNvDrv()
public INvDrvServices()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
@ -45,10 +46,12 @@ namespace Ryujinx.Core.OsHle.Services.Nv
{ ("/dev/nvhost-as-gpu", 0x4106), NvGpuAsIoctlMapBufferEx },
{ ("/dev/nvhost-as-gpu", 0x4108), NvGpuAsIoctlGetVaRegions },
{ ("/dev/nvhost-as-gpu", 0x4109), NvGpuAsIoctlInitializeEx },
{ ("/dev/nvhost-as-gpu", 0x4114), NvGpuAsIoctlRemap },
{ ("/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", 0x4703), NvGpuIoctlZbcSetTable },
{ ("/dev/nvhost-ctrl-gpu", 0x4705), NvGpuIoctlGetCharacteristics },
{ ("/dev/nvhost-ctrl-gpu", 0x4706), NvGpuIoctlGetTpcMasks },
{ ("/dev/nvhost-ctrl-gpu", 0x4714), NvGpuIoctlZbcGetActiveSlotMask },
@ -71,7 +74,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
Event = new KEvent();
}
static ServiceNvDrv()
static INvDrvServices()
{
Fds = new GlobalStateTable();
@ -98,7 +101,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
{
int Fd = Context.RequestData.ReadInt32();
int Cmd = Context.RequestData.ReadInt32() & 0xffff;
NvFd FdData = Fds.GetData<NvFd>(Context.Process, Fd);
long Position = Context.Request.GetSendBuffPtr();
@ -206,7 +209,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
int Flags = Reader.ReadInt32();
int Kind = Reader.ReadInt32();
int Handle = Reader.ReadInt32();
int PageSize = Reader.ReadInt32();
int PageSize = Reader.ReadInt32();
long BuffAddr = Reader.ReadInt64();
long MapSize = Reader.ReadInt64();
long Offset = Reader.ReadInt64();
@ -225,8 +228,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
@ -241,6 +244,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
Context.Memory.WriteInt64(Position + 0x20, Offset);
Map.GpuAddress = Offset;
return 0;
}
@ -291,6 +296,35 @@ namespace Ryujinx.Core.OsHle.Services.Nv
return 0;
}
private long NvGpuAsIoctlRemap(ServiceCtx Context)
{
Context.RequestData.BaseStream.Seek(-4, SeekOrigin.Current);
int Cmd = Context.RequestData.ReadInt32();
int Size = (Cmd >> 16) & 0xff;
int Count = Size / 0x18;
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
for (int Index = 0; Index < Count; Index++)
{
int Flags = Reader.ReadInt32();
int Kind = Reader.ReadInt32();
int Handle = Reader.ReadInt32();
int Padding = Reader.ReadInt32();
int Offset = Reader.ReadInt32();
int Pages = Reader.ReadInt32();
}
//TODO
return 0;
}
private long NvHostIoctlCtrlGetConfig(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
@ -351,6 +385,32 @@ namespace Ryujinx.Core.OsHle.Services.Nv
return 0;
}
private long NvGpuIoctlZbcSetTable(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
MemReader Reader = new MemReader(Context.Memory, Position);
int[] ColorDs = new int[4];
int[] ColorL2 = new int[4];
ColorDs[0] = Reader.ReadInt32();
ColorDs[1] = Reader.ReadInt32();
ColorDs[2] = Reader.ReadInt32();
ColorDs[3] = Reader.ReadInt32();
ColorL2[0] = Reader.ReadInt32();
ColorL2[1] = Reader.ReadInt32();
ColorL2[2] = Reader.ReadInt32();
ColorL2[3] = Reader.ReadInt32();
int Depth = Reader.ReadInt32();
int Format = Reader.ReadInt32();
int Type = Reader.ReadInt32();
return 0;
}
private long NvGpuIoctlGetCharacteristics(ServiceCtx Context)
{
long Position = Context.Request.GetSendBuffPtr();
@ -480,9 +540,9 @@ namespace Ryujinx.Core.OsHle.Services.Nv
{
byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, CpuAddr, Size);
NsGpuPBEntry[] PushBuffer = NsGpuPBEntry.DecodePushBuffer(Data);
NsGpuPBEntry[] PushBuffer = NvGpuPushBuffer.Decode(Data);
Context.Ns.Gpu.ProcessPushBuffer(PushBuffer, Context.Memory);
Context.Ns.Gpu.Fifo.PushBuffer(Context.Memory, PushBuffer);
}
}
@ -574,7 +634,7 @@ namespace Ryujinx.Core.OsHle.Services.Nv
Context.Memory.WriteInt32(Position + 4, Map.Handle);
Logging.Info($"NvMap {Map.Id} created with size {Size:x8}!");
Logging.Info(LogClass.ServiceNv, $"NvMap {Map.Id} created with size {Size:x8}!");
return 0;
}
@ -589,8 +649,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Id {Id}!");
Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Id {Id}!");
return -1; //TODO: Corrent error code.
}
@ -616,14 +676,14 @@ namespace Ryujinx.Core.OsHle.Services.Nv
if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
Map.CpuAddress = Addr;
Map.Align = Align;
Map.Kind = Kind;
Map.Align = Align;
Map.Kind = Kind;
return 0;
}
@ -642,8 +702,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
@ -667,8 +727,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}
@ -697,8 +757,8 @@ namespace Ryujinx.Core.OsHle.Services.Nv
if (Map == null)
{
Logging.Warn($"Trying to use invalid NvMap Handle {Handle}!");
Logging.Warn(LogClass.ServiceNv, $"Trying to use invalid NvMap Handle {Handle}!");
return -1; //TODO: Corrent error code.
}

View file

@ -8,5 +8,6 @@ namespace Ryujinx.Core.OsHle.Services.Nv
public int Align;
public int Kind;
public long CpuAddress;
public long GpuAddress;
}
}

View file

@ -3,13 +3,13 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Pctl
{
class ServicePctl : IpcService
class IParentalControlServiceFactory : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServicePctl()
public IParentalControlServiceFactory()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{

View file

@ -3,13 +3,13 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Pl
{
class ServicePl : IpcService
class ISharedFontManager : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServicePl()
public ISharedFontManager()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{

View file

@ -7,7 +7,6 @@ using Ryujinx.Core.OsHle.Services.Friend;
using Ryujinx.Core.OsHle.Services.FspSrv;
using Ryujinx.Core.OsHle.Services.Hid;
using Ryujinx.Core.OsHle.Services.Lm;
using Ryujinx.Core.OsHle.Services.Nifm;
using Ryujinx.Core.OsHle.Services.Ns;
using Ryujinx.Core.OsHle.Services.Nv;
using Ryujinx.Core.OsHle.Services.Pctl;
@ -16,7 +15,6 @@ using Ryujinx.Core.OsHle.Services.Set;
using Ryujinx.Core.OsHle.Services.Sfdnsres;
using Ryujinx.Core.OsHle.Services.Sm;
using Ryujinx.Core.OsHle.Services.Ssl;
using Ryujinx.Core.OsHle.Services.Time;
using Ryujinx.Core.OsHle.Services.Vi;
using System;
@ -29,88 +27,88 @@ namespace Ryujinx.Core.OsHle.Services
switch (Name)
{
case "acc:u0":
return new ServiceAcc();
return new IAccountServiceForApplication();
case "aoc:u":
return new ServiceNs();
return new IAddOnContentManager();
case "apm":
return new ServiceApm();
return new IManager();
case "apm:p":
return new ServiceApm();
return new IManager();
case "appletOE":
return new ServiceAppletOE();
return new IApplicationProxyService();
case "audout:u":
return new ServiceAudOut();
return new IAudioOutManager();
case "audren:u":
return new ServiceAudRen();
return new IAudioRendererManager();
case "bsd:s":
return new ServiceBsd();
return new IClient();
case "bsd:u":
return new ServiceBsd();
return new IClient();
case "friend:a":
return new ServiceFriend();
return new IServiceCreator();
case "fsp-srv":
return new ServiceFspSrv();
return new IFileSystemProxy();
case "hid":
return new ServiceHid();
return new IHidServer();
case "lm":
return new ServiceLm();
return new ILogService();
case "nifm:u":
return new ServiceNifm();
return new Nifm.IStaticService();
case "nvdrv":
return new ServiceNvDrv();
return new INvDrvServices();
case "nvdrv:a":
return new ServiceNvDrv();
return new INvDrvServices();
case "pctl:a":
return new ServicePctl();
return new IParentalControlServiceFactory();
case "pl:u":
return new ServicePl();
return new ISharedFontManager();
case "set":
return new ServiceSet();
return new ISettingsServer();
case "set:sys":
return new ServiceSetSys();
return new ISystemSettingsServer();
case "sfdnsres":
return new ServiceSfdnsres();
return new IResolver();
case "sm:":
return new ServiceSm();
return new IUserInterface();
case "ssl":
return new ServiceSsl();
return new ISslService();
case "time:s":
return new ServiceTime();
return new Time.IStaticService();
case "time:u":
return new ServiceTime();
return new Time.IStaticService();
case "vi:m":
return new ServiceVi();
return new IManagerRootService();
case "vi:s":
return new ServiceVi();
return new ISystemRootService();
case "vi:u":
return new ServiceVi();
return new IApplicationRootService();
}
throw new NotImplementedException(Name);

View file

@ -0,0 +1,81 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Set
{
class ISettingsServer : IpcService
{
private static string[] LanguageCodes = new string[]
{
"ja",
"en-US",
"fr",
"de",
"it",
"es",
"zh-CN",
"ko",
"nl",
"pt",
"ru",
"zh-TW",
"en-GB",
"fr-CA",
"es-419",
"zh-Hans",
"zh-Hant"
};
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ISettingsServer()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, GetAvailableLanguageCodes },
{ 3, GetAvailableLanguageCodeCount }
};
}
public static long GetAvailableLanguageCodes(ServiceCtx Context)
{
long Position = Context.Request.RecvListBuff[0].Position;
short Size = Context.Request.RecvListBuff[0].Size;
int Count = (int)((uint)Size / 8);
if (Count > LanguageCodes.Length)
{
Count = LanguageCodes.Length;
}
for (int Index = 0; Index < Count; Index++)
{
string LanguageCode = LanguageCodes[Index];
foreach (char Chr in LanguageCode)
{
Context.Memory.WriteByte(Position++, (byte)Chr);
}
for (int Offs = 0; Offs < (8 - LanguageCode.Length); Offs++)
{
Context.Memory.WriteByte(Position++, 0);
}
}
Context.ResponseData.Write(Count);
return 0;
}
public static long GetAvailableLanguageCodeCount(ServiceCtx Context)
{
Context.ResponseData.Write(LanguageCodes.Length);
return 0;
}
}
}

View file

@ -1,15 +1,16 @@
using Ryujinx.Core.OsHle.Ipc;
using Ryujinx.Core.Settings;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Set
{
class ServiceSetSys : IpcService
class ISystemSettingsServer : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceSetSys()
public ISystemSettingsServer()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
@ -26,7 +27,10 @@ namespace Ryujinx.Core.OsHle.Services.Set
}
public static long SetColorSetId(ServiceCtx Context)
{
{
int ColorSetId = Context.RequestData.ReadInt32();
Context.Ns.Settings.ThemeColor = (ColorSet)ColorSetId;
return 0;
}
}

View file

@ -1,45 +0,0 @@
using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Ipc;
using System;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Set
{
class ServiceSet : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceSet()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, GetAvailableLanguageCodes }
};
}
private const int LangCodesCount = 13;
public static long GetAvailableLanguageCodes(ServiceCtx Context)
{
int PtrBuffSize = Context.RequestData.ReadInt32();
if (Context.Request.RecvListBuff.Count > 0)
{
long Position = Context.Request.RecvListBuff[0].Position;
short Size = Context.Request.RecvListBuff[0].Size;
//This should return an array of ints with values matching the LanguageCode enum.
foreach (long value in new long[] { 0L, 1L, 2L, 3L, 4L, 5L, 6L, 7L })
{
AMemoryHelper.WriteBytes(Context.Memory, Position += 8, BitConverter.GetBytes(value));
}
}
Context.ResponseData.Write(LangCodesCount);
return 0;
}
}
}

View file

@ -3,17 +3,17 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Sfdnsres
{
class ServiceSfdnsres : IpcService
class IResolver : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceSfdnsres()
public IResolver()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//{ 0, Function }
//...
};
}
}

View file

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Sm
{
class ServiceSm : IpcService
class IUserInterface : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
@ -12,7 +12,7 @@ namespace Ryujinx.Core.OsHle.Services.Sm
private bool IsInitialized;
public ServiceSm()
public IUserInterface()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
@ -57,7 +57,7 @@ namespace Ryujinx.Core.OsHle.Services.Sm
return 0;
}
KSession Session = new KSession(ServiceFactory.MakeService(Name));
KSession Session = new KSession(ServiceFactory.MakeService(Name), Name);
int Handle = Context.Process.HandleTable.OpenHandle(Session);

View file

@ -3,17 +3,17 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Ssl
{
class ServiceSsl : IpcService
class ISslService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceSsl()
public ISslService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
//{ 0, Function }
//...
};
}
}

View file

@ -3,13 +3,13 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Time
{
class ServiceTime : IpcService
class IStaticService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceTime()
public IStaticService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{

View file

@ -10,7 +10,7 @@ namespace Ryujinx.Core.OsHle.Services.Time
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private static DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private SystemClockType ClockType;

View file

@ -10,7 +10,7 @@ namespace Ryujinx.Core.OsHle.Services.Time
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
private static DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local);
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public ITimeZoneService()
{
@ -20,30 +20,18 @@ namespace Ryujinx.Core.OsHle.Services.Time
};
}
//(nn::time::PosixTime)-> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
public long ToCalendarTimeWithMyRule(ServiceCtx Context)
{
long PosixTime = Context.RequestData.ReadInt64();
Epoch = Epoch.AddSeconds(PosixTime).ToLocalTime();
DateTime CurrentTime = Epoch.AddSeconds(PosixTime).ToLocalTime();
/*
struct CalendarTime {
u16_le year;
u8 month; // Starts at 1
u8 day; // Starts at 1
u8 hour;
u8 minute;
u8 second;
INSERT_PADDING_BYTES(1);
};
*/
Context.ResponseData.Write((short)Epoch.Year);
Context.ResponseData.Write((byte)Epoch.Month);
Context.ResponseData.Write((byte)Epoch.Day);
Context.ResponseData.Write((byte)Epoch.Hour);
Context.ResponseData.Write((byte)Epoch.Minute);
Context.ResponseData.Write((byte)Epoch.Second);
Context.ResponseData.Write((ushort)CurrentTime.Year);
Context.ResponseData.Write((byte)CurrentTime.Month);
Context.ResponseData.Write((byte)CurrentTime.Day);
Context.ResponseData.Write((byte)CurrentTime.Hour);
Context.ResponseData.Write((byte)CurrentTime.Minute);
Context.ResponseData.Write((byte)CurrentTime.Second);
Context.ResponseData.Write((byte)0);
/* Thanks to TuxSH
@ -57,11 +45,16 @@ namespace Ryujinx.Core.OsHle.Services.Time
};
};
*/
Context.ResponseData.Write((int)Epoch.DayOfWeek);
Context.ResponseData.Write(Epoch.DayOfYear);
Context.ResponseData.Write((int)CurrentTime.DayOfWeek);
Context.ResponseData.Write(CurrentTime.DayOfYear);
//TODO: Find out the names used.
Context.ResponseData.Write(new byte[8]);
Context.ResponseData.Write(Convert.ToByte(Epoch.IsDaylightSavingTime()));
Context.ResponseData.Write(0);
Context.ResponseData.Write((byte)(CurrentTime.IsDaylightSavingTime() ? 1 : 0));
Context.ResponseData.Write((int)TimeZoneInfo.Local.GetUtcOffset(CurrentTime).TotalSeconds);
return 0;
}

View file

@ -25,6 +25,7 @@ namespace Ryujinx.Core.OsHle.Services.Vi
{ 103, GetIndirectDisplayTransactionService },
{ 1010, OpenDisplay },
{ 1020, CloseDisplay },
{ 1102, GetDisplayResolution },
{ 2020, OpenLayer },
{ 2021, CloseLayer },
{ 2030, CreateStrayLayer },
@ -84,6 +85,16 @@ namespace Ryujinx.Core.OsHle.Services.Vi
return 0;
}
public long GetDisplayResolution(ServiceCtx Context)
{
long DisplayId = Context.RequestData.ReadInt32();
Context.ResponseData.Write(1280);
Context.ResponseData.Write(720);
return 0;
}
public long OpenLayer(ServiceCtx Context)
{
long LayerId = Context.RequestData.ReadInt64();

View file

@ -0,0 +1,29 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Vi
{
class IApplicationRootService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public IApplicationRootService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, GetDisplayService }
};
}
public long GetDisplayService(ServiceCtx Context)
{
int ServiceType = Context.RequestData.ReadInt32();
MakeObject(Context, new IApplicationDisplayService());
return 0;
}
}
}

View file

@ -3,13 +3,13 @@ using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Vi
{
class ServiceVi : IpcService
class IManagerRootService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ServiceVi()
public IManagerRootService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
@ -19,7 +19,7 @@ namespace Ryujinx.Core.OsHle.Services.Vi
public long GetDisplayService(ServiceCtx Context)
{
int Unknown = Context.RequestData.ReadInt32();
int ServiceType = Context.RequestData.ReadInt32();
MakeObject(Context, new IApplicationDisplayService());

View file

@ -13,7 +13,8 @@ namespace Ryujinx.Core.OsHle.Services.Vi
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 2205, SetLayerZ }
{ 2205, SetLayerZ },
{ 2207, SetLayerVisibility }
};
}
@ -21,5 +22,10 @@ namespace Ryujinx.Core.OsHle.Services.Vi
{
return 0;
}
public static long SetLayerVisibility(ServiceCtx Context)
{
return 0;
}
}
}

View file

@ -0,0 +1,29 @@
using Ryujinx.Core.OsHle.Ipc;
using System.Collections.Generic;
namespace Ryujinx.Core.OsHle.Services.Vi
{
class ISystemRootService : IpcService
{
private Dictionary<int, ServiceProcessRequest> m_Commands;
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands;
public ISystemRootService()
{
m_Commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 1, GetDisplayService }
};
}
public long GetDisplayService(ServiceCtx Context)
{
int ServiceType = Context.RequestData.ReadInt32();
MakeObject(Context, new IApplicationDisplayService());
return 0;
}
}
}

View file

@ -2,6 +2,7 @@ using ChocolArm64.Memory;
using Ryujinx.Core.OsHle.Handles;
using Ryujinx.Core.OsHle.Services.Nv;
using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Gpu;
using System;
using System.Collections.Generic;
using System.IO;
@ -62,14 +63,8 @@ namespace Ryujinx.Core.OsHle.Services.Android
private BufferEntry[] BufferQueue;
private ManualResetEvent WaitBufferFree;
private object RenderQueueLock;
private int RenderQueueCount;
private bool NvFlingerDisposed;
private bool KeepRunning;
private bool Disposed;
public NvFlinger(IGalRenderer Renderer, KEvent ReleaseEvent)
{
@ -85,17 +80,13 @@ namespace Ryujinx.Core.OsHle.Services.Android
{ ("android.gui.IGraphicBufferProducer", 0xb), GbpDisconnect },
{ ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
};
this.Renderer = Renderer;
this.ReleaseEvent = ReleaseEvent;
BufferQueue = new BufferEntry[0x40];
WaitBufferFree = new ManualResetEvent(false);
RenderQueueLock = new object();
KeepRunning = true;
}
public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code)
@ -121,7 +112,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
if (Commands.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq))
{
Logging.Debug($"{InterfaceName} {ProcReq.Method.Name}");
Logging.Debug(LogClass.ServiceNv, $"{InterfaceName} {ProcReq.Method.Name}");
return ProcReq(Context, Reader);
}
@ -139,7 +130,7 @@ namespace Ryujinx.Core.OsHle.Services.Android
using (MemoryStream MS = new MemoryStream())
{
BinaryWriter Writer = new BinaryWriter(MS);
BufferEntry Entry = BufferQueue[Slot];
int BufferCount = 1; //?
@ -243,13 +234,17 @@ namespace Ryujinx.Core.OsHle.Services.Android
private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader)
{
int Slot = ParcelReader.ReadInt32();
int BufferCount = ParcelReader.ReadInt32();
long BufferSize = ParcelReader.ReadInt64();
BufferQueue[Slot].State = BufferState.Free;
int BufferCount = ParcelReader.ReadInt32();
BufferQueue[Slot].Data = new GbpBuffer(ParcelReader);
if (BufferCount > 0)
{
long BufferSize = ParcelReader.ReadInt64();
BufferQueue[Slot].State = BufferState.Free;
BufferQueue[Slot].Data = new GbpBuffer(ParcelReader);
}
return MakeReplyParcel(Context, 0);
}
@ -281,35 +276,24 @@ namespace Ryujinx.Core.OsHle.Services.Android
return 0;
}
private unsafe void SendFrameBuffer(ServiceCtx Context, int Slot)
private void SendFrameBuffer(ServiceCtx Context, int Slot)
{
int FbWidth = BufferQueue[Slot].Data.Width;
int FbHeight = BufferQueue[Slot].Data.Height;
long FbSize = (uint)FbWidth * FbHeight * 4;
int FbWidth = 1280;
int FbHeight = 720;
NvMap Map = GetNvMap(Context, Slot);
NvMapFb MapFb = (NvMapFb)ServiceNvDrv.NvMapsFb.GetData(Context.Process, 0);
NvMapFb MapFb = (NvMapFb)INvDrvServices.NvMapsFb.GetData(Context.Process, 0);
long CpuAddr = Map.CpuAddress;
long GpuAddr = Map.GpuAddress;
long Address = Map.CpuAddress;
if (MapFb.HasBufferOffset(Slot))
{
Address += MapFb.GetBufferOffset(Slot);
}
CpuAddr += MapFb.GetBufferOffset(Slot);
if ((ulong)(Address + FbSize) > AMemoryMgr.AddrSize)
{
Logging.Error($"Frame buffer address {Address:x16} is invalid!");
BufferQueue[Slot].State = BufferState.Free;
ReleaseEvent.Handle.Set();
WaitBufferFree.Set();
return;
//TODO: Enable once the frame buffers problems are fixed.
//GpuAddr += MapFb.GetBufferOffset(Slot);
}
BufferQueue[Slot].State = BufferState.Acquired;
@ -363,41 +347,28 @@ namespace Ryujinx.Core.OsHle.Services.Android
Rotate = -MathF.PI * 0.5f;
}
lock (RenderQueueLock)
{
if (NvFlingerDisposed)
{
return;
}
Renderer.SetFrameBufferTransform(ScaleX, ScaleY, Rotate, OffsX, OffsY);
Interlocked.Increment(ref RenderQueueCount);
//TODO: Support double buffering here aswell, it is broken for GPU
//frame buffers because it seems to be completely out of sync.
if (Context.Ns.Gpu.Engine3d.IsFrameBufferPosition(GpuAddr))
{
//Frame buffer is rendered to by the GPU, we can just
//bind the frame buffer texture, it's not necessary to read anything.
Renderer.SetFrameBuffer(GpuAddr);
}
else
{
//Frame buffer is not set on the GPU registers, in this case
//assume that the app is manually writing to it.
Texture Texture = new Texture(CpuAddr, FbWidth, FbHeight);
byte[] Data = TextureReader.Read(Context.Memory, Texture);
Renderer.SetFrameBuffer(Data, FbWidth, FbHeight);
}
byte* Fb = (byte*)Context.Memory.Ram + Address;
Context.Ns.Gpu.Renderer.QueueAction(delegate()
{
Context.Ns.Gpu.Renderer.SetFrameBuffer(
Fb,
FbWidth,
FbHeight,
ScaleX,
ScaleY,
OffsX,
OffsY,
Rotate);
BufferQueue[Slot].State = BufferState.Free;
Interlocked.Decrement(ref RenderQueueCount);
ReleaseEvent.Handle.Set();
lock (WaitBufferFree)
{
WaitBufferFree.Set();
}
});
Context.Ns.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot));
}
private NvMap GetNvMap(ServiceCtx Context, int Slot)
@ -413,7 +384,19 @@ namespace Ryujinx.Core.OsHle.Services.Android
NvMapHandle = BitConverter.ToInt32(RawValue, 0);
}
return ServiceNvDrv.NvMaps.GetData<NvMap>(Context.Process, NvMapHandle);
return INvDrvServices.NvMaps.GetData<NvMap>(Context.Process, NvMapHandle);
}
private void ReleaseBuffer(int Slot)
{
BufferQueue[Slot].State = BufferState.Free;
ReleaseEvent.Handle.Set();
lock (WaitBufferFree)
{
WaitBufferFree.Set();
}
}
private int GetFreeSlotBlocking(int Width, int Height)
@ -429,9 +412,9 @@ namespace Ryujinx.Core.OsHle.Services.Android
break;
}
Logging.Debug("Waiting for a free BufferQueue slot...");
Logging.Debug(LogClass.ServiceNv, "Waiting for a free BufferQueue slot...");
if (!KeepRunning)
if (Disposed)
{
break;
}
@ -441,9 +424,9 @@ namespace Ryujinx.Core.OsHle.Services.Android
WaitBufferFree.WaitOne();
}
while (KeepRunning);
while (!Disposed);
Logging.Debug($"Found free BufferQueue slot {Slot}!");
Logging.Debug(LogClass.ServiceNv, $"Found free BufferQueue slot {Slot}!");
return Slot;
}
@ -481,26 +464,12 @@ namespace Ryujinx.Core.OsHle.Services.Android
protected virtual void Dispose(bool Disposing)
{
if (Disposing && !NvFlingerDisposed)
if (Disposing && !Disposed)
{
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();
Disposed = true;
lock (WaitBufferFree)
{
KeepRunning = false;
WaitBufferFree.Set();
}

View file

@ -40,6 +40,7 @@ namespace Ryujinx.Core.OsHle.Svc
{ 0x0c, SvcGetThreadPriority },
{ 0x0d, SvcSetThreadPriority },
{ 0x0f, SvcSetThreadCoreMask },
{ 0x10, SvcGetCurrentProcessorNumber },
{ 0x12, SvcClearEvent },
{ 0x13, SvcMapSharedMemory },
{ 0x14, SvcUnmapSharedMemory },
@ -79,11 +80,11 @@ namespace Ryujinx.Core.OsHle.Svc
if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func))
{
Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} called.");
Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} called.");
Func(ThreadState);
Logging.Trace($"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended.");
Logging.Trace(LogClass.KernelSvc, $"(Thread {ThreadState.ThreadId}) {Func.Method.Name} ended.");
}
else
{

View file

@ -57,7 +57,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to map Memory at invalid src address {Src:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to map Memory at invalid src address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -66,7 +66,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidMapPosition(Dst))
{
Logging.Warn($"Tried to map Memory at invalid dst address {Dst:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to map Memory at invalid dst address {Dst:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -92,7 +92,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to unmap Memory at invalid src address {Src:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to unmap Memory at invalid src address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -101,7 +101,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidMapPosition(Dst))
{
Logging.Warn($"Tried to unmap Memory at invalid dst address {Dst:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to unmap Memory at invalid dst address {Dst:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -158,7 +158,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to map SharedMemory at invalid address {Src:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to map SharedMemory at invalid address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -196,7 +196,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to unmap SharedMemory at invalid address {Src:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to unmap SharedMemory at invalid address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);
@ -230,7 +230,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (!IsValidPosition(Src))
{
Logging.Warn($"Tried to create TransferMemory at invalid address {Src:x16}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to create TransferMemory at invalid address {Src:x16}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange);

View file

@ -39,7 +39,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (Obj == null)
{
Logging.Warn($"Tried to CloseHandle on invalid handle 0x{Handle:x8}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to CloseHandle on invalid handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
@ -75,7 +75,7 @@ namespace Ryujinx.Core.OsHle.Svc
}
else
{
Logging.Warn($"Tried to ResetSignal on invalid event handle 0x{Handle:x8}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to ResetSignal on invalid event handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
@ -99,7 +99,7 @@ namespace Ryujinx.Core.OsHle.Svc
if (SyncObj == null)
{
Logging.Warn($"Tried to WaitSynchronization on invalid handle 0x{Handle:x8}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to WaitSynchronization on invalid handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
@ -153,10 +153,10 @@ namespace Ryujinx.Core.OsHle.Svc
//TODO: Validate that app has perms to access the service, and that the service
//actually exists, return error codes otherwise.
KSession Session = new KSession(ServiceFactory.MakeService(Name));
KSession Session = new KSession(ServiceFactory.MakeService(Name), Name);
ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session);
ThreadState.X0 = 0;
ThreadState.X1 = Handle;
}
@ -199,7 +199,7 @@ namespace Ryujinx.Core.OsHle.Svc
}
else
{
Logging.Warn($"Tried to SendSyncRequest on invalid session handle 0x{Handle:x8}!");
Logging.Warn(LogClass.KernelSvc, $"Tried to SendSyncRequest on invalid session handle 0x{Handle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
@ -221,7 +221,7 @@ namespace Ryujinx.Core.OsHle.Svc
string Str = AMemoryHelper.ReadAsciiString(Memory, Position, Size);
Logging.Info($"SvcOutputDebugString: {Str}");
Logging.Info(LogClass.KernelSvc, Str);
ThreadState.X0 = 0;
}
@ -235,7 +235,8 @@ namespace Ryujinx.Core.OsHle.Svc
//Fail for info not available on older Kernel versions.
if (InfoType == 18 ||
InfoType == 19)
InfoType == 19 ||
InfoType == 20)
{
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidInfo);
@ -267,7 +268,7 @@ namespace Ryujinx.Core.OsHle.Svc
case 6:
ThreadState.X1 = MemoryRegions.TotalMemoryAvailable;
break;
case 7:
ThreadState.X1 = MemoryRegions.TotalMemoryUsed + CurrentHeapSize;
break;

View file

@ -1,6 +1,8 @@
using ChocolArm64.State;
using Ryujinx.Core.OsHle.Handles;
using static Ryujinx.Core.OsHle.ErrorCode;
namespace Ryujinx.Core.OsHle.Svc
{
partial class SvcHandler
@ -61,11 +63,11 @@ namespace Ryujinx.Core.OsHle.Svc
}
private void SvcSleepThread(AThreadState ThreadState)
{
{
ulong NanoSecs = ThreadState.X0;
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
if (NanoSecs == 0)
{
Process.Scheduler.Yield(CurrThread);
@ -115,9 +117,16 @@ namespace Ryujinx.Core.OsHle.Svc
//TODO: Error codes.
}
private void SvcGetCurrentProcessorNumber(AThreadState ThreadState)
{
KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
ThreadState.X0 = (ulong)CurrThread.ProcessorId;
}
private void SvcGetThreadId(AThreadState ThreadState)
{
int Handle = (int)ThreadState.X0;
int Handle = (int)ThreadState.X1;
KThread Thread = Process.HandleTable.GetData<KThread>(Handle);
@ -126,8 +135,12 @@ namespace Ryujinx.Core.OsHle.Svc
ThreadState.X0 = 0;
ThreadState.X1 = (ulong)Thread.ThreadId;
}
else
{
Logging.Warn(LogClass.KernelSvc, $"Tried to GetThreadId on invalid thread handle 0x{Handle:x8}!");
//TODO: Error codes.
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
}
}
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalBlendEquation
{
FuncAdd = 1,
FuncSubtract = 2,
FuncReverseSubtract = 3,
Min = 4,
Max = 5
}
}

View file

@ -0,0 +1,25 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalBlendFactor
{
Zero = 0x1,
One = 0x2,
SrcColor = 0x3,
OneMinusSrcColor = 0x4,
SrcAlpha = 0x5,
OneMinusSrcAlpha = 0x6,
DstAlpha = 0x7,
OneMinusDstAlpha = 0x8,
DstColor = 0x9,
OneMinusDstColor = 0xa,
SrcAlphaSaturate = 0xb,
Src1Color = 0x10,
OneMinusSrc1Color = 0x11,
Src1Alpha = 0x12,
OneMinusSrc1Alpha = 0x13,
ConstantColor = 0x61,
OneMinusConstantColor = 0x62,
ConstantAlpha = 0x63,
OneMinusConstantAlpha = 0x64
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace Ryujinx.Graphics.Gal
{
[Flags]
public enum GalClearBufferFlags
{
Depth = 1 << 0,
Stencil = 1 << 1,
ColorRed = 1 << 2,
ColorGreen = 1 << 3,
ColorBlue = 1 << 4,
ColorAlpha = 1 << 5
}
}

View file

@ -0,0 +1,22 @@
namespace Ryujinx.Graphics.Gal
{
public struct GalColorF
{
public float Red { get; private set; }
public float Green { get; private set; }
public float Blue { get; private set; }
public float Alpha { get; private set; }
public GalColorF(
float Red,
float Green,
float Blue,
float Alpha)
{
this.Red = Red;
this.Green = Green;
this.Blue = Blue;
this.Alpha = Alpha;
}
}
}

View file

@ -0,0 +1,7 @@
namespace Ryujinx.Graphics.Gal
{
public static class GalConsts
{
public const string FlipUniformName = "flip";
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalIndexFormat
{
Byte = 0,
Int16 = 1,
Int32 = 2
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalShaderType
{
Vertex = 0,
TessControl = 1,
TessEvaluation = 2,
Geometry = 3,
Fragment = 4
}
}

View file

@ -0,0 +1,20 @@
namespace Ryujinx.Graphics.Gal
{
public struct GalTexture
{
public byte[] Data;
public int Width;
public int Height;
public GalTextureFormat Format;
public GalTexture(byte[] Data, int Width, int Height, GalTextureFormat Format)
{
this.Data = Data;
this.Width = Width;
this.Height = Height;
this.Format = Format;
}
}
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalTextureFilter
{
Nearest = 1,
Linear = 2
}
}

View file

@ -0,0 +1,14 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalTextureFormat
{
A8B8G8R8 = 0x8,
A1B5G5R5 = 0x14,
B5G6R5 = 0x15,
BC1 = 0x24,
BC2 = 0x25,
BC3 = 0x26,
BC4 = 0x27,
BC5 = 0x28
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalTextureMipFilter
{
None = 1,
Nearest = 2,
Linear = 3
}
}

View file

@ -0,0 +1,33 @@
namespace Ryujinx.Graphics.Gal
{
public struct GalTextureSampler
{
public GalTextureWrap AddressU { get; private set; }
public GalTextureWrap AddressV { get; private set; }
public GalTextureWrap AddressP { get; private set; }
public GalTextureFilter MinFilter { get; private set; }
public GalTextureFilter MagFilter { get; private set; }
public GalTextureMipFilter MipFilter { get; private set; }
public GalColorF BorderColor { get; private set; }
public GalTextureSampler(
GalTextureWrap AddressU,
GalTextureWrap AddressV,
GalTextureWrap AddressP,
GalTextureFilter MinFilter,
GalTextureFilter MagFilter,
GalTextureMipFilter MipFilter,
GalColorF BorderColor)
{
this.AddressU = AddressU;
this.AddressV = AddressV;
this.AddressP = AddressP;
this.MinFilter = MinFilter;
this.MagFilter = MagFilter;
this.MipFilter = MipFilter;
this.BorderColor = BorderColor;
}
}
}

View file

@ -0,0 +1,14 @@
namespace Ryujinx.Graphics.Gal
{
public enum GalTextureWrap
{
Repeat = 0,
MirroredRepeat = 1,
ClampToEdge = 2,
ClampToBorder = 3,
Clamp = 4,
MirrorClampToEdge = 5,
MirrorClampToBorder = 6,
MirrorClamp = 7
}
}

View file

@ -2,8 +2,6 @@ namespace Ryujinx.Graphics.Gal
{
public struct GalVertexAttrib
{
public int Index { get; private set; }
public int Buffer { get; private set; }
public bool IsConst { get; private set; }
public int Offset { get; private set; }
@ -13,16 +11,12 @@ namespace Ryujinx.Graphics.Gal
public bool IsBgra { get; private set; }
public GalVertexAttrib(
int Index,
int Buffer,
bool IsConst,
int Offset,
GalVertexAttribSize Size,
GalVertexAttribType Type,
bool IsBgra)
{
this.Index = Index;
this.Buffer = Buffer;
this.IsConst = IsConst;
this.Offset = Offset;
this.Size = Size;

View file

@ -1,29 +1,77 @@
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Gal
{
public unsafe interface IGalRenderer
{
void QueueAction(Action ActionMthd);
void RunActions();
void InitializeFrameBuffer();
void ResetFrameBuffer();
void Render();
void SetWindowSize(int Width, int Height);
void SetFrameBuffer(
byte* Fb,
int Width,
int Height,
float ScaleX,
float ScaleY,
float OffsX,
float OffsY,
float Rotate);
void SendVertexBuffer(int Index, byte[] Buffer, int Stride, GalVertexAttrib[] Attribs);
//Blend
void SetBlendEnable(bool Enable);
void SendR8G8B8A8Texture(int Index, byte[] Buffer, int Width, int Height);
void SetBlend(
GalBlendEquation Equation,
GalBlendFactor FuncSrc,
GalBlendFactor FuncDst);
void SetBlendSeparate(
GalBlendEquation EquationRgb,
GalBlendEquation EquationAlpha,
GalBlendFactor FuncSrcRgb,
GalBlendFactor FuncDstRgb,
GalBlendFactor FuncSrcAlpha,
GalBlendFactor FuncDstAlpha);
//Frame Buffer
void CreateFrameBuffer(long Tag, int Width, int Height);
void BindFrameBuffer(long Tag);
void BindFrameBufferTexture(long Tag, int Index, GalTextureSampler Sampler);
void SetFrameBuffer(long Tag);
void SetFrameBuffer(byte[] Data, int Width, int Height);
void SetFrameBufferTransform(float SX, float SY, float Rotate, float TX, float TY);
void SetViewport(int X, int Y, int Width, int Height);
//Rasterizer
void ClearBuffers(int RtIndex, GalClearBufferFlags Flags);
void SetVertexArray(int VbIndex, int Stride, byte[] Buffer, GalVertexAttrib[] Attribs);
void SetIndexArray(byte[] Buffer, GalIndexFormat Format);
void DrawArrays(int VbIndex, GalPrimitiveType PrimType);
void DrawElements(int VbIndex, int First, GalPrimitiveType PrimType);
//Shader
void CreateShader(long Tag, GalShaderType Type, byte[] Data);
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Tag);
void SetConstBuffer(long Tag, int Cbuf, byte[] Data);
void SetUniform1(string UniformName, int Value);
void SetUniform2F(string UniformName, float X, float Y);
void BindShader(long Tag);
void BindProgram();
//Texture
void SetTextureAndSampler(int Index, GalTexture Texture, GalTextureSampler Sampler);
void BindTexture(int Index);
}

View file

@ -1,250 +0,0 @@
using OpenTK;
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
unsafe class FrameBuffer
{
public int WindowWidth { get; set; }
public int WindowHeight { get; set; }
private int VtxShaderHandle;
private int FragShaderHandle;
private int PrgShaderHandle;
private int TexHandle;
private int TexWidth;
private int TexHeight;
private int VaoHandle;
private int VboHandle;
private int[] Pixels;
private byte* FbPtr;
private object FbPtrLock;
public FrameBuffer(int Width, int Height)
{
if (Width < 0)
{
throw new ArgumentOutOfRangeException(nameof(Width));
}
if (Height < 0)
{
throw new ArgumentOutOfRangeException(nameof(Height));
}
FbPtrLock = new object();
TexWidth = Width;
TexHeight = Height;
WindowWidth = Width;
WindowHeight = Height;
SetupShaders();
SetupTexture();
SetupVertex();
}
private void SetupShaders()
{
VtxShaderHandle = GL.CreateShader(ShaderType.VertexShader);
FragShaderHandle = GL.CreateShader(ShaderType.FragmentShader);
string VtxShaderSource = EmbeddedResource.GetString("GlFbVtxShader");
string FragShaderSource = EmbeddedResource.GetString("GlFbFragShader");
GL.ShaderSource(VtxShaderHandle, VtxShaderSource);
GL.ShaderSource(FragShaderHandle, FragShaderSource);
GL.CompileShader(VtxShaderHandle);
GL.CompileShader(FragShaderHandle);
PrgShaderHandle = GL.CreateProgram();
GL.AttachShader(PrgShaderHandle, VtxShaderHandle);
GL.AttachShader(PrgShaderHandle, FragShaderHandle);
GL.LinkProgram(PrgShaderHandle);
GL.UseProgram(PrgShaderHandle);
int TexUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "tex");
GL.Uniform1(TexUniformLocation, 0);
int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size");
GL.Uniform2(WindowSizeUniformLocation, new Vector2(1280.0f, 720.0f));
}
private void SetupTexture()
{
Pixels = new int[TexWidth * TexHeight];
if (TexHandle == 0)
{
TexHandle = GL.GenTexture();
}
GL.BindTexture(TextureTarget.Texture2D, TexHandle);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexImage2D(TextureTarget.Texture2D,
0,
PixelInternalFormat.Rgba,
TexWidth,
TexHeight,
0,
PixelFormat.Rgba,
PixelType.UnsignedByte,
IntPtr.Zero);
}
private void SetupVertex()
{
VaoHandle = GL.GenVertexArray();
VboHandle = GL.GenBuffer();
float[] Buffer = new float[]
{
-1, 1, 0, 0,
1, 1, 1, 0,
-1, -1, 0, 1,
1, -1, 1, 1
};
IntPtr Length = new IntPtr(Buffer.Length * 4);
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
GL.BufferData(BufferTarget.ArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindVertexArray(VaoHandle);
GL.EnableVertexAttribArray(0);
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 16, 0);
GL.EnableVertexAttribArray(1);
GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle);
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 16, 8);
GL.BindVertexArray(0);
}
public unsafe void Set(byte* Fb, int Width, int Height, Matrix2 Transform, Vector2 Offs)
{
if (Fb == null)
{
throw new ArgumentNullException(nameof(Fb));
}
if (Width < 0)
{
throw new ArgumentOutOfRangeException(nameof(Width));
}
if (Height < 0)
{
throw new ArgumentOutOfRangeException(nameof(Height));
}
lock (FbPtrLock)
{
FbPtr = Fb;
}
if (Width != TexWidth ||
Height != TexHeight)
{
TexWidth = Width;
TexHeight = Height;
SetupTexture();
}
GL.UseProgram(PrgShaderHandle);
int TransformUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "transform");
GL.UniformMatrix2(TransformUniformLocation, false, ref Transform);
int WindowSizeUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "window_size");
GL.Uniform2(WindowSizeUniformLocation, new Vector2(WindowWidth, WindowHeight));
int OffsetUniformLocation = GL.GetUniformLocation(PrgShaderHandle, "offset");
GL.Uniform2(OffsetUniformLocation, Offs);
}
public void Reset()
{
lock (FbPtrLock)
{
FbPtr = null;
}
}
public void Render()
{
lock (FbPtrLock)
{
if (FbPtr == null)
{
return;
}
for (int Y = 0; Y < TexHeight; Y++)
for (int X = 0; X < TexWidth; X++)
{
Pixels[X + Y * TexWidth] = *((int*)(FbPtr + GetSwizzleOffset(X, Y)));
}
}
GL.BindTexture(TextureTarget.Texture2D, TexHandle);
GL.TexSubImage2D(TextureTarget.Texture2D,
0,
0,
0,
TexWidth,
TexHeight,
PixelFormat.Rgba,
PixelType.UnsignedByte,
Pixels);
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindVertexArray(VaoHandle);
GL.UseProgram(PrgShaderHandle);
GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
}
private int GetSwizzleOffset(int X, int Y)
{
int Pos;
Pos = (Y & 0x7f) >> 4;
Pos += (X >> 4) << 3;
Pos += (Y >> 7) * ((TexWidth >> 4) << 3);
Pos *= 1024;
Pos += ((Y & 0xf) >> 3) << 9;
Pos += ((X & 0xf) >> 3) << 8;
Pos += ((Y & 0x7) >> 1) << 6;
Pos += ((X & 0x7) >> 2) << 5;
Pos += ((Y & 0x1) >> 0) << 4;
Pos += ((X & 0x3) >> 0) << 2;
return Pos;
}
}
}

View file

@ -0,0 +1,49 @@
using OpenTK.Graphics.OpenGL;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLBlend
{
public void Enable()
{
GL.Enable(EnableCap.Blend);
}
public void Disable()
{
GL.Disable(EnableCap.Blend);
}
public void Set(
GalBlendEquation Equation,
GalBlendFactor FuncSrc,
GalBlendFactor FuncDst)
{
GL.BlendEquation(
OGLEnumConverter.GetBlendEquation(Equation));
GL.BlendFunc(
OGLEnumConverter.GetBlendFactorSrc(FuncSrc),
OGLEnumConverter.GetBlendFactorDst(FuncDst));
}
public void SetSeparate(
GalBlendEquation EquationRgb,
GalBlendEquation EquationAlpha,
GalBlendFactor FuncSrcRgb,
GalBlendFactor FuncDstRgb,
GalBlendFactor FuncSrcAlpha,
GalBlendFactor FuncDstAlpha)
{
GL.BlendEquationSeparate(
OGLEnumConverter.GetBlendEquation(EquationRgb),
OGLEnumConverter.GetBlendEquation(EquationAlpha));
GL.BlendFuncSeparate(
OGLEnumConverter.GetBlendFactorSrc(FuncSrcRgb),
OGLEnumConverter.GetBlendFactorDst(FuncDstRgb),
OGLEnumConverter.GetBlendFactorSrc(FuncSrcAlpha),
OGLEnumConverter.GetBlendFactorDst(FuncDstAlpha));
}
}
}

View file

@ -0,0 +1,198 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
static class OGLEnumConverter
{
public static DrawElementsType GetDrawElementsType(GalIndexFormat Format)
{
switch (Format)
{
case GalIndexFormat.Byte: return DrawElementsType.UnsignedByte;
case GalIndexFormat.Int16: return DrawElementsType.UnsignedShort;
case GalIndexFormat.Int32: return DrawElementsType.UnsignedInt;
}
throw new ArgumentException(nameof(Format));
}
public static PrimitiveType GetPrimitiveType(GalPrimitiveType Type)
{
switch (Type)
{
case GalPrimitiveType.Points: return PrimitiveType.Points;
case GalPrimitiveType.Lines: return PrimitiveType.Lines;
case GalPrimitiveType.LineLoop: return PrimitiveType.LineLoop;
case GalPrimitiveType.LineStrip: return PrimitiveType.LineStrip;
case GalPrimitiveType.Triangles: return PrimitiveType.Triangles;
case GalPrimitiveType.TriangleStrip: return PrimitiveType.TriangleStrip;
case GalPrimitiveType.TriangleFan: return PrimitiveType.TriangleFan;
case GalPrimitiveType.Quads: return PrimitiveType.Quads;
case GalPrimitiveType.QuadStrip: return PrimitiveType.QuadStrip;
case GalPrimitiveType.Polygon: return PrimitiveType.Polygon;
case GalPrimitiveType.LinesAdjacency: return PrimitiveType.LinesAdjacency;
case GalPrimitiveType.LineStripAdjacency: return PrimitiveType.LineStripAdjacency;
case GalPrimitiveType.TrianglesAdjacency: return PrimitiveType.TrianglesAdjacency;
case GalPrimitiveType.TriangleStripAdjacency: return PrimitiveType.TriangleStripAdjacency;
case GalPrimitiveType.Patches: return PrimitiveType.Patches;
}
throw new ArgumentException(nameof(Type));
}
public static ShaderType GetShaderType(GalShaderType Type)
{
switch (Type)
{
case GalShaderType.Vertex: return ShaderType.VertexShader;
case GalShaderType.TessControl: return ShaderType.TessControlShader;
case GalShaderType.TessEvaluation: return ShaderType.TessEvaluationShader;
case GalShaderType.Geometry: return ShaderType.GeometryShader;
case GalShaderType.Fragment: return ShaderType.FragmentShader;
}
throw new ArgumentException(nameof(Type));
}
public static (PixelFormat, PixelType) GetTextureFormat(GalTextureFormat Format)
{
switch (Format)
{
case GalTextureFormat.A8B8G8R8: return (PixelFormat.Rgba, PixelType.UnsignedByte);
case GalTextureFormat.A1B5G5R5: return (PixelFormat.Rgba, PixelType.UnsignedShort5551);
case GalTextureFormat.B5G6R5: return (PixelFormat.Rgb, PixelType.UnsignedShort565);
}
throw new NotImplementedException(Format.ToString());
}
public static PixelInternalFormat GetCompressedTextureFormat(GalTextureFormat Format)
{
switch (Format)
{
case GalTextureFormat.BC1: return PixelInternalFormat.CompressedRgbaS3tcDxt1Ext;
case GalTextureFormat.BC2: return PixelInternalFormat.CompressedRgbaS3tcDxt3Ext;
case GalTextureFormat.BC3: return PixelInternalFormat.CompressedRgbaS3tcDxt5Ext;
case GalTextureFormat.BC4: return PixelInternalFormat.CompressedRedRgtc1;
case GalTextureFormat.BC5: return PixelInternalFormat.CompressedRgRgtc2;
}
throw new NotImplementedException(Format.ToString());
}
public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap)
{
switch (Wrap)
{
case GalTextureWrap.Repeat: return TextureWrapMode.Repeat;
case GalTextureWrap.MirroredRepeat: return TextureWrapMode.MirroredRepeat;
case GalTextureWrap.ClampToEdge: return TextureWrapMode.ClampToEdge;
case GalTextureWrap.ClampToBorder: return TextureWrapMode.ClampToBorder;
case GalTextureWrap.Clamp: return TextureWrapMode.Clamp;
//TODO: Those needs extensions (and are currently wrong).
case GalTextureWrap.MirrorClampToEdge: return TextureWrapMode.ClampToEdge;
case GalTextureWrap.MirrorClampToBorder: return TextureWrapMode.ClampToBorder;
case GalTextureWrap.MirrorClamp: return TextureWrapMode.Clamp;
}
throw new ArgumentException(nameof(Wrap));
}
public static TextureMinFilter GetTextureMinFilter(
GalTextureFilter MinFilter,
GalTextureMipFilter MipFilter)
{
//TODO: Mip (needs mipmap support first).
switch (MinFilter)
{
case GalTextureFilter.Nearest: return TextureMinFilter.Nearest;
case GalTextureFilter.Linear: return TextureMinFilter.Linear;
}
throw new ArgumentException(nameof(MinFilter));
}
public static TextureMagFilter GetTextureMagFilter(GalTextureFilter Filter)
{
switch (Filter)
{
case GalTextureFilter.Nearest: return TextureMagFilter.Nearest;
case GalTextureFilter.Linear: return TextureMagFilter.Linear;
}
throw new ArgumentException(nameof(Filter));
}
public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation)
{
switch (BlendEquation)
{
case GalBlendEquation.FuncAdd: return BlendEquationMode.FuncAdd;
case GalBlendEquation.FuncSubtract: return BlendEquationMode.FuncSubtract;
case GalBlendEquation.FuncReverseSubtract: return BlendEquationMode.FuncReverseSubtract;
case GalBlendEquation.Min: return BlendEquationMode.Min;
case GalBlendEquation.Max: return BlendEquationMode.Max;
}
throw new ArgumentException(nameof(BlendEquation));
}
public static BlendingFactorSrc GetBlendFactorSrc(GalBlendFactor BlendFactor)
{
switch (BlendFactor)
{
case GalBlendFactor.Zero: return BlendingFactorSrc.Zero;
case GalBlendFactor.One: return BlendingFactorSrc.One;
case GalBlendFactor.SrcColor: return BlendingFactorSrc.SrcColor;
case GalBlendFactor.OneMinusSrcColor: return BlendingFactorSrc.OneMinusSrcColor;
case GalBlendFactor.DstColor: return BlendingFactorSrc.DstColor;
case GalBlendFactor.OneMinusDstColor: return BlendingFactorSrc.OneMinusDstColor;
case GalBlendFactor.SrcAlpha: return BlendingFactorSrc.SrcAlpha;
case GalBlendFactor.OneMinusSrcAlpha: return BlendingFactorSrc.OneMinusSrcAlpha;
case GalBlendFactor.DstAlpha: return BlendingFactorSrc.DstAlpha;
case GalBlendFactor.OneMinusDstAlpha: return BlendingFactorSrc.OneMinusDstAlpha;
case GalBlendFactor.ConstantColor: return BlendingFactorSrc.ConstantColor;
case GalBlendFactor.OneMinusConstantColor: return BlendingFactorSrc.OneMinusConstantColor;
case GalBlendFactor.ConstantAlpha: return BlendingFactorSrc.ConstantAlpha;
case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactorSrc.OneMinusConstantAlpha;
case GalBlendFactor.SrcAlphaSaturate: return BlendingFactorSrc.SrcAlphaSaturate;
case GalBlendFactor.Src1Color: return BlendingFactorSrc.Src1Color;
case GalBlendFactor.OneMinusSrc1Color: return BlendingFactorSrc.OneMinusSrc1Color;
case GalBlendFactor.Src1Alpha: return BlendingFactorSrc.Src1Alpha;
case GalBlendFactor.OneMinusSrc1Alpha: return BlendingFactorSrc.OneMinusSrc1Alpha;
}
throw new ArgumentException(nameof(BlendFactor));
}
public static BlendingFactorDest GetBlendFactorDst(GalBlendFactor BlendFactor)
{
switch (BlendFactor)
{
case GalBlendFactor.Zero: return BlendingFactorDest.Zero;
case GalBlendFactor.One: return BlendingFactorDest.One;
case GalBlendFactor.SrcColor: return BlendingFactorDest.SrcColor;
case GalBlendFactor.OneMinusSrcColor: return BlendingFactorDest.OneMinusSrcColor;
case GalBlendFactor.DstColor: return BlendingFactorDest.DstColor;
case GalBlendFactor.OneMinusDstColor: return BlendingFactorDest.OneMinusDstColor;
case GalBlendFactor.SrcAlpha: return BlendingFactorDest.SrcAlpha;
case GalBlendFactor.OneMinusSrcAlpha: return BlendingFactorDest.OneMinusSrcAlpha;
case GalBlendFactor.DstAlpha: return BlendingFactorDest.DstAlpha;
case GalBlendFactor.OneMinusDstAlpha: return BlendingFactorDest.OneMinusDstAlpha;
case GalBlendFactor.ConstantColor: return BlendingFactorDest.ConstantColor;
case GalBlendFactor.OneMinusConstantColor: return BlendingFactorDest.OneMinusConstantColor;
case GalBlendFactor.ConstantAlpha: return BlendingFactorDest.ConstantAlpha;
case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactorDest.OneMinusConstantAlpha;
case GalBlendFactor.SrcAlphaSaturate: return BlendingFactorDest.SrcAlphaSaturate;
case GalBlendFactor.Src1Color: return BlendingFactorDest.Src1Color;
case GalBlendFactor.OneMinusSrc1Color: return BlendingFactorDest.OneMinusSrc1Color;
case GalBlendFactor.Src1Alpha: return BlendingFactorDest.Src1Alpha;
case GalBlendFactor.OneMinusSrc1Alpha: return BlendingFactorDest.OneMinusSrc1Alpha;
}
throw new ArgumentException(nameof(BlendFactor));
}
}
}

Some files were not shown because too many files have changed in this diff Show more