Merge pull request #1025 from Nekotekina/master

Various fixes
This commit is contained in:
Raul Tambre 2015-03-01 09:57:25 +02:00
commit 31bf130b1c
34 changed files with 1014 additions and 853 deletions

View file

@ -50,9 +50,9 @@ namespace Log
enum LogSeverity : u32 enum LogSeverity : u32
{ {
Success = 0, Notice = 0,
Notice,
Warning, Warning,
Success,
Error, Error,
}; };

View file

@ -465,7 +465,7 @@ typedef ucontext_t x64_context;
#define X64REG(context, reg) (darwin_x64reg(context, reg)) #define X64REG(context, reg) (darwin_x64reg(context, reg))
#define XMMREG(context, reg) (reinterpret_cast<u128*>(&(context)->uc_mcontext->__fs.__fpu_xmm0[reg])) #define XMMREG(context, reg) (reinterpret_cast<u128*>(&(context)->uc_mcontext->__fs.__fpu_xmm0[reg]))
#define EFLAGS(context) ((context)->uc_mcontext->__ss.__eflags) #define EFLAGS(context) ((context)->uc_mcontext->__ss.__rflags)
uint64_t* darwin_x64reg(x64_context *context, int reg) uint64_t* darwin_x64reg(x64_context *context, int reg)
{ {
@ -832,7 +832,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
return false; return false;
} }
memcpy(vm::get_priv_ptr(addr), XMMREG(context, reg - X64R_XMM0), 16); memcpy(vm::priv_ptr(addr), XMMREG(context, reg - X64R_XMM0), 16);
break; break;
} }
@ -842,7 +842,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
return false; return false;
} }
memcpy(vm::get_priv_ptr(addr), &reg_value, d_size); memcpy(vm::priv_ptr(addr), &reg_value, d_size);
break; break;
} }
case X64OP_MOVS: case X64OP_MOVS:
@ -867,7 +867,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
// copy data // copy data
memcpy(&value, (void*)RSI(context), d_size); memcpy(&value, (void*)RSI(context), d_size);
memcpy(vm::get_priv_ptr(a_addr), &value, d_size); memcpy(vm::priv_ptr(a_addr), &value, d_size);
// shift pointers // shift pointers
if (EFLAGS(context) & 0x400 /* direction flag */) if (EFLAGS(context) & 0x400 /* direction flag */)
@ -925,7 +925,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
while (a_addr >> 12 == addr >> 12) while (a_addr >> 12 == addr >> 12)
{ {
// fill data with value // fill data with value
memcpy(vm::get_priv_ptr(a_addr), &value, d_size); memcpy(vm::priv_ptr(a_addr), &value, d_size);
// shift pointers // shift pointers
if (EFLAGS(context) & 0x400 /* direction flag */) if (EFLAGS(context) & 0x400 /* direction flag */)
@ -966,10 +966,10 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
switch (d_size) switch (d_size)
{ {
case 1: reg_value = vm::get_priv_ref<atomic_le_t<u8>>(addr).exchange((u8)reg_value); break; case 1: reg_value = vm::priv_ref<atomic_le_t<u8>>(addr).exchange((u8)reg_value); break;
case 2: reg_value = vm::get_priv_ref<atomic_le_t<u16>>(addr).exchange((u16)reg_value); break; case 2: reg_value = vm::priv_ref<atomic_le_t<u16>>(addr).exchange((u16)reg_value); break;
case 4: reg_value = vm::get_priv_ref<atomic_le_t<u32>>(addr).exchange((u32)reg_value); break; case 4: reg_value = vm::priv_ref<atomic_le_t<u32>>(addr).exchange((u32)reg_value); break;
case 8: reg_value = vm::get_priv_ref<atomic_le_t<u64>>(addr).exchange((u64)reg_value); break; case 8: reg_value = vm::priv_ref<atomic_le_t<u64>>(addr).exchange((u64)reg_value); break;
default: return false; default: return false;
} }
@ -989,10 +989,10 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
switch (d_size) switch (d_size)
{ {
case 1: old_value = vm::get_priv_ref<atomic_le_t<u8>>(addr).compare_and_swap((u8)cmp_value, (u8)reg_value); break; case 1: old_value = vm::priv_ref<atomic_le_t<u8>>(addr).compare_and_swap((u8)cmp_value, (u8)reg_value); break;
case 2: old_value = vm::get_priv_ref<atomic_le_t<u16>>(addr).compare_and_swap((u16)cmp_value, (u16)reg_value); break; case 2: old_value = vm::priv_ref<atomic_le_t<u16>>(addr).compare_and_swap((u16)cmp_value, (u16)reg_value); break;
case 4: old_value = vm::get_priv_ref<atomic_le_t<u32>>(addr).compare_and_swap((u32)cmp_value, (u32)reg_value); break; case 4: old_value = vm::priv_ref<atomic_le_t<u32>>(addr).compare_and_swap((u32)cmp_value, (u32)reg_value); break;
case 8: old_value = vm::get_priv_ref<atomic_le_t<u64>>(addr).compare_and_swap((u64)cmp_value, (u64)reg_value); break; case 8: old_value = vm::priv_ref<atomic_le_t<u64>>(addr).compare_and_swap((u64)cmp_value, (u64)reg_value); break;
default: return false; default: return false;
} }

View file

@ -1,27 +0,0 @@
#include "stdafx.h"
#include "PPCThread.h"
#include "Emu/Memory/Memory.h"
PPCThread* GetCurrentPPCThread()
{
CPUThread* thread = GetCurrentCPUThread();
if(!thread || (thread->GetType() != CPU_THREAD_PPU && thread->GetType() != CPU_THREAD_SPU && thread->GetType() != CPU_THREAD_RAW_SPU))
{
throw std::string("GetCurrentPPCThread: bad thread");
}
return (PPCThread*)thread;
}
PPCThread::PPCThread(CPUThreadType type) : CPUThread(type)
{
}
PPCThread::~PPCThread()
{
}
void PPCThread::DoReset()
{
}

View file

@ -1,22 +0,0 @@
#pragma once
#include "Emu/CPU/CPUThread.h"
class PPCThread : public CPUThread
{
public:
virtual std::string GetThreadName() const
{
return fmt::format("%s[0x%08x]", GetFName(), PC);
}
protected:
PPCThread(CPUThreadType type);
public:
virtual ~PPCThread();
protected:
virtual void DoReset() override;
};
PPCThread* GetCurrentPPCThread();

View file

@ -652,22 +652,53 @@ namespace PPU_instr
r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21,
r22, r23, r24, r25, r26, r27, r28, r29, r30, r31 r22, r23, r24, r25, r26, r27, r28, r29, r30, r31
}; };
enum
{
cr0, cr1, cr2, cr3, cr4, cr5, cr6, cr7
};
} }
namespace implicts namespace implicts
{ {
using namespace lists; using namespace lists;
//static auto LIS = std::bind(ADDIS, std::placeholders::_1, r0, std::placeholders::_2); inline u32 LIS(u32 reg, u32 imm) { return ADDIS(reg, r0, imm); }
//static auto LI = std::bind(ADDI, std::placeholders::_1, r0, std::placeholders::_2); inline u32 LI_(u32 reg, u32 imm) { return ADDI(reg, r0, imm); }
static auto NOP = std::bind(ORI, r0, r0, 0); inline u32 NOP() { return ORI(r0, r0, 0); }
static auto MR = std::bind(OR, std::placeholders::_1, std::placeholders::_2, std::placeholders::_2, false); inline u32 MR(u32 x, u32 y) { return OR(x, y, y, false); }
static auto BLR = std::bind(BCLR, 0x10 | 0x04, 0, 0, 0); inline u32 BLR() { return BCLR(0x10 | 0x04, 0, 0, 0); }
static auto BCTR = std::bind(BCCTR, 0x10 | 0x04, 0, 0, 0); inline u32 BCTR() { return BCCTR(0x10 | 0x04, 0, 0, 0); }
static auto BCTRL = std::bind(BCCTR, 0x10 | 0x04, 0, 0, 1); inline u32 BCTRL() { return BCCTR(0x10 | 0x04, 0, 0, 1); }
static auto MTCTR = std::bind(MTSPR, (0x1 << 5) | 0x8, std::placeholders::_1); inline u32 MFCTR(u32 reg) { return MFSPR(reg, 9 << 5); }
} inline u32 MTCTR(u32 reg) { return MTSPR(9 << 5, reg); }
inline u32 MFLR(u32 reg) { return MFSPR(reg, 8 << 5); }
inline u32 MTLR(u32 reg) { return MTSPR(8 << 5, reg); }
inline u32 BNE(u32 cr, s32 imm) { return BC(4, 2 | cr << 2, imm, 0, 0); }
inline u32 BEQ(u32 cr, s32 imm) { return BC(12, 2 | cr << 2, imm, 0, 0); }
inline u32 BGT(u32 cr, s32 imm) { return BC(12, 1 | cr << 2, imm, 0, 0); }
inline u32 BNE(s32 imm) { return BNE(cr0, imm); }
inline u32 BEQ(s32 imm) { return BEQ(cr0, imm); }
inline u32 BGT(s32 imm) { return BGT(cr0, imm); }
inline u32 CMPDI(u32 cr, u32 reg, u32 imm) { return CMPI(cr, 1, reg, imm); }
inline u32 CMPDI(u32 reg, u32 imm) { return CMPDI(cr0, reg, imm); }
inline u32 CMPWI(u32 cr, u32 reg, u32 imm) { return CMPI(cr, 0, reg, imm); }
inline u32 CMPWI(u32 reg, u32 imm) { return CMPWI(cr0, reg, imm); }
inline u32 CMPLDI(u32 cr, u32 reg, u32 imm) { return CMPLI(cr, 1, reg, imm); }
inline u32 CMPLDI(u32 reg, u32 imm) { return CMPLDI(cr0, reg, imm); }
inline u32 CMPLWI(u32 cr, u32 reg, u32 imm) { return CMPLI(cr, 0, reg, imm); }
inline u32 CMPLWI(u32 reg, u32 imm) { return CMPLWI(cr0, reg, imm); }
inline u32 EXTRDI(u32 x, u32 y, u32 n, u32 b) { return RLDICL(x, y, b + n, 64 - b, false); }
inline u32 SRDI(u32 x, u32 y, u32 n) { return RLDICL(x, y, 64 - n, n, false); }
inline u32 CLRLDI(u32 x, u32 y, u32 n) { return RLDICL(x, y, 0, n, false); }
}
using namespace lists; using namespace lists;
using namespace implicts; using namespace implicts;

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "PPUInstrTable.h" /*#include "PPUInstrTable.h"
#include "Loader/ELF64.h" #include "Loader/ELF64.h"
/*
enum ArgType enum ArgType
{ {
ARG_ERR = 0, ARG_ERR = 0,

View file

@ -19,14 +19,14 @@ extern void ppu_free_tls(u32 thread);
PPUThread& GetCurrentPPUThread() PPUThread& GetCurrentPPUThread()
{ {
PPCThread* thread = GetCurrentPPCThread(); CPUThread* thread = GetCurrentCPUThread();
if(!thread || thread->GetType() != CPU_THREAD_PPU) throw std::string("GetCurrentPPUThread: bad thread"); if(!thread || thread->GetType() != CPU_THREAD_PPU) throw std::string("GetCurrentPPUThread: bad thread");
return *(PPUThread*)thread; return *(PPUThread*)thread;
} }
PPUThread::PPUThread() : PPCThread(CPU_THREAD_PPU) PPUThread::PPUThread() : CPUThread(CPU_THREAD_PPU)
{ {
owned_mutexes = 0; owned_mutexes = 0;
Reset(); Reset();
@ -39,8 +39,6 @@ PPUThread::~PPUThread()
void PPUThread::DoReset() void PPUThread::DoReset()
{ {
PPCThread::DoReset();
//reset regs //reset regs
memset(VPR, 0, sizeof(VPR)); memset(VPR, 0, sizeof(VPR));
memset(FPR, 0, sizeof(FPR)); memset(FPR, 0, sizeof(FPR));

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "Emu/Cell/Common.h" #include "Emu/Cell/Common.h"
#include "Emu/Cell/PPCThread.h" #include "Emu/CPU/CPUThread.h"
#include "Emu/Memory/vm.h" #include "Emu/Memory/vm.h"
enum enum
@ -467,7 +467,7 @@ struct FPRdouble
static int Cmp(PPCdouble a, PPCdouble b); static int Cmp(PPCdouble a, PPCdouble b);
}; };
class PPUThread : public PPCThread class PPUThread : public CPUThread
{ {
public: public:
PPCdouble FPR[32]; //Floating Point Register PPCdouble FPR[32]; //Floating Point Register

View file

@ -47,7 +47,7 @@ bool RawSPUThread::Read32(const u32 addr, u32* value)
case SPU_MBox_Status_offs: case SPU_MBox_Status_offs:
{ {
*value = (SPU.Out_MBox.GetCount() & 0xff) | (SPU.In_MBox.GetFreeCount() << 8); *value = (SPU.Out_MBox.GetCount() & 0xff) | (SPU.In_MBox.GetFreeCount() << 8) | (SPU.Out_IntrMBox.GetCount() << 16);
break; break;
} }

View file

@ -23,7 +23,7 @@
SPUThread& GetCurrentSPUThread() SPUThread& GetCurrentSPUThread()
{ {
PPCThread* thread = GetCurrentPPCThread(); CPUThread* thread = GetCurrentCPUThread();
if(!thread || (thread->GetType() != CPU_THREAD_SPU && thread->GetType() != CPU_THREAD_RAW_SPU)) if(!thread || (thread->GetType() != CPU_THREAD_SPU && thread->GetType() != CPU_THREAD_RAW_SPU))
{ {
@ -33,7 +33,7 @@ SPUThread& GetCurrentSPUThread()
return *(SPUThread*)thread; return *(SPUThread*)thread;
} }
SPUThread::SPUThread(CPUThreadType type) : PPCThread(type) SPUThread::SPUThread(CPUThreadType type) : CPUThread(type)
{ {
assert(type == CPU_THREAD_SPU || type == CPU_THREAD_RAW_SPU); assert(type == CPU_THREAD_SPU || type == CPU_THREAD_RAW_SPU);
@ -73,10 +73,8 @@ void SPUThread::Task()
void SPUThread::DoReset() void SPUThread::DoReset()
{ {
PPCThread::DoReset();
//reset regs //reset regs
memset(GPR, 0, sizeof(u128) * 128); memset(GPR, 0, sizeof(GPR));
} }
void SPUThread::InitRegs() void SPUThread::InitRegs()
@ -412,7 +410,7 @@ void SPUThread::EnqMfcCmd(MFCReg& MFCArgs)
{ {
vm::reservation_op(vm::cast(ea), 128, [this, tag, lsa, ea]() vm::reservation_op(vm::cast(ea), 128, [this, tag, lsa, ea]()
{ {
memcpy(vm::get_priv_ptr(vm::cast(ea)), vm::get_ptr(ls_offset + lsa), 128); memcpy(vm::priv_ptr(vm::cast(ea)), vm::get_ptr(ls_offset + lsa), 128);
}); });
if (op == MFC_PUTLLUC_CMD) if (op == MFC_PUTLLUC_CMD)
@ -568,7 +566,7 @@ void SPUThread::WriteChannel(u32 ch, const u128& r)
return; return;
} }
//if (Ini.HLELogging.GetValue()) if (Ini.HLELogging.GetValue())
{ {
LOG_WARNING(Log::SPU, "sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x)", spup, v & 0x00ffffff, data); LOG_WARNING(Log::SPU, "sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x)", spup, v & 0x00ffffff, data);
} }

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "Emu/Cell/Common.h" #include "Emu/Cell/Common.h"
#include "Emu/CPU/CPUThread.h"
#include "Emu/Memory/atomic_type.h" #include "Emu/Memory/atomic_type.h"
#include "PPCThread.h"
#include "Emu/SysCalls/lv2/sleep_queue_type.h" #include "Emu/SysCalls/lv2/sleep_queue_type.h"
#include "Emu/SysCalls/lv2/sys_event.h" #include "Emu/SysCalls/lv2/sys_event.h"
#include "Emu/Event.h" #include "Emu/Event.h"
@ -115,8 +115,6 @@ enum
struct g_imm_table_struct struct g_imm_table_struct
{ {
//u16 cntb_table[65536];
__m128i fsmb_table[65536]; __m128i fsmb_table[65536];
__m128i fsmh_table[256]; __m128i fsmh_table[256];
__m128i fsm_table[16]; __m128i fsm_table[16];
@ -127,20 +125,8 @@ struct g_imm_table_struct
g_imm_table_struct() g_imm_table_struct()
{ {
/*static_assert(offsetof(g_imm_table_struct, cntb_table) == 0, "offsetof(cntb_table) != 0");
for (u32 i = 0; i < sizeof(cntb_table) / sizeof(cntb_table[0]); i++)
{
u32 cnt_low = 0, cnt_high = 0;
for (u32 j = 0; j < 8; j++)
{
cnt_low += (i >> j) & 1;
cnt_high += (i >> (j + 8)) & 1;
}
cntb_table[i] = (cnt_high << 8) | cnt_low;
}*/
for (u32 i = 0; i < sizeof(fsm_table) / sizeof(fsm_table[0]); i++) for (u32 i = 0; i < sizeof(fsm_table) / sizeof(fsm_table[0]); i++)
{ {
for (u32 j = 0; j < 4; j++) mmToU32Ptr(fsm_table[i])[j] = (i & (1 << j)) ? ~0 : 0; for (u32 j = 0; j < 4; j++) mmToU32Ptr(fsm_table[i])[j] = (i & (1 << j)) ? ~0 : 0;
} }
for (u32 i = 0; i < sizeof(fsmh_table) / sizeof(fsmh_table[0]); i++) for (u32 i = 0; i < sizeof(fsmh_table) / sizeof(fsmh_table[0]); i++)
@ -287,7 +273,7 @@ union SPU_SNRConfig_hdr
struct SpuGroupInfo; struct SpuGroupInfo;
class SPUThread : public PPCThread class SPUThread : public CPUThread
{ {
public: public:
u128 GPR[128]; // General-Purpose Registers u128 GPR[128]; // General-Purpose Registers

View file

@ -264,7 +264,7 @@ namespace vm
_reservation_set(addr, true); _reservation_set(addr, true);
// update memory using privileged access // update memory using privileged access
memcpy(vm::get_priv_ptr(addr), data, size); memcpy(vm::priv_ptr(addr), data, size);
// remove callback to not call it on successful update // remove callback to not call it on successful update
g_reservation_cb = nullptr; g_reservation_cb = nullptr;
@ -362,7 +362,7 @@ namespace vm
} }
void* real_addr = vm::get_ptr(addr); void* real_addr = vm::get_ptr(addr);
void* priv_addr = vm::get_priv_ptr(addr); void* priv_addr = vm::priv_ptr(addr);
#ifdef _WIN32 #ifdef _WIN32
auto protection = flags & page_writable ? PAGE_READWRITE : (flags & page_readable ? PAGE_READONLY : PAGE_NOACCESS); auto protection = flags & page_writable ? PAGE_READWRITE : (flags & page_readable ? PAGE_READONLY : PAGE_NOACCESS);
@ -464,7 +464,7 @@ namespace vm
} }
void* real_addr = vm::get_ptr(addr); void* real_addr = vm::get_ptr(addr);
void* priv_addr = vm::get_priv_ptr(addr); void* priv_addr = vm::priv_ptr(addr);
#ifdef _WIN32 #ifdef _WIN32
DWORD old; DWORD old;

