From 0c688b421cf3488c9b4a46a8ca58053be9defad3 Mon Sep 17 00:00:00 2001
From: Subv <subv2112@gmail.com>
Date: Mon, 4 Jun 2018 13:24:31 -0500
Subject: [PATCH] GPU: Implemented the LOP32I instruction.

---
 src/video_core/engines/shader_bytecode.h      | 16 ++++++-
 .../renderer_opengl/gl_shader_decompiler.cpp  | 43 +++++++++++++++++++
 2 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index da64430e9..14d72920f 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -156,6 +156,13 @@ enum class PredOperation : u64 {
     Xor = 2,
 };
 
+enum class LogicOperation : u64 {
+    And = 0,
+    Or = 1,
+    Xor = 2,
+    PassB = 3,
+};
+
 enum class SubOp : u64 {
     Cos = 0x0,
     Sin = 0x1,
@@ -202,6 +209,12 @@ union Instruction {
             BitField<42, 1, u64> negate_pred;
         } fmnmx;
 
+        union {
+            BitField<53, 2, LogicOperation> operation;
+            BitField<55, 1, u64> invert_a;
+            BitField<56, 1, u64> invert_b;
+        } lop;
+
         float GetImm20_19() const {
             float result{};
             u32 imm{static_cast<u32>(imm20_19)};
@@ -367,6 +380,7 @@ public:
     enum class Type {
         Trivial,
         Arithmetic,
+        Logic,
         Ffma,
         Flow,
         Memory,
@@ -499,7 +513,6 @@ private:
             INST("0100110010110---", Id::F2I_C, Type::Arithmetic, "F2I_C"),
             INST("0101110010110---", Id::F2I_R, Type::Arithmetic, "F2I_R"),
             INST("0011100-10110---", Id::F2I_IMM, Type::Arithmetic, "F2I_IMM"),
-            INST("000001----------", Id::LOP32I, Type::Arithmetic, "LOP32I"),
             INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
             INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
             INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
@@ -510,6 +523,7 @@ private:
             INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"),
             INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"),
             INST("0011100-01100---", Id::FMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"),
+            INST("000001----------", Id::LOP32I, Type::Logic, "LOP32I"),
             INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"),
             INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"),
             INST("01110001-1000---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"),
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index bb5209a7e..e29f0a1d3 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -808,6 +808,49 @@ private:
             }
             break;
         }
+        case OpCode::Type::Logic: {
+            std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, false);
+
+            if (instr.alu.lop.invert_a)
+                op_a = "~(" + op_a + ')';
+
+            switch (opcode->GetId()) {
+            case OpCode::Id::LOP32I: {
+                u32 imm = static_cast<u32>(instr.alu.imm20_32.Value());
+
+                if (instr.alu.lop.invert_b)
+                    imm = ~imm;
+
+                switch (instr.alu.lop.operation) {
+                case Tegra::Shader::LogicOperation::And: {
+                    regs.SetRegisterToInteger(instr.gpr0, false, 0,
+                                              '(' + op_a + " & " + std::to_string(imm) + ')', 1, 1);
+                    break;
+                }
+                case Tegra::Shader::LogicOperation::Or: {
+                    regs.SetRegisterToInteger(instr.gpr0, false, 0,
+                                              '(' + op_a + " | " + std::to_string(imm) + ')', 1, 1);
+                    break;
+                }
+                case Tegra::Shader::LogicOperation::Xor: {
+                    regs.SetRegisterToInteger(instr.gpr0, false, 0,
+                                              '(' + op_a + " ^ " + std::to_string(imm) + ')', 1, 1);
+                    break;
+                }
+                default:
+                    NGLOG_CRITICAL(HW_GPU, "Unimplemented lop32i operation: {}",
+                                   static_cast<u32>(instr.alu.lop.operation.Value()));
+                    UNREACHABLE();
+                }
+                break;
+            }
+            default: {
+                NGLOG_CRITICAL(HW_GPU, "Unhandled logic instruction: {}", opcode->GetName());
+                UNREACHABLE();
+            }
+            }
+            break;
+        }
         case OpCode::Type::Ffma: {
             std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
             std::string op_b = instr.ffma.negate_b ? "-" : "";