Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
9d987f24a0
14 changed files with 358 additions and 20 deletions
|
@ -144,6 +144,50 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
EmitFsetp(Block, OpCode, ShaderOper.RR);
|
EmitFsetp(Block, OpCode, ShaderOper.RR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Iadd_C(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitIadd(Block, OpCode, ShaderOper.CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Iadd_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitIadd(Block, OpCode, ShaderOper.Imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Iadd_I32(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
ShaderIrNode OperA = GetOperGpr8 (OpCode);
|
||||||
|
ShaderIrNode OperB = GetOperImm32_20(OpCode);
|
||||||
|
|
||||||
|
bool NegA = ((OpCode >> 56) & 1) != 0;
|
||||||
|
|
||||||
|
OperA = GetAluIneg(OperA, NegA);
|
||||||
|
|
||||||
|
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Add, OperA, OperB);
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Iadd_R(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitIadd(Block, OpCode, ShaderOper.RR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Iadd3_C(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitIadd3(Block, OpCode, ShaderOper.CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Iadd3_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitIadd3(Block, OpCode, ShaderOper.Imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Iadd3_R(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitIadd3(Block, OpCode, ShaderOper.RR);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Imnmx_C(ShaderIrBlock Block, long OpCode)
|
public static void Imnmx_C(ShaderIrBlock Block, long OpCode)
|
||||||
{
|
{
|
||||||
EmitImnmx(Block, OpCode, ShaderOper.CR);
|
EmitImnmx(Block, OpCode, ShaderOper.CR);
|
||||||
|
@ -184,6 +228,21 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
EmitIscadd(Block, OpCode, ShaderOper.RR);
|
EmitIscadd(Block, OpCode, ShaderOper.RR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Iset_C(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitIset(Block, OpCode, ShaderOper.CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Iset_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitIset(Block, OpCode, ShaderOper.Imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Iset_R(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitIset(Block, OpCode, ShaderOper.RR);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Isetp_C(ShaderIrBlock Block, long OpCode)
|
public static void Isetp_C(ShaderIrBlock Block, long OpCode)
|
||||||
{
|
{
|
||||||
EmitIsetp(Block, OpCode, ShaderOper.CR);
|
EmitIsetp(Block, OpCode, ShaderOper.CR);
|
||||||
|
@ -215,13 +274,13 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
case 2: Inst = ShaderIrInst.Xor; break;
|
case 2: Inst = ShaderIrInst.Xor; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA);
|
ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), InvB);
|
||||||
|
|
||||||
//SubOp == 3 is pass, used by the not instruction
|
//SubOp == 3 is pass, used by the not instruction
|
||||||
//which just moves the inverted register value.
|
//which just moves the inverted register value.
|
||||||
if (SubOp < 3)
|
if (SubOp < 3)
|
||||||
{
|
{
|
||||||
ShaderIrNode OperB = GetAluNot(GetOperImm32_20(OpCode), InvB);
|
ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA);
|
||||||
|
|
||||||
ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB);
|
ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB);
|
||||||
|
|
||||||
|
@ -229,10 +288,25 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode));
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperB), OpCode));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Lop_C(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitLop(Block, OpCode, ShaderOper.CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Lop_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitLop(Block, OpCode, ShaderOper.Imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Lop_R(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitLop(Block, OpCode, ShaderOper.RR);
|
||||||
|
}
|
||||||
|
|
||||||
public static void Mufu(ShaderIrBlock Block, long OpCode)
|
public static void Mufu(ShaderIrBlock Block, long OpCode)
|
||||||
{
|
{
|
||||||
int SubOp = (int)(OpCode >> 20) & 0xf;
|
int SubOp = (int)(OpCode >> 20) & 0xf;
|
||||||
|
@ -533,6 +607,92 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EmitIadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
|
{
|
||||||
|
ShaderIrNode OperA = GetOperGpr8(OpCode);
|
||||||
|
ShaderIrNode OperB;
|
||||||
|
|
||||||
|
switch (Oper)
|
||||||
|
{
|
||||||
|
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
|
||||||
|
case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
|
||||||
|
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException(nameof(Oper));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NegA = ((OpCode >> 49) & 1) != 0;
|
||||||
|
bool NegB = ((OpCode >> 48) & 1) != 0;
|
||||||
|
|
||||||
|
OperA = GetAluIneg(OperA, NegA);
|
||||||
|
OperB = GetAluIneg(OperB, NegB);
|
||||||
|
|
||||||
|
ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Add, OperA, OperB);
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitIadd3(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
|
{
|
||||||
|
int Mode = (int)((OpCode >> 37) & 3);
|
||||||
|
|
||||||
|
bool Neg1 = ((OpCode >> 51) & 1) != 0;
|
||||||
|
bool Neg2 = ((OpCode >> 50) & 1) != 0;
|
||||||
|
bool Neg3 = ((OpCode >> 49) & 1) != 0;
|
||||||
|
|
||||||
|
int Height1 = (int)((OpCode >> 35) & 3);
|
||||||
|
int Height2 = (int)((OpCode >> 33) & 3);
|
||||||
|
int Height3 = (int)((OpCode >> 31) & 3);
|
||||||
|
|
||||||
|
ShaderIrNode OperB;
|
||||||
|
|
||||||
|
switch (Oper)
|
||||||
|
{
|
||||||
|
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
|
||||||
|
case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
|
||||||
|
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException(nameof(Oper));
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrNode ApplyHeight(ShaderIrNode Src, int Height)
|
||||||
|
{
|
||||||
|
if (Oper != ShaderOper.RR)
|
||||||
|
{
|
||||||
|
return Src;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (Height)
|
||||||
|
{
|
||||||
|
case 0: return Src;
|
||||||
|
case 1: return new ShaderIrOp(ShaderIrInst.And, Src, new ShaderIrOperImm(0xffff));
|
||||||
|
case 2: return new ShaderIrOp(ShaderIrInst.Lsr, Src, new ShaderIrOperImm(16));
|
||||||
|
|
||||||
|
default: throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrNode Src1 = GetAluIneg(ApplyHeight(GetOperGpr8(OpCode), Height1), Neg1);
|
||||||
|
ShaderIrNode Src2 = GetAluIneg(ApplyHeight(OperB, Height2), Neg2);
|
||||||
|
ShaderIrNode Src3 = GetAluIneg(ApplyHeight(GetOperGpr39(OpCode), Height3), Neg3);
|
||||||
|
|
||||||
|
ShaderIrOp Sum = new ShaderIrOp(ShaderIrInst.Add, Src1, Src2);
|
||||||
|
|
||||||
|
if (Oper == ShaderOper.RR)
|
||||||
|
{
|
||||||
|
switch (Mode)
|
||||||
|
{
|
||||||
|
case 1: Sum = new ShaderIrOp(ShaderIrInst.Lsr, Sum, new ShaderIrOperImm(16)); break;
|
||||||
|
case 2: Sum = new ShaderIrOp(ShaderIrInst.Lsl, Sum, new ShaderIrOperImm(16)); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Note: Here there should be a "+ 1" when carry flag is set
|
||||||
|
//but since carry is mostly ignored by other instructions, it's excluded for now
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), new ShaderIrOp(ShaderIrInst.Add, Sum, Src3)), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitIscadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
private static void EmitIscadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
{
|
{
|
||||||
bool NegB = ((OpCode >> 48) & 1) != 0;
|
bool NegB = ((OpCode >> 48) & 1) != 0;
|
||||||
|
@ -821,6 +981,54 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
|
Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EmitLop(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
|
{
|
||||||
|
int SubOp = (int)(OpCode >> 41) & 3;
|
||||||
|
|
||||||
|
bool InvA = ((OpCode >> 39) & 1) != 0;
|
||||||
|
bool InvB = ((OpCode >> 40) & 1) != 0;
|
||||||
|
|
||||||
|
ShaderIrInst Inst = 0;
|
||||||
|
|
||||||
|
switch (SubOp)
|
||||||
|
{
|
||||||
|
case 0: Inst = ShaderIrInst.And; break;
|
||||||
|
case 1: Inst = ShaderIrInst.Or; break;
|
||||||
|
case 2: Inst = ShaderIrInst.Xor; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrNode OperA = GetAluNot(GetOperGpr8(OpCode), InvA);
|
||||||
|
ShaderIrNode OperB;
|
||||||
|
|
||||||
|
switch (Oper)
|
||||||
|
{
|
||||||
|
case ShaderOper.CR: OperB = GetOperCbuf34 (OpCode); break;
|
||||||
|
case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break;
|
||||||
|
case ShaderOper.RR: OperB = GetOperGpr20 (OpCode); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException(nameof(Oper));
|
||||||
|
}
|
||||||
|
|
||||||
|
OperB = GetAluNot(OperB, InvB);
|
||||||
|
|
||||||
|
ShaderIrNode Op;
|
||||||
|
|
||||||
|
if (SubOp < 3)
|
||||||
|
{
|
||||||
|
Op = new ShaderIrOp(Inst, OperA, OperB);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Op = OperB;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderIrNode Compare = new ShaderIrOp(ShaderIrInst.Cne, Op, new ShaderIrOperImm(0));
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperPred48(OpCode), Compare), OpCode));
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
{
|
{
|
||||||
//TODO: Confirm SignAB/C, it is just a guess.
|
//TODO: Confirm SignAB/C, it is just a guess.
|
||||||
|
|
|
@ -156,6 +156,11 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
return new ShaderIrOperPred((int)(OpCode >> 39) & 7);
|
return new ShaderIrOperPred((int)(OpCode >> 39) & 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ShaderIrOperPred GetOperPred48(long OpCode)
|
||||||
|
{
|
||||||
|
return new ShaderIrOperPred((int)((OpCode >> 48) & 7));
|
||||||
|
}
|
||||||
|
|
||||||
public static ShaderIrInst GetCmp(long OpCode)
|
public static ShaderIrInst GetCmp(long OpCode)
|
||||||
{
|
{
|
||||||
switch ((int)(OpCode >> 49) & 7)
|
switch ((int)(OpCode >> 49) & 7)
|
||||||
|
|
|
@ -113,6 +113,21 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Gpr), OpCode));
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Gpr), OpCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Sel_C(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitSel(Block, OpCode, ShaderOper.CR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Sel_I(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitSel(Block, OpCode, ShaderOper.Imm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Sel_R(ShaderIrBlock Block, long OpCode)
|
||||||
|
{
|
||||||
|
EmitSel(Block, OpCode, ShaderOper.RR);
|
||||||
|
}
|
||||||
|
|
||||||
private static void EmitF2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
private static void EmitF2f(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
{
|
{
|
||||||
bool NegA = ((OpCode >> 45) & 1) != 0;
|
bool NegA = ((OpCode >> 45) & 1) != 0;
|
||||||
|
@ -340,6 +355,28 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode));
|
Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), OperA), OpCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EmitSel(ShaderIrBlock Block, long OpCode, ShaderOper Oper)
|
||||||
|
{
|
||||||
|
ShaderIrOperGpr Dst = GetOperGpr0 (OpCode);
|
||||||
|
ShaderIrNode Pred = GetOperPred39N(OpCode);
|
||||||
|
|
||||||
|
ShaderIrNode ResultA = GetOperGpr8(OpCode);
|
||||||
|
ShaderIrNode ResultB;
|
||||||
|
|
||||||
|
switch (Oper)
|
||||||
|
{
|
||||||
|
case ShaderOper.CR: ResultB = GetOperCbuf34 (OpCode); break;
|
||||||
|
case ShaderOper.Imm: ResultB = GetOperImm19_20(OpCode); break;
|
||||||
|
case ShaderOper.RR: ResultB = GetOperGpr20 (OpCode); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException(nameof(Oper));
|
||||||
|
}
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrCond(Pred, new ShaderIrAsg(Dst, ResultA), false), OpCode));
|
||||||
|
|
||||||
|
Block.AddNode(GetPredNode(new ShaderIrCond(Pred, new ShaderIrAsg(Dst, ResultB), true), OpCode));
|
||||||
|
}
|
||||||
|
|
||||||
private static IntType GetIntType(long OpCode)
|
private static IntType GetIntType(long OpCode)
|
||||||
{
|
{
|
||||||
bool Signed = ((OpCode >> 13) & 1) != 0;
|
bool Signed = ((OpCode >> 13) & 1) != 0;
|
||||||
|
|
|
@ -64,6 +64,13 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Set("0100110011100x", ShaderDecode.I2i_C);
|
Set("0100110011100x", ShaderDecode.I2i_C);
|
||||||
Set("0011100x11100x", ShaderDecode.I2i_I);
|
Set("0011100x11100x", ShaderDecode.I2i_I);
|
||||||
Set("0101110011100x", ShaderDecode.I2i_R);
|
Set("0101110011100x", ShaderDecode.I2i_R);
|
||||||
|
Set("0100110000010x", ShaderDecode.Iadd_C);
|
||||||
|
Set("0011100000010x", ShaderDecode.Iadd_I);
|
||||||
|
Set("0001110x0xxxxx", ShaderDecode.Iadd_I32);
|
||||||
|
Set("0101110000010x", ShaderDecode.Iadd_R);
|
||||||
|
Set("010011001100xx", ShaderDecode.Iadd3_C);
|
||||||
|
Set("001110001100xx", ShaderDecode.Iadd3_I);
|
||||||
|
Set("010111001100xx", ShaderDecode.Iadd3_R);
|
||||||
Set("0100110000100x", ShaderDecode.Imnmx_C);
|
Set("0100110000100x", ShaderDecode.Imnmx_C);
|
||||||
Set("0011100x00100x", ShaderDecode.Imnmx_I);
|
Set("0011100x00100x", ShaderDecode.Imnmx_I);
|
||||||
Set("0101110000100x", ShaderDecode.Imnmx_R);
|
Set("0101110000100x", ShaderDecode.Imnmx_R);
|
||||||
|
@ -71,13 +78,19 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Set("0100110000011x", ShaderDecode.Iscadd_C);
|
Set("0100110000011x", ShaderDecode.Iscadd_C);
|
||||||
Set("0011100x00011x", ShaderDecode.Iscadd_I);
|
Set("0011100x00011x", ShaderDecode.Iscadd_I);
|
||||||
Set("0101110000011x", ShaderDecode.Iscadd_R);
|
Set("0101110000011x", ShaderDecode.Iscadd_R);
|
||||||
|
Set("010010110101xx", ShaderDecode.Iset_C);
|
||||||
|
Set("001101100101xx", ShaderDecode.Iset_I);
|
||||||
|
Set("010110110101xx", ShaderDecode.Iset_R);
|
||||||
Set("010010110110xx", ShaderDecode.Isetp_C);
|
Set("010010110110xx", ShaderDecode.Isetp_C);
|
||||||
Set("0011011x0110xx", ShaderDecode.Isetp_I);
|
Set("0011011x0110xx", ShaderDecode.Isetp_I);
|
||||||
Set("010110110110xx", ShaderDecode.Isetp_R);
|
Set("010110110110xx", ShaderDecode.Isetp_R);
|
||||||
Set("111000110011xx", ShaderDecode.Kil);
|
Set("111000110011xx", ShaderDecode.Kil);
|
||||||
Set("1110111111011x", ShaderDecode.Ld_A);
|
Set("1110111111011x", ShaderDecode.Ld_A);
|
||||||
Set("1110111110010x", ShaderDecode.Ld_C);
|
Set("1110111110010x", ShaderDecode.Ld_C);
|
||||||
|
Set("0100110001000x", ShaderDecode.Lop_C);
|
||||||
|
Set("0011100001000x", ShaderDecode.Lop_I);
|
||||||
Set("000001xxxxxxxx", ShaderDecode.Lop_I32);
|
Set("000001xxxxxxxx", ShaderDecode.Lop_I32);
|
||||||
|
Set("0101110001000x", ShaderDecode.Lop_R);
|
||||||
Set("0100110010011x", ShaderDecode.Mov_C);
|
Set("0100110010011x", ShaderDecode.Mov_C);
|
||||||
Set("0011100x10011x", ShaderDecode.Mov_I);
|
Set("0011100x10011x", ShaderDecode.Mov_I);
|
||||||
Set("000000010000xx", ShaderDecode.Mov_I32);
|
Set("000000010000xx", ShaderDecode.Mov_I32);
|
||||||
|
@ -87,6 +100,9 @@ namespace Ryujinx.Graphics.Gal.Shader
|
||||||
Set("0100110010010x", ShaderDecode.Rro_C);
|
Set("0100110010010x", ShaderDecode.Rro_C);
|
||||||
Set("0011100x10010x", ShaderDecode.Rro_I);
|
Set("0011100x10010x", ShaderDecode.Rro_I);
|
||||||
Set("0101110010010x", ShaderDecode.Rro_R);
|
Set("0101110010010x", ShaderDecode.Rro_R);
|
||||||
|
Set("0100110010100x", ShaderDecode.Sel_C);
|
||||||
|
Set("0011100010100x", ShaderDecode.Sel_I);
|
||||||
|
Set("0101110010100x", ShaderDecode.Sel_R);
|
||||||
Set("0100110001001x", ShaderDecode.Shl_C);
|
Set("0100110001001x", ShaderDecode.Shl_C);
|
||||||
Set("0011100x01001x", ShaderDecode.Shl_I);
|
Set("0011100x01001x", ShaderDecode.Shl_I);
|
||||||
Set("0101110001001x", ShaderDecode.Shl_R);
|
Set("0101110001001x", ShaderDecode.Shl_R);
|
||||||
|
|
|
@ -2,6 +2,7 @@ using ChocolArm64.Memory;
|
||||||
using Ryujinx.HLE.Loaders.Executables;
|
using Ryujinx.HLE.Loaders.Executables;
|
||||||
using Ryujinx.HLE.OsHle;
|
using Ryujinx.HLE.OsHle;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders
|
namespace Ryujinx.HLE.Loaders
|
||||||
{
|
{
|
||||||
|
@ -15,6 +16,8 @@ namespace Ryujinx.HLE.Loaders
|
||||||
|
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
public string FilePath { get; private set; }
|
||||||
|
|
||||||
private AMemory Memory;
|
private AMemory Memory;
|
||||||
|
|
||||||
public long ImageBase { get; private set; }
|
public long ImageBase { get; private set; }
|
||||||
|
@ -26,7 +29,12 @@ namespace Ryujinx.HLE.Loaders
|
||||||
|
|
||||||
m_SymbolTable = new Dictionary<long, string>();
|
m_SymbolTable = new Dictionary<long, string>();
|
||||||
|
|
||||||
Name = Exe.Name;
|
FilePath = Exe.FilePath;
|
||||||
|
|
||||||
|
if (FilePath != null)
|
||||||
|
{
|
||||||
|
Name = Path.GetFileNameWithoutExtension(FilePath.Replace(Homebrew.TemporaryNroSuffix, ""));
|
||||||
|
}
|
||||||
|
|
||||||
this.Memory = Memory;
|
this.Memory = Memory;
|
||||||
this.ImageBase = ImageBase;
|
this.ImageBase = ImageBase;
|
||||||
|
|
|
@ -2,7 +2,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
public interface IExecutable
|
public interface IExecutable
|
||||||
{
|
{
|
||||||
string Name { get; }
|
string FilePath { get; }
|
||||||
|
|
||||||
byte[] Text { get; }
|
byte[] Text { get; }
|
||||||
byte[] RO { get; }
|
byte[] RO { get; }
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
class Nro : IExecutable
|
class Nro : IExecutable
|
||||||
{
|
{
|
||||||
public string Name { get; private set; }
|
public string FilePath { get; private set; }
|
||||||
|
|
||||||
public byte[] Text { get; private set; }
|
public byte[] Text { get; private set; }
|
||||||
public byte[] RO { get; private set; }
|
public byte[] RO { get; private set; }
|
||||||
|
@ -16,9 +16,9 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
public int DataOffset { get; private set; }
|
public int DataOffset { get; private set; }
|
||||||
public int BssSize { get; private set; }
|
public int BssSize { get; private set; }
|
||||||
|
|
||||||
public Nro(Stream Input, string Name)
|
public Nro(Stream Input, string FilePath)
|
||||||
{
|
{
|
||||||
this.Name = Name;
|
this.FilePath = FilePath;
|
||||||
|
|
||||||
BinaryReader Reader = new BinaryReader(Input);
|
BinaryReader Reader = new BinaryReader(Input);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
{
|
{
|
||||||
class Nso : IExecutable
|
class Nso : IExecutable
|
||||||
{
|
{
|
||||||
public string Name { get; private set; }
|
public string FilePath { get; private set; }
|
||||||
|
|
||||||
public byte[] Text { get; private set; }
|
public byte[] Text { get; private set; }
|
||||||
public byte[] RO { get; private set; }
|
public byte[] RO { get; private set; }
|
||||||
|
@ -29,9 +29,9 @@ namespace Ryujinx.HLE.Loaders.Executables
|
||||||
HasDataHash = 1 << 5
|
HasDataHash = 1 << 5
|
||||||
}
|
}
|
||||||
|
|
||||||
public Nso(Stream Input, string Name)
|
public Nso(Stream Input, string FilePath)
|
||||||
{
|
{
|
||||||
this.Name = Name;
|
this.FilePath = FilePath;
|
||||||
|
|
||||||
BinaryReader Reader = new BinaryReader(Input);
|
BinaryReader Reader = new BinaryReader(Input);
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
using ChocolArm64.Memory;
|
using ChocolArm64.Memory;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.OsHle
|
namespace Ryujinx.HLE.OsHle
|
||||||
{
|
{
|
||||||
static class Homebrew
|
static class Homebrew
|
||||||
{
|
{
|
||||||
|
public const string TemporaryNroSuffix = ".ryu_tmp.nro";
|
||||||
|
|
||||||
//http://switchbrew.org/index.php?title=Homebrew_ABI
|
//http://switchbrew.org/index.php?title=Homebrew_ABI
|
||||||
public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle)
|
public static void WriteHbAbiData(AMemory Memory, long Position, int MainThreadHandle, string SwitchPath)
|
||||||
{
|
{
|
||||||
Memory.Manager.Map(Position, AMemoryMgr.PageSize, (int)MemoryType.Normal, AMemoryPerm.RW);
|
Memory.Manager.Map(Position, AMemoryMgr.PageSize, (int)MemoryType.Normal, AMemoryPerm.RW);
|
||||||
|
|
||||||
|
@ -15,6 +18,11 @@ namespace Ryujinx.HLE.OsHle
|
||||||
//NextLoadPath
|
//NextLoadPath
|
||||||
WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400);
|
WriteConfigEntry(Memory, ref Position, 2, 0, Position + 0x200, Position + 0x400);
|
||||||
|
|
||||||
|
// Argv
|
||||||
|
long ArgvPosition = Position + 0xC00;
|
||||||
|
WriteConfigEntry(Memory, ref Position, 5, 0, 0, ArgvPosition);
|
||||||
|
Memory.WriteBytes(ArgvPosition, Encoding.ASCII.GetBytes(SwitchPath + "\0"));
|
||||||
|
|
||||||
//AppletType
|
//AppletType
|
||||||
WriteConfigEntry(Memory, ref Position, 7);
|
WriteConfigEntry(Memory, ref Position, 7);
|
||||||
|
|
||||||
|
|
|
@ -87,19 +87,36 @@ namespace Ryujinx.HLE.OsHle
|
||||||
MainProcess.Run();
|
MainProcess.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadProgram(string FileName)
|
public void LoadProgram(string FilePath)
|
||||||
{
|
{
|
||||||
bool IsNro = Path.GetExtension(FileName).ToLower() == ".nro";
|
bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
|
||||||
|
|
||||||
string Name = Path.GetFileNameWithoutExtension(FileName);
|
string Name = Path.GetFileNameWithoutExtension(FilePath);
|
||||||
|
string SwitchFilePath = Ns.VFs.SystemPathToSwitchPath(FilePath);
|
||||||
|
|
||||||
|
if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/")))
|
||||||
|
{
|
||||||
|
// TODO: avoid copying the file if we are already inside a sdmc directory
|
||||||
|
string SwitchPath = $"sdmc:/switch/{Name}{Homebrew.TemporaryNroSuffix}";
|
||||||
|
string TempPath = Ns.VFs.SwitchPathToSystemPath(SwitchPath);
|
||||||
|
|
||||||
|
string SwitchDir = Path.GetDirectoryName(TempPath);
|
||||||
|
if (!Directory.Exists(SwitchDir))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(SwitchDir);
|
||||||
|
}
|
||||||
|
File.Copy(FilePath, TempPath, true);
|
||||||
|
|
||||||
|
FilePath = TempPath;
|
||||||
|
}
|
||||||
|
|
||||||
Process MainProcess = MakeProcess();
|
Process MainProcess = MakeProcess();
|
||||||
|
|
||||||
using (FileStream Input = new FileStream(FileName, FileMode.Open))
|
using (FileStream Input = new FileStream(FilePath, FileMode.Open))
|
||||||
{
|
{
|
||||||
MainProcess.LoadProgram(IsNro
|
MainProcess.LoadProgram(IsNro
|
||||||
? (IExecutable)new Nro(Input, Name)
|
? (IExecutable)new Nro(Input, FilePath)
|
||||||
: (IExecutable)new Nso(Input, Name));
|
: (IExecutable)new Nso(Input, FilePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
MainProcess.SetEmptyArgs();
|
MainProcess.SetEmptyArgs();
|
||||||
|
|
|
@ -13,6 +13,7 @@ using Ryujinx.HLE.OsHle.Services.Nv;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.OsHle
|
namespace Ryujinx.HLE.OsHle
|
||||||
|
@ -155,7 +156,9 @@ namespace Ryujinx.HLE.OsHle
|
||||||
{
|
{
|
||||||
HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd);
|
HbAbiDataPosition = AMemoryHelper.PageRoundUp(Executables[0].ImageEnd);
|
||||||
|
|
||||||
Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle);
|
string SwitchPath = Ns.VFs.SystemPathToSwitchPath(Executables[0].FilePath);
|
||||||
|
|
||||||
|
Homebrew.WriteHbAbiData(Memory, HbAbiDataPosition, Handle, SwitchPath);
|
||||||
|
|
||||||
MainThread.Thread.ThreadState.X0 = (ulong)HbAbiDataPosition;
|
MainThread.Thread.ThreadState.X0 = (ulong)HbAbiDataPosition;
|
||||||
MainThread.Thread.ThreadState.X1 = ulong.MaxValue;
|
MainThread.Thread.ThreadState.X1 = ulong.MaxValue;
|
||||||
|
@ -400,6 +403,11 @@ namespace Ryujinx.HLE.OsHle
|
||||||
{
|
{
|
||||||
if (Disposing && !Disposed)
|
if (Disposing && !Disposed)
|
||||||
{
|
{
|
||||||
|
if (NeedsHbAbi && Executables[0].FilePath.EndsWith(Homebrew.TemporaryNroSuffix))
|
||||||
|
{
|
||||||
|
File.Delete(Executables[0].FilePath);
|
||||||
|
}
|
||||||
|
|
||||||
//If there is still some thread running, disposing the objects is not
|
//If there is still some thread running, disposing the objects is not
|
||||||
//safe as the thread may try to access those resources. Instead, we set
|
//safe as the thread may try to access those resources. Instead, we set
|
||||||
//the flag to have the Process disposed when all threads finishes.
|
//the flag to have the Process disposed when all threads finishes.
|
||||||
|
|
|
@ -81,8 +81,9 @@ namespace Ryujinx.HLE
|
||||||
Gpu.Fifo.DispatchCalls();
|
Gpu.Fifo.DispatchCalls();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal virtual void OnFinish(EventArgs e)
|
public virtual void OnFinish(EventArgs e)
|
||||||
{
|
{
|
||||||
|
Os.Dispose();
|
||||||
Finish?.Invoke(this, e);
|
Finish?.Invoke(this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,35 @@ namespace Ryujinx.HLE
|
||||||
|
|
||||||
public string GetGameSavesPath() => MakeDirAndGetFullPath(NandPath);
|
public string GetGameSavesPath() => MakeDirAndGetFullPath(NandPath);
|
||||||
|
|
||||||
|
public string SwitchPathToSystemPath(string SwitchPath)
|
||||||
|
{
|
||||||
|
string[] Parts = SwitchPath.Split(":");
|
||||||
|
if (Parts.Length != 2)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return GetFullPath(MakeDirAndGetFullPath(Parts[0]), Parts[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SystemPathToSwitchPath(string SystemPath)
|
||||||
|
{
|
||||||
|
string BaseSystemPath = GetBasePath() + "/";
|
||||||
|
if (SystemPath.StartsWith(BaseSystemPath))
|
||||||
|
{
|
||||||
|
string RawPath = SystemPath.Replace(BaseSystemPath, "");
|
||||||
|
int FirstSeparatorOffset = RawPath.IndexOf('/');
|
||||||
|
if (FirstSeparatorOffset == -1)
|
||||||
|
{
|
||||||
|
return $"{RawPath}:/";
|
||||||
|
}
|
||||||
|
|
||||||
|
string BasePath = RawPath.Substring(0, FirstSeparatorOffset);
|
||||||
|
string FileName = RawPath.Substring(FirstSeparatorOffset + 1);
|
||||||
|
return $"{BasePath}:/{FileName}";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private string MakeDirAndGetFullPath(string Dir)
|
private string MakeDirAndGetFullPath(string Dir)
|
||||||
{
|
{
|
||||||
string FullPath = Path.Combine(GetBasePath(), Dir);
|
string FullPath = Path.Combine(GetBasePath(), Dir);
|
||||||
|
|
|
@ -68,6 +68,7 @@ namespace Ryujinx
|
||||||
};
|
};
|
||||||
|
|
||||||
Screen.MainLoop();
|
Screen.MainLoop();
|
||||||
|
Ns.OnFinish(EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
|
|
Loading…
Reference in a new issue