From 71814debac055130c0140acad6d13edb5012e74d Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sun, 25 Sep 2022 23:59:09 +0300 Subject: [PATCH] [PICA interpreter] Implement ifc, cmp --- include/PICA/shader.hpp | 18 +++++- src/core/PICA/shader_interpreter.cpp | 95 ++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/include/PICA/shader.hpp b/include/PICA/shader.hpp index 18df2fc5..0ad4ca51 100644 --- a/include/PICA/shader.hpp +++ b/include/PICA/shader.hpp @@ -19,7 +19,10 @@ namespace ShaderOpcodes { MOVA = 0x12, MOV = 0x13, END = 0x22, - LOOP = 0x29 + IFC = 0x28, + LOOP = 0x29, + CMP1 = 0x2E, // Both of these instructions are CMP + CMP2 = 0x2F }; } @@ -34,6 +37,12 @@ class PICAShader { u32 increment; // How much to increment the loop counter after each iteration }; + // Info for ifc/ifu stack + struct ConditionalInfo { + u32 endingPC; // PC at the end of the if block (= DST) + u32 newPC; // PC after the if block is done executing (= DST + NUM) + }; + int bufferIndex; // Index of the next instruction to overwrite for shader uploads int opDescriptorIndex; // Index of the next operand descriptor we'll overwrite u32 floatUniformIndex = 0; // Which float uniform are we writing to? ([0, 95] range) @@ -44,11 +53,15 @@ class PICAShader { std::array operandDescriptors; std::array tempRegisters; // General purpose registers the shader can use for temp values OpenGL::Vector addrRegister; // Address register + bool cmpRegister[2]; // Comparison registers where the result of CMP is stored in u32 loopCounter; u32 pc = 0; // Program counter: Index of the next instruction we're going to execute u32 loopIndex = 0; // The index of our loop stack (0 = empty, 4 = full) + u32 ifIndex = 0; // The index of our IF stack + std::array loopInfo; + std::array conditionalInfo; ShaderType type; @@ -57,8 +70,10 @@ class PICAShader { // Shader opcodes void add(u32 instruction); + void cmp(u32 instruction); void dp3(u32 instruction); void dp4(u32 instruction); + void ifc(u32 instruction); void loop(u32 instruction); void mov(u32 instruction); void mova(u32 instruction); @@ -112,6 +127,7 @@ class PICAShader { } u8 getIndexedSource(u32 source, u32 index); + bool isCondTrue(u32 instruction); public: std::array loadedShader; // Currently loaded & active shader diff --git a/src/core/PICA/shader_interpreter.cpp b/src/core/PICA/shader_interpreter.cpp index 1a67e094..d4ad08ac 100644 --- a/src/core/PICA/shader_interpreter.cpp +++ b/src/core/PICA/shader_interpreter.cpp @@ -3,6 +3,7 @@ void PICAShader::run() { pc = 0; loopIndex = 0; + ifIndex = 0; while (true) { const u32 instruction = loadedShader[pc++]; @@ -10,9 +11,13 @@ void PICAShader::run() { switch (opcode) { case ShaderOpcodes::ADD: add(instruction); break; + case ShaderOpcodes::CMP1: case ShaderOpcodes::CMP2: + cmp(instruction); + break; case ShaderOpcodes::DP3: dp3(instruction); break; case ShaderOpcodes::DP4: dp4(instruction); break; case ShaderOpcodes::END: return; // Stop running shader + case ShaderOpcodes::IFC: ifc(instruction); break; case ShaderOpcodes::LOOP: loop(instruction); break; case ShaderOpcodes::MOV: mov(instruction); break; case ShaderOpcodes::MOVA: mova(instruction); break; @@ -20,6 +25,7 @@ void PICAShader::run() { default:Helpers::panic("Unimplemented PICA instruction %08X (Opcode = %02X)", instruction, opcode); } + // Handle control flow statements. The ordering is important as the priority goes: LOOP > IF > CALL // Handle loop if (loopIndex != 0) { auto& loop = loopInfo[loopIndex - 1]; @@ -32,6 +38,15 @@ void PICAShader::run() { pc = loop.startingPC; } } + + // Handle ifs + if (ifIndex != 0) { + auto& info = conditionalInfo[ifIndex - 1]; + if (pc == info.endingPC) { // Check if the IF block ended + pc = info.newPC; + ifIndex -= 1; + } + } } } @@ -73,6 +88,23 @@ PICAShader::vec4f& PICAShader::getDest(u32 dest) { Helpers::panic("[PICA] Unimplemented dest: %X", dest); } +bool PICAShader::isCondTrue(u32 instruction) { + u32 condition = (instruction >> 22) & 3; + bool refX = ((instruction >> 24) & 1) != 0; + bool refY = ((instruction >> 25) & 1) != 0; + + switch (condition) { + case 0: // Either cmp register matches + return cmpRegister[0] == refX || cmpRegister[1] == refY; + case 1: // Both cmp registers match + return cmpRegister[0] == refX && cmpRegister[1] == refY; + case 2: // At least cmp.x matches + return cmpRegister[0] == refX; + case 3: // At least cmp.y matches + return cmpRegister[1] == refY; + } +} + void PICAShader::add(u32 instruction) { const u32 operandDescriptor = operandDescriptors[instruction & 0x7f]; const u32 src1 = (instruction >> 12) & 0x7f; @@ -193,6 +225,69 @@ void PICAShader::dp4(u32 instruction) { } } +void PICAShader::cmp(u32 instruction) { + const u32 operandDescriptor = operandDescriptors[instruction & 0x7f]; + const u32 src1 = (instruction >> 12) & 0x7f; + const u32 src2 = (instruction >> 7) & 0x1f; // src2 coming first because PICA moment + const u32 idx = (instruction >> 19) & 3; + const u32 cmpX = (instruction >> 21) & 7; + const u32 cmpY = (instruction >> 24) & 7; + const u32 cmpOperations[2] = { cmpX, cmpY }; + + if (idx) Helpers::panic("[PICA] CMP: idx != 0"); + vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor); + vec4f srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor); + + for (int i = 0; i < 2; i++) { + switch (cmpOperations[i]) { + case 0: // Equal + cmpRegister[i] = srcVec1[i] == srcVec2[i]; + break; + + case 1: // Not equal + cmpRegister[i] = srcVec1[i] != srcVec2[i]; + break; + + case 2: // Less than + cmpRegister[i] = srcVec1[i] < srcVec2[i]; + break; + + case 3: // Less than or equal + cmpRegister[i] = srcVec1[i] <= srcVec2[i]; + break; + + case 4: // Greater than + cmpRegister[i] = srcVec1[i] > srcVec2[i]; + break; + + case 5: // Greater than or equal + cmpRegister[i] = srcVec1[i] >= srcVec2[i]; + break; + + default: + cmpRegister[i] = true; + break; + } + } +} + +void PICAShader::ifc(u32 instruction) { + const u32 dest = (instruction >> 10) & 0xfff; + + if (isCondTrue(instruction)) { + if (ifIndex >= 8) [[unlikely]] + Helpers::panic("[PICA] Overflowed IF stack"); + + const u32 num = instruction & 0xff; + + auto& block = conditionalInfo[ifIndex++]; + block.endingPC = dest; + block.newPC = dest + num; + } else { + pc = dest; + } +} + void PICAShader::loop(u32 instruction) { if (loopIndex >= 4) [[unlikely]] Helpers::panic("[PICA] Overflowed loop stack");