[PICA interpreter] Implement ifc, cmp

This commit is contained in:
wheremyfoodat 2022-09-25 23:59:09 +03:00
parent cd2da6d50f
commit 71814debac
2 changed files with 112 additions and 1 deletions

View file

@ -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<u32, 128> operandDescriptors;
std::array<vec4f, 16> tempRegisters; // General purpose registers the shader can use for temp values
OpenGL::Vector<s32, 2> 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<Loop, 4> loopInfo;
std::array<ConditionalInfo, 8> 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<u32, 512> loadedShader; // Currently loaded & active shader

View file

@ -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");