View file

@ -77,15 +77,15 @@ namespace vm
} }
template<typename T = void> template<typename T = void>
T* const get_priv_ptr(u32 addr) T* const priv_ptr(u32 addr)
{ {
return reinterpret_cast<T*>(static_cast<u8*>(g_priv_addr) + addr); return reinterpret_cast<T*>(static_cast<u8*>(g_priv_addr) + addr);
} }
template<typename T> template<typename T>
T& get_priv_ref(u32 addr) T& priv_ref(u32 addr)
{ {
return *get_priv_ptr<T>(addr); return *priv_ptr<T>(addr);
} }
u32 get_addr(const void* real_pointer); u32 get_addr(const void* real_pointer);

View file

@ -214,9 +214,9 @@ namespace vm
return vm::get_ptr<T>(vm::cast(m_addr)); return vm::get_ptr<T>(vm::cast(m_addr));
} }
T* get_priv_ptr() const T* priv_ptr() const
{ {
return vm::get_priv_ptr<T>(vm::cast(m_addr)); return vm::priv_ptr<T>(vm::cast(m_addr));
} }
static const _ptr_base make(const AT& addr) static const _ptr_base make(const AT& addr)
@ -248,9 +248,9 @@ namespace vm
return vm::get_ptr<void>(vm::cast(m_addr)); return vm::get_ptr<void>(vm::cast(m_addr));
} }
void* get_priv_ptr() const void* priv_ptr() const
{ {
return vm::get_priv_ptr<void>(vm::cast(m_addr)); return vm::priv_ptr<void>(vm::cast(m_addr));
} }
explicit operator void*() const explicit operator void*() const
@ -311,9 +311,9 @@ namespace vm
return vm::get_ptr<const void>(vm::cast(m_addr)); return vm::get_ptr<const void>(vm::cast(m_addr));
} }
const void* get_priv_ptr() const const void* priv_ptr() const
{ {
return vm::get_priv_ptr<const void>(vm::cast(m_addr)); return vm::priv_ptr<const void>(vm::cast(m_addr));
} }
explicit operator const void*() const explicit operator const void*() const

