Implement spu_iname helper

Remove old code
Report $SP anomalies
This commit is contained in:
Nekotekina 2018-06-28 14:03:25 +03:00
parent e009bbac10
commit 41eab62ed7
4 changed files with 361 additions and 354 deletions

View File

@ -35,6 +35,19 @@ void fmt_class_string<bs_t<ppu_attr>>::format(std::string& out, u64 arg)
format_bitset(out, arg, "[", ",", "]", &fmt_class_string<ppu_attr>::format);
}
template <>
void fmt_class_string<ppu_iname::type>::format(std::string& out, u64 arg)
{
// Decode instruction name from the enum value
for (u32 i = 0; i < 10; i++)
{
if (u64 value = (arg >> (54 - i * 6)) & 0x3f)
{
out += static_cast<char>(value + 0x20);
}
}
}
void ppu_module::validate(u32 reloc)
{
// Load custom PRX configuration if available

View File

@ -1,331 +1,15 @@
#include "stdafx.h"
#include "Emu/Memory/vm.h"
#include "SPUAnalyser.h"
#include "SPURecompiler.h"
#include "SPUOpcodes.h"
const spu_decoder<spu_itype> s_spu_itype;
std::shared_ptr<spu_function> spu_analyse(const be_t<u32>* ls, u32 entry, u32 max_limit)
template <>
void fmt_class_string<spu_iname::type>::format(std::string& out, u64 arg)
{
// Check arguments (bounds and alignment)
if (max_limit > 0x40000 || entry >= max_limit || entry % 4 || max_limit % 4)
// Decode instruction name from the enum value
for (u32 i = 0; i < 10; i++)
{
fmt::throw_exception("Invalid arguments (entry=0x%05x, limit=0x%05x)" HERE, entry, max_limit);
}
// Key for multimap
const u64 key = entry | u64{ ls[entry / 4] } << 32;
const be_t<u32>* base = ls + entry / 4;
const u32 block_sz = max_limit - entry;
{
//reader_lock lock(m_mutex);
// Try to find existing function in the database
// if (auto func = find(base, key, block_sz))
// {
// return func;
// }
}
{
//writer_lock lock(m_mutex);
// Double-check
// if (auto func = find(base, key, block_sz))
// {
// return func;
// }
}
// Initialize block entries with the function entry point
std::set<u32> blocks{ entry };
// Entries of adjacent functions; jump table entries
std::set<u32> adjacent, jt;
// Set initial limit which will be narrowed later
u32 limit = max_limit;
// Minimal position of ila $SP,* instruction
u32 ila_sp_pos = max_limit;
// pigeonhole optimization, addr of last ila r2, addr, or 0 if last instruction was not
u32 ila_r2_addr = 0;
// Find preliminary set of possible block entries (first pass), `start` is the current block address
for (u32 start = entry, pos = entry; pos < limit; pos += 4)
{
const spu_opcode_t op{ ls[pos / 4] };
const auto type = s_spu_itype.decode(op.opcode);
if (u64 value = (arg >> (54 - i * 6)) & 0x3f)
{
//reader_lock lock(m_mutex);
// Find existing function
// if (pos != entry && find(ls + pos / 4, pos | u64{ op.opcode } << 32, limit - pos))
// {
// limit = pos;
// break;
// }
}
// Additional analysis at the beginning of the block
if (start != entry && start == pos)
{
std::vector<u32> jt_abs;
std::vector<u32> jt_rel;
for (; pos < limit; pos += 4)
{
const u32 target = ls[pos / 4];
if (target % 4)
{
// Address cannot be misaligned: abort jt scan
break;
}
if (target >= entry && target < limit)
{
// Possible jump table entry (absolute)
jt_abs.emplace_back(target);
}
if (target + start >= entry && target + start < limit)
{
// Possible jump table entry (relative)
jt_rel.emplace_back(target + start);
}
if (std::max(jt_abs.size(), jt_rel.size()) * 4 + start <= pos)
{
break;
}
}
if (pos - start >= 8)
{
// Register jump table block (at least 2 entries) as an ordinary block
blocks.emplace(start);
// Register jump table entries (absolute)
if (jt_abs.size() * 4 == pos - start)
{
blocks.insert(jt_abs.begin(), jt_abs.end());
jt.insert(jt_abs.begin(), jt_abs.end());
}
// Register jump table entries (relative)
if (jt_rel.size() * 4 == pos - start)
{
blocks.insert(jt_rel.begin(), jt_rel.end());
jt.insert(jt_rel.begin(), jt_rel.end());
}
// Fix pos value
start = pos; pos = pos - 4;
continue;
}
// Restore pos value
pos = start;
}
if (!type || (start == pos && start > *blocks.rbegin())) // Invalid instruction or "unrelated" block started
{
// Discard current block and abort the operation
limit = start;
break;
}
if (op.opcode == 0) // Hack: special case (STOP 0)
{
limit = pos + 4;
break;
}
// if upcoming instruction is not BI, reset the pigeonhole optimization
// todo: can constant propogation somewhere get rid of this check?
if ((type != spu_itype::BI))
ila_r2_addr = 0; // reset
if (type == spu_itype::BI || type == spu_itype::IRET) // Branch Indirect
{
blocks.emplace(start);
start = pos + 4;
if (op.ra == 2 && ila_r2_addr > entry)
blocks.emplace(ila_r2_addr);
}
else if (type == spu_itype::BR || type == spu_itype::BRA) // Branch Relative/Absolute
{
const u32 target = spu_branch_target(type == spu_itype::BR ? pos : 0, op.i16);
// Add adjacent function because it always could be
adjacent.emplace(target);
if (target > entry)
{
blocks.emplace(target);
}
blocks.emplace(start);
start = pos + 4;
}
else if (type == spu_itype::BRSL || type == spu_itype::BRASL) // Branch Relative/Absolute and Set Link
{
const u32 target = spu_branch_target(type == spu_itype::BRSL ? pos : 0, op.i16);
if (target == pos + 4)
{
// Branch to the next instruction and set link ("get next instruction address" idiom)
if (op.rt == 0) LOG_ERROR(SPU, "[0x%05x] Branch-to-next with $LR", pos);
}
else
{
// Add adjacent function
adjacent.emplace(target);
if (target > entry)
{
limit = std::min<u32>(limit, target);
}
if (op.rt != 0) LOG_ERROR(SPU, "[0x%05x] Function call without $LR", pos);
}
}
else if (type == spu_itype::BISL || type == spu_itype::BISLED) // Branch Indirect and Set Link
{
if (op.rt != 0) LOG_ERROR(SPU, "[0x%05x] Indirect function call without $LR", pos);
}
else if (type == spu_itype::BRNZ || type == spu_itype::BRZ || type == spu_itype::BRHNZ || type == spu_itype::BRHZ) // Branch Relative if (Not) Zero (Half)word
{
const u32 target = spu_branch_target(pos, op.i16);
// Add adjacent function because it always could be
adjacent.emplace(target);
if (target > entry)
{
blocks.emplace(target);
}
}
else if (type == spu_itype::LNOP || type == spu_itype::NOP) {
// theres a chance that theres some random lnops/nops after the end of a function
// havent found a definite pattern, but, is an easy optimization to check for, just push start down if lnop is tagged as a start
// todo: remove the last added start pos as its probly unnecessary
if (pos == start)
start = pos + 4;
}
else // Other instructions (writing rt reg)
{
const u32 rt = type & spu_itype::_quadrop ? +op.rt4 : +op.rt;
// Analyse link register access
if (rt == 0)
{
}
// Analyse stack pointer access
else if (rt == 1)
{
if (type == spu_itype::ILA && pos < ila_sp_pos)
{
// set minimal ila $SP,* instruction position
ila_sp_pos = pos;
}
}
// pigeonhole optimize
// ila r2, addr
// bi r2
else if (rt == 2) {
if (type == spu_itype::ILA)
ila_r2_addr = spu_branch_target(op.i18);
}
out += static_cast<char>(value + 0x20);
}
}
// Find more function calls (second pass, questionable)
for (u32 pos = 0; pos < 0x40000; pos += 4)
{
const spu_opcode_t op{ ls[pos / 4] };
const auto type = s_spu_itype.decode(op.opcode);
if (type == spu_itype::BRSL || type == spu_itype::BRASL) // Branch Relative/Absolute and Set Link
{
const u32 target = spu_branch_target(type == spu_itype::BRSL ? pos : 0, op.i16);
if (target != pos + 4 && target > entry && limit > target)
{
// Narrow the limit
limit = target;
}
}
else if (!type) // Invalid instruction
{
break;
}
}
if (limit <= entry)
{
LOG_ERROR(SPU, "Function not found [0x%05x]", entry);
return nullptr;
}
// Prepare new function (set addr and size)
auto func = std::make_shared<spu_function>(entry, limit - entry);
// Copy function contents
func->data = { ls + entry / 4, ls + limit / 4 };
// Fill function block info
for (auto i = blocks.crbegin(); i != blocks.crend(); i++)
{
if (limit > *i)
{
func->blocks.emplace_hint(func->blocks.begin(), *i);
}
}
// Fill adjacent function info
for (auto i = adjacent.crbegin(); i != adjacent.crend(); i++)
{
if (limit <= *i || entry >= *i)
{
func->adjacent.emplace_hint(func->adjacent.begin(), *i);
}
}
// Fill jump table entries
for (auto i = jt.crbegin(); i != jt.crend(); i++)
{
if (limit > *i)
{
func->jtable.emplace_hint(func->jtable.begin(), *i);
}
}
// Set whether the function can reset stack
func->does_reset_stack = ila_sp_pos < limit;
// Lock here just before we write to the db
// Its is unlikely that the second check will pass anyway so we delay this step since compiling functions is very fast
{
//writer_lock lock(m_mutex);
// Add function to the database
//m_db.emplace(key, func);
}
LOG_NOTICE(SPU, "Function detected [0x%05x-0x%05x] (size=0x%x)", func->addr, func->addr + func->size, func->size);
return func;
}

View File

@ -1,8 +1,5 @@
#pragma once
#include <vector>
#include <set>
// SPU Instruction Type
struct spu_itype
{
@ -243,38 +240,225 @@ struct spu_itype
}
};
class SPUThread;
// SPU basic function information structure
struct spu_function
// Encode instruction name: 6 bits per character (0x20..0x5f), max 10
static constexpr u64 spu_iname_encode(const char* ptr, u64 value = 0)
{
// Entry point (LS address)
const u32 addr;
return *ptr == '\0' ? value : spu_iname_encode(ptr + 1, (*ptr - 0x20) | (value << 6));
}
// Function size (in bytes)
const u32 size;
#define NAME(x) x = spu_iname_encode(#x)
// Function contents (binary copy)
std::vector<be_t<u32>> data;
// Basic blocks (start addresses)
std::set<u32> blocks;
// Functions possibly called by this function (may not be available)
std::set<u32> adjacent;
// Jump table values (start addresses)
std::set<u32> jtable;
// Whether ila $SP,* instruction found
bool does_reset_stack;
// Pointer to the compiled function
u32(*compiled)(SPUThread* _spu, be_t<u32>* _ls) = nullptr;
spu_function(u32 addr, u32 size)
: addr(addr)
, size(size)
struct spu_iname
{
enum type : u64
{
NAME(UNK),
NAME(HEQ),
NAME(HEQI),
NAME(HGT),
NAME(HGTI),
NAME(HLGT),
NAME(HLGTI),
NAME(HBR),
NAME(HBRA),
NAME(HBRR),
NAME(STOP),
NAME(STOPD),
NAME(LNOP),
NAME(NOP),
NAME(SYNC),
NAME(DSYNC),
NAME(MFSPR),
NAME(MTSPR),
NAME(RDCH),
NAME(RCHCNT),
NAME(WRCH),
NAME(LQD),
NAME(LQX),
NAME(LQA),
NAME(LQR),
NAME(STQD),
NAME(STQX),
NAME(STQA),
NAME(STQR),
NAME(CBD),
NAME(CBX),
NAME(CHD),
NAME(CHX),
NAME(CWD),
NAME(CWX),
NAME(CDD),
NAME(CDX),
NAME(ILH),
NAME(ILHU),
NAME(IL),
NAME(ILA),
NAME(IOHL),
NAME(FSMBI),
NAME(AH),
NAME(AHI),
NAME(A),
NAME(AI),
NAME(SFH),
NAME(SFHI),
NAME(SF),
NAME(SFI),
NAME(ADDX),
NAME(CG),
NAME(CGX),
NAME(SFX),
NAME(BG),
NAME(BGX),
NAME(MPY),
NAME(MPYU),
NAME(MPYI),
NAME(MPYUI),
NAME(MPYH),
NAME(MPYS),
NAME(MPYHH),
NAME(MPYHHA),
NAME(MPYHHU),
NAME(MPYHHAU),
NAME(CLZ),
NAME(CNTB),
NAME(FSMB),
NAME(FSMH),
NAME(FSM),
NAME(GBB),
NAME(GBH),
NAME(GB),
NAME(AVGB),
NAME(ABSDB),
NAME(SUMB),
NAME(XSBH),
NAME(XSHW),
NAME(XSWD),
NAME(AND),
NAME(ANDC),
NAME(ANDBI),
NAME(ANDHI),
NAME(ANDI),
NAME(OR),
NAME(ORC),
NAME(ORBI),
NAME(ORHI),
NAME(ORI),
NAME(ORX),
NAME(XOR),
NAME(XORBI),
NAME(XORHI),
NAME(XORI),
NAME(NAND),
NAME(NOR),
NAME(EQV),
NAME(MPYA),
NAME(SELB),
NAME(SHUFB),
NAME(SHLH),
NAME(SHLHI),
NAME(SHL),
NAME(SHLI),
NAME(SHLQBI),
NAME(SHLQBII),
NAME(SHLQBY),
NAME(SHLQBYI),
NAME(SHLQBYBI),
NAME(ROTH),
NAME(ROTHI),
NAME(ROT),
NAME(ROTI),
NAME(ROTQBY),
NAME(ROTQBYI),
NAME(ROTQBYBI),
NAME(ROTQBI),
NAME(ROTQBII),
NAME(ROTHM),
NAME(ROTHMI),
NAME(ROTM),
NAME(ROTMI),
NAME(ROTQMBY),
NAME(ROTQMBYI),
NAME(ROTQMBYBI),
NAME(ROTQMBI),
NAME(ROTQMBII),
NAME(ROTMAH),
NAME(ROTMAHI),
NAME(ROTMA),
NAME(ROTMAI),
NAME(CEQB),
NAME(CEQBI),
NAME(CEQH),
NAME(CEQHI),
NAME(CEQ),
NAME(CEQI),
NAME(CGTB),
NAME(CGTBI),
NAME(CGTH),
NAME(CGTHI),
NAME(CGT),
NAME(CGTI),
NAME(CLGTB),
NAME(CLGTBI),
NAME(CLGTH),
NAME(CLGTHI),
NAME(CLGT),
NAME(CLGTI),
NAME(BR),
NAME(BRA),
NAME(BRSL),
NAME(BRASL),
NAME(BI),
NAME(IRET),
NAME(BISLED),
NAME(BISL),
NAME(BRNZ),
NAME(BRZ),
NAME(BRHNZ),
NAME(BRHZ),
NAME(BIZ),
NAME(BINZ),
NAME(BIHZ),
NAME(BIHNZ),
NAME(FA),
NAME(DFA),
NAME(FS),
NAME(DFS),
NAME(FM),
NAME(DFM),
NAME(DFMA),
NAME(DFNMS),
NAME(DFMS),
NAME(DFNMA),
NAME(FREST),
NAME(FRSQEST),
NAME(FI),
NAME(CSFLT),
NAME(CFLTS),
NAME(CUFLT),
NAME(CFLTU),
NAME(FRDS),
NAME(FESD),
NAME(FCEQ),
NAME(FCMEQ),
NAME(FCGT),
NAME(FCMGT),
NAME(FSCRWR),
NAME(FSCRRD),
NAME(DFCEQ),
NAME(DFCMEQ),
NAME(DFCGT),
NAME(DFCMGT),
NAME(DFTSV),
NAME(FMA),
NAME(FNMS),
NAME(FMS),
};
// Enable address-of operator for spu_decoder<>
friend constexpr type operator &(type value)
{
return value;
}
};
#undef NAME

