1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 10:42:36 +01:00

PPUAnalyser.h added

This commit is contained in:
Nekotekina 2016-06-05 13:14:20 +03:00
parent ff0bb52689
commit 6fa5e2cc7c
7 changed files with 674 additions and 263 deletions

View File

@ -0,0 +1,397 @@
#pragma once
// PPU Instruction Type
struct ppu_itype
{
enum type
{
UNK = 0,
MFVSCR,
MTVSCR,
VADDCUW,
VADDFP,
VADDSBS,
VADDSHS,
VADDSWS,
VADDUBM,
VADDUBS,
VADDUHM,
VADDUHS,
VADDUWM,
VADDUWS,
VAND,
VANDC,
VAVGSB,
VAVGSH,
VAVGSW,
VAVGUB,
VAVGUH,
VAVGUW,
VCFSX,
VCFUX,
VCMPBFP,
VCMPEQFP,
VCMPEQUB,
VCMPEQUH,
VCMPEQUW,
VCMPGEFP,
VCMPGTFP,
VCMPGTSB,
VCMPGTSH,
VCMPGTSW,
VCMPGTUB,
VCMPGTUH,
VCMPGTUW,
VCTSXS,
VCTUXS,
VEXPTEFP,
VLOGEFP,
VMADDFP,
VMAXFP,
VMAXSB,
VMAXSH,
VMAXSW,
VMAXUB,
VMAXUH,
VMAXUW,
VMHADDSHS,
VMHRADDSHS,
VMINFP,
VMINSB,
VMINSH,
VMINSW,
VMINUB,
VMINUH,
VMINUW,
VMLADDUHM,
VMRGHB,
VMRGHH,
VMRGHW,
VMRGLB,
VMRGLH,
VMRGLW,
VMSUMMBM,
VMSUMSHM,
VMSUMSHS,
VMSUMUBM,
VMSUMUHM,
VMSUMUHS,
VMULESB,
VMULESH,
VMULEUB,
VMULEUH,
VMULOSB,
VMULOSH,
VMULOUB,
VMULOUH,
VNMSUBFP,
VNOR,
VOR,
VPERM,
VPKPX,
VPKSHSS,
VPKSHUS,
VPKSWSS,
VPKSWUS,
VPKUHUM,
VPKUHUS,
VPKUWUM,
VPKUWUS,
VREFP,
VRFIM,
VRFIN,
VRFIP,
VRFIZ,
VRLB,
VRLH,
VRLW,
VRSQRTEFP,
VSEL,
VSL,
VSLB,
VSLDOI,
VSLH,
VSLO,
VSLW,
VSPLTB,
VSPLTH,
VSPLTISB,
VSPLTISH,
VSPLTISW,
VSPLTW,
VSR,
VSRAB,
VSRAH,
VSRAW,
VSRB,
VSRH,
VSRO,
VSRW,
VSUBCUW,
VSUBFP,
VSUBSBS,
VSUBSHS,
VSUBSWS,
VSUBUBM,
VSUBUBS,
VSUBUHM,
VSUBUHS,
VSUBUWM,
VSUBUWS,
VSUMSWS,
VSUM2SWS,
VSUM4SBS,
VSUM4SHS,
VSUM4UBS,
VUPKHPX,
VUPKHSB,
VUPKHSH,
VUPKLPX,
VUPKLSB,
VUPKLSH,
VXOR,
TDI,
TWI,
MULLI,
SUBFIC,
CMPLI,
CMPI,
ADDIC,
ADDI,
ADDIS,
BC,
HACK,
SC,
B,
MCRF,
BCLR,
CRNOR,
CRANDC,
ISYNC,
CRXOR,
CRNAND,
CRAND,
CREQV,
CRORC,
CROR,
BCCTR,
RLWIMI,
RLWINM,
RLWNM,
ORI,
ORIS,
XORI,
XORIS,
ANDI,
ANDIS,
RLDICL,
RLDICR,
RLDIC,
RLDIMI,
RLDCL,
RLDCR,
CMP,
TW,
LVSL,
LVEBX,
SUBFC,
ADDC,
MULHDU,
MULHWU,
MFOCRF,
LWARX,
LDX,
LWZX,
SLW,
CNTLZW,
SLD,
AND,
CMPL,
LVSR,
LVEHX,
SUBF,
LDUX,
DCBST,
LWZUX,
CNTLZD,
ANDC,
TD,
LVEWX,
MULHD,
MULHW,
LDARX,
DCBF,
LBZX,
LVX,
NEG,
LBZUX,
NOR,
STVEBX,
SUBFE,
ADDE,
MTOCRF,
STDX,
STWCX,
STWX,
STVEHX,
STDUX,
STWUX,
STVEWX,
SUBFZE,
ADDZE,
STDCX,
STBX,
STVX,
SUBFME,
MULLD,
ADDME,
MULLW,
DCBTST,
STBUX,
ADD,
DCBT,
LHZX,
EQV,
ECIWX,
LHZUX,
XOR,
MFSPR,
LWAX,
DST,
LHAX,
LVXL,
MFTB,
LWAUX,
DSTST,
LHAUX,
STHX,
ORC,
ECOWX,
STHUX,
OR,
DIVDU,
DIVWU,
MTSPR,
DCBI,
NAND,
STVXL,
DIVD,
DIVW,
LVLX,
LDBRX,
LSWX,
LWBRX,
LFSX,
SRW,
SRD,
LVRX,
LSWI,
LFSUX,
SYNC,
LFDX,
LFDUX,
STVLX,
STDBRX,
STSWX,
STWBRX,
STFSX,
STVRX,
STFSUX,
STSWI,
STFDX,
STFDUX,
LVLXL,
LHBRX,
SRAW,
SRAD,
LVRXL,
DSS,
SRAWI,
SRADI,
EIEIO,
STVLXL,
STHBRX,
EXTSH,
STVRXL,
EXTSB,
STFIWX,
EXTSW,
ICBI,
DCBZ,
LWZ,
LWZU,
LBZ,
LBZU,
STW,
STWU,
STB,
STBU,
LHZ,
LHZU,
LHA,
LHAU,
STH,
STHU,
LMW,
STMW,
LFS,
LFSU,
LFD,
LFDU,
STFS,
STFSU,
STFD,
STFDU,
LD,
LDU,
LWA,
STD,
STDU,
FDIVS,
FSUBS,
FADDS,
FSQRTS,
FRES,
FMULS,
FMADDS,
FMSUBS,
FNMSUBS,
FNMADDS,
MTFSB1,
MCRFS,
MTFSB0,
MTFSFI,
MFFS,
MTFSF,
FCMPU,
FRSP,
FCTIW,
FCTIWZ,
FDIV,
FSUB,
FADD,
FSQRT,
FSEL,
FMUL,
FRSQRTE,
FMSUB,
FMADD,
FNMSUB,
FNMADD,
FCMPO,
FNEG,
FMR,
FNABS,
FABS,
FCTID,
FCTIDZ,
FCFID,
};
// Enable address-of operator for ppu_decoder<>
friend constexpr type operator &(type value)
{
return value;
}
};