View file

@ -53,6 +53,7 @@ extern Module sys_fs;
extern Module sys_io; extern Module sys_io;
extern Module sys_net; extern Module sys_net;
extern Module sysPrxForUser; extern Module sysPrxForUser;
extern Module sys_libc;
struct ModuleInfo struct ModuleInfo
{ {
@ -166,24 +167,27 @@ static const g_module_list[] =
{ -1, "cellSysmodule", &cellSysmodule }, { -1, "cellSysmodule", &cellSysmodule },
{ -1, "libmixer", &libmixer }, { -1, "libmixer", &libmixer },
{ -1, "sysPrxForUser", &sysPrxForUser }, { -1, "sysPrxForUser", &sysPrxForUser },
{ -1, "sys_libc", &sys_libc },
}; };
void ModuleManager::Init() void ModuleManager::Init()
{ {
if (!initialized) if (initialized)
{ {
clear_ppu_functions(); Close();
for (auto& m : g_module_list)
{
if (m.module)
{
m.module->Init();
}
}
initialized = true;
} }
clear_ppu_functions();
for (auto& m : g_module_list)
{
if (m.module)
{
m.module->Init();
}
}
initialized = true;
} }
ModuleManager::ModuleManager() ModuleManager::ModuleManager()

View file

@ -30,21 +30,29 @@ u32 add_ppu_func_sub(StaticFunc func)
return func.index; return func.index;
} }
u32 add_ppu_func_sub(const char group[8], const u64 ops[], const char* name, Module* module, ppu_func_caller func) u32 add_ppu_func_sub(const char group[8], const SearchPatternEntry ops[], const size_t count, const char* name, Module* module, ppu_func_caller func)
{ {
char group_name[9] = {};
if (group)
{
strcpy_trunc(group_name, group);
}
StaticFunc sf; StaticFunc sf;
sf.index = add_ppu_func(ModuleFunc(get_function_id(name), 0, module, func)); sf.index = add_ppu_func(ModuleFunc(get_function_id(name), 0, module, func));
sf.name = name; sf.name = name;
sf.group = *(u64*)group; sf.group = *(u64*)group_name;
sf.found = 0; sf.found = 0;
// TODO: check for self-inclusions, use CRC for (u32 i = 0; i < count; i++)
for (u32 i = 0; ops[i]; i++)
{ {
SFuncOp op; SearchPatternEntry op;
op.mask = re32((u32)(ops[i] >> 32)); op.type = ops[i].type;
op.crc = re32((u32)(ops[i])); op.data = re32(ops[i].data);
if (op.mask) op.crc &= op.mask; op.mask = re32(ops[i].mask);
op.num = ops[i].num;
assert(!op.mask || (op.data & ~op.mask) == 0);
sf.ops.push_back(op); sf.ops.push_back(op);
} }
@ -166,90 +174,155 @@ u32 get_function_id(const char* name)
return (u32&)output[0]; return (u32&)output[0];
} }
void hook_ppu_funcs(u32* base, u32 size) void hook_ppu_func(vm::ptr<u32> base, u32 pos, u32 size)
{ {
size /= 4; using namespace PPU_instr;
for (auto& sub : g_ppu_func_subs)
{
bool found = true;
for (u32 k = pos, x = 0; x + 1 <= sub.ops.size(); k++, x++)
{
if (k >= size)
{
found = false;
break;
}
// skip NOP
if (base[k].data() == se32(0x60000000))
{
x--;
continue;
}
const u32 data = sub.ops[x].data;
const u32 mask = sub.ops[x].mask;
const bool match = (base[k].data() & mask) == data;
switch (sub.ops[x].type)
{
case SPET_MASKED_OPCODE:
{
// masked pattern
if (!match)
{
found = false;
}
break;
}
case SPET_OPTIONAL_MASKED_OPCODE:
{
// optional masked pattern
if (!match)
{
k--;
}
break;
}
case SPET_LABEL:
{
const auto addr = (base + k--).addr();
const auto lnum = data;
const auto label = sub.labels.find(lnum);
if (label == sub.labels.end()) // register the label
{
sub.labels[lnum] = addr;
}
else if (label->second != addr) // or check registered label
{
found = false;
}
break;
}
case SPET_BRANCH_TO_LABEL:
{
if (!match)
{
found = false;
break;
}
const auto addr = (base[k].data() & se32(2) ? 0 : (base + k).addr()) + ((s32)base[k] << cntlz32(mask) >> (cntlz32(mask) + 2));
const auto lnum = sub.ops[x].num;
const auto label = sub.labels.find(lnum);
if (label == sub.labels.end()) // register the label
{
sub.labels[lnum] = addr;
}
else if (label->second != addr) // or check registered label
{
found = false;
}
break;
}
//case SPET_BRANCH_TO_FUNC:
//{
// if (!match)
// {
// found = false;
// break;
// }
// const auto addr = (base[k].data() & se32(2) ? 0 : (base + k).addr()) + ((s32)base[k] << cntlz32(mask) >> (cntlz32(mask) + 2));
// const auto nid = sub.ops[x].num;
// // TODO: recursive call
//}
default:
{
LOG_ERROR(LOADER, "Unknown search pattern type (%d)", sub.ops[x].type);
assert(0);
return;
}
}
if (!found)
{
break;
}
}
if (found)
{
LOG_SUCCESS(LOADER, "Function '%s' hooked (addr=0x%x)", sub.name, (base + pos).addr());
sub.found++;
base[pos] = HACK(sub.index | EIF_PERFORM_BLR);
}
if (sub.labels.size())
{
sub.labels.clear();
}
}
}
void hook_ppu_funcs(vm::ptr<u32> base, u32 size)
{
using namespace PPU_instr;
if (!Ini.HLEHookStFunc.GetValue()) if (!Ini.HLEHookStFunc.GetValue())
{
return; return;
}
// TODO: optimize search // TODO: optimize search
for (u32 i = 0; i < size; i++) for (u32 i = 0; i < size; i++)
{ {
for (u32 j = 0; j < g_ppu_func_subs.size(); j++) // skip NOP
if (base[i].data() == se32(0x60000000))
{ {
if ((base[i] & g_ppu_func_subs[j].ops[0].mask) == g_ppu_func_subs[j].ops[0].crc) continue;
{
bool found = true;
u32 can_skip = 0;
for (u32 k = i, x = 0; x + 1 <= g_ppu_func_subs[j].ops.size(); k++, x++)
{
if (k >= size)
{
found = false;
break;
}
// skip NOP
if (base[k] == se32(0x60000000))
{
x--;
continue;
}
const u32 mask = g_ppu_func_subs[j].ops[x].mask;
const u32 crc = g_ppu_func_subs[j].ops[x].crc;
if (!mask)
{
// TODO: define syntax
if (crc < 4) // skip various number of instructions that don't match next pattern entry
{
can_skip += crc;
k--; // process this position again
}
else if (base[k] != crc) // skippable pattern ("optional" instruction), no mask allowed
{
k--;
if (can_skip) // cannot define this behaviour properly
{
LOG_WARNING(LOADER, "hook_ppu_funcs(): can_skip = %d (unchanged)", can_skip);
}
}
else
{
if (can_skip) // cannot define this behaviour properly
{
LOG_WARNING(LOADER, "hook_ppu_funcs(): can_skip = %d (set to 0)", can_skip);
can_skip = 0;
}
}
}
else if ((base[k] & mask) != crc) // masked pattern
{
if (can_skip)
{
can_skip--;
}
else
{
found = false;
break;
}
}
else
{
can_skip = 0;
}
}
if (found)
{
LOG_NOTICE(LOADER, "Function '%s' hooked (addr=0x%x)", g_ppu_func_subs[j].name, vm::get_addr(base + i * 4));
g_ppu_func_subs[j].found++;
base[i] = re32(0x04000000 | g_ppu_func_subs[j].index | EIF_PERFORM_BLR); // hack
}
}
} }
hook_ppu_func(base, i, size);
} }
// check function groups // check function groups
@ -259,6 +332,12 @@ void hook_ppu_funcs(u32* base, u32 size)
{ {
const u64 group = g_ppu_func_subs[i].group; const u64 group = g_ppu_func_subs[i].group;
if (!group)
{
// skip if group not set
continue;
}
enum GroupSearchResult : u32 enum GroupSearchResult : u32
{ {
GSR_SUCCESS = 0, // every function from this group has been found once GSR_SUCCESS = 0, // every function from this group has been found once
@ -320,17 +399,17 @@ void hook_ppu_funcs(u32* base, u32 size)
if (g_ppu_func_subs[j].group == group) g_ppu_func_subs[j].found = 0; if (g_ppu_func_subs[j].group == group) g_ppu_func_subs[j].found = 0;
} }
char name[9] = "????????"; char group_name[9] = {};
*(u64*)name = group; *(u64*)group_name = group;
if (res == GSR_SUCCESS) if (res == GSR_SUCCESS)
{ {
LOG_SUCCESS(LOADER, "Function group [%s] successfully hooked", std::string(name, 9).c_str()); LOG_SUCCESS(LOADER, "Function group [%s] successfully hooked", group_name);
} }
else else
{ {
LOG_ERROR(LOADER, "Function group [%s] failed:%s%s", std::string(name, 9).c_str(), LOG_ERROR(LOADER, "Function group [%s] failed:%s%s", group_name,
(res & GSR_MISSING ? " missing;" : ""), (res & GSR_MISSING ? " missing;" : ""),
(res & GSR_EXCESS ? " excess;" : "")); (res & GSR_EXCESS ? " excess;" : ""));
} }
@ -338,6 +417,102 @@ void hook_ppu_funcs(u32* base, u32 size)
} }
} }
bool patch_ppu_import(u32 addr, u32 index)
{
const auto data = vm::ptr<const u32>::make(addr);
using namespace PPU_instr;
// check different patterns:
if (vm::check_addr(addr, 32) &&
(data[0] & 0xffff0000) == LI_(r12, 0) &&
(data[1] & 0xffff0000) == ORIS(r12, r12, 0) &&
(data[2] & 0xffff0000) == LWZ(r12, r12, 0) &&
data[3] == STD(r2, r1, 0x28) &&
data[4] == LWZ(r0, r12, 0) &&
data[5] == LWZ(r2, r12, 4) &&
data[6] == MTCTR(r0) &&
data[7] == BCTR())
{
vm::write32(addr, HACK(index | EIF_SAVE_RTOC | EIF_PERFORM_BLR));
return true;
}
if (vm::check_addr(addr, 12) &&
(data[0] & 0xffff0000) == LI_(r0, 0) &&
(data[1] & 0xffff0000) == ORIS(r0, r0, 0) &&
(data[2] & 0xfc000003) == B(0, 0, 0))
{
const auto sub = vm::ptr<const u32>::make(addr + 8 + ((s32)data[2] << 6 >> 8 << 2));
if (vm::check_addr(sub.addr(), 60) &&
sub[0x0] == STDU(r1, r1, -0x80) &&
sub[0x1] == STD(r2, r1, 0x70) &&
sub[0x2] == MR(r2, r0) &&
sub[0x3] == MFLR(r0) &&
sub[0x4] == STD(r0, r1, 0x90) &&
sub[0x5] == LWZ(r2, r2, 0) &&
sub[0x6] == LWZ(r0, r2, 0) &&
sub[0x7] == LWZ(r2, r2, 4) &&
sub[0x8] == MTCTR(r0) &&
sub[0x9] == BCTRL() &&
sub[0xa] == LD(r2, r1, 0x70) &&
sub[0xb] == ADDI(r1, r1, 0x80) &&
sub[0xc] == LD(r0, r1, 0x10) &&
sub[0xd] == MTLR(r0) &&
sub[0xe] == BLR())
{
vm::write32(addr, HACK(index | EIF_PERFORM_BLR));
return true;
}
}
if (vm::check_addr(addr, 64) &&
data[0x0] == MFLR(r0) &&
data[0x1] == STD(r0, r1, 0x10) &&
data[0x2] == STDU(r1, r1, -0x80) &&
data[0x3] == STD(r2, r1, 0x70) &&
(data[0x4] & 0xffff0000) == LI_(r2, 0) &&
(data[0x5] & 0xffff0000) == ORIS(r2, r2, 0) &&
data[0x6] == LWZ(r2, r2, 0) &&
data[0x7] == LWZ(r0, r2, 0) &&
data[0x8] == LWZ(r2, r2, 4) &&
data[0x9] == MTCTR(r0) &&
data[0xa] == BCTRL() &&
data[0xb] == LD(r2, r1, 0x70) &&
data[0xc] == ADDI(r1, r1, 0x80) &&
data[0xd] == LD(r0, r1, 0x10) &&
data[0xe] == MTLR(r0) &&
data[0xf] == BLR())
{
vm::write32(addr, HACK(index | EIF_PERFORM_BLR));
return true;
}
if (vm::check_addr(addr, 56) &&
(data[0x0] & 0xffff0000) == LI_(r12, 0) &&
(data[0x1] & 0xffff0000) == ORIS(r12, r12, 0) &&
(data[0x2] & 0xffff0000) == LWZ(r12, r12, 0) &&
data[0x3] == STD(r2, r1, 0x28) &&
data[0x4] == MFLR(r0) &&
data[0x5] == STD(r0, r1, 0x20) &&
data[0x6] == LWZ(r0, r12, 0) &&
data[0x7] == LWZ(r2, r12, 4) &&
data[0x8] == MTCTR(r0) &&
data[0x9] == BCTRL() &&
data[0xa] == LD(r0, r1, 0x20) &&
data[0xb] == MTLR(r0) &&
data[0xc] == LD(r2, r1, 0x28) &&
data[0xd] == BLR())
{
vm::write32(addr, HACK(index | EIF_PERFORM_BLR));
return true;
}
return false;
}
Module::Module(const char* name, void(*init)()) Module::Module(const char* name, void(*init)())
: m_is_loaded(false) : m_is_loaded(false)
, m_name(name) , m_name(name)

