mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-14 10:09:48 +12:00
[PICA interpreter] Implement ifc, cmp
This commit is contained in:
parent
cd2da6d50f
commit
71814debac
2 changed files with 112 additions and 1 deletions
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Add table
Reference in a new issue