diff --git a/rpcs3/Emu/Cell/SPUDisAsm.cpp b/rpcs3/Emu/Cell/SPUDisAsm.cpp index ce6a87b8b8..07783f1deb 100644 --- a/rpcs3/Emu/Cell/SPUDisAsm.cpp +++ b/rpcs3/Emu/Cell/SPUDisAsm.cpp @@ -33,7 +33,18 @@ std::pair SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const if (type & spu_itype::branch || type == spu_itype::UNK || !opcode) { - return {}; + if (reg < 80u) + { + return {}; + } + + // We do not care about function calls if register is non-volatile + if ((type != spu_itype::BRSL && type != spu_itype::BRASL && type != spu_itype::BISL) || op0.rt == reg) + { + return {}; + } + + continue; } const auto flag = s_spu_iflag.decode(opcode); @@ -56,6 +67,40 @@ std::pair SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const { return { true, v128::from32p(op0.i16 << 16) }; } + case spu_itype::CBD: + case spu_itype::CHD: + case spu_itype::CWD: + case spu_itype::CDD: + { + // Aligned stack assumption + if (op0.ra == 1u) + { + u32 size; + + switch (type) + { + case spu_itype::CBD: size = 1; break; + case spu_itype::CHD: size = 2; break; + case spu_itype::CWD: size = 4; break; + case spu_itype::CDD: size = 8; break; + } + + const u32 index = (~op0.i7 & 0xf) / size; + auto res = v128::from64(0x18191A1B1C1D1E1Full, 0x1011121314151617ull); + + switch (size) + { + case 1: res._u8[index] = 0x03; break; + case 2: res._u16[index] = 0x0203; break; + case 4: res._u32[index] = 0x00010203; break; + case 8: res._u64[index] = 0x0001020304050607ull; break; + } + + return {true, res}; + } + + return {}; + } case spu_itype::FSMBI: { v128 res; @@ -106,6 +151,70 @@ std::pair SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const return {}; } +typename SPUDisAsm::insert_mask_info SPUDisAsm::try_get_insert_mask_info(v128 mask) +{ + if ((mask & v128::from8p(0xe0)) != v128{}) + { + return {}; + } + + s32 first = -1, src_first = 0, last = 16; + + auto access = [&](u32 index) -> u8 + { + return mask._u8[index ^ 0xf]; + }; + + for (s32 i = 0; i < 16; i++) + { + if (access(i) & 0x10) + { + if ((access(i) & 0x0f) != i) + { + return {}; + } + + if (first != -1 && last == 16) + { + last = i; + } + + continue; + } + + if (last != 16) + { + return {}; + } + + if (first == -1) + { + src_first = access(i); + first = i; + } + + if (src_first + (i - first) != access(i)) + { + return {}; + } + } + + if (first == -1) + { + return {}; + } + + const u32 size = last - first; + + if ((size | src_first | first) & (size - 1)) + { + return {}; + } + + // [type size, dst index, src index] + return {size, first / size, src_first / size}; +} + void SPUDisAsm::WRCH(spu_opcode_t op) { const auto [is_const, value] = try_get_const_value(op.rt); diff --git a/rpcs3/Emu/Cell/SPUDisAsm.h b/rpcs3/Emu/Cell/SPUDisAsm.h index 1cc341d6de..403935596d 100644 --- a/rpcs3/Emu/Cell/SPUDisAsm.h +++ b/rpcs3/Emu/Cell/SPUDisAsm.h @@ -159,6 +159,15 @@ public: u32 disasm(u32 pc) override; std::pair try_get_const_value(u32 reg, u32 pc = -1) const; + struct insert_mask_info + { + u32 type_size; + u32 dst_index; + u32 src_index; + }; + + static insert_mask_info try_get_insert_mask_info(v128 mask); + //0 - 10 void STOP(spu_opcode_t op) { @@ -959,6 +968,27 @@ public: } void SHUFB(spu_opcode_t op) { + const auto [is_const, value] = try_get_const_value(op.rc); + + if (is_const) + { + const auto [size, dst, src] = try_get_insert_mask_info(value); + + if (size) + { + if ((size >= 4u && !src) || (size == 2u && src == 1u) || (size == 1u && src == 3u)) + { + // Comment insertion pattern for CWD-alike instruction + DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], fmt::format("%s #i%u[%u]", spu_reg_name[op.rc], size * 8, dst).c_str()); + return; + } + + // Comment insertion pattern for unknown instruction formations + DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], fmt::format("%s #i%u[%u] = [%u]", spu_reg_name[op.rc], size * 8, dst, src).c_str()); + return; + } + } + DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], spu_reg_name[op.rc]); } void MPYA(spu_opcode_t op)