View file

@ -43,19 +43,31 @@ struct ModuleFunc
} }
}; };
struct SFuncOp enum : u32
{ {
u32 crc; SPET_MASKED_OPCODE,
SPET_OPTIONAL_MASKED_OPCODE,
SPET_LABEL,
SPET_BRANCH_TO_LABEL,
SPET_BRANCH_TO_FUNC,
};
struct SearchPatternEntry
{
u32 type;
u32 data;
u32 mask; u32 mask;
u32 num; // supplement info
}; };
struct StaticFunc struct StaticFunc
{ {
u32 index; u32 index;
const char* name; const char* name;
std::vector<SFuncOp> ops; std::vector<SearchPatternEntry> ops;
u64 group; u64 group;
u32 found; u32 found;
std::unordered_map<u32, u32> labels;
}; };
class Module : public LogBase class Module : public LogBase
@ -138,17 +150,26 @@ void clear_ppu_functions();
u32 get_function_id(const char* name); u32 get_function_id(const char* name);
u32 add_ppu_func_sub(StaticFunc sf); u32 add_ppu_func_sub(StaticFunc sf);
u32 add_ppu_func_sub(const char group[8], const u64 ops[], const char* name, Module* module, ppu_func_caller func); u32 add_ppu_func_sub(const char group[8], const SearchPatternEntry ops[], size_t count, const char* name, Module* module, ppu_func_caller func);
void hook_ppu_funcs(u32* base, u32 size); void hook_ppu_funcs(vm::ptr<u32> base, u32 size);
bool patch_ppu_import(u32 addr, u32 index);
#define REG_FUNC(module, name) add_ppu_func(ModuleFunc(get_function_id(#name), 0, &module, bind_func(name))) #define REG_FUNC(module, name) add_ppu_func(ModuleFunc(get_function_id(#name), 0, &module, bind_func(name)))
#define REG_FUNC_FH(module, name) add_ppu_func(ModuleFunc(get_function_id(#name), MFF_FORCED_HLE, &module, bind_func(name))) #define REG_FUNC_FH(module, name) add_ppu_func(ModuleFunc(get_function_id(#name), MFF_FORCED_HLE, &module, bind_func(name)))
#define REG_UNNAMED(module, nid) add_ppu_func(ModuleFunc(0x##nid, 0, &module, bind_func(_nid_##nid))) #define REG_UNNAMED(module, nid) add_ppu_func(ModuleFunc(0x##nid, 0, &module, bind_func(_nid_##nid)))
#define REG_SUB(module, group, name, ...) \ #define REG_SUB(module, group, ns, name, ...) \
static const u64 name ## _table[] = {__VA_ARGS__ , 0}; \ const SearchPatternEntry name##_table[] = {__VA_ARGS__}; \
if (name ## _table[0]) add_ppu_func_sub(group, name ## _table, #name, &module, bind_func(name)) add_ppu_func_sub(group, name##_table, sizeof(name##_table) / sizeof(SearchPatternEntry), #name, &module, bind_func(ns::name))
#define se_op_all(type, op, sup) []() { s32 XXX = 0; SearchPatternEntry res = { (type), (op), 0, (sup) }; XXX = -1; res.mask = (op) ^ ~res.data; return res; }()
#define se_op(op) se_op_all(SPET_MASKED_OPCODE, op, 0)
#define se_opt_op(op) se_op_all(SPET_OPTIONAL_MASKED_OPCODE, op, 0)
#define se_label(label) { SPET_LABEL, (label) }
#define se_br_label(op, label) se_op_all(SPET_BRANCH_TO_LABEL, op, label)
#define se_func_call(op, name) se_op_all(SPET_BRANCH_TO_FUNC, op, get_function_id(#name))
#define UNIMPLEMENTED_FUNC(module) module.Error("%s", __FUNCTION__) #define UNIMPLEMENTED_FUNC(module) module.Error("%s", __FUNCTION__)

View file

@ -168,7 +168,7 @@ bool spursKernel1SelectWorkload(SPUThread & spu) {
vm::reservation_op(vm::cast(ctxt->spurs.addr()), 128, [&]() { vm::reservation_op(vm::cast(ctxt->spurs.addr()), 128, [&]() {
// lock the first 0x80 bytes of spurs // lock the first 0x80 bytes of spurs
auto spurs = ctxt->spurs.get_priv_ptr(); auto spurs = ctxt->spurs.priv_ptr();
// Calculate the contention (number of SPUs used) for each workload // Calculate the contention (number of SPUs used) for each workload
u8 contention[CELL_SPURS_MAX_WORKLOAD]; u8 contention[CELL_SPURS_MAX_WORKLOAD];
@ -325,7 +325,7 @@ bool spursKernel2SelectWorkload(SPUThread & spu) {
vm::reservation_op(vm::cast(ctxt->spurs.addr()), 128, [&]() { vm::reservation_op(vm::cast(ctxt->spurs.addr()), 128, [&]() {
// lock the first 0x80 bytes of spurs // lock the first 0x80 bytes of spurs
auto spurs = ctxt->spurs.get_priv_ptr(); auto spurs = ctxt->spurs.priv_ptr();
// Calculate the contention (number of SPUs used) for each workload // Calculate the contention (number of SPUs used) for each workload
u8 contention[CELL_SPURS_MAX_WORKLOAD2]; u8 contention[CELL_SPURS_MAX_WORKLOAD2];
@ -696,7 +696,7 @@ void spursSysServiceMain(SPUThread & spu, u32 pollStatus) {
vm::reservation_acquire(vm::get_ptr(spu.ls_offset + 0x100), vm::cast(ctxt->spurs.addr()), 128); vm::reservation_acquire(vm::get_ptr(spu.ls_offset + 0x100), vm::cast(ctxt->spurs.addr()), 128);
vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1)), 128, [&]() { vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1)), 128, [&]() {
auto spurs = ctxt->spurs.get_priv_ptr(); auto spurs = ctxt->spurs.priv_ptr();
// Halt if already initialised // Halt if already initialised
if (spurs->m.sysSrvOnSpu & (1 << ctxt->spuNum)) { if (spurs->m.sysSrvOnSpu & (1 << ctxt->spuNum)) {
@ -786,7 +786,7 @@ void spursSysServiceProcessRequests(SPUThread & spu, SpursKernelContext * ctxt)
bool terminate = false; bool terminate = false;
vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1)), 128, [&]() { vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1)), 128, [&]() {
auto spurs = ctxt->spurs.get_priv_ptr(); auto spurs = ctxt->spurs.priv_ptr();
// Terminate request // Terminate request
if (spurs->m.sysSrvMsgTerminate & (1 << ctxt->spuNum)) { if (spurs->m.sysSrvMsgTerminate & (1 << ctxt->spuNum)) {
@ -853,7 +853,7 @@ void spursSysServiceActivateWorkload(SPUThread & spu, SpursKernelContext * ctxt)
} }
vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1)), 128, [&]() { vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1)), 128, [&]() {
auto spurs = ctxt->spurs.get_priv_ptr(); auto spurs = ctxt->spurs.priv_ptr();
for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++) { for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++) {
// Update workload status and runnable flag based on the workload state // Update workload status and runnable flag based on the workload state
@ -910,7 +910,7 @@ void spursSysServiceUpdateShutdownCompletionEvents(SPUThread & spu, SpursKernelC
u32 wklNotifyBitSet; u32 wklNotifyBitSet;
u8 spuPort; u8 spuPort;
vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1)), 128, [&]() { vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1)), 128, [&]() {
auto spurs = ctxt->spurs.get_priv_ptr(); auto spurs = ctxt->spurs.priv_ptr();
wklNotifyBitSet = 0; wklNotifyBitSet = 0;
spuPort = spurs->m.spuPort;; spuPort = spurs->m.spuPort;;
@ -952,7 +952,7 @@ void spursSysServiceTraceUpdate(SPUThread & spu, SpursKernelContext * ctxt, u32
u8 sysSrvMsgUpdateTrace; u8 sysSrvMsgUpdateTrace;
vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1)), 128, [&]() { vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1)), 128, [&]() {
auto spurs = ctxt->spurs.get_priv_ptr(); auto spurs = ctxt->spurs.priv_ptr();
sysSrvMsgUpdateTrace = spurs->m.sysSrvMsgUpdateTrace; sysSrvMsgUpdateTrace = spurs->m.sysSrvMsgUpdateTrace;
spurs->m.sysSrvMsgUpdateTrace &= ~(1 << ctxt->spuNum); spurs->m.sysSrvMsgUpdateTrace &= ~(1 << ctxt->spuNum);
@ -1006,7 +1006,7 @@ void spursSysServiceCleanupAfterSystemWorkload(SPUThread & spu, SpursKernelConte
bool do_return = false; bool do_return = false;
vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1)), 128, [&]() { vm::reservation_op(vm::cast(ctxt->spurs.addr() + offsetof(CellSpurs, m.wklState1)), 128, [&]() {
auto spurs = ctxt->spurs.get_priv_ptr(); auto spurs = ctxt->spurs.priv_ptr();
if (spurs->m.sysSrvWorkload[ctxt->spuNum] == 0xFF) { if (spurs->m.sysSrvWorkload[ctxt->spuNum] == 0xFF) {
do_return = true; do_return = true;
@ -1024,7 +1024,7 @@ void spursSysServiceCleanupAfterSystemWorkload(SPUThread & spu, SpursKernelConte
spursSysServiceActivateWorkload(spu, ctxt); spursSysServiceActivateWorkload(spu, ctxt);
vm::reservation_op(vm::cast(ctxt->spurs.addr()), 128, [&]() { vm::reservation_op(vm::cast(ctxt->spurs.addr()), 128, [&]() {
auto spurs = ctxt->spurs.get_priv_ptr(); auto spurs = ctxt->spurs.priv_ptr();
if (wklId >= CELL_SPURS_MAX_WORKLOAD) { if (wklId >= CELL_SPURS_MAX_WORKLOAD) {
spurs->m.wklCurrentContention[wklId & 0x0F] -= 0x10; spurs->m.wklCurrentContention[wklId & 0x0F] -= 0x10;
@ -1158,7 +1158,7 @@ s32 spursTasksetProcessRequest(SPUThread & spu, s32 request, u32 * taskId, u32 *
s32 rc = CELL_OK; s32 rc = CELL_OK;
s32 numNewlyReadyTasks; s32 numNewlyReadyTasks;
vm::reservation_op(vm::cast(ctxt->taskset.addr()), 128, [&]() { vm::reservation_op(vm::cast(ctxt->taskset.addr()), 128, [&]() {
auto taskset = ctxt->taskset.get_priv_ptr(); auto taskset = ctxt->taskset.priv_ptr();
// Verify taskset state is valid // Verify taskset state is valid
auto _0 = be_t<u128>::make(u128::from32(0)); auto _0 = be_t<u128>::make(u128::from32(0));
@ -1299,7 +1299,7 @@ s32 spursTasksetProcessRequest(SPUThread & spu, s32 request, u32 * taskId, u32 *
// Increment the ready count of the workload by the number of tasks that have become ready // Increment the ready count of the workload by the number of tasks that have become ready
vm::reservation_op(vm::cast(kernelCtxt->spurs.addr()), 128, [&]() { vm::reservation_op(vm::cast(kernelCtxt->spurs.addr()), 128, [&]() {
auto spurs = kernelCtxt->spurs.get_priv_ptr(); auto spurs = kernelCtxt->spurs.priv_ptr();
s32 readyCount = kernelCtxt->wklCurrentId < CELL_SPURS_MAX_WORKLOAD ? spurs->m.wklReadyCount1[kernelCtxt->wklCurrentId].read_relaxed() : spurs->m.wklIdleSpuCountOrReadyCount2[kernelCtxt->wklCurrentId & 0x0F].read_relaxed(); s32 readyCount = kernelCtxt->wklCurrentId < CELL_SPURS_MAX_WORKLOAD ? spurs->m.wklReadyCount1[kernelCtxt->wklCurrentId].read_relaxed() : spurs->m.wklIdleSpuCountOrReadyCount2[kernelCtxt->wklCurrentId & 0x0F].read_relaxed();
readyCount += numNewlyReadyTasks; readyCount += numNewlyReadyTasks;

File diff suppressed because it is too large Load diff

View file

@ -559,13 +559,12 @@ s32 _sys_printf(vm::ptr<const char> fmt) // va_args...
return CELL_OK; return CELL_OK;
} }
s32 _nid_E75C40F2(u32 dest) s32 sys_process_get_paramsfo(vm::ptr<char> buffer)
{ {
sysPrxForUser.Todo("Unnamed function 0xE75C40F2 (dest=0x%x) -> CELL_ENOENT", dest); sysPrxForUser.Warning("sys_process_get_paramsfo(buffer=0x%x)", buffer);
// prx: load some data (0x40 bytes) previously set by sys_process_get_paramsfo // prx: load some data (0x40 bytes) previously set by _sys_process_get_paramsfo syscall
//memset(Memory + dest, 0, 0x40); return _sys_process_get_paramsfo(buffer);
return CELL_ENOENT;
} }
Module sysPrxForUser("sysPrxForUser", []() Module sysPrxForUser("sysPrxForUser", []()
@ -674,5 +673,5 @@ Module sysPrxForUser("sysPrxForUser", []()
REG_FUNC(sysPrxForUser, _sys_printf); REG_FUNC(sysPrxForUser, _sys_printf);
REG_UNNAMED(sysPrxForUser, E75C40F2); REG_FUNC(sysPrxForUser, sys_process_get_paramsfo);
}); });

View file

@ -0,0 +1,33 @@
#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/SysCalls/Modules.h"
#include "Emu/Cell/PPUInstrTable.h"
extern Module sys_libc;
namespace sys_libc_func
{
void memcpy(vm::ptr<void> dst, vm::ptr<const void> src, u32 size)
{
sys_libc.Warning("memcpy(dst=0x%x, src=0x%x, size=0x%x)", dst, src, size);
::memcpy(dst.get_ptr(), src.get_ptr(), size);
}
}
Module sys_libc("sys_libc", []()
{
using namespace PPU_instr;
REG_SUB(sys_libc, "", sys_libc_func, memcpy,
se_op(CMPLDI(cr7, r5, 7)),
se_op(CLRLDI(r3, r3, 32)),
se_op(CLRLDI(r4, r4, 32)),
se_op(MR(r11, r3)),
se_op(BGT(cr7, XXX & 0xff)),
se_op(CMPDI(r5, 0)),
se_opt_op(MR(r9, r3)),
{ SPET_MASKED_OPCODE, 0x4d820020, 0xffffffff },
);
});

View file

@ -80,7 +80,7 @@ const ppu_func_caller sc_table[1024] =
null_func,//bind_func(), //27 (0x01B) DBG null_func,//bind_func(), //27 (0x01B) DBG
null_func,//bind_func(_sys_process_get_number_of_object)//28 (0x01C) ROOT null_func,//bind_func(_sys_process_get_number_of_object)//28 (0x01C) ROOT
bind_func(sys_process_get_id), //29 (0x01D) ROOT bind_func(sys_process_get_id), //29 (0x01D) ROOT
bind_func(sys_process_get_paramsfo), //30 (0x01E) bind_func(_sys_process_get_paramsfo), //30 (0x01E)
null_func,//bind_func(sys_process_get_ppu_guid), //31 (0x01F) null_func,//bind_func(sys_process_get_ppu_guid), //31 (0x01F)
null_func, null_func, null_func, null_func, null_func, null_func, null_func, null_func, null_func, //32-40 UNS null_func, null_func, null_func, null_func, null_func, null_func, null_func, null_func, null_func, //32-40 UNS

View file

@ -4,6 +4,8 @@
#include "Emu/System.h" #include "Emu/System.h"
#include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SysCalls.h"
#include "Emu/FS/vfsFile.h"
#include "Loader/PSF.h"
#include "sys_memory.h" #include "sys_memory.h"
#include "sys_process.h" #include "sys_process.h"
@ -286,29 +288,25 @@ s32 sys_process_is_spu_lock_line_reservation_address(u32 addr, u64 flags)
return process_is_spu_lock_line_reservation_address(addr, flags); return process_is_spu_lock_line_reservation_address(addr, flags);
} }
s32 sys_process_get_paramsfo(vm::ptr<u8> buffer) s32 _sys_process_get_paramsfo(vm::ptr<char> buffer)
{ {
sys_process.Todo("sys_process_get_paramsfo(buffer_addr=0x%x) -> CELL_ENOENT", buffer.addr()); sys_process.Warning("_sys_process_get_paramsfo(buffer=0x%x)", buffer);
return CELL_ENOENT;
/*//Before uncommenting this code, we should check if it is actually working. if (!Emu.GetTitleID().length())
MemoryAllocator<be_t<u32>> fd; {
char filePath [] = "/app_home/../PARAM.SFO";
if (!cellFsOpen(Memory.RealToVirtualAddr(filePath), 0, fd, NULL, 0))
return CELL_ENOENT; return CELL_ENOENT;
}
MemoryAllocator<be_t<u64>> pos, nread; memset(buffer.get_ptr(), 0, 0x40);
cellFsLseek(fd, 0, CELL_SEEK_SET, pos); //TODO: Move to the appropriate offset (probably 0x3F7) memcpy(buffer.get_ptr() + 1, Emu.GetTitleID().c_str(), std::min<size_t>(Emu.GetTitleID().length(), 9));
cellFsRead(fd, buffer.addr(), 40, nread); //WARNING: If offset==0x3F7: The file will end before the buffer (40 bytes) is filled!
cellFsClose(fd);
return CELL_OK;*/ return CELL_OK;
} }
s32 process_get_sdk_version(u32 pid, s32& ver) s32 process_get_sdk_version(u32 pid, s32& ver)
{ {
// TODO: get correct SDK version for selected pid // get correct SDK version for selected pid
ver = Emu.m_sdk_version; ver = Emu.GetSDKVersion();
return CELL_OK; return CELL_OK;
} }

View file

@ -33,7 +33,7 @@ s32 sys_process_getpid();
s32 sys_process_getppid(); s32 sys_process_getppid();
s32 sys_process_get_number_of_object(u32 object, vm::ptr<u32> nump); s32 sys_process_get_number_of_object(u32 object, vm::ptr<u32> nump);
s32 sys_process_get_id(u32 object, vm::ptr<u32> buffer, u32 size, vm::ptr<u32> set_size); s32 sys_process_get_id(u32 object, vm::ptr<u32> buffer, u32 size, vm::ptr<u32> set_size);
s32 sys_process_get_paramsfo(vm::ptr<u8> buffer); s32 _sys_process_get_paramsfo(vm::ptr<char> buffer);
s32 sys_process_get_sdk_version(u32 pid, vm::ptr<s32> version); s32 sys_process_get_sdk_version(u32 pid, vm::ptr<s32> version);
s32 sys_process_get_status(u64 unk); s32 sys_process_get_status(u64 unk);
s32 sys_process_is_spu_lock_line_reservation_address(u32 addr, u64 flags); s32 sys_process_is_spu_lock_line_reservation_address(u32 addr, u64 flags);

View file

@ -38,11 +38,6 @@ static const std::string& BreakPointsDBName = "BreakPoints.dat";
static const u16 bpdb_version = 0x1000; static const u16 bpdb_version = 0x1000;
extern std::atomic<u32> g_thread_count; extern std::atomic<u32> g_thread_count;
ModuleInitializer::ModuleInitializer()
{
Emu.AddModuleInit(std::move(std::unique_ptr<ModuleInitializer>(this)));
}
Emulator::Emulator() Emulator::Emulator()
: m_status(Stopped) : m_status(Stopped)
, m_mode(DisAsm) , m_mode(DisAsm)
@ -82,11 +77,6 @@ Emulator::~Emulator()
void Emulator::Init() void Emulator::Init()
{ {
while(m_modules_init.size())
{
m_modules_init[0]->Init();
m_modules_init.erase(m_modules_init.begin());
}
} }
void Emulator::SetPath(const std::string& path, const std::string& elf_path) void Emulator::SetPath(const std::string& path, const std::string& elf_path)
@ -265,24 +255,7 @@ void Emulator::Load()
vm::close(); vm::close();
return; return;
} }
// trying to load some info from PARAM.SFO
vfsFile f2("/app_home/../PARAM.SFO");
if (f2.IsOpened())
{
PSFLoader psf(f2);
if (psf.Load(false))
{
std::string version = psf.GetString("PS3_SYSTEM_VER");
const size_t dot = version.find('.');
if (dot != std::string::npos)
{
Emu.m_sdk_version = (std::stoi(version, nullptr, 16) << 20) | ((std::stoi(version.substr(dot + 1), nullptr, 16) & 0xffff) << 4) | 1;
}
}
}
LoadPoints(BreakPointsDBName); LoadPoints(BreakPointsDBName);
m_status = Ready; m_status = Ready;

View file

@ -61,14 +61,6 @@ public:
u32 GetTLSMemsz() const { return tls_memsz; } u32 GetTLSMemsz() const { return tls_memsz; }
}; };
class ModuleInitializer
{
public:
ModuleInitializer();
virtual void Init() = 0;
};
class Emulator class Emulator
{ {
enum Mode enum Mode
@ -83,7 +75,6 @@ class Emulator
u32 m_rsx_callback; u32 m_rsx_callback;
u32 m_cpu_thr_stop; u32 m_cpu_thr_stop;
std::vector<std::unique_ptr<ModuleInitializer>> m_modules_init;
std::vector<u64> m_break_points; std::vector<u64> m_break_points;
std::vector<u64> m_marked_points; std::vector<u64> m_marked_points;
@ -112,7 +103,6 @@ public:
std::string m_emu_path; std::string m_emu_path;
std::string m_title_id; std::string m_title_id;
std::string m_title; std::string m_title;
s32 m_sdk_version;
Emulator(); Emulator();
~Emulator(); ~Emulator();
@ -132,12 +122,12 @@ public:
return m_emu_path; return m_emu_path;
} }
std::string GetTitleID() const const std::string& GetTitleID() const
{ {
return m_title_id; return m_title_id;
} }
std::string GetTitle() const const std::string& GetTitle() const
{ {
return m_title; return m_title;
} }
@ -164,11 +154,6 @@ public:
ModuleManager& GetModuleManager() { return *m_module_manager; } ModuleManager& GetModuleManager() { return *m_module_manager; }
SyncPrimManager& GetSyncPrimManager() { return *m_sync_prim_manager; } SyncPrimManager& GetSyncPrimManager() { return *m_sync_prim_manager; }
void AddModuleInit(std::unique_ptr<ModuleInitializer> m)
{
m_modules_init.push_back(std::move(m));
}
void SetTLSData(u32 addr, u32 filesz, u32 memsz) void SetTLSData(u32 addr, u32 filesz, u32 memsz)
{ {
m_info.SetTLSData(addr, filesz, memsz); m_info.SetTLSData(addr, filesz, memsz);
@ -191,6 +176,7 @@ public:
u32 GetTLSMemsz() const { return m_info.GetTLSMemsz(); } u32 GetTLSMemsz() const { return m_info.GetTLSMemsz(); }
u32 GetMallocPageSize() { return m_info.GetProcParam().malloc_pagesize; } u32 GetMallocPageSize() { return m_info.GetProcParam().malloc_pagesize; }
u32 GetSDKVersion() { return m_info.GetProcParam().sdk_version; }
u32 GetRSXCallback() const { return m_rsx_callback; } u32 GetRSXCallback() const { return m_rsx_callback; }
u32 GetCPUThreadStop() const { return m_cpu_thr_stop; } u32 GetCPUThreadStop() const { return m_cpu_thr_stop; }

View file

@ -1,8 +1,9 @@
#include "stdafx_gui.h" #include "stdafx_gui.h"
#include "Utilities/rMsgBox.h" #include "Utilities/rMsgBox.h"
#include "Emu/Cell/PPUProgramCompiler.h" //#include "Emu/Cell/PPUProgramCompiler.h"
using namespace PPU_opcodes; //using namespace PPU_opcodes;
#include "CompilerELF.h" #include "CompilerELF.h"
enum CompilerIDs enum CompilerIDs
@ -392,8 +393,8 @@ void CompilerELF::LoadElf(wxCommandEvent& event)
LoadElf(fmt::ToUTF8(ctrl.GetPath())); LoadElf(fmt::ToUTF8(ctrl.GetPath()));
} }
#include "Emu/Cell/PPUDisAsm.h" //#include "Emu/Cell/PPUDisAsm.h"
#include "Emu/Cell/PPUDecoder.h" //#include "Emu/Cell/PPUDecoder.h"
void CompilerELF::LoadElf(const std::string& path) void CompilerELF::LoadElf(const std::string& path)
{ {

View file

@ -29,20 +29,22 @@ struct wxWriter : Log::LogListener
wxTextAttr m_color_white; wxTextAttr m_color_white;
wxTextAttr m_color_yellow; wxTextAttr m_color_yellow;
wxTextAttr m_color_red; wxTextAttr m_color_red;
wxTextAttr m_color_green;
MTRingbuffer<char, BUFFER_MAX_SIZE> messages; MTRingbuffer<char, BUFFER_MAX_SIZE> messages;
std::atomic<bool> newLog; std::atomic<bool> newLog;
bool inited; bool inited;
wxWriter(wxTextCtrl* p_log, wxTextCtrl* p_tty) : wxWriter(wxTextCtrl* p_log, wxTextCtrl* p_tty)
m_color_white(wxColour(255, 255, 255)) , : m_color_white(wxColour(255, 255, 255))
m_color_yellow(wxColour(255, 255, 0)) , , m_color_yellow(wxColour(255, 255, 0))
m_color_red(wxColour(255, 0, 0)) , , m_color_red(wxColour(255, 0, 0))
m_log(p_log), , m_color_green(wxColour(0, 255, 0))
m_tty(p_tty), , m_log(p_log)
newLog(false), , m_tty(p_tty)
inited(false) , newLog(false)
, inited(false)
{ {
m_log->Bind(EVT_LOG_COMMAND, [this](wxCommandEvent &evt){this->write(evt);}); m_log->Bind(EVT_LOG_COMMAND, [this](wxCommandEvent &evt){ this->write(evt); });
} }
wxWriter(wxWriter &other) = delete; wxWriter(wxWriter &other) = delete;
@ -83,6 +85,9 @@ struct wxWriter : Log::LogListener
case Log::Error: case Log::Error:
llogcon->SetDefaultStyle(m_color_red); llogcon->SetDefaultStyle(m_color_red);
break; break;
case Log::Success:
llogcon->SetDefaultStyle(m_color_green);
break;
default: default:
break; break;
} }

View file

@ -7,7 +7,7 @@
#include "Debugger.h" #include "Debugger.h"
#include "InterpreterDisAsm.h" #include "InterpreterDisAsm.h"
#include "Emu/CPU/CPUThreadManager.h" #include "Emu/CPU/CPUThreadManager.h"
#include "Emu/Cell/PPCThread.h" #include "Emu/CPU/CPUThread.h"
class DbgEmuPanel : public wxPanel class DbgEmuPanel : public wxPanel
@ -95,7 +95,7 @@ public:
break; break;
case DID_EXIT_THR_SYSCALL: case DID_EXIT_THR_SYSCALL:
Emu.GetCPU().RemoveThread(((PPCThread*)event.GetClientData())->GetId()); Emu.GetCPU().RemoveThread(((CPUThread*)event.GetClientData())->GetId());
break; break;
} }

View file

@ -62,7 +62,7 @@ void GLGSFrame::Flip(void* context)
canvas->SwapBuffers(); canvas->SwapBuffers();
m_frames++; m_frames++;
const std::string sub_title = Emu.GetTitle() += Emu.GetTitleID().length() ? " [" + Emu.GetTitleID() + "] | " : " | "; const std::string sub_title = Emu.GetTitle() + (Emu.GetTitleID().length() ? " [" + Emu.GetTitleID() + "] | " : " | ");
if (fps_t.GetElapsedTimeInSec() >= 0.5) if (fps_t.GetElapsedTimeInSec() >= 0.5)
{ {

View file

@ -487,8 +487,8 @@ void MainFrame::Config(wxCommandEvent& WXUNUSED(event))
cbox_camera_type->Append("USB Video Class 1.1"); cbox_camera_type->Append("USB Video Class 1.1");
cbox_hle_loglvl->Append("All"); cbox_hle_loglvl->Append("All");
cbox_hle_loglvl->Append("Success");
cbox_hle_loglvl->Append("Warnings"); cbox_hle_loglvl->Append("Warnings");
cbox_hle_loglvl->Append("Success");
cbox_hle_loglvl->Append("Errors"); cbox_hle_loglvl->Append("Errors");
cbox_hle_loglvl->Append("Nothing"); cbox_hle_loglvl->Append("Nothing");

View file

@ -307,8 +307,6 @@ namespace loader
return load_sprx(info); return load_sprx(info);
} }
Emu.m_sdk_version = -1;
//store elf to memory //store elf to memory
vm::ps3::init(); vm::ps3::init();
@ -467,14 +465,10 @@ namespace loader
LOG_NOTICE(LOADER, "Imported function '%s' (0x%x)", SysCalls::GetHLEFuncName(nid), addr); LOG_NOTICE(LOADER, "Imported function '%s' (0x%x)", SysCalls::GetHLEFuncName(nid), addr);
} }
if (!vm::check_addr(addr, 4)) if (!patch_ppu_import(addr, index))
{ {
LOG_ERROR(LOADER, "Failed to inject code for function '%s' (0x%x)", SysCalls::GetHLEFuncName(nid), addr); LOG_ERROR(LOADER, "Failed to inject code for function '%s' (0x%x)", SysCalls::GetHLEFuncName(nid), addr);
} }
else
{
vm::write32(addr, HACK(index | EIF_SAVE_RTOC | EIF_PERFORM_BLR));
}
} }
} }
} }
@ -576,7 +570,7 @@ namespace loader
{ {
m_stream->Seek(handler::get_stream_offset() + phdr.p_offset); m_stream->Seek(handler::get_stream_offset() + phdr.p_offset);
m_stream->Read(phdr.p_vaddr.get_ptr(), phdr.p_filesz); m_stream->Read(phdr.p_vaddr.get_ptr(), phdr.p_filesz);
hook_ppu_funcs((u32*)phdr.p_vaddr.get_ptr(), vm::cast(phdr.p_filesz)); hook_ppu_funcs(vm::ptr<u32>::make(phdr.p_vaddr.addr()), vm::cast(phdr.p_filesz) / 4);
} }
} }
break; break;
@ -618,7 +612,6 @@ namespace loader
*/ */
info = proc_param.info; info = proc_param.info;
Emu.m_sdk_version = info.sdk_version;
} }
} }
break; break;
@ -689,7 +682,10 @@ namespace loader
LOG_NOTICE(LOADER, "Imported %sfunction '%s' in '%s' module (0x%x)", is_lle ? "LLE " : "", SysCalls::GetHLEFuncName(nid), module_name, addr); LOG_NOTICE(LOADER, "Imported %sfunction '%s' in '%s' module (0x%x)", is_lle ? "LLE " : "", SysCalls::GetHLEFuncName(nid), module_name, addr);
} }
vm::write32(addr, HACK(index | EIF_SAVE_RTOC | EIF_PERFORM_BLR)); if (!patch_ppu_import(addr, index))
{
LOG_ERROR(LOADER, "Failed to inject code at address 0x%x", addr);
}
//if (!func || !func->lle_func) //if (!func || !func->lle_func)
//{ //{