View File

@ -20,6 +20,7 @@ extern atomic_t<u64> g_progr_ptotal;
extern atomic_t<u64> g_progr_pdone;
const spu_decoder<spu_itype> s_spu_itype;
const spu_decoder<spu_iname> s_spu_iname;
spu_cache::spu_cache(const std::string& loc)
: m_file(loc, fs::read + fs::write + fs::create)
@ -480,6 +481,11 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = +vf::is_const;
values[op.rt] = pos + 4;
if (op.rt == 1 && (pos + 4) % 16)
{
LOG_WARNING(SPU, "[0x%x] Unexpected instruction on $SP: BISL", pos);
}
}
if (test(af, vf::is_const))
@ -710,6 +716,11 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
vflags[op.rt] = +vf::is_const;
values[op.rt] = pos + 4;
if (op.rt == 1 && (pos + 4) % 16)
{
LOG_WARNING(SPU, "[0x%x] Unexpected instruction on $SP: BRSL", pos);
}
if (target == pos + 4)
{
// Get next instruction address idiom
@ -795,6 +806,17 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
break;
}
case spu_itype::LQA:
case spu_itype::LQD:
case spu_itype::LQR:
case spu_itype::LQX:
{
// Unconst
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = {};
break;
}
case spu_itype::HBR:
{
hbr_loc = spu_branch_target(pos, op.roh << 7 | op.rt);
@ -821,6 +843,12 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = +vf::is_const;
values[op.rt] = op.si16;
if (op.rt == 1 && values[1] & ~0x3fff0u)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: IL -> 0x%x", pos, values[1]);
}
break;
}
case spu_itype::ILA:
@ -828,6 +856,12 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = +vf::is_const;
values[op.rt] = op.i18;
if (op.rt == 1 && values[1] & ~0x3fff0u)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: ILA -> 0x%x", pos, values[1]);
}
break;
}
case spu_itype::ILH:
@ -835,6 +869,12 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = +vf::is_const;
values[op.rt] = op.i16 << 16 | op.i16;
if (op.rt == 1 && values[1] & ~0x3fff0u)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: ILH -> 0x%x", pos, values[1]);
}
break;
}
case spu_itype::ILHU:
@ -842,12 +882,24 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = +vf::is_const;
values[op.rt] = op.i16 << 16;
if (op.rt == 1 && values[1] & ~0x3fff0u)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: ILHU -> 0x%x", pos, values[1]);
}
break;
}
case spu_itype::IOHL:
{
m_regmod[pos / 4] = op.rt;
values[op.rt] = values[op.rt] | op.i16;
if (op.rt == 1 && op.i16 % 16)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: IOHL, 0x%x", pos, op.i16);
}
break;
}
case spu_itype::ORI:
@ -855,6 +907,12 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = vflags[op.ra] & vf::is_const;
values[op.rt] = values[op.ra] | op.si10;
if (op.rt == 1)
{
LOG_WARNING(SPU, "[0x%x] Unexpected instruction on $SP: ORI", pos);
}
break;
}
case spu_itype::OR:
@ -862,6 +920,12 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = vflags[op.ra] & vflags[op.rb] & vf::is_const;
values[op.rt] = values[op.ra] | values[op.rb];
if (op.rt == 1)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: OR", pos);
}
break;
}
case spu_itype::ANDI:
@ -869,6 +933,12 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = vflags[op.ra] & vf::is_const;
values[op.rt] = values[op.ra] & op.si10;
if (op.rt == 1)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: ANDI", pos);
}
break;
}
case spu_itype::AND:
@ -876,6 +946,12 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = vflags[op.ra] & vflags[op.rb] & vf::is_const;
values[op.rt] = values[op.ra] & values[op.rb];
if (op.rt == 1)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: AND", pos);
}
break;
}
case spu_itype::AI:
@ -883,6 +959,12 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = vflags[op.ra] & vf::is_const;
values[op.rt] = values[op.ra] + op.si10;
if (op.rt == 1 && op.si10 % 16)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: AI, 0x%x", pos, op.si10 + 0u);
}
break;
}
case spu_itype::A:
@ -890,6 +972,22 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = vflags[op.ra] & vflags[op.rb] & vf::is_const;
values[op.rt] = values[op.ra] + values[op.rb];
if (op.rt == 1)
{
if (op.ra == 1 || op.rb == 1)
{
const u32 r2 = op.ra == 1 ? +op.rb : +op.ra;
if (test(vflags[r2], vf::is_const) && (values[r2] % 16) == 0)
{
break;
}
}
LOG_WARNING(SPU, "[0x%x] Unexpected instruction on $SP: A", pos);
}
break;
}
case spu_itype::SFI:
@ -897,6 +995,12 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = vflags[op.ra] & vf::is_const;
values[op.rt] = op.si10 - values[op.ra];
if (op.rt == 1)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: SFI", pos);
}
break;
}
case spu_itype::SF:
@ -904,12 +1008,23 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
m_regmod[pos / 4] = op.rt;
vflags[op.rt] = vflags[op.ra] & vflags[op.rb] & vf::is_const;
values[op.rt] = values[op.rb] - values[op.ra];
if (op.rt == 1)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: SF", pos);
}
break;
}
case spu_itype::ROTMI:
{
m_regmod[pos / 4] = op.rt;
if (op.rt == 1)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: ROTMI", pos);
}
if (-op.i7 & 0x20)
{
vflags[op.rt] = +vf::is_const;
@ -925,6 +1040,11 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
{
m_regmod[pos / 4] = op.rt;
if (op.rt == 1)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: SHLI", pos);
}
if (op.i7 & 0x20)
{
vflags[op.rt] = +vf::is_const;
@ -943,6 +1063,12 @@ std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point
const u32 op_rt = type & spu_itype::_quadrop ? +op.rt4 : +op.rt;
m_regmod[pos / 4] = op_rt;
vflags[op_rt] = {};
if (op_rt == 1)
{
LOG_TODO(SPU, "[0x%x] Unexpected instruction on $SP: %s", pos, s_spu_iname.decode(data));
}
break;
}
}