mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-16 02:49: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,
|
MOVA = 0x12,
|
||||||
MOV = 0x13,
|
MOV = 0x13,
|
||||||
END = 0x22,
|
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
|
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 bufferIndex; // Index of the next instruction to overwrite for shader uploads
|
||||||
int opDescriptorIndex; // Index of the next operand descriptor we'll overwrite
|
int opDescriptorIndex; // Index of the next operand descriptor we'll overwrite
|
||||||
u32 floatUniformIndex = 0; // Which float uniform are we writing to? ([0, 95] range)
|
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<u32, 128> operandDescriptors;
|
||||||
std::array<vec4f, 16> tempRegisters; // General purpose registers the shader can use for temp values
|
std::array<vec4f, 16> tempRegisters; // General purpose registers the shader can use for temp values
|
||||||
OpenGL::Vector<s32, 2> addrRegister; // Address register
|
OpenGL::Vector<s32, 2> addrRegister; // Address register
|
||||||
|
bool cmpRegister[2]; // Comparison registers where the result of CMP is stored in
|
||||||
u32 loopCounter;
|
u32 loopCounter;
|
||||||
|
|
||||||
u32 pc = 0; // Program counter: Index of the next instruction we're going to execute
|
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 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<Loop, 4> loopInfo;
|
||||||
|
std::array<ConditionalInfo, 8> conditionalInfo;
|
||||||
|
|
||||||
ShaderType type;
|
ShaderType type;
|
||||||
|
|
||||||
|
@ -57,8 +70,10 @@ class PICAShader {
|
||||||
|
|
||||||
// Shader opcodes
|
// Shader opcodes
|
||||||
void add(u32 instruction);
|
void add(u32 instruction);
|
||||||
|
void cmp(u32 instruction);
|
||||||
void dp3(u32 instruction);
|
void dp3(u32 instruction);
|
||||||
void dp4(u32 instruction);
|
void dp4(u32 instruction);
|
||||||
|
void ifc(u32 instruction);
|
||||||
void loop(u32 instruction);
|
void loop(u32 instruction);
|
||||||
void mov(u32 instruction);
|
void mov(u32 instruction);
|
||||||
void mova(u32 instruction);
|
void mova(u32 instruction);
|
||||||
|
@ -112,6 +127,7 @@ class PICAShader {
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 getIndexedSource(u32 source, u32 index);
|
u8 getIndexedSource(u32 source, u32 index);
|
||||||
|
bool isCondTrue(u32 instruction);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::array<u32, 512> loadedShader; // Currently loaded & active shader
|
std::array<u32, 512> loadedShader; // Currently loaded & active shader
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
void PICAShader::run() {
|
void PICAShader::run() {
|
||||||
pc = 0;
|
pc = 0;
|
||||||
loopIndex = 0;
|
loopIndex = 0;
|
||||||
|
ifIndex = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const u32 instruction = loadedShader[pc++];
|
const u32 instruction = loadedShader[pc++];
|
||||||
|
@ -10,9 +11,13 @@ void PICAShader::run() {
|
||||||
|
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case ShaderOpcodes::ADD: add(instruction); break;
|
case ShaderOpcodes::ADD: add(instruction); break;
|
||||||
|
case ShaderOpcodes::CMP1: case ShaderOpcodes::CMP2:
|
||||||
|
cmp(instruction);
|
||||||
|
break;
|
||||||
case ShaderOpcodes::DP3: dp3(instruction); break;
|
case ShaderOpcodes::DP3: dp3(instruction); break;
|
||||||
case ShaderOpcodes::DP4: dp4(instruction); break;
|
case ShaderOpcodes::DP4: dp4(instruction); break;
|
||||||
case ShaderOpcodes::END: return; // Stop running shader
|
case ShaderOpcodes::END: return; // Stop running shader
|
||||||
|
case ShaderOpcodes::IFC: ifc(instruction); break;
|
||||||
case ShaderOpcodes::LOOP: loop(instruction); break;
|
case ShaderOpcodes::LOOP: loop(instruction); break;
|
||||||
case ShaderOpcodes::MOV: mov(instruction); break;
|
case ShaderOpcodes::MOV: mov(instruction); break;
|
||||||
case ShaderOpcodes::MOVA: mova(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);
|
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
|
// Handle loop
|
||||||
if (loopIndex != 0) {
|
if (loopIndex != 0) {
|
||||||
auto& loop = loopInfo[loopIndex - 1];
|
auto& loop = loopInfo[loopIndex - 1];
|
||||||
|
@ -32,6 +38,15 @@ void PICAShader::run() {
|
||||||
pc = loop.startingPC;
|
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);
|
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) {
|
void PICAShader::add(u32 instruction) {
|
||||||
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
||||||
const u32 src1 = (instruction >> 12) & 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) {
|
void PICAShader::loop(u32 instruction) {
|
||||||
if (loopIndex >= 4) [[unlikely]]
|
if (loopIndex >= 4) [[unlikely]]
|
||||||
Helpers::panic("[PICA] Overflowed loop stack");
|
Helpers::panic("[PICA] Overflowed loop stack");
|
||||||
|
|
Loading…
Add table
Reference in a new issue