#include "runtime/ps2_vu1.h" #include "runtime/ps2_gif_arbiter.h" #include "runtime/ps2_gs_gpu.h" #include "runtime/ps2_memory.h" #include "ps2_vu1_detail.h" #include #include #include namespace { float vuEatan(float value) { constexpr float coefficients[] = { 0.999999344348907f, -0.333298563957214f, 0.199465364217758f, -0.13085337519646f, 0.096420042216778f, -0.055909886956215f, 0.021861229091883f, -0.004054057877511f}; constexpr float quarterPi = 0.785398185253143f; const float squared = value * value; float polynomial = coefficients[7]; for (int index = 6; index >= 0; --index) polynomial = coefficients[index] + squared * polynomial; return quarterPi + value * polynomial; } float vuEsin(float value) { constexpr float coefficients[] = { 1.0f, -0.166666567325592f, 0.008333025500178f, -0.000198074136279f, 0.000002601886990f}; const float squared = value * value; float polynomial = coefficients[4]; for (int index = 3; index >= 0; --index) polynomial = coefficients[index] + squared * polynomial; return value * polynomial; } float vuEexp(float value) { constexpr float coefficients[] = { 0.249998688697815f, 0.031257584691048f, 0.002591371303424f, 0.000171562001924f, 0.000005430199963f, 0.000000690600018f}; float polynomial = coefficients[5]; for (int index = 4; index >= 0; --index) polynomial = coefficients[index] + value * polynomial; polynomial = 1.0f + value * polynomial; polynomial *= polynomial; polynomial *= polynomial; return polynomial != 0.0f ? 1.0f / polynomial : std::numeric_limits::max(); } } // ============================================================================ // Lower instructions // ============================================================================ void VU1Interpreter::execLower(uint32_t instr, uint8_t *vuData, uint32_t dataSize, GS &gs, PS2Memory *memory, uint32_t upperInstr) { (void)upperInstr; if (instr == 0x00000000 || instr == 0x8000033C) // NOP return; uint8_t opHi = (instr >> 25) & 0x7F; const uint32_t pcMask = microAddressMask(); // The lower instruction encoding uses bits 31:25 for the primary opcode switch (opHi) { case 0x00: // LQ (Load Quadword from VU data memory) { uint8_t it = FT(instr); // VF destination uint8_t is = VIS(instr); // VI base uint8_t dest = (instr >> 21) & 0xF; int16_t imm = IMM11(instr); uint32_t addr = ((uint32_t)(int32_t)(m_state.vi[is] + imm)) * 16u; addr &= (dataSize - 1); if (addr + 16 <= dataSize) { float tmp[4]; std::memcpy(tmp, vuData + addr, 16); applyDest(m_state.vf[it], tmp, dest); } return; } case 0x01: // SQ (Store Quadword to VU data memory) { uint8_t is = FS(instr); // VF source uint8_t it = VIT(instr); // VI base uint8_t dest = (instr >> 21) & 0xF; int16_t imm = IMM11(instr); uint32_t addr = ((uint32_t)(int32_t)(m_state.vi[it] + imm)) * 16u; addr &= (dataSize - 1); if (addr + 16 <= dataSize) { uint32_t words[4]{}; std::memcpy(words, m_state.vf[is], sizeof(words)); queueStore(addr, words, dest); } return; } case 0x04: // ILW (Integer Load Word from VU data memory) { uint8_t it = VIT(instr); // VI destination uint8_t is = VIS(instr); // VI base uint8_t dest = (instr >> 21) & 0xF; int16_t imm = IMM11(instr); uint32_t addr = ((uint32_t)(int32_t)(m_state.vi[is] + imm)) * 16u; addr &= (dataSize - 1); if (addr + 16 <= dataSize) { int comp = 0; if (dest & 0x8) comp = 0; else if (dest & 0x4) comp = 1; else if (dest & 0x2) comp = 2; else comp = 3; uint32_t v; std::memcpy(&v, vuData + addr + comp * 4, 4); if (it != 0) m_state.vi[it] = (int32_t)(int16_t)(v & 0xFFFF); } return; } case 0x05: // ISW (Integer Store Word to VU data memory) { uint8_t it = VIT(instr); // VI source uint8_t is = VIS(instr); // VI base uint8_t dest = (instr >> 21) & 0xF; int16_t imm = IMM11(instr); uint32_t addr = ((uint32_t)(int32_t)(m_state.vi[is] + imm)) * 16u; addr &= (dataSize - 1); if (addr + 16 <= dataSize) { const uint32_t val = static_cast(static_cast(m_state.vi[it] & 0xFFFF)); const uint32_t words[4] = {val, val, val, val}; queueStore(addr, words, dest); } return; } case 0x08: // IADDIU { uint8_t it = VIT(instr); uint8_t is = VIS(instr); int16_t imm = (int16_t)(instr & 0x7FF) | ((instr >> 10) & 0x7800); if (it != 0) m_state.vi[it] = (int16_t)(m_state.vi[is] + imm); return; } case 0x09: // ISUBIU { uint8_t it = VIT(instr); uint8_t is = VIS(instr); int16_t imm = (int16_t)(instr & 0x7FF) | ((instr >> 10) & 0x7800); if (it != 0) m_state.vi[it] = (int16_t)(m_state.vi[is] - imm); return; } case 0x10: // FCEQ { uint32_t imm24 = instr & 0xFFFFFF; if (1 != 0) m_state.vi[1] = ((m_state.clip & 0xFFFFFF) == imm24) ? 1 : 0; return; } case 0x11: // FCSET { queueFcset(instr & 0xFFFFFFu); return; } case 0x12: // FCAND { uint32_t imm24 = instr & 0xFFFFFF; if (1 != 0) m_state.vi[1] = ((m_state.clip & imm24) != 0) ? 1 : 0; return; } case 0x13: // FCOR { uint32_t imm24 = instr & 0xFFFFFF; if (1 != 0) m_state.vi[1] = ((m_state.clip | imm24) == 0xFFFFFF) ? 1 : 0; return; } case 0x14: // FSEQ { const uint8_t it = VIT(instr); const uint16_t imm12 = static_cast((((instr >> 21) & 0x1u) << 11) | (instr & 0x7FFu)); if (it != 0) m_state.vi[it] = ((m_state.status & 0xFFFu) == imm12) ? 1 : 0; return; } case 0x15: // FSSET { const uint16_t imm12 = static_cast((((instr >> 21) & 0x1u) << 11) | (instr & 0x7FFu)); queueFsset(imm12); return; } case 0x16: // FSAND { const uint8_t it = VIT(instr); const uint16_t imm12 = static_cast((((instr >> 21) & 0x1u) << 11) | (instr & 0x7FFu)); if (it != 0) m_state.vi[it] = static_cast((m_state.status & 0xFFFu) & imm12); return; } case 0x17: // FSOR { const uint8_t it = VIT(instr); const uint16_t imm12 = static_cast((((instr >> 21) & 0x1u) << 11) | (instr & 0x7FFu)); if (it != 0) m_state.vi[it] = static_cast((m_state.status & 0xFFFu) | imm12); return; } case 0x18: // FMEQ { uint8_t it = VIT(instr); uint8_t is = VIS(instr); if (it != 0) m_state.vi[it] = ((m_state.mac & 0xFFFF) == (uint32_t)(uint16_t)m_state.vi[is]) ? 1 : 0; return; } case 0x1A: // FMAND { uint8_t it = VIT(instr); uint8_t is = VIS(instr); if (it != 0) m_state.vi[it] = (int32_t)(m_state.mac & (uint32_t)(uint16_t)m_state.vi[is]); return; } case 0x1B: // FMOR { uint8_t it = VIT(instr); uint8_t is = VIS(instr); if (it != 0) m_state.vi[it] = (int32_t)(m_state.mac | (uint32_t)(uint16_t)m_state.vi[is]); return; } case 0x1C: // FCGET { const uint8_t it = VIT(instr); if (it != 0) m_state.vi[it] = static_cast(m_state.clip & 0x0FFFu); return; } case 0x20: // B (unconditional branch) { int16_t imm = IMM11(instr); uint32_t target = (m_state.pc + 8 + imm * 8) & pcMask; m_state.branchPending = true; m_state.branchTarget = target; m_state.branchDelay = 1; return; } case 0x21: // BAL (Branch and link) { uint8_t it = VIT(instr); int16_t imm = IMM11(instr); uint32_t target = (m_state.pc + 8 + imm * 8) & pcMask; if (it != 0) m_state.vi[it] = (int32_t)((m_state.pc + 16) / 8); m_state.branchPending = true; m_state.branchTarget = target; m_state.branchDelay = 1; return; } case 0x24: // JR { uint8_t is = VIS(instr); uint32_t target = ((uint32_t)(uint16_t)readBranchVi(is) * 8u) & pcMask; m_state.branchPending = true; m_state.branchTarget = target; m_state.branchDelay = 1; return; } case 0x25: // JALR { uint8_t it = VIT(instr); uint8_t is = VIS(instr); uint32_t target = ((uint32_t)(uint16_t)readBranchVi(is) * 8u) & pcMask; if (it != 0) m_state.vi[it] = (int32_t)((m_state.pc + 16) / 8); m_state.branchPending = true; m_state.branchTarget = target; m_state.branchDelay = 1; return; } case 0x28: // IBEQ { uint8_t it = VIT(instr); uint8_t is = VIS(instr); int16_t imm = IMM11(instr); if ((int16_t)readBranchVi(is) == (int16_t)readBranchVi(it)) { uint32_t target = (m_state.pc + 8 + imm * 8) & pcMask; m_state.branchPending = true; m_state.branchTarget = target; m_state.branchDelay = 1; } return; } case 0x29: // IBNE { uint8_t it = VIT(instr); uint8_t is = VIS(instr); int16_t imm = IMM11(instr); if ((int16_t)readBranchVi(is) != (int16_t)readBranchVi(it)) { uint32_t target = (m_state.pc + 8 + imm * 8) & pcMask; m_state.branchPending = true; m_state.branchTarget = target; m_state.branchDelay = 1; } return; } case 0x2C: // IBLTZ { uint8_t is = VIS(instr); int16_t imm = IMM11(instr); if ((int16_t)readBranchVi(is) < 0) { uint32_t target = (m_state.pc + 8 + imm * 8) & pcMask; m_state.branchPending = true; m_state.branchTarget = target; m_state.branchDelay = 1; } return; } case 0x2D: // IBGTZ { uint8_t is = VIS(instr); int16_t imm = IMM11(instr); if ((int16_t)readBranchVi(is) > 0) { uint32_t target = (m_state.pc + 8 + imm * 8) & pcMask; m_state.branchPending = true; m_state.branchTarget = target; m_state.branchDelay = 1; } return; } case 0x2E: // IBLEZ { uint8_t is = VIS(instr); int16_t imm = IMM11(instr); if ((int16_t)readBranchVi(is) <= 0) { uint32_t target = (m_state.pc + 8 + imm * 8) & pcMask; m_state.branchPending = true; m_state.branchTarget = target; m_state.branchDelay = 1; } return; } case 0x2F: // IBGEZ { uint8_t is = VIS(instr); int16_t imm = IMM11(instr); if ((int16_t)readBranchVi(is) >= 0) { uint32_t target = (m_state.pc + 8 + imm * 8) & pcMask; m_state.branchPending = true; m_state.branchTarget = target; m_state.branchDelay = 1; } return; } case 0x40: // Lower1 / lower special. Bit31 set; low 6 bits select integer or special op. { const uint8_t funct = instr & 0x3Fu; const uint8_t vfT = FT(instr); const uint8_t vfS = FS(instr); const uint8_t viT = VIT(instr); const uint8_t viS = VIS(instr); const uint8_t viD = VID(instr); const uint8_t dest = (instr >> 21) & 0xF; switch (funct) { case 0x30: // IADD if (viD != 0) m_state.vi[viD] = (int16_t)(m_state.vi[viS] + m_state.vi[viT]); return; case 0x31: // ISUB if (viD != 0) m_state.vi[viD] = (int16_t)(m_state.vi[viS] - m_state.vi[viT]); return; case 0x32: // IADDI { int16_t imm5 = (int16_t)((int32_t)((instr >> 6) & 0x1F) << 27 >> 27); if (viT != 0) m_state.vi[viT] = (int16_t)(m_state.vi[viS] + imm5); return; } case 0x34: // IAND if (viD != 0) m_state.vi[viD] = m_state.vi[viS] & m_state.vi[viT]; return; case 0x35: // IOR if (viD != 0) m_state.vi[viD] = m_state.vi[viS] | m_state.vi[viT]; return; case 0x3C: case 0x3D: case 0x3E: case 0x3F: // Lower1 special. Dobie decodes this as (instr & 3) | ((instr >> 4) & 0x7C). { const uint8_t funct2 = (uint8_t)((instr & 0x3u) | ((instr >> 4) & 0x7Cu)); switch (funct2) { case 0x30: // MOVE { float tmp[4]; std::memcpy(tmp, m_state.vf[vfS], 16); applyDest(m_state.vf[vfT], tmp, dest); return; } case 0x31: // MR32 (rotate right by 32 bits = shift xyzw -> yzwx) { float tmp[4] = {m_state.vf[vfS][1], m_state.vf[vfS][2], m_state.vf[vfS][3], m_state.vf[vfS][0]}; applyDest(m_state.vf[vfT], tmp, dest); return; } case 0x34: // LQI (Load Quadword, post-increment) { uint32_t addr = ((uint32_t)(uint16_t)m_state.vi[viS]) * 16u; addr &= (dataSize - 1); if (addr + 16 <= dataSize) { float tmp[4]; std::memcpy(tmp, vuData + addr, 16); applyDest(m_state.vf[vfT], tmp, dest); } if (viS != 0) m_state.vi[viS] = (int16_t)(m_state.vi[viS] + 1); return; } case 0x35: // SQI (Store Quadword, post-increment) { uint32_t addr = ((uint32_t)(uint16_t)m_state.vi[viT]) * 16u; addr &= (dataSize - 1); if (addr + 16 <= dataSize) { uint32_t words[4]{}; std::memcpy(words, m_state.vf[vfS], sizeof(words)); queueStore(addr, words, dest); } if (viT != 0) m_state.vi[viT] = (int16_t)(m_state.vi[viT] + 1); return; } case 0x36: // LQD (Load Quadword, pre-decrement) { if (viS != 0) m_state.vi[viS] = (int16_t)(m_state.vi[viS] - 1); uint32_t addr = ((uint32_t)(uint16_t)m_state.vi[viS]) * 16u; addr &= (dataSize - 1); if (addr + 16 <= dataSize) { float tmp[4]; std::memcpy(tmp, vuData + addr, 16); applyDest(m_state.vf[vfT], tmp, dest); } return; } case 0x37: // SQD (Store Quadword, pre-decrement) { if (viT != 0) m_state.vi[viT] = (int16_t)(m_state.vi[viT] - 1); uint32_t addr = ((uint32_t)(uint16_t)m_state.vi[viT]) * 16u; addr &= (dataSize - 1); if (addr + 16 <= dataSize) { uint32_t words[4]{}; std::memcpy(words, m_state.vf[vfS], sizeof(words)); queueStore(addr, words, dest); } return; } case 0x38: // DIV { int fsf = (instr >> 21) & 0x3; int ftf = (instr >> 23) & 0x3; const float num = normalizeOperand(m_state.vf[vfS][fsf]); const float den = normalizeOperand(m_state.vf[vfT][ftf]); uint32_t statusDi = 0u; float result = 0.0f; if (den == 0.0f) { statusDi = num == 0.0f ? 0x10u : 0x20u; result = std::signbit(num) != std::signbit(den) ? -std::numeric_limits::max() : std::numeric_limits::max(); } else { result = num / den; } uint32_t ignoredFlags = 0u; result = normalizeResult(result, ignoredFlags); queueQ(result, 7u, statusDi); return; } case 0x39: // SQRT { int ftf = (instr >> 23) & 0x3; const float val = normalizeOperand(m_state.vf[vfT][ftf]); queueQ(std::sqrt(std::fabs(val)), 7u, val < 0.0f ? 0x10u : 0u); return; } case 0x3A: // RSQRT { int fsf = (instr >> 21) & 0x3; int ftf = (instr >> 23) & 0x3; const float num = normalizeOperand(m_state.vf[vfS][fsf]); const float radicand = normalizeOperand(m_state.vf[vfT][ftf]); const float den = std::sqrt(std::fabs(radicand)); uint32_t statusDi = radicand < 0.0f ? 0x10u : 0u; float result = 0.0f; if (den != 0.0f) result = num / den; else { statusDi = num == 0.0f ? 0x10u : 0x20u; result = std::signbit(num) ? -std::numeric_limits::max() : std::numeric_limits::max(); } uint32_t ignoredFlags = 0u; result = normalizeResult(result, ignoredFlags); queueQ(result, 13u, statusDi); return; } case 0x3B: // WAITQ return; case 0x3C: // MTIR (Move To Integer Register) { // MTIR encodes a two-bit fsf component selector in bits // 22:21. It is not a four-bit destination mask. const uint32_t comp = (instr >> 21) & 0x3u; uint32_t fval; std::memcpy(&fval, &m_state.vf[vfS][comp], 4); if (viT != 0) m_state.vi[viT] = (int32_t)(int16_t)(fval & 0xFFFF); return; } case 0x3D: // MFIR (Move From Integer Register) { float result[4]; int32_t val = (int32_t)(int16_t)(m_state.vi[viS] & 0xFFFF); std::memcpy(&result[0], &val, 4); result[1] = result[0]; result[2] = result[0]; result[3] = result[0]; applyDest(m_state.vf[vfT], result, dest); return; } case 0x3E: // ILWR - integer load word from address in VI[is] { uint32_t addr = ((uint32_t)(uint16_t)m_state.vi[viS]) * 16u; addr &= (dataSize - 1); if (addr + 16 <= dataSize) { int comp = 0; if (dest & 0x8) comp = 0; else if (dest & 0x4) comp = 1; else if (dest & 0x2) comp = 2; else comp = 3; uint32_t v; std::memcpy(&v, vuData + addr + comp * 4, 4); if (viT != 0) m_state.vi[viT] = (int32_t)(int16_t)(v & 0xFFFF); } return; } case 0x3F: // ISWR - integer store word to address in VI[is] { uint32_t addr = ((uint32_t)(uint16_t)m_state.vi[viS]) * 16u; addr &= (dataSize - 1); if (addr + 16 <= dataSize) { const uint32_t val = static_cast(static_cast(m_state.vi[viT] & 0xFFFF)); const uint32_t words[4] = {val, val, val, val}; queueStore(addr, words, dest); } return; } case 0x40: // RNEXT { const uint32_t x = (m_state.r >> 4) & 1u; const uint32_t y = (m_state.r >> 22) & 1u; m_state.r = ((m_state.r << 1) ^ x ^ y) & 0x007FFFFFu; m_state.r |= 0x3F800000u; float value = 0.0f; std::memcpy(&value, &m_state.r, sizeof(value)); const float result[4] = {value, value, value, value}; applyDest(m_state.vf[vfT], result, dest); return; } case 0x41: // RGET { float value = 0.0f; std::memcpy(&value, &m_state.r, sizeof(value)); const float result[4] = {value, value, value, value}; applyDest(m_state.vf[vfT], result, dest); return; } case 0x42: // RINIT { const uint32_t component = (instr >> 21) & 3u; uint32_t bits = 0u; std::memcpy(&bits, &m_state.vf[vfS][component], sizeof(bits)); m_state.r = 0x3F800000u | (bits & 0x007FFFFFu); return; } case 0x43: // RXOR { const uint32_t component = (instr >> 21) & 3u; uint32_t bits = 0u; std::memcpy(&bits, &m_state.vf[vfS][component], sizeof(bits)); m_state.r = 0x3F800000u | ((m_state.r ^ bits) & 0x007FFFFFu); return; } case 0x64: // MFP (Move From P register) { float result[4] = {m_state.p, m_state.p, m_state.p, m_state.p}; applyDest(m_state.vf[vfT], result, dest); return; } case 0x68: // XTOP - move current VIF1 TOP into VI register { if (viT != 0) m_state.vi[viT] = (int32_t)(m_state.top & 0x3FFu); return; } case 0x69: // XITOP - move current VIF1 ITOP into VI register { if (viT != 0) m_state.vi[viT] = (int32_t)(m_state.itop & 0x3FFu); return; } case 0x6C: // XGKICK - send GIF packet from VU1 data memory startXgkick(static_cast(static_cast(m_state.vi[viS]))); return; case 0x70: // ESADD { const float x = normalizeOperand(m_state.vf[vfS][0]); const float y = normalizeOperand(m_state.vf[vfS][1]); const float z = normalizeOperand(m_state.vf[vfS][2]); queueP(x * x + y * y + z * z, 11u); return; } case 0x71: // ERSADD { const float x = normalizeOperand(m_state.vf[vfS][0]); const float y = normalizeOperand(m_state.vf[vfS][1]); const float z = normalizeOperand(m_state.vf[vfS][2]); const float sum = x * x + y * y + z * z; queueP(sum != 0.0f ? 1.0f / sum : sum, 18u); return; } case 0x72: // ELENG { const float x = normalizeOperand(m_state.vf[vfS][0]); const float y = normalizeOperand(m_state.vf[vfS][1]); const float z = normalizeOperand(m_state.vf[vfS][2]); queueP(std::sqrt(x * x + y * y + z * z), 18u); return; } case 0x73: // ERLENG { const float x = normalizeOperand(m_state.vf[vfS][0]); const float y = normalizeOperand(m_state.vf[vfS][1]); const float z = normalizeOperand(m_state.vf[vfS][2]); const float len = std::sqrt(x * x + y * y + z * z); queueP(len != 0.0f ? 1.0f / len : len, 24u); return; } case 0x74: // EATANxy { const float x = normalizeOperand(m_state.vf[vfS][0]); const float y = normalizeOperand(m_state.vf[vfS][1]); queueP(x != 0.0f ? vuEatan(y / x) : 0.0f, 54u); return; } case 0x75: // EATANxz { const float x = normalizeOperand(m_state.vf[vfS][0]); const float z = normalizeOperand(m_state.vf[vfS][2]); queueP(x != 0.0f ? vuEatan(z / x) : 0.0f, 54u); return; } case 0x76: // ESUM { float sum = 0.0f; for (uint32_t component = 0; component < 4u; ++component) sum += normalizeOperand(m_state.vf[vfS][component]); queueP(sum, 12u); return; } case 0x77: // ERSQRT { const uint32_t component = (instr >> 21) & 3u; const float value = normalizeOperand(m_state.vf[vfS][component]); float result = value; if (result >= 0.0f) { result = std::sqrt(result); if (result != 0.0f) result = 1.0f / result; } queueP(result, 18u); return; } case 0x78: // ESQRT { const uint32_t component = (instr >> 21) & 3u; const float value = normalizeOperand(m_state.vf[vfS][component]); queueP(value >= 0.0f ? std::sqrt(value) : value, 12u); return; } case 0x79: // ESIN { const uint32_t component = (instr >> 21) & 3u; const float value = normalizeOperand(m_state.vf[vfS][component]); queueP(vuEsin(value), 29u); return; } case 0x7A: // ERCPR { const uint32_t component = (instr >> 21) & 3u; const float value = normalizeOperand(m_state.vf[vfS][component]); queueP(value != 0.0f ? 1.0f / value : value, 12u); return; } case 0x7B: // WAITP return; case 0x7C: // EATAN { const uint32_t component = (instr >> 21) & 3u; queueP(vuEatan(normalizeOperand(m_state.vf[vfS][component])), 54u); return; } case 0x7D: // EEXP { const uint32_t component = (instr >> 21) & 3u; queueP(vuEexp(normalizeOperand(m_state.vf[vfS][component])), 44u); return; } default: reportReservedInstruction(false, instr); return; } } default: reportReservedInstruction(false, instr); return; } } default: reportReservedInstruction(false, instr); break; } }