mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-06 22:25:41 +12:00
710 lines
No EOL
22 KiB
C++
710 lines
No EOL
22 KiB
C++
#include <cmath>
|
|
|
|
#include "PICA/shader.hpp"
|
|
|
|
using namespace Helpers;
|
|
|
|
void PICAShader::run() {
|
|
pc = entrypoint;
|
|
loopIndex = 0;
|
|
ifIndex = 0;
|
|
callIndex = 0;
|
|
|
|
while (true) {
|
|
const u32 instruction = loadedShader[pc++];
|
|
const u32 opcode = instruction >> 26; // Top 6 bits are the opcode
|
|
|
|
switch (opcode) {
|
|
case ShaderOpcodes::ADD: add(instruction); break;
|
|
case ShaderOpcodes::CALL: call(instruction); break;
|
|
case ShaderOpcodes::CALLC: callc(instruction); break;
|
|
case ShaderOpcodes::CALLU: callu(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::DPHI: dphi(instruction); break;
|
|
case ShaderOpcodes::END: return; // Stop running shader
|
|
case ShaderOpcodes::EX2: ex2(instruction); break;
|
|
case ShaderOpcodes::FLR: flr(instruction); break;
|
|
case ShaderOpcodes::IFC: ifc(instruction); break;
|
|
case ShaderOpcodes::IFU: ifu(instruction); break;
|
|
case ShaderOpcodes::JMPC: jmpc(instruction); break;
|
|
case ShaderOpcodes::JMPU: jmpu(instruction); break;
|
|
case ShaderOpcodes::LG2: lg2(instruction); break;
|
|
case ShaderOpcodes::LOOP: loop(instruction); break;
|
|
case ShaderOpcodes::MAX: max(instruction); break;
|
|
case ShaderOpcodes::MIN: min(instruction); break;
|
|
case ShaderOpcodes::MOV: mov(instruction); break;
|
|
case ShaderOpcodes::MOVA: mova(instruction); break;
|
|
case ShaderOpcodes::MUL: mul(instruction); break;
|
|
case ShaderOpcodes::NOP: break; // Do nothing
|
|
case ShaderOpcodes::RCP: rcp(instruction); break;
|
|
case ShaderOpcodes::RSQ: rsq(instruction); break;
|
|
case ShaderOpcodes::SGEI: sgei(instruction); break;
|
|
case ShaderOpcodes::SLT: slt(instruction); break;
|
|
case ShaderOpcodes::SLTI: slti(instruction); break;
|
|
|
|
case 0x30:
|
|
case 0x31:
|
|
case 0x32:
|
|
case 0x33:
|
|
case 0x34:
|
|
case 0x35:
|
|
case 0x36:
|
|
case 0x37: {
|
|
madi(instruction);
|
|
break;
|
|
}
|
|
|
|
case 0x38:
|
|
case 0x39:
|
|
case 0x3A:
|
|
case 0x3B:
|
|
case 0x3C:
|
|
case 0x3D:
|
|
case 0x3E:
|
|
case 0x3F: {
|
|
mad(instruction);
|
|
break;
|
|
}
|
|
|
|
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];
|
|
if (pc == loop.endingPC) { // Check if the loop needs to start over
|
|
loop.iterations -= 1;
|
|
if (loop.iterations == 0) // If the loop ended, go one level down on the loop stack
|
|
loopIndex -= 1;
|
|
|
|
loopCounter += loop.increment;
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Handle calls
|
|
if (callIndex != 0) {
|
|
auto& info = callInfo[callIndex - 1];
|
|
if (pc == info.endingPC) { // Check if the CALL block ended
|
|
pc = info.returnPC;
|
|
callIndex -= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate the actual source value using an instruction's source field and it's respective index value
|
|
// The index value is used to apply relative addressing when index != 0 by adding one of the 3 addr registers to the
|
|
// source field, but only with the original source field is pointing at a vector uniform register
|
|
u8 PICAShader::getIndexedSource(u32 source, u32 index) {
|
|
if (source < 0x20) // No offset is applied if the source isn't pointing to a vector uniform reg
|
|
return source;
|
|
|
|
switch (index) {
|
|
case 0: [[likely]] return u8(source); // No offset applied
|
|
case 1: return u8(source + addrRegister[0]);
|
|
case 2: return u8(source + addrRegister[1]);
|
|
case 3: return u8(source + loopCounter);
|
|
}
|
|
|
|
Helpers::panic("Reached unreachable path in PICAShader::getIndexedSource");
|
|
return 0;
|
|
}
|
|
|
|
PICAShader::vec4f PICAShader::getSource(u32 source) {
|
|
if (source < 0x10)
|
|
return inputs[source];
|
|
else if (source < 0x20)
|
|
return tempRegisters[source - 0x10];
|
|
else if (source <= 0x7f)
|
|
return floatUniforms[source - 0x20];
|
|
else {
|
|
Helpers::warn("[PICA] Unimplemented source value: %X\n", source);
|
|
return vec4f({f24::zero(), f24::zero(), f24::zero(), f24::zero()});
|
|
}
|
|
}
|
|
|
|
PICAShader::vec4f& PICAShader::getDest(u32 dest) {
|
|
if (dest < 0x10) {
|
|
return outputs[dest];
|
|
} else if (dest < 0x20) {
|
|
return tempRegisters[dest - 0x10];
|
|
}
|
|
Helpers::panic("[PICA] Unimplemented dest: %X", dest);
|
|
}
|
|
|
|
bool PICAShader::isCondTrue(u32 instruction) {
|
|
u32 condition = getBits<22, 2>(instruction);
|
|
bool refY = (getBit<24>(instruction)) != 0;
|
|
bool refX = (getBit<25>(instruction)) != 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;
|
|
default: // At least cmp.y matches
|
|
return cmpRegister[1] == refY;
|
|
}
|
|
}
|
|
|
|
void PICAShader::add(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
u32 src1 = getBits<12, 7>(instruction);
|
|
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
src1 = getIndexedSource(src1, idx);
|
|
vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
vec4f srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor);
|
|
|
|
vec4f& destVector = getDest(dest);
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = srcVec1[3 - i] + srcVec2[3 - i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::mul(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
u32 src1 = getBits<12, 7>(instruction);
|
|
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
src1 = getIndexedSource(src1, idx);
|
|
vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
vec4f srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor);
|
|
|
|
vec4f& destVector = getDest(dest);
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = srcVec1[3 - i] * srcVec2[3 - i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::flr(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
u32 src = getBits<12, 7>(instruction);
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
src = getIndexedSource(src, idx);
|
|
vec4f srcVector = getSourceSwizzled<1>(src, operandDescriptor);
|
|
vec4f& destVector = getDest(dest);
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = f24::fromFloat32(std::floor(srcVector[3 - 1].toFloat32()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::max(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
const u32 src1 = getBits<12, 7>(instruction);
|
|
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
if (idx) Helpers::panic("[PICA] MAX: idx != 0");
|
|
vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
vec4f srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor);
|
|
|
|
vec4f& destVector = getDest(dest);
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
const auto maximum = srcVec1[3 - i] > srcVec2[3 - i] ? srcVec1[3 - i] : srcVec2[3 - i];
|
|
destVector[3 - i] = maximum;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::min(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
const u32 src1 = getBits<12, 7>(instruction);
|
|
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
if (idx) Helpers::panic("[PICA] MIN: idx != 0");
|
|
vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
vec4f srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor);
|
|
|
|
vec4f& destVector = getDest(dest);
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
const auto mininum = srcVec1[3 - i] < srcVec2[3 - i] ? srcVec1[3 - i] : srcVec2[3 - i];
|
|
destVector[3 - i] = mininum;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::mov(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
u32 src = getBits<12, 7>(instruction);
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
src = getIndexedSource(src, idx);
|
|
vec4f srcVector = getSourceSwizzled<1>(src, operandDescriptor);
|
|
vec4f& destVector = getDest(dest);
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = srcVector[3 - i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::mova(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
const u32 src = getBits<12, 7>(instruction);
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
|
|
if (idx) Helpers::panic("[PICA] MOVA: idx != 0");
|
|
vec4f srcVector = getSourceSwizzled<1>(src, operandDescriptor);
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
if (componentMask & 0b1000) // x component
|
|
addrRegister[0] = static_cast<s32>(srcVector[0].toFloat32());
|
|
if (componentMask & 0b0100) // y component
|
|
addrRegister[1] = static_cast<s32>(srcVector[1].toFloat32());
|
|
}
|
|
|
|
void PICAShader::dp3(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
u32 src1 = getBits<12, 7>(instruction);
|
|
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
src1 = getIndexedSource(src1, idx);
|
|
vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
vec4f srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor);
|
|
|
|
vec4f& destVector = getDest(dest);
|
|
f24 dot = srcVec1[0] * srcVec2[0] + srcVec1[1] * srcVec2[1] + srcVec1[2] * srcVec2[2];
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = dot;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::dp4(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
u32 src1 = getBits<12, 7>(instruction);
|
|
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
src1 = getIndexedSource(src1, idx);
|
|
vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
vec4f srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor);
|
|
|
|
vec4f& destVector = getDest(dest);
|
|
f24 dot = srcVec1[0] * srcVec2[0] + srcVec1[1] * srcVec2[1] + srcVec1[2] * srcVec2[2] + srcVec1[3] * srcVec2[3];
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = dot;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::dphi(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
const u32 src1 = getBits<14, 5>(instruction);
|
|
u32 src2 = getBits<7, 7>(instruction);
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
src2 = getIndexedSource(src2, idx);
|
|
vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
vec4f srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor);
|
|
|
|
vec4f& destVector = getDest(dest);
|
|
// srcVec1[3] is supposed to be replaced with 1.0 in the dot product, so we just add srcVec2[3] without multiplying it with anything
|
|
f24 dot = srcVec1[0] * srcVec2[0] + srcVec1[1] * srcVec2[1] + srcVec1[2] * srcVec2[2] + srcVec2[3];
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = dot;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::rcp(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
const u32 src1 = getBits<12, 7>(instruction);
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
if (idx) Helpers::panic("[PICA] RCP: idx != 0");
|
|
vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
|
|
vec4f& destVector = getDest(dest);
|
|
f24 res = f24::fromFloat32(1.0f) / srcVec1[0];
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = res;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::rsq(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
const u32 src1 = getBits<12, 7>(instruction);
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
if (idx) Helpers::panic("[PICA] RSQ: idx != 0");
|
|
vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
|
|
vec4f& destVector = getDest(dest);
|
|
f24 res = f24::fromFloat32(1.0f / std::sqrt(srcVec1[0].toFloat32()));
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = res;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::ex2(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
u32 src = getBits<12, 7>(instruction);
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
src = getIndexedSource(src, idx);
|
|
vec4f srcVec = getSourceSwizzled<1>(src, operandDescriptor);
|
|
|
|
vec4f& destVector = getDest(dest);
|
|
f24 res = f24::fromFloat32(std::exp2(srcVec[0].toFloat32()));
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = res;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::lg2(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
u32 src = getBits<12, 7>(instruction);
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
src = getIndexedSource(src, idx);
|
|
vec4f srcVec = getSourceSwizzled<1>(src, operandDescriptor);
|
|
|
|
vec4f& destVector = getDest(dest);
|
|
f24 res = f24::fromFloat32(std::log2(srcVec[0].toFloat32()));
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = res;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::mad(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x1f];
|
|
const u32 src1 = getBits<17, 5>(instruction);
|
|
u32 src2 = getBits<10, 7>(instruction);
|
|
const u32 src3 = getBits<5, 5>(instruction);
|
|
const u32 idx = getBits<22, 2>(instruction);
|
|
const u32 dest = getBits<24, 5>(instruction);
|
|
|
|
src2 = getIndexedSource(src2, idx);
|
|
|
|
auto srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
auto srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor);
|
|
auto srcVec3 = getSourceSwizzled<3>(src3, operandDescriptor);
|
|
auto& destVector = getDest(dest);
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = srcVec1[3 - i] * srcVec2[3 - i] + srcVec3[3 - i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::madi(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x1f];
|
|
const u32 src1 = getBits<17, 5>(instruction);
|
|
const u32 src2 = getBits<12, 5>(instruction);
|
|
u32 src3 = getBits<5, 7>(instruction);
|
|
const u32 idx = getBits<22, 2>(instruction);
|
|
const u32 dest = getBits<24, 5>(instruction);
|
|
|
|
src3 = getIndexedSource(src3, idx);
|
|
|
|
auto srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
auto srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor);
|
|
auto srcVec3 = getSourceSwizzled<3>(src3, operandDescriptor);
|
|
auto& destVector = getDest(dest);
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = srcVec1[3 - i] * srcVec2[3 - i] + srcVec3[3 - i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::slt(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
u32 src1 = getBits<12, 7>(instruction);
|
|
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
src1 = getIndexedSource(src1, idx);
|
|
vec4f srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
vec4f srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor);
|
|
auto& destVector = getDest(dest);
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = srcVec1[3 - i] < srcVec2[3 - i] ? f24::fromFloat32(1.0) : f24::zero();
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::sgei(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
const u32 src1 = getBits<14, 5>(instruction);
|
|
u32 src2 = getBits<7, 7>(instruction);
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
src2 = getIndexedSource(src2, idx);
|
|
|
|
auto srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
auto srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor);
|
|
auto& destVector = getDest(dest);
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = srcVec1[3 - i] >= srcVec2[3 - i] ? f24::fromFloat32(1.0) : f24::zero();
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::slti(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
const u32 src1 = getBits<14, 5>(instruction);
|
|
u32 src2 = getBits<7, 7>(instruction);
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 dest = getBits<21, 5>(instruction);
|
|
|
|
src2 = getIndexedSource(src2, idx);
|
|
|
|
auto srcVec1 = getSourceSwizzled<1>(src1, operandDescriptor);
|
|
auto srcVec2 = getSourceSwizzled<2>(src2, operandDescriptor);
|
|
auto& destVector = getDest(dest);
|
|
|
|
u32 componentMask = operandDescriptor & 0xf;
|
|
for (int i = 0; i < 4; i++) {
|
|
if (componentMask & (1 << i)) {
|
|
destVector[3 - i] = srcVec1[3 - i] < srcVec2[3 - i] ? f24::fromFloat32(1.0) : f24::zero();
|
|
}
|
|
}
|
|
}
|
|
|
|
void PICAShader::cmp(u32 instruction) {
|
|
const u32 operandDescriptor = operandDescriptors[instruction & 0x7f];
|
|
const u32 src1 = getBits<12, 7>(instruction);
|
|
const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment
|
|
const u32 idx = getBits<19, 2>(instruction);
|
|
const u32 cmpY = getBits<21, 3>(instruction);
|
|
const u32 cmpX = getBits<24, 3>(instruction);
|
|
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 = getBits<10, 12>(instruction);
|
|
|
|
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::ifu(u32 instruction) {
|
|
const u32 dest = getBits<10, 12>(instruction);
|
|
const u32 bit = getBits<22, 4>(instruction); // Bit of the bool uniform to check
|
|
|
|
if (boolUniform & (1 << bit)) {
|
|
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::call(u32 instruction) {
|
|
if (callIndex >= 4) [[unlikely]]
|
|
Helpers::panic("[PICA] Overflowed CALL stack");
|
|
|
|
const u32 num = instruction & 0xff;
|
|
const u32 dest = getBits<10, 12>(instruction);
|
|
|
|
auto& block = callInfo[callIndex++];
|
|
block.endingPC = dest + num;
|
|
block.returnPC = pc;
|
|
|
|
pc = dest;
|
|
}
|
|
|
|
void PICAShader::callc(u32 instruction) {
|
|
if (isCondTrue(instruction)) {
|
|
call(instruction); // Pls inline
|
|
}
|
|
}
|
|
|
|
void PICAShader::callu(u32 instruction) {
|
|
const u32 bit = getBits<22, 4>(instruction); // Bit of the bool uniform to check
|
|
|
|
if (boolUniform & (1 << bit)) {
|
|
if (callIndex >= 4) [[unlikely]]
|
|
Helpers::panic("[PICA] Overflowed CALL stack");
|
|
|
|
const u32 num = instruction & 0xff;
|
|
const u32 dest = getBits<10, 12>(instruction);
|
|
|
|
auto& block = callInfo[callIndex++];
|
|
block.endingPC = dest + num;
|
|
block.returnPC = pc;
|
|
|
|
pc = dest;
|
|
}
|
|
}
|
|
|
|
void PICAShader::loop(u32 instruction) {
|
|
if (loopIndex >= 4) [[unlikely]]
|
|
Helpers::panic("[PICA] Overflowed loop stack");
|
|
|
|
u32 dest = getBits<10, 12>(instruction);
|
|
auto& uniform = intUniforms[getBits<22, 2>(instruction)]; // The uniform we'll get loop info from
|
|
loopCounter = uniform[1];
|
|
auto& loop = loopInfo[loopIndex++];
|
|
|
|
loop.startingPC = pc;
|
|
loop.endingPC = dest + 1; // Loop is inclusive so we need + 1 here
|
|
loop.iterations = uniform[0] + 1;
|
|
loop.increment = uniform[2];
|
|
}
|
|
|
|
void PICAShader::jmpc(u32 instruction) {
|
|
if (isCondTrue(instruction)) {
|
|
pc = getBits<10, 12>(instruction);
|
|
}
|
|
}
|
|
|
|
void PICAShader::jmpu(u32 instruction) {
|
|
const u32 test = (instruction & 1) ^ 1; // If the LSB is 0 we want to compare to true, otherwise compare to false
|
|
const u32 dest = getBits<10, 12>(instruction);
|
|
const u32 bit = getBits<22, 4>(instruction); // Bit of the bool uniform to check
|
|
|
|
if (((boolUniform >> bit) & 1) == test) // Jump if the bool uniform is the value we want
|
|
pc = dest;
|
|
} |