diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs index eef36a95..edbd8bb2 100644 --- a/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs @@ -179,6 +179,8 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("010010111101xx", InstEmit.Lea, typeof(OpCodeAluCbuf)); Set("0011011x11010x", InstEmit.Lea, typeof(OpCodeAluImm)); Set("0101101111010x", InstEmit.Lea, typeof(OpCodeAluReg)); + Set("000110xxxxxxxx", InstEmit.Lea_Hi, typeof(OpCodeAluCbuf)); + Set("0101101111011x", InstEmit.Lea_Hi, typeof(OpCodeAluReg)); Set("0100110001000x", InstEmit.Lop, typeof(OpCodeLopCbuf)); Set("0011100001000x", InstEmit.Lop, typeof(OpCodeLopImm)); Set("000001xxxxxxxx", InstEmit.Lop, typeof(OpCodeLopImm32)); diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs index b883edc1..5d00adb5 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs @@ -405,6 +405,51 @@ namespace Ryujinx.Graphics.Shader.Instructions // TODO: CC, X } + public static void Lea_Hi(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + bool isReg = op is OpCodeAluReg; + bool negateA; + int shift; + + if (isReg) + { + negateA = op.RawOpCode.Extract(37); + shift = op.RawOpCode.Extract(28, 5); + } + else + { + negateA = op.RawOpCode.Extract(56); + shift = op.RawOpCode.Extract(51, 5); + } + + Operand srcA = GetSrcA(context); + Operand srcB = GetSrcB(context); + Operand srcC = GetSrcC(context); + + Operand aLow = context.ShiftLeft(srcA, Const(shift)); + Operand aHigh = shift == 0 ? Const(0) : context.ShiftRightU32(srcA, Const(32 - shift)); + aHigh = context.BitwiseOr(aHigh, context.ShiftLeft(srcC, Const(shift))); + + if (negateA) + { + // Perform 64-bit negation by doing bitwise not of the value, + // then adding 1 and carrying over from low to high. + aLow = context.BitwiseNot(aLow); + aHigh = context.BitwiseNot(aHigh); + + aLow = AddWithCarry(context, aLow, Const(1), out Operand aLowCOut); + aHigh = context.IAdd(aHigh, aLowCOut); + } + + Operand res = context.IAdd(aHigh, srcB); + + context.Copy(GetDest(context), res); + + // TODO: CC, X + } + public static void Lop(EmitterContext context) { IOpCodeLop op = (IOpCodeLop)context.CurrOp; diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs index 588ec216..7fe969a0 100644 --- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs @@ -100,5 +100,15 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(GetNF(), context.FPCompareLess (dest, zero, fpType)); } } + + public static Operand AddWithCarry(EmitterContext context, Operand lhs, Operand rhs, out Operand carryOut) + { + Operand result = context.IAdd(lhs, rhs); + + // C = Rd < Rn + carryOut = context.INegate(context.ICompareLessUnsigned(result, lhs)); + + return result; + } } } \ No newline at end of file