View File

@ -8,11 +8,14 @@
#include "Emu/Cell/PPUOpcodes.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/PPUAnalyser.h"
#include "Emu/Cell/lv2/sys_prx.h"
#include <unordered_set>
const ppu_decoder<ppu_itype> s_ppu_itype;
LOG_CHANNEL(cellAdec);
LOG_CHANNEL(cellAtrac);
LOG_CHANNEL(cellAtracMulti);

View File

@ -209,7 +209,7 @@ void spu_recompiler::compile(spu_function_t& f)
compiler.endFunc();
// Compile and store function address
f.compiled = asmjit_cast<spu_jit_func_t>(compiler.make());
f.compiled = asmjit_cast<decltype(f.compiled)>(compiler.make());
// Add ASMJIT logs
log += logger.getString();

View File

@ -1,24 +1,20 @@
#include "stdafx.h"
#include "Crypto/sha1.h"
#include "SPURecompiler.h"
#include "SPUAnalyser.h"
#include "SPURecompiler.h"
#include "SPUOpcodes.h"
const spu_decoder<spu_itype::type> s_spu_itype;
const spu_decoder<spu_itype> s_spu_itype;
std::shared_ptr<spu_function_t> SPUDatabase::find(const be_t<u32>* data, u64 key, u32 max_size)
{
for (auto found = m_db.find(key); found != m_db.end() && found->first == key; found++)
for (auto found = m_db.equal_range(key); found.first != found.second; found.first++)
{
if (found->second->size > max_size)
{
continue;
}
const auto& func = found.first->second;
// Compare binary data explicitly (TODO: optimize)
if (std::equal(found->second->data.begin(), found->second->data.end(), data))
if (LIKELY(func->size <= max_size) && std::memcmp(func->data.data(), data, func->size) == 0)
{
return found->second;
return func;
}
}
@ -85,8 +81,6 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
const auto type = s_spu_itype.decode(op.opcode);
using namespace spu_itype;
// Find existing function
if (pos != entry && find(ls + pos / 4, pos | u64{ op.opcode } << 32, limit - pos))
{
@ -97,8 +91,8 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
// Additional analysis at the beginning of the block
if (start != entry && start == pos)
{
// Possible jump table
std::vector<u32> jt_abs, jt_rel;
std::vector<u32> jt_abs;
std::vector<u32> jt_rel;
for (; pos < limit; pos += 4)
{
@ -106,7 +100,7 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
if (target % 4)
{
// Misaligned address: abort analysis
// Address cannot be misaligned: abort jt scan
break;
}
@ -172,15 +166,16 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
break;
}
if (type == &type::BI || type == &type::IRET) // Branch Indirect
if (type == BI || type == IRET) // Branch Indirect
{
if (type == &type::IRET) LOG_ERROR(SPU, "[0x%05x] Interrupt Return", pos);
if (type == IRET) LOG_ERROR(SPU, "[0x%05x] Interrupt Return", pos);
blocks.emplace(start); start = pos + 4;
blocks.emplace(start);
start = pos + 4;
}
else if (type == &type::BR || type == &type::BRA) // Branch Relative/Absolute
else if (type == BR || type == BRA) // Branch Relative/Absolute
{
const u32 target = spu_branch_target(type == &type::BR ? pos : 0, op.i16);
const u32 target = spu_branch_target(type == BR ? pos : 0, op.i16);
// Add adjacent function because it always could be
adjacent.emplace(target);
@ -190,11 +185,12 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
blocks.emplace(target);
}
blocks.emplace(start); start = pos + 4;
blocks.emplace(start);
start = pos + 4;
}
else if (type == &type::BRSL || type == &type::BRASL) // Branch Relative/Absolute and Set Link
else if (type == BRSL || type == BRASL) // Branch Relative/Absolute and Set Link
{
const u32 target = spu_branch_target(type == &type::BRSL ? pos : 0, op.i16);
const u32 target = spu_branch_target(type == BRSL ? pos : 0, op.i16);
if (target == pos + 4)
{
@ -215,11 +211,11 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
if (op.rt != 0) LOG_ERROR(SPU, "[0x%05x] Function call without $LR", pos);
}
}
else if (type == &type::BISL || type == &type::BISLED) // Branch Indirect and Set Link
else if (type == BISL || type == BISLED) // Branch Indirect and Set Link
{
if (op.rt != 0) LOG_ERROR(SPU, "[0x%05x] Indirect function call without $LR", pos);
}
else if (type == &type::BRNZ || type == &type::BRZ || type == &type::BRHNZ || type == &type::BRHZ) // Branch Relative if (Not) Zero (Half)word
else if (type == BRNZ || type == BRZ || type == BRHNZ || type == BRHZ) // Branch Relative if (Not) Zero (Half)word
{
const u32 target = spu_branch_target(pos, op.i16);
@ -231,24 +227,9 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
blocks.emplace(target);
}
}
else if (type == &type::BINZ || type == &type::BIZ || type == &type::BIHNZ || type == &type::BIHZ) // Branch Indirect if (Not) Zero (Half)word
{
}
else if (type == &type::HBR || type == &type::HBRA || type == &type::HBRR) // Hint for Branch
{
}
else if (type == &type::STQA || type == &type::STQD || type == &type::STQR || type == &type::STQX || type == &type::FSCRWR || type == &type::MTSPR || type == &type::WRCH) // Store
{
}
else if (type == &type::HEQ || type == &type::HEQI || type == &type::HGT || type == &type::HGTI || type == &type::HLGT || type == &type::HLGTI) // Halt
{
}
else if (type == &type::STOP || type == &type::STOPD || type == &type::NOP || type == &type::LNOP || type == &type::SYNC || type == &type::DSYNC) // Miscellaneous
{
}
else // Other instructions (writing rt reg)
{
const u32 rt = type == &type::SELB || type == &type::SHUFB || type == &type::MPYA || type == &type::FNMS || type == &type::FMA || type == &type::FMS ? +op.rc : +op.rt;
const u32 rt = type & spu_itype::_quadrop ? +op.rt4 : +op.rt;
// Analyse link register access
if (rt == 0)
@ -258,7 +239,7 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
// Analyse stack pointer access
if (rt == 1)
{
if (type == &type::ILA && pos < ila_sp_pos)
if (type == ILA && pos < ila_sp_pos)
{
// set minimal ila $SP,* instruction position
ila_sp_pos = pos;
@ -274,15 +255,9 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
const auto type = s_spu_itype.decode(op.opcode);
using namespace spu_itype;
if (!type) // Invalid instruction
if (type == BRSL || type == BRASL) // Branch Relative/Absolute and Set Link
{
break;
}
else if (type == &type::BRSL || type == &type::BRASL) // Branch Relative/Absolute and Set Link
{
const u32 target = spu_branch_target(type == &type::BRSL ? pos : 0, op.i16);
const u32 target = spu_branch_target(type == BRSL ? pos : 0, op.i16);
if (target != pos + 4 && target > entry && limit > target)
{
@ -290,6 +265,10 @@ std::shared_ptr<spu_function_t> SPUDatabase::analyse(const be_t<u32>* ls, u32 en
limit = target;
}
}
else if (!type) // Invalid instruction
{
break;
}
}
if (limit <= entry)

View File

@ -1,223 +1,251 @@
#pragma once
#include "Emu/Cell/SPUOpcodes.h"
#include "Utilities/SharedMutex.h"
#include <set>
class SPUThread;
// Type of the runtime functions generated by SPU recompiler
using spu_jit_func_t = u32(*)(SPUThread* _spu, be_t<u32>* _ls);
// SPU Instruction Classifier
namespace spu_itype
// SPU Instruction Type
struct spu_itype
{
struct type
enum
{
u32 UNK;
u32 STOP;
u32 LNOP;
u32 SYNC;
u32 DSYNC;
u32 MFSPR;
u32 RDCH;
u32 RCHCNT;
u32 SF;
u32 OR;
u32 BG;
u32 SFH;
u32 NOR;
u32 ABSDB;
u32 ROT;
u32 ROTM;
u32 ROTMA;
u32 SHL;
u32 ROTH;
u32 ROTHM;
u32 ROTMAH;
u32 SHLH;
u32 ROTI;
u32 ROTMI;
u32 ROTMAI;
u32 SHLI;
u32 ROTHI;
u32 ROTHMI;
u32 ROTMAHI;
u32 SHLHI;
u32 A;
u32 AND;
u32 CG;
u32 AH;
u32 NAND;
u32 AVGB;
u32 MTSPR;
u32 WRCH;
u32 BIZ;
u32 BINZ;
u32 BIHZ;
u32 BIHNZ;
u32 STOPD;
u32 STQX;
u32 BI;
u32 BISL;
u32 IRET;
u32 BISLED;
u32 HBR;
u32 GB;
u32 GBH;
u32 GBB;
u32 FSM;
u32 FSMH;
u32 FSMB;
u32 FREST;
u32 FRSQEST;
u32 LQX;
u32 ROTQBYBI;
u32 ROTQMBYBI;
u32 SHLQBYBI;
u32 CBX;
u32 CHX;
u32 CWX;
u32 CDX;
u32 ROTQBI;
u32 ROTQMBI;
u32 SHLQBI;
u32 ROTQBY;
u32 ROTQMBY;
u32 SHLQBY;
u32 ORX;
u32 CBD;
u32 CHD;
u32 CWD;
u32 CDD;
u32 ROTQBII;
u32 ROTQMBII;
u32 SHLQBII;
u32 ROTQBYI;
u32 ROTQMBYI;
u32 SHLQBYI;
u32 NOP;
u32 CGT;
u32 XOR;
u32 CGTH;
u32 EQV;
u32 CGTB;
u32 SUMB;
u32 HGT;
u32 CLZ;
u32 XSWD;
u32 XSHW;
u32 CNTB;
u32 XSBH;
u32 CLGT;
u32 ANDC;
u32 FCGT;
u32 DFCGT;
u32 FA;
u32 FS;
u32 FM;
u32 CLGTH;
u32 ORC;
u32 FCMGT;
u32 DFCMGT;
u32 DFA;
u32 DFS;
u32 DFM;
u32 CLGTB;
u32 HLGT;
u32 DFMA;
u32 DFMS;
u32 DFNMS;
u32 DFNMA;
u32 CEQ;
u32 MPYHHU;
u32 ADDX;
u32 SFX;
u32 CGX;
u32 BGX;
u32 MPYHHA;
u32 MPYHHAU;
u32 FSCRRD;
u32 FESD;
u32 FRDS;
u32 FSCRWR;
u32 DFTSV;
u32 FCEQ;
u32 DFCEQ;
u32 MPY;
u32 MPYH;
u32 MPYHH;
u32 MPYS;
u32 CEQH;
u32 FCMEQ;
u32 DFCMEQ;
u32 MPYU;
u32 CEQB;
u32 FI;
u32 HEQ;
u32 CFLTS;
u32 CFLTU;
u32 CSFLT;
u32 CUFLT;
u32 BRZ;
u32 STQA;
u32 BRNZ;
u32 BRHZ;
u32 BRHNZ;
u32 STQR;
u32 BRA;
u32 LQA;
u32 BRASL;
u32 BR;
u32 FSMBI;
u32 BRSL;
u32 LQR;
u32 IL;
u32 ILHU;
u32 ILH;
u32 IOHL;
u32 ORI;
u32 ORHI;
u32 ORBI;
u32 SFI;
u32 SFHI;
u32 ANDI;
u32 ANDHI;
u32 ANDBI;
u32 AI;
u32 AHI;
u32 STQD;
u32 LQD;
u32 XORI;
u32 XORHI;
u32 XORBI;
u32 CGTI;
u32 CGTHI;
u32 CGTBI;
u32 HGTI;
u32 CLGTI;
u32 CLGTHI;
u32 CLGTBI;
u32 HLGTI;
u32 MPYI;
u32 MPYUI;
u32 CEQI;
u32 CEQHI;
u32 CEQBI;
u32 HEQI;
u32 HBRA;
u32 HBRR;
u32 ILA;
u32 SELB;
u32 SHUFB;
u32 MPYA;
u32 FNMS;
u32 FMA;
u32 FMS;
memory = 1 << 8, // Memory Load/Store Instructions
constant = 1 << 9, // Constant Formation Instructions
integer = 1 << 10, // Integer and Logical Instructions
shiftrot = 1 << 11, // Shift and Rotate Instructions
compare = 1 << 12, // Compare Instructions
branch = 1 << 13, // Branch Instructions
floating = 1 << 14, // Floating-Point Instructions
_quadrop = 1 << 15, // 4-op Instructions
};
enum type
{
UNK = 0,
HEQ,
HEQI,
HGT,
HGTI,
HLGT,
HLGTI,
HBR,
HBRA,
HBRR,
STOP,
STOPD,
LNOP,
NOP,
SYNC,
DSYNC,
MFSPR,
MTSPR,
RDCH,
RCHCNT,
WRCH,
LQD = memory,
LQX,
LQA,
LQR,
STQD,
STQX,
STQA,
STQR,
CBD = constant,
CBX,
CHD,
CHX,
CWD,
CWX,
CDD,
CDX,
ILH,
ILHU,
IL,
ILA,
IOHL,
FSMBI,
AH = integer,
AHI,
A,
AI,
SFH,
SFHI,
SF,
SFI,
ADDX,
CG,
CGX,
SFX,
BG,
BGX,
MPY,
MPYU,
MPYI,
MPYUI,
MPYH,
MPYS,
MPYHH,
MPYHHA,
MPYHHU,
MPYHHAU,
CLZ,
CNTB,
FSMB,
FSMH,
FSM,
GBB,
GBH,
GB,
AVGB,
ABSDB,
SUMB,
XSBH,
XSHW,
XSWD,
AND,
ANDC,
ANDBI,
ANDHI,
ANDI,
OR,
ORC,
ORBI,
ORHI,
ORI,
ORX,
XOR,
XORBI,
XORHI,
XORI,
NAND,
NOR,
EQV,
MPYA = integer | _quadrop,
SELB,
SHUFB,
SHLH = shiftrot,
SHLHI,
SHL,
SHLI,
SHLQBI,
SHLQBII,
SHLQBY,
SHLQBYI,
SHLQBYBI,
ROTH,
ROTHI,
ROT,
ROTI,
ROTQBY,
ROTQBYI,
ROTQBYBI,
ROTQBI,
ROTQBII,
ROTHM,
ROTHMI,
ROTM,
ROTMI,
ROTQMBY,
ROTQMBYI,
ROTQMBYBI,
ROTQMBI,
ROTQMBII,
ROTMAH,
ROTMAHI,
ROTMA,
ROTMAI,
CEQB = compare,
CEQBI,
CEQH,
CEQHI,
CEQ,
CEQI,
CGTB,
CGTBI,
CGTH,
CGTHI,
CGT,
CGTI,
CLGTB,
CLGTBI,
CLGTH,
CLGTHI,
CLGT,
CLGTI,
BR = branch,
BRA,
BRSL,
BRASL,
BI,
IRET,
BISLED,
BISL,
BRNZ,
BRZ,
BRHNZ,
BRHZ,
BIZ,
BINZ,
BIHZ,
BIHNZ,
FA = floating,
DFA,
FS,
DFS,
FM,
DFM,
DFMA,
DFNMS,
DFMS,
DFNMA,
FREST,
FRSQEST,
FI,
CSFLT,
CFLTS,
CUFLT,
CFLTU,
FRDS,
FESD,
FCEQ,
FCMEQ,
FCGT,
FCMGT,
FSCRWR,
FSCRRD,
DFCEQ,
DFCMEQ,
DFCGT,
DFCMGT,
DFTSV,
FMA = floating | _quadrop,
FNMS,
FMS,
};
// Enable address-of operator for spu_decoder<>
friend constexpr type operator &(type value)
{
return value;
}
};
class SPUThread;
// SPU basic function information structure
struct spu_function_t
{
@ -243,7 +271,7 @@ struct spu_function_t
bool does_reset_stack;
// Pointer to the compiled function
spu_jit_func_t compiled = nullptr;
u32(*compiled)(SPUThread* _spu, be_t<u32>* _ls) = nullptr;
spu_function_t(u32 addr, u32 size)
: addr(addr)
@ -253,7 +281,7 @@ struct spu_function_t
};
// SPU Function Database (must be global or PS3 process-local)
class SPUDatabase final
class SPUDatabase final : spu_itype
{
shared_mutex m_mutex;

View File

@ -424,6 +424,7 @@
<ClInclude Include="Crypto\unself.h" />
<ClInclude Include="Crypto\utils.h" />
<ClInclude Include="define_new_memleakdetect.h" />
<ClInclude Include="Emu\Cell\PPUAnalyser.h" />
<ClInclude Include="Emu\IPC.h" />
<ClInclude Include="Emu\PSP2\ARMv7Callback.h" />
<ClInclude Include="Emu\PSP2\ARMv7DisAsm.h" />

View File

@ -1396,6 +1396,9 @@
<ClInclude Include="Emu\Cell\PPUModule.h">
<Filter>Emu\Cell</Filter>
</ClInclude>
<ClInclude Include="Emu\Cell\PPUAnalyser.h">
<Filter>Emu\Cell</Filter>
</ClInclude>
<ClInclude Include="..\3rdparty\stblib\stb_image.h">
<Filter>Header Files</Filter>
</ClInclude>