private static void EmitAluBinary( ShaderIrBlock block, long opCode, ShaderOper oper, ShaderIrInst inst) { ShaderIrNode operA = opCode.Gpr8(), operB; switch (oper) { case ShaderOper.Cr: operB = opCode.Cbuf34(); break; case ShaderOper.Imm: operB = opCode.Imm19_20(); break; case ShaderOper.Rr: operB = opCode.Gpr20(); break; default: throw new ArgumentException(nameof(oper)); } ShaderIrNode op = new ShaderIrOp(inst, operA, operB); block.AddNode(opCode.PredNode(new ShaderIrAsg(opCode.Gpr0(), op))); }
private static void EmitAluBinary( ShaderIrBlock Block, long OpCode, ShaderOper Oper, ShaderIrInst Inst) { ShaderIrNode OperA = OpCode.Gpr8(), OperB; switch (Oper) { case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; case ShaderOper.RR: OperB = OpCode.Gpr20(); break; default: throw new ArgumentException(nameof(Oper)); } ShaderIrNode Op = new ShaderIrOp(Inst, OperA, OperB); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); }
private static bool IsFlowChange(ShaderIrInst Inst) { return(Inst == ShaderIrInst.Exit); }
private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) { //TODO: Support other formats. ShaderIrNode OperA = GetOperGpr8(OpCode); ShaderIrNode OperB = GetOperGpr20(OpCode); ShaderIrNode OperC = GetOperImm13_36(OpCode); int LutIndex; LutIndex = GetOperGpr0(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 1 : 0; LutIndex |= GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 2 : 0; if (LutIndex == 0) { //Both registers are RZ, color is not written anywhere. //So, the intruction is basically a no-op. return; } int ChMask = MaskLut[LutIndex, (OpCode >> 50) & 7]; for (int Ch = 0; Ch < 4; Ch++) { ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta); Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode)); } int RegInc = 0; ShaderIrOperGpr GetDst() { ShaderIrOperGpr Dst; switch (LutIndex) { case 1: Dst = GetOperGpr0(OpCode); break; case 2: Dst = GetOperGpr28(OpCode); break; case 3: Dst = (RegInc >> 1) != 0 ? GetOperGpr28(OpCode) : GetOperGpr0(OpCode); break; default: throw new InvalidOperationException(); } Dst.Index += RegInc++ & 1; return(Dst); } for (int Ch = 0; Ch < 4; Ch++) { if (!IsChannelUsed(ChMask, Ch)) { continue; } ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrOperGpr Dst = GetDst(); if (Dst.Index != ShaderIrOperGpr.ZRIndex) { Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode)); } } }
private static void EmitSetp(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) { bool Aa = ((OpCode >> 7) & 1) != 0; bool Np = ((OpCode >> 42) & 1) != 0; bool Na = ((OpCode >> 43) & 1) != 0; bool Ab = ((OpCode >> 44) & 1) != 0; ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; switch (Oper) { case ShaderOper.CR: OperB = GetOperCbuf34(OpCode); break; case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break; case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break; case ShaderOper.RR: OperB = GetOperGpr20(OpCode); break; default: throw new ArgumentException(nameof(Oper)); } ShaderIrInst CmpInst; if (IsFloat) { OperA = GetAluAbsNeg(OperA, Aa, Na); OperB = GetAluAbs(OperB, Ab); CmpInst = GetCmpF(OpCode); } else { CmpInst = GetCmp(OpCode); } ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB); ShaderIrOperPred P0Node = GetOperPred3(OpCode); ShaderIrOperPred P1Node = GetOperPred0(OpCode); ShaderIrOperPred P2Node = GetOperPred39(OpCode); Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode)); ShaderIrInst LopInst = GetBLop(OpCode); if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst) { return; } ShaderIrNode P2NNode = P2Node; if (Np) { P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode); } Op = new ShaderIrOp(ShaderIrInst.Bnot, P0Node); Op = new ShaderIrOp(LopInst, Op, P2NNode); Block.AddNode(GetPredNode(new ShaderIrAsg(P1Node, Op), OpCode)); Op = new ShaderIrOp(LopInst, P0Node, P2NNode); Block.AddNode(GetPredNode(new ShaderIrAsg(P0Node, Op), OpCode)); }
private static void EmitSet(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) { bool Na = ((OpCode >> 43) & 1) != 0; bool Ab = ((OpCode >> 44) & 1) != 0; bool Nb = ((OpCode >> 53) & 1) != 0; bool Aa = ((OpCode >> 54) & 1) != 0; ShaderIrNode OperA = GetOperGpr8(OpCode), OperB; switch (Oper) { case ShaderOper.CR: OperB = GetOperCbuf34(OpCode); break; case ShaderOper.Imm: OperB = GetOperImm19_20(OpCode); break; case ShaderOper.Immf: OperB = GetOperImmf19_20(OpCode); break; case ShaderOper.RR: OperB = GetOperGpr20(OpCode); break; default: throw new ArgumentException(nameof(Oper)); } ShaderIrInst CmpInst; if (IsFloat) { OperA = GetAluAbsNeg(OperA, Aa, Na); OperB = GetAluAbsNeg(OperB, Ab, Nb); CmpInst = GetCmpF(OpCode); } else { CmpInst = GetCmp(OpCode); } ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB); ShaderIrInst LopInst = GetBLop(OpCode); ShaderIrOperPred PNode = GetOperPred39(OpCode); ShaderIrOperImmf Imm0 = new ShaderIrOperImmf(0); ShaderIrOperImmf Imm1 = new ShaderIrOperImmf(1); ShaderIrNode Asg0 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm0); ShaderIrNode Asg1 = new ShaderIrAsg(GetOperGpr0(OpCode), Imm1); if (LopInst != ShaderIrInst.Band || !PNode.IsConst) { ShaderIrOp Op2 = new ShaderIrOp(LopInst, Op, PNode); Asg0 = new ShaderIrCond(Op2, Asg0, Not: true); Asg1 = new ShaderIrCond(Op2, Asg1, Not: false); } else { Asg0 = new ShaderIrCond(Op, Asg0, Not: true); Asg1 = new ShaderIrCond(Op, Asg1, Not: false); } Block.AddNode(GetPredNode(Asg0, OpCode)); Block.AddNode(GetPredNode(Asg1, OpCode)); }
private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) { //TODO: Support other formats. int LutIndex; LutIndex = !OpCode.Gpr0().IsConst ? 1 : 0; LutIndex |= !OpCode.Gpr28().IsConst ? 2 : 0; if (LutIndex == 0) { //Both destination registers are RZ, do nothing. return; } bool Fp16 = !OpCode.Read(59); int DstIncrement = 0; ShaderIrOperGpr GetDst() { ShaderIrOperGpr Dst; if (Fp16) { //FP16 mode, two components are packed on the two //halfs of a 32-bits register, as two half-float values. int HalfPart = DstIncrement & 1; switch (LutIndex) { case 1: Dst = OpCode.GprHalf0(HalfPart); break; case 2: Dst = OpCode.GprHalf28(HalfPart); break; case 3: Dst = (DstIncrement >> 1) != 0 ? OpCode.GprHalf28(HalfPart) : OpCode.GprHalf0(HalfPart); break; default: throw new InvalidOperationException(); } } else { //32-bits mode, each component uses one register. //Two components uses two consecutive registers. switch (LutIndex) { case 1: Dst = OpCode.Gpr0(); break; case 2: Dst = OpCode.Gpr28(); break; case 3: Dst = (DstIncrement >> 1) != 0 ? OpCode.Gpr28() : OpCode.Gpr0(); break; default: throw new InvalidOperationException(); } Dst.Index += DstIncrement & 1; } DstIncrement++; return(Dst); } int ChMask = MaskLut[LutIndex, OpCode.Read(50, 7)]; if (ChMask == 0) { //All channels are disabled, do nothing. return; } ShaderIrNode OperC = OpCode.Imm13_36(); ShaderIrOperGpr Coord0 = ShaderIrOperGpr.MakeTemporary(0); ShaderIrOperGpr Coord1 = ShaderIrOperGpr.MakeTemporary(1); Block.AddNode(new ShaderIrAsg(Coord0, OpCode.Gpr8())); Block.AddNode(new ShaderIrAsg(Coord1, OpCode.Gpr20())); for (int Ch = 0; Ch < 4; Ch++) { if (!IsChannelUsed(ChMask, Ch)) { continue; } ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrOp Op = new ShaderIrOp(Inst, Coord0, Coord1, OperC, Meta); ShaderIrOperGpr Dst = GetDst(); if (Dst.IsValidRegister && !Dst.IsConst) { Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); } } }
private static void EmitMnmx(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) { bool NegB = OpCode.Read(45); bool AbsA = OpCode.Read(46); bool NegA = OpCode.Read(48); bool AbsB = OpCode.Read(49); ShaderIrNode OperA = OpCode.Gpr8(), OperB; if (IsFloat) { OperA = GetAluFabsFneg(OperA, AbsA, NegA); } else { OperA = GetAluIabsIneg(OperA, AbsA, NegA); } switch (Oper) { case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break; case ShaderOper.RR: OperB = OpCode.Gpr20(); break; default: throw new ArgumentException(nameof(Oper)); } if (IsFloat) { OperB = GetAluFabsFneg(OperB, AbsB, NegB); } else { OperB = GetAluIabsIneg(OperB, AbsB, NegB); } ShaderIrOperPred Pred = OpCode.Pred39(); ShaderIrOp Op; ShaderIrInst MaxInst = IsFloat ? ShaderIrInst.Fmax : ShaderIrInst.Max; ShaderIrInst MinInst = IsFloat ? ShaderIrInst.Fmin : ShaderIrInst.Min; if (Pred.IsConst) { bool IsMax = OpCode.Read(42); Op = new ShaderIrOp(IsMax ? MaxInst : MinInst, OperA, OperB); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); } else { ShaderIrNode PredN = OpCode.Pred39N(); ShaderIrOp OpMax = new ShaderIrOp(MaxInst, OperA, OperB); ShaderIrOp OpMin = new ShaderIrOp(MinInst, OperA, OperB); ShaderIrAsg AsgMax = new ShaderIrAsg(OpCode.Gpr0(), OpMax); ShaderIrAsg AsgMin = new ShaderIrAsg(OpCode.Gpr0(), OpMin); Block.AddNode(OpCode.PredNode(new ShaderIrCond(PredN, AsgMax, Not: true))); Block.AddNode(OpCode.PredNode(new ShaderIrCond(PredN, AsgMin, Not: false))); } }
private static void EmitSetp(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) { bool AbsA = OpCode.Read(7); bool NegP = OpCode.Read(42); bool NegA = OpCode.Read(43); bool AbsB = OpCode.Read(44); ShaderIrNode OperA = OpCode.Gpr8(), OperB; switch (Oper) { case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break; case ShaderOper.RR: OperB = OpCode.Gpr20(); break; default: throw new ArgumentException(nameof(Oper)); } ShaderIrInst CmpInst; if (IsFloat) { OperA = GetAluFabsFneg(OperA, AbsA, NegA); OperB = GetAluFabs(OperB, AbsB); CmpInst = OpCode.CmpF(); } else { CmpInst = OpCode.Cmp(); } ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB); ShaderIrOperPred P0Node = OpCode.Pred3(); ShaderIrOperPred P1Node = OpCode.Pred0(); ShaderIrOperPred P2Node = OpCode.Pred39(); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op))); ShaderIrInst LopInst = OpCode.BLop45(); if (LopInst == ShaderIrInst.Band && P1Node.IsConst && P2Node.IsConst) { return; } ShaderIrNode P2NNode = P2Node; if (NegP) { P2NNode = new ShaderIrOp(ShaderIrInst.Bnot, P2NNode); } Op = new ShaderIrOp(ShaderIrInst.Bnot, P0Node); Op = new ShaderIrOp(LopInst, Op, P2NNode); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P1Node, Op))); Op = new ShaderIrOp(LopInst, P0Node, P2NNode); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(P0Node, Op))); }
private static void EmitTex(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) { //TODO: Support other formats. ShaderIrNode OperA = GetOperGpr8(OpCode); ShaderIrNode OperB = GetOperGpr20(OpCode); ShaderIrNode OperC = GetOperImm13_36(OpCode); bool TwoDests = GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex; int ChMask; switch ((OpCode >> 50) & 7) { case 0: ChMask = TwoDests ? 0x7 : 0x1; break; case 1: ChMask = TwoDests ? 0xb : 0x2; break; case 2: ChMask = TwoDests ? 0xd : 0x4; break; case 3: ChMask = TwoDests ? 0xe : 0x8; break; case 4: ChMask = TwoDests ? 0xf : 0x3; break; default: throw new InvalidOperationException(); } for (int Ch = 0; Ch < 4; Ch++) { ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta); Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode)); } int RegInc = 0; for (int Ch = 0; Ch < 4; Ch++) { if (!IsChannelUsed(ChMask, Ch)) { continue; } ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrOperGpr Dst = (RegInc >> 1) != 0 ? GetOperGpr28(OpCode) : GetOperGpr0(OpCode); Dst.Index += RegInc++ & 1; if (Dst.Index >= ShaderIrOperGpr.ZRIndex) { continue; } Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode)); } }
private static void EmitSet(ShaderIrBlock block, long opCode, bool isFloat, ShaderOper oper) { bool negA = opCode.Read(43); bool absB = opCode.Read(44); bool negB = opCode.Read(53); bool absA = opCode.Read(54); bool boolFloat = opCode.Read(isFloat ? 52 : 44); ShaderIrNode operA = opCode.Gpr8(), operB; switch (oper) { case ShaderOper.Cr: operB = opCode.Cbuf34(); break; case ShaderOper.Imm: operB = opCode.Imm19_20(); break; case ShaderOper.Immf: operB = opCode.Immf19_20(); break; case ShaderOper.Rr: operB = opCode.Gpr20(); break; default: throw new ArgumentException(nameof(oper)); } ShaderIrInst cmpInst; if (isFloat) { operA = GetAluFabsFneg(operA, absA, negA); operB = GetAluFabsFneg(operB, absB, negB); cmpInst = opCode.CmpF(); } else { cmpInst = opCode.Cmp(); } ShaderIrOp op = new ShaderIrOp(cmpInst, operA, operB); ShaderIrInst lopInst = opCode.BLop45(); ShaderIrOperPred pNode = opCode.Pred39(); ShaderIrNode imm0, imm1; if (boolFloat) { imm0 = new ShaderIrOperImmf(0); imm1 = new ShaderIrOperImmf(1); } else { imm0 = new ShaderIrOperImm(0); imm1 = new ShaderIrOperImm(-1); } ShaderIrNode asg0 = new ShaderIrAsg(opCode.Gpr0(), imm0); ShaderIrNode asg1 = new ShaderIrAsg(opCode.Gpr0(), imm1); if (lopInst != ShaderIrInst.Band || !pNode.IsConst) { ShaderIrOp op2 = new ShaderIrOp(lopInst, op, pNode); asg0 = new ShaderIrCond(op2, asg0, not: true); asg1 = new ShaderIrCond(op2, asg1, not: false); } else { asg0 = new ShaderIrCond(op, asg0, not: true); asg1 = new ShaderIrCond(op, asg1, not: false); } block.AddNode(opCode.PredNode(asg0)); block.AddNode(opCode.PredNode(asg1)); }
private static void EmitMnmx(ShaderIrBlock block, long opCode, bool isFloat, ShaderOper oper) { bool negB = opCode.Read(45); bool absA = opCode.Read(46); bool negA = opCode.Read(48); bool absB = opCode.Read(49); ShaderIrNode operA = opCode.Gpr8(), operB; if (isFloat) { operA = GetAluFabsFneg(operA, absA, negA); } else { operA = GetAluIabsIneg(operA, absA, negA); } switch (oper) { case ShaderOper.Cr: operB = opCode.Cbuf34(); break; case ShaderOper.Imm: operB = opCode.Imm19_20(); break; case ShaderOper.Immf: operB = opCode.Immf19_20(); break; case ShaderOper.Rr: operB = opCode.Gpr20(); break; default: throw new ArgumentException(nameof(oper)); } if (isFloat) { operB = GetAluFabsFneg(operB, absB, negB); } else { operB = GetAluIabsIneg(operB, absB, negB); } ShaderIrOperPred pred = opCode.Pred39(); ShaderIrOp op; ShaderIrInst maxInst = isFloat ? ShaderIrInst.Fmax : ShaderIrInst.Max; ShaderIrInst minInst = isFloat ? ShaderIrInst.Fmin : ShaderIrInst.Min; if (pred.IsConst) { bool isMax = opCode.Read(42); op = new ShaderIrOp(isMax ? maxInst : minInst, operA, operB); block.AddNode(opCode.PredNode(new ShaderIrAsg(opCode.Gpr0(), op))); } else { ShaderIrNode predN = opCode.Pred39N(); ShaderIrOp opMax = new ShaderIrOp(maxInst, operA, operB); ShaderIrOp opMin = new ShaderIrOp(minInst, operA, operB); ShaderIrAsg asgMax = new ShaderIrAsg(opCode.Gpr0(), opMax); ShaderIrAsg asgMin = new ShaderIrAsg(opCode.Gpr0(), opMin); block.AddNode(opCode.PredNode(new ShaderIrCond(predN, asgMax, not: true))); block.AddNode(opCode.PredNode(new ShaderIrCond(predN, asgMin, not: false))); } }
private static void EmitXmad(ShaderIrBlock block, long opCode, ShaderOper oper) { bool signedA = opCode.Read(48); bool signedB = opCode.Read(49); bool highB = opCode.Read(52); bool highA = opCode.Read(53); int mode = opCode.Read(50, 7); ShaderIrNode operA = opCode.Gpr8(), operB, operC; switch (oper) { case ShaderOper.Cr: operB = opCode.Cbuf34(); break; case ShaderOper.Imm: operB = opCode.ImmU16_20(); break; case ShaderOper.Rc: operB = opCode.Gpr39(); break; case ShaderOper.Rr: operB = opCode.Gpr20(); break; default: throw new ArgumentException(nameof(oper)); } ShaderIrNode operB2 = operB; if (oper == ShaderOper.Imm) { int imm = ((ShaderIrOperImm)operB2).Value; if (!highB) { imm <<= 16; } if (signedB) { imm >>= 16; } else { imm = (int)((uint)imm >> 16); } operB2 = new ShaderIrOperImm(imm); } ShaderIrOperImm imm16 = new ShaderIrOperImm(16); //If we are working with the lower 16-bits of the A/B operands, //we need to shift the lower 16-bits to the top 16-bits. Later, //they will be right shifted. For U16 types, this will be a logical //right shift, and for S16 types, a arithmetic right shift. if (!highA) { operA = new ShaderIrOp(ShaderIrInst.Lsl, operA, imm16); } if (!highB && oper != ShaderOper.Imm) { operB2 = new ShaderIrOp(ShaderIrInst.Lsl, operB2, imm16); } ShaderIrInst shiftA = signedA ? ShaderIrInst.Asr : ShaderIrInst.Lsr; ShaderIrInst shiftB = signedB ? ShaderIrInst.Asr : ShaderIrInst.Lsr; operA = new ShaderIrOp(shiftA, operA, imm16); if (oper != ShaderOper.Imm) { operB2 = new ShaderIrOp(shiftB, operB2, imm16); } bool productShiftLeft = false; bool merge = false; if (oper == ShaderOper.Rc) { operC = opCode.Cbuf34(); } else { operC = opCode.Gpr39(); productShiftLeft = opCode.Read(36); merge = opCode.Read(37); } ShaderIrOp mulOp = new ShaderIrOp(ShaderIrInst.Mul, operA, operB2); if (productShiftLeft) { mulOp = new ShaderIrOp(ShaderIrInst.Lsl, mulOp, imm16); } switch ((XmadMode)mode) { case XmadMode.Clo: operC = ExtendTo32(operC, signed: false, size: 16); break; case XmadMode.Chi: operC = new ShaderIrOp(ShaderIrInst.Lsr, operC, imm16); break; case XmadMode.Cbcc: { ShaderIrOp operBLsh16 = new ShaderIrOp(ShaderIrInst.Lsl, operB, imm16); operC = new ShaderIrOp(ShaderIrInst.Add, operC, operBLsh16); break; } case XmadMode.Csfu: { ShaderIrOperImm imm31 = new ShaderIrOperImm(31); ShaderIrOp signAdjustA = new ShaderIrOp(ShaderIrInst.Lsr, operA, imm31); ShaderIrOp signAdjustB = new ShaderIrOp(ShaderIrInst.Lsr, operB2, imm31); signAdjustA = new ShaderIrOp(ShaderIrInst.Lsl, signAdjustA, imm16); signAdjustB = new ShaderIrOp(ShaderIrInst.Lsl, signAdjustB, imm16); ShaderIrOp signAdjust = new ShaderIrOp(ShaderIrInst.Add, signAdjustA, signAdjustB); operC = new ShaderIrOp(ShaderIrInst.Sub, operC, signAdjust); break; } } ShaderIrOp addOp = new ShaderIrOp(ShaderIrInst.Add, mulOp, operC); if (merge) { ShaderIrOperImm imm16Mask = new ShaderIrOperImm(0xffff); addOp = new ShaderIrOp(ShaderIrInst.And, addOp, imm16Mask); operB = new ShaderIrOp(ShaderIrInst.Lsl, operB, imm16); addOp = new ShaderIrOp(ShaderIrInst.Or, addOp, operB); } block.AddNode(opCode.PredNode(new ShaderIrAsg(opCode.Gpr0(), addOp))); }
private static void EmitSetp(ShaderIrBlock block, long opCode, bool isFloat, ShaderOper oper) { bool absA = opCode.Read(7); bool negP = opCode.Read(42); bool negA = opCode.Read(43); bool absB = opCode.Read(44); ShaderIrNode operA = opCode.Gpr8(), operB; switch (oper) { case ShaderOper.Cr: operB = opCode.Cbuf34(); break; case ShaderOper.Imm: operB = opCode.Imm19_20(); break; case ShaderOper.Immf: operB = opCode.Immf19_20(); break; case ShaderOper.Rr: operB = opCode.Gpr20(); break; default: throw new ArgumentException(nameof(oper)); } ShaderIrInst cmpInst; if (isFloat) { operA = GetAluFabsFneg(operA, absA, negA); operB = GetAluFabs(operB, absB); cmpInst = opCode.CmpF(); } else { cmpInst = opCode.Cmp(); } ShaderIrOp op = new ShaderIrOp(cmpInst, operA, operB); ShaderIrOperPred p0Node = opCode.Pred3(); ShaderIrOperPred p1Node = opCode.Pred0(); ShaderIrOperPred p2Node = opCode.Pred39(); block.AddNode(opCode.PredNode(new ShaderIrAsg(p0Node, op))); ShaderIrInst lopInst = opCode.BLop45(); if (lopInst == ShaderIrInst.Band && p1Node.IsConst && p2Node.IsConst) { return; } ShaderIrNode p2NNode = p2Node; if (negP) { p2NNode = new ShaderIrOp(ShaderIrInst.Bnot, p2NNode); } op = new ShaderIrOp(ShaderIrInst.Bnot, p0Node); op = new ShaderIrOp(lopInst, op, p2NNode); block.AddNode(opCode.PredNode(new ShaderIrAsg(p1Node, op))); op = new ShaderIrOp(lopInst, p0Node, p2NNode); block.AddNode(opCode.PredNode(new ShaderIrAsg(p0Node, op))); }
private static void EmitF2I(ShaderIrBlock block, long opCode, ShaderOper oper) { IntType type = GetIntType(opCode); if (type == IntType.U64 || type == IntType.S64) { //TODO: 64-bits support. //Note: GLSL doesn't support 64-bits integers. throw new NotImplementedException(); } bool negA = opCode.Read(45); bool absA = opCode.Read(49); ShaderIrNode operA; switch (oper) { case ShaderOper.Cr: operA = opCode.Cbuf34(); break; case ShaderOper.Immf: operA = opCode.Immf19_20(); break; case ShaderOper.Rr: operA = opCode.Gpr20(); break; default: throw new ArgumentException(nameof(oper)); } operA = GetAluFabsFneg(operA, absA, negA); ShaderIrInst roundInst = GetRoundInst(opCode); if (roundInst != ShaderIrInst.Invalid) { operA = new ShaderIrOp(roundInst, operA); } bool signed = type >= IntType.S8; int size = 8 << ((int)type & 3); if (size < 32) { uint mask = uint.MaxValue >> (32 - size); float cMin = 0; float cMax = mask; if (signed) { uint halfMask = mask >> 1; cMin -= halfMask + 1; cMax = halfMask; } ShaderIrOperImmf min = new ShaderIrOperImmf(cMin); ShaderIrOperImmf max = new ShaderIrOperImmf(cMax); operA = new ShaderIrOp(ShaderIrInst.Fclamp, operA, min, max); } ShaderIrInst inst = signed ? ShaderIrInst.Ftos : ShaderIrInst.Ftou; ShaderIrNode op = new ShaderIrOp(inst, operA); block.AddNode(opCode.PredNode(new ShaderIrAsg(opCode.Gpr0(), op))); }
private static void EmitF2i(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { IntType Type = GetIntType(OpCode); if (Type == IntType.U64 || Type == IntType.S64) { //TODO: 64-bits support. //Note: GLSL doesn't support 64-bits integers. throw new NotImplementedException(); } bool NegA = ((OpCode >> 45) & 1) != 0; bool AbsA = ((OpCode >> 49) & 1) != 0; ShaderIrNode OperA; switch (Oper) { case ShaderOper.CR: OperA = GetOperCbuf34(OpCode); break; case ShaderOper.Immf: OperA = GetOperImmf19_20(OpCode); break; case ShaderOper.RR: OperA = GetOperGpr20(OpCode); break; default: throw new ArgumentException(nameof(Oper)); } OperA = GetAluFabsFneg(OperA, AbsA, NegA); ShaderIrInst RoundInst = GetRoundInst(OpCode); if (RoundInst != ShaderIrInst.Invalid) { OperA = new ShaderIrOp(RoundInst, OperA); } bool Signed = Type >= IntType.S8; int Size = 8 << ((int)Type & 3); if (Size < 32) { uint Mask = uint.MaxValue >> (32 - Size); float CMin = 0; float CMax = Mask; if (Signed) { uint HalfMask = Mask >> 1; CMin -= HalfMask + 1; CMax = HalfMask; } ShaderIrOperImmf IMin = new ShaderIrOperImmf(CMin); ShaderIrOperImmf IMax = new ShaderIrOperImmf(CMax); OperA = new ShaderIrOp(ShaderIrInst.Fclamp, OperA, IMin, IMax); } ShaderIrInst Inst = Signed ? ShaderIrInst.Ftos : ShaderIrInst.Ftou; ShaderIrNode Op = new ShaderIrOp(Inst, OperA); Block.AddNode(GetPredNode(new ShaderIrAsg(GetOperGpr0(OpCode), Op), OpCode)); }
private static void EmitTex(ShaderIrBlock block, long opCode, TextureInstructionSuffix textureInstructionSuffix, bool gprHandle) { bool isArray = opCode.HasArray(); GalTextureTarget textureTarget = TexToTextureTarget(opCode.Read(28, 6), isArray); bool hasDepthCompare = opCode.Read(0x32); if (hasDepthCompare) { textureInstructionSuffix |= TextureInstructionSuffix.Dc; } ShaderIrOperGpr[] coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(textureTarget)]; int indexExtraCoord = 0; if (isArray) { indexExtraCoord++; coords[coords.Length - 1] = opCode.Gpr8(); } for (int index = 0; index < coords.Length - indexExtraCoord; index++) { ShaderIrOperGpr coordReg = opCode.Gpr8(); coordReg.Index += index; coordReg.Index += indexExtraCoord; if (!coordReg.IsValidRegister) { coordReg.Index = ShaderIrOperGpr.ZrIndex; } coords[index] = coordReg; } int chMask = opCode.Read(31, 0xf); ShaderIrOperGpr levelOfDetail = null; ShaderIrOperGpr offset = null; ShaderIrOperGpr depthCompare = null; // TODO: determine first argument when TEX.B is used int operBIndex = gprHandle ? 1 : 0; if ((textureInstructionSuffix & TextureInstructionSuffix.Ll) != 0 || (textureInstructionSuffix & TextureInstructionSuffix.Lb) != 0 || (textureInstructionSuffix & TextureInstructionSuffix.Lba) != 0 || (textureInstructionSuffix & TextureInstructionSuffix.Lla) != 0) { levelOfDetail = opCode.Gpr20(); levelOfDetail.Index += operBIndex; operBIndex++; } if ((textureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0) { offset = opCode.Gpr20(); offset.Index += operBIndex; operBIndex++; } if ((textureInstructionSuffix & TextureInstructionSuffix.Dc) != 0) { depthCompare = opCode.Gpr20(); depthCompare.Index += operBIndex; operBIndex++; } // ??? ShaderIrNode operC = gprHandle ? (ShaderIrNode)opCode.Gpr20() : (ShaderIrNode)opCode.Imm13_36(); ShaderIrInst inst = gprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs; coords = CoordsRegistersToTempRegisters(block, coords); int regInc = 0; for (int ch = 0; ch < 4; ch++) { if (!IsChannelUsed(chMask, ch)) { continue; } ShaderIrOperGpr dst = opCode.Gpr0(); dst.Index += regInc++; if (!dst.IsValidRegister || dst.IsConst) { continue; } ShaderIrMetaTex meta = new ShaderIrMetaTex(ch, textureTarget, textureInstructionSuffix, coords) { LevelOfDetail = levelOfDetail, Offset = offset, DepthCompare = depthCompare }; ShaderIrOp op = new ShaderIrOp(inst, coords[0], coords.Length > 1 ? coords[1] : null, operC, meta); block.AddNode(opCode.PredNode(new ShaderIrAsg(dst, op))); } }
private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { //TODO: Confirm SignAB/C, it is just a guess. //TODO: Implement Mode 3 (CSFU), what it does? bool SignAB = OpCode.Read(48); bool SignC = OpCode.Read(49); bool HighB = OpCode.Read(52); bool HighA = OpCode.Read(53); int Mode = OpCode.Read(50, 7); ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC; ShaderIrOperImm Imm16 = new ShaderIrOperImm(16); ShaderIrOperImm ImmMsk = new ShaderIrOperImm(0xffff); ShaderIrInst ShiftAB = SignAB ? ShaderIrInst.Asr : ShaderIrInst.Lsr; ShaderIrInst ShiftC = SignC ? ShaderIrInst.Asr : ShaderIrInst.Lsr; if (HighA) { OperA = new ShaderIrOp(ShiftAB, OperA, Imm16); } switch (Oper) { case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; case ShaderOper.RC: OperB = OpCode.Gpr39(); break; case ShaderOper.RR: OperB = OpCode.Gpr20(); break; default: throw new ArgumentException(nameof(Oper)); } bool ProductShiftLeft = false, Merge = false; if (Oper == ShaderOper.RC) { OperC = OpCode.Cbuf34(); } else { OperC = OpCode.Gpr39(); ProductShiftLeft = OpCode.Read(36); Merge = OpCode.Read(37); } switch (Mode) { //CLO. case 1: OperC = ExtendTo32(OperC, SignC, 16); break; //CHI. case 2: OperC = new ShaderIrOp(ShiftC, OperC, Imm16); break; } ShaderIrNode OperBH = OperB; if (HighB) { OperBH = new ShaderIrOp(ShiftAB, OperBH, Imm16); } ShaderIrOp MulOp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperBH); if (ProductShiftLeft) { MulOp = new ShaderIrOp(ShaderIrInst.Lsl, MulOp, Imm16); } ShaderIrOp AddOp = new ShaderIrOp(ShaderIrInst.Add, MulOp, OperC); if (Merge) { AddOp = new ShaderIrOp(ShaderIrInst.And, AddOp, ImmMsk); OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16); AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB); } if (Mode == 4) { OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16); AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB); } Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), AddOp))); }
private static void EmitTexs(ShaderIrBlock block, long opCode, ShaderIrInst inst, GalTextureTarget textureTarget, TextureInstructionSuffix textureInstructionSuffix) { if (inst == ShaderIrInst.Txlf && textureTarget == GalTextureTarget.CubeArray) { throw new InvalidOperationException("TLDS instructions cannot use CUBE modifier!"); } bool isArray = ImageUtils.IsArray(textureTarget); ShaderIrOperGpr[] coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(textureTarget)]; ShaderIrOperGpr operA = opCode.Gpr8(); ShaderIrOperGpr operB = opCode.Gpr20(); ShaderIrOperGpr suffixExtra = opCode.Gpr20(); suffixExtra.Index += 1; int coordStartIndex = 0; if (isArray) { coordStartIndex++; coords[coords.Length - 1] = opCode.Gpr8(); } switch (coords.Length - coordStartIndex) { case 1: coords[0] = opCode.Gpr8(); break; case 2: coords[0] = opCode.Gpr8(); coords[0].Index += coordStartIndex; break; case 3: coords[0] = opCode.Gpr8(); coords[0].Index += coordStartIndex; coords[1] = opCode.Gpr8(); coords[1].Index += 1 + coordStartIndex; break; default: throw new NotSupportedException($"{coords.Length - coordStartIndex} coords textures aren't supported in TEXS"); } int operBIndex = 0; ShaderIrOperGpr levelOfDetail = null; ShaderIrOperGpr offset = null; ShaderIrOperGpr depthCompare = null; // OperB is always the last value // Not applicable to 1d textures if (coords.Length - coordStartIndex != 1) { coords[coords.Length - coordStartIndex - 1] = operB; operBIndex++; } // Encoding of TEXS/TLDS is a bit special and change for 2d textures // NOTE: OperA seems to hold at best two args. // On 2D textures, if no suffix need an additional values, Y is stored in OperB, otherwise coords are in OperA and the additional values is in OperB. if (textureInstructionSuffix != TextureInstructionSuffix.None && textureInstructionSuffix != TextureInstructionSuffix.Lz && textureTarget == GalTextureTarget.TwoD) { coords[coords.Length - coordStartIndex - 1] = opCode.Gpr8(); coords[coords.Length - coordStartIndex - 1].Index += coords.Length - coordStartIndex - 1; operBIndex--; } // TODO: Find what MZ does and what changes about the encoding (Maybe Multisample?) if ((textureInstructionSuffix & TextureInstructionSuffix.Ll) != 0) { levelOfDetail = opCode.Gpr20(); levelOfDetail.Index += operBIndex; operBIndex++; } if ((textureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0) { offset = opCode.Gpr20(); offset.Index += operBIndex; operBIndex++; } if ((textureInstructionSuffix & TextureInstructionSuffix.Dc) != 0) { depthCompare = opCode.Gpr20(); depthCompare.Index += operBIndex; operBIndex++; } int lutIndex; lutIndex = !opCode.Gpr0().IsConst ? 1 : 0; lutIndex |= !opCode.Gpr28().IsConst ? 2 : 0; if (lutIndex == 0) { //Both destination registers are RZ, do nothing. return; } bool fp16 = !opCode.Read(59); int dstIncrement = 0; ShaderIrOperGpr GetDst() { ShaderIrOperGpr dst; if (fp16) { //FP16 mode, two components are packed on the two //halfs of a 32-bits register, as two half-float values. int halfPart = dstIncrement & 1; switch (lutIndex) { case 1: dst = opCode.GprHalf0(halfPart); break; case 2: dst = opCode.GprHalf28(halfPart); break; case 3: dst = (dstIncrement >> 1) != 0 ? opCode.GprHalf28(halfPart) : opCode.GprHalf0(halfPart); break; default: throw new InvalidOperationException(); } } else { //32-bits mode, each component uses one register. //Two components uses two consecutive registers. switch (lutIndex) { case 1: dst = opCode.Gpr0(); break; case 2: dst = opCode.Gpr28(); break; case 3: dst = (dstIncrement >> 1) != 0 ? opCode.Gpr28() : opCode.Gpr0(); break; default: throw new InvalidOperationException(); } dst.Index += dstIncrement & 1; } dstIncrement++; return(dst); } int chMask = _maskLut[lutIndex, opCode.Read(50, 7)]; if (chMask == 0) { //All channels are disabled, do nothing. return; } ShaderIrNode operC = opCode.Imm13_36(); coords = CoordsRegistersToTempRegisters(block, coords); for (int ch = 0; ch < 4; ch++) { if (!IsChannelUsed(chMask, ch)) { continue; } ShaderIrMetaTex meta = new ShaderIrMetaTex(ch, textureTarget, textureInstructionSuffix, coords) { LevelOfDetail = levelOfDetail, Offset = offset, DepthCompare = depthCompare }; ShaderIrOp op = new ShaderIrOp(inst, operA, operB, operC, meta); ShaderIrOperGpr dst = GetDst(); if (dst.IsValidRegister && !dst.IsConst) { block.AddNode(opCode.PredNode(new ShaderIrAsg(dst, op))); } } }
private static void EmitSet(ShaderIrBlock Block, long OpCode, bool IsFloat, ShaderOper Oper) { bool NegA = OpCode.Read(43); bool AbsB = OpCode.Read(44); bool NegB = OpCode.Read(53); bool AbsA = OpCode.Read(54); bool BoolFloat = OpCode.Read(IsFloat ? 52 : 44); ShaderIrNode OperA = OpCode.Gpr8(), OperB; switch (Oper) { case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; case ShaderOper.Imm: OperB = OpCode.Imm19_20(); break; case ShaderOper.Immf: OperB = OpCode.Immf19_20(); break; case ShaderOper.RR: OperB = OpCode.Gpr20(); break; default: throw new ArgumentException(nameof(Oper)); } ShaderIrInst CmpInst; if (IsFloat) { OperA = GetAluFabsFneg(OperA, AbsA, NegA); OperB = GetAluFabsFneg(OperB, AbsB, NegB); CmpInst = OpCode.CmpF(); } else { CmpInst = OpCode.Cmp(); } ShaderIrOp Op = new ShaderIrOp(CmpInst, OperA, OperB); ShaderIrInst LopInst = OpCode.BLop45(); ShaderIrOperPred PNode = OpCode.Pred39(); ShaderIrNode Imm0, Imm1; if (BoolFloat) { Imm0 = new ShaderIrOperImmf(0); Imm1 = new ShaderIrOperImmf(1); } else { Imm0 = new ShaderIrOperImm(0); Imm1 = new ShaderIrOperImm(-1); } ShaderIrNode Asg0 = new ShaderIrAsg(OpCode.Gpr0(), Imm0); ShaderIrNode Asg1 = new ShaderIrAsg(OpCode.Gpr0(), Imm1); if (LopInst != ShaderIrInst.Band || !PNode.IsConst) { ShaderIrOp Op2 = new ShaderIrOp(LopInst, Op, PNode); Asg0 = new ShaderIrCond(Op2, Asg0, Not: true); Asg1 = new ShaderIrCond(Op2, Asg1, Not: false); } else { Asg0 = new ShaderIrCond(Op, Asg0, Not: true); Asg1 = new ShaderIrCond(Op, Asg1, Not: false); } Block.AddNode(OpCode.PredNode(Asg0)); Block.AddNode(OpCode.PredNode(Asg1)); }
private static void EmitXmad(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { bool SignedA = OpCode.Read(48); bool SignedB = OpCode.Read(49); bool HighB = OpCode.Read(52); bool HighA = OpCode.Read(53); int Mode = OpCode.Read(50, 7); ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC; switch (Oper) { case ShaderOper.CR: OperB = OpCode.Cbuf34(); break; case ShaderOper.Imm: OperB = OpCode.ImmU16_20(); break; case ShaderOper.RC: OperB = OpCode.Gpr39(); break; case ShaderOper.RR: OperB = OpCode.Gpr20(); break; default: throw new ArgumentException(nameof(Oper)); } ShaderIrNode OperB2 = OperB; if (Oper == ShaderOper.Imm) { int Imm = ((ShaderIrOperImm)OperB2).Value; if (!HighB) { Imm <<= 16; } if (SignedB) { Imm >>= 16; } else { Imm = (int)((uint)Imm >> 16); } OperB2 = new ShaderIrOperImm(Imm); } ShaderIrOperImm Imm16 = new ShaderIrOperImm(16); //If we are working with the lower 16-bits of the A/B operands, //we need to shift the lower 16-bits to the top 16-bits. Later, //they will be right shifted. For U16 types, this will be a logical //right shift, and for S16 types, a arithmetic right shift. if (!HighA) { OperA = new ShaderIrOp(ShaderIrInst.Lsl, OperA, Imm16); } if (!HighB && Oper != ShaderOper.Imm) { OperB2 = new ShaderIrOp(ShaderIrInst.Lsl, OperB2, Imm16); } ShaderIrInst ShiftA = SignedA ? ShaderIrInst.Asr : ShaderIrInst.Lsr; ShaderIrInst ShiftB = SignedB ? ShaderIrInst.Asr : ShaderIrInst.Lsr; OperA = new ShaderIrOp(ShiftA, OperA, Imm16); if (Oper != ShaderOper.Imm) { OperB2 = new ShaderIrOp(ShiftB, OperB2, Imm16); } bool ProductShiftLeft = false; bool Merge = false; if (Oper == ShaderOper.RC) { OperC = OpCode.Cbuf34(); } else { OperC = OpCode.Gpr39(); ProductShiftLeft = OpCode.Read(36); Merge = OpCode.Read(37); } ShaderIrOp MulOp = new ShaderIrOp(ShaderIrInst.Mul, OperA, OperB2); if (ProductShiftLeft) { MulOp = new ShaderIrOp(ShaderIrInst.Lsl, MulOp, Imm16); } switch ((XmadMode)Mode) { case XmadMode.Clo: OperC = ExtendTo32(OperC, Signed: false, Size: 16); break; case XmadMode.Chi: OperC = new ShaderIrOp(ShaderIrInst.Lsr, OperC, Imm16); break; case XmadMode.Cbcc: { ShaderIrOp OperBLsh16 = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16); OperC = new ShaderIrOp(ShaderIrInst.Add, OperC, OperBLsh16); break; } case XmadMode.Csfu: { ShaderIrOperImm Imm31 = new ShaderIrOperImm(31); ShaderIrOp SignAdjustA = new ShaderIrOp(ShaderIrInst.Lsr, OperA, Imm31); ShaderIrOp SignAdjustB = new ShaderIrOp(ShaderIrInst.Lsr, OperB2, Imm31); SignAdjustA = new ShaderIrOp(ShaderIrInst.Lsl, SignAdjustA, Imm16); SignAdjustB = new ShaderIrOp(ShaderIrInst.Lsl, SignAdjustB, Imm16); ShaderIrOp SignAdjust = new ShaderIrOp(ShaderIrInst.Add, SignAdjustA, SignAdjustB); OperC = new ShaderIrOp(ShaderIrInst.Sub, OperC, SignAdjust); break; } } ShaderIrOp AddOp = new ShaderIrOp(ShaderIrInst.Add, MulOp, OperC); if (Merge) { ShaderIrOperImm Imm16Mask = new ShaderIrOperImm(0xffff); AddOp = new ShaderIrOp(ShaderIrInst.And, AddOp, Imm16Mask); OperB = new ShaderIrOp(ShaderIrInst.Lsl, OperB, Imm16); AddOp = new ShaderIrOp(ShaderIrInst.Or, AddOp, OperB); } Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), AddOp))); }