View file

@ -131,7 +131,6 @@
<ClCompile Include="Emu\Audio\XAudio2\XAudio2Thread.cpp" /> <ClCompile Include="Emu\Audio\XAudio2\XAudio2Thread.cpp" />
<ClCompile Include="Emu\Cell\MFC.cpp" /> <ClCompile Include="Emu\Cell\MFC.cpp" />
<ClCompile Include="Emu\Cell\PPCDecoder.cpp" /> <ClCompile Include="Emu\Cell\PPCDecoder.cpp" />
<ClCompile Include="Emu\Cell\PPCThread.cpp" />
<ClCompile Include="Emu\Cell\PPULLVMRecompilerTests.cpp"> <ClCompile Include="Emu\Cell\PPULLVMRecompilerTests.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'">true</ExcludedFromBuild>
@ -279,6 +278,7 @@
<ClCompile Include="Emu\SysCalls\Modules\sysPrxForUser.cpp" /> <ClCompile Include="Emu\SysCalls\Modules\sysPrxForUser.cpp" />
<ClCompile Include="Emu\SysCalls\Modules\sys_http.cpp" /> <ClCompile Include="Emu\SysCalls\Modules\sys_http.cpp" />
<ClCompile Include="Emu\SysCalls\Modules\sys_io.cpp" /> <ClCompile Include="Emu\SysCalls\Modules\sys_io.cpp" />
<ClCompile Include="Emu\SysCalls\Modules\sys_libc.cpp" />
<ClCompile Include="Emu\SysCalls\Modules\sys_net.cpp" /> <ClCompile Include="Emu\SysCalls\Modules\sys_net.cpp" />
<ClCompile Include="Emu\SysCalls\SyncPrimitivesManager.cpp" /> <ClCompile Include="Emu\SysCalls\SyncPrimitivesManager.cpp" />
<ClCompile Include="Emu\SysCalls\SysCalls.cpp" /> <ClCompile Include="Emu\SysCalls\SysCalls.cpp" />
@ -362,7 +362,6 @@
<ClInclude Include="Emu\Cell\PPCDecoder.h" /> <ClInclude Include="Emu\Cell\PPCDecoder.h" />
<ClInclude Include="Emu\Cell\PPCDisAsm.h" /> <ClInclude Include="Emu\Cell\PPCDisAsm.h" />
<ClInclude Include="Emu\Cell\PPCInstrTable.h" /> <ClInclude Include="Emu\Cell\PPCInstrTable.h" />
<ClInclude Include="Emu\Cell\PPCThread.h" />
<ClInclude Include="Emu\Cell\PPUDecoder.h" /> <ClInclude Include="Emu\Cell\PPUDecoder.h" />
<ClInclude Include="Emu\Cell\PPUDisAsm.h" /> <ClInclude Include="Emu\Cell\PPUDisAsm.h" />
<ClInclude Include="Emu\Cell\PPUInstrTable.h" /> <ClInclude Include="Emu\Cell\PPUInstrTable.h" />

View file

@ -341,9 +341,6 @@
<ClCompile Include="Emu\Cell\PPCDecoder.cpp"> <ClCompile Include="Emu\Cell\PPCDecoder.cpp">
<Filter>Emu\CPU\Cell</Filter> <Filter>Emu\CPU\Cell</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Emu\Cell\PPCThread.cpp">
<Filter>Emu\CPU\Cell</Filter>
</ClCompile>
<ClCompile Include="Emu\Cell\PPUThread.cpp"> <ClCompile Include="Emu\Cell\PPUThread.cpp">
<Filter>Emu\CPU\Cell</Filter> <Filter>Emu\CPU\Cell</Filter>
</ClCompile> </ClCompile>
@ -860,6 +857,9 @@
<ClCompile Include="Emu\RSX\CgBinaryVertexProgram.cpp"> <ClCompile Include="Emu\RSX\CgBinaryVertexProgram.cpp">
<Filter>Emu\GPU\RSX</Filter> <Filter>Emu\GPU\RSX</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Emu\SysCalls\Modules\sys_libc.cpp">
<Filter>Emu\SysCalls\Modules</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Crypto\aes.h"> <ClInclude Include="Crypto\aes.h">
@ -1105,9 +1105,6 @@
<ClInclude Include="Emu\Cell\PPCInstrTable.h"> <ClInclude Include="Emu\Cell\PPCInstrTable.h">
<Filter>Emu\CPU\Cell</Filter> <Filter>Emu\CPU\Cell</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Emu\Cell\PPCThread.h">
<Filter>Emu\CPU\Cell</Filter>
</ClInclude>
<ClInclude Include="Emu\Cell\PPUDecoder.h"> <ClInclude Include="Emu\Cell\PPUDecoder.h">
<Filter>Emu\CPU\Cell</Filter> <Filter>Emu\CPU\Cell</Filter>
</ClInclude> </ClInclude>