Merge pull request #1531 from reicast/fh/smc-option
dynarec: add option to control smc code checks: faster, fast, full
This commit is contained in:
commit
26f02e22da
|
@ -304,6 +304,8 @@ void bm_Rebuild()
|
|||
{
|
||||
return;
|
||||
|
||||
die("this is broken in multiple levels, including compile options");
|
||||
|
||||
void RASDASD();
|
||||
RASDASD();
|
||||
|
||||
|
@ -321,7 +323,7 @@ void bm_Rebuild()
|
|||
//constprop(all_blocks[i]);
|
||||
//#endif
|
||||
}
|
||||
ngen_Compile(all_blocks[i],false,false,all_blocks[i]->staging_runs>0,do_opts);
|
||||
ngen_Compile(all_blocks[i],NoCheck,false,all_blocks[i]->staging_runs>0,do_opts);
|
||||
|
||||
blkmap.insert(all_blocks[i]);
|
||||
verify(bm_GetBlock((RuntimeBlockInfo*)all_blocks[i]->code)==all_blocks[i]);
|
||||
|
|
|
@ -86,6 +86,15 @@ void recSh4_Run()
|
|||
|
||||
sh4_dyna_rcb=(u8*)&Sh4cntx + sizeof(Sh4cntx);
|
||||
printf("cntx // fpcb offset: %td // pc offset: %td // pc %08X\n",(u8*)&sh4rcb.fpcb - sh4_dyna_rcb, (u8*)&sh4rcb.cntx.pc - sh4_dyna_rcb,sh4rcb.cntx.pc);
|
||||
|
||||
if (!settings.dynarec.safemode)
|
||||
printf("Warning: Dynarec safe mode is off\n");
|
||||
|
||||
if (settings.dynarec.unstable_opt)
|
||||
printf("Warning: Unstable optimizations is on\n");
|
||||
|
||||
if (settings.dynarec.SmcCheckLevel != FullCheck)
|
||||
printf("Warning: SMC check mode is %d\n", settings.dynarec.SmcCheckLevel);
|
||||
|
||||
verify(rcb_noffs(&next_pc)==-184);
|
||||
ngen_mainloop(sh4_dyna_rcb);
|
||||
|
@ -119,34 +128,51 @@ u32 emit_FreeSpace()
|
|||
}
|
||||
|
||||
|
||||
bool DoCheck(u32 pc)
|
||||
SmcCheckEnum DoCheck(u32 pc)
|
||||
{
|
||||
if (IsOnRam(pc))
|
||||
{
|
||||
if (!settings.dynarec.unstable_opt)
|
||||
return true;
|
||||
|
||||
pc&=0xFFFFFF;
|
||||
switch(pc)
|
||||
{
|
||||
//DOA2LE
|
||||
case 0x3DAFC6:
|
||||
case 0x3C83F8:
|
||||
switch (settings.dynarec.SmcCheckLevel) {
|
||||
|
||||
//Shenmue 2
|
||||
case 0x348000:
|
||||
|
||||
//Shenmue
|
||||
case 0x41860e:
|
||||
|
||||
// Heuristic-elimintaed FastChecks
|
||||
case NoCheck: {
|
||||
if (IsOnRam(pc))
|
||||
{
|
||||
pc&=0xFFFFFF;
|
||||
switch(pc)
|
||||
{
|
||||
//DOA2LE
|
||||
case 0x3DAFC6:
|
||||
case 0x3C83F8:
|
||||
|
||||
return true;
|
||||
//Shenmue 2
|
||||
case 0x348000:
|
||||
|
||||
//Shenmue
|
||||
case 0x41860e:
|
||||
|
||||
|
||||
default:
|
||||
return false;
|
||||
return FastCheck;
|
||||
|
||||
default:
|
||||
return NoCheck;
|
||||
}
|
||||
}
|
||||
return NoCheck;
|
||||
}
|
||||
break;
|
||||
|
||||
// Fast Check everything
|
||||
case FastCheck:
|
||||
return FastCheck;
|
||||
|
||||
// Full Check everything
|
||||
case FullCheck:
|
||||
return FullCheck;
|
||||
|
||||
default:
|
||||
die("Unhandled settings.dynarec.SmcCheckLevel");
|
||||
return FullCheck;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnalyseBlock(RuntimeBlockInfo* blk);
|
||||
|
|
|
@ -85,7 +85,7 @@ u32 DYNACALL rdv_DoInterrupts_pc(u32 pc);
|
|||
void ngen_init();
|
||||
|
||||
//Called to compile a block
|
||||
void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool staging,bool optimise);
|
||||
void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging,bool optimise);
|
||||
|
||||
//Called when blocks are reseted
|
||||
void ngen_ResetBlocks();
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
//Types
|
||||
|
||||
#define printf_smc(...) // printf
|
||||
|
||||
|
||||
u32 CCN_QACR_TR[2];
|
||||
|
||||
|
@ -72,13 +74,18 @@ void CCN_CCR_write(u32 addr, u32 value)
|
|||
temp.reg_data=value;
|
||||
|
||||
|
||||
//what is 0xAC13DBF8 from ?
|
||||
if (temp.ICI && curr_pc!=0xAC13DBF8)
|
||||
{
|
||||
//printf("Sh4: i-cache invalidation %08X\n",curr_pc);
|
||||
// Shikigami No Shiro II sets ICI frequently
|
||||
// Any reason to flush the dynarec cache for this?
|
||||
//sh4_cpu.ResetCache();
|
||||
if (temp.ICI) {
|
||||
printf_smc("Sh4: i-cache invalidation %08X\n",curr_pc);
|
||||
|
||||
if (settings.dynarec.SmcCheckLevel != FullCheck) {
|
||||
//TODO: Add skip/check vectors for Shikigami No Shiro II (uses ICI frequently)
|
||||
//which game is 0xAC13DBF8 from ?
|
||||
if (curr_pc != 0xAC13DBF8)
|
||||
{
|
||||
printf("Sh4: code cache clear (ICI) pc: %08X\n",curr_pc);
|
||||
sh4_cpu.ResetCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
temp.ICI=0;
|
||||
|
|
|
@ -495,6 +495,7 @@ void InitSettings()
|
|||
settings.dreamcast.broadcast = 4; // default
|
||||
settings.dreamcast.language = 6; // default
|
||||
settings.dreamcast.FullMMU = false;
|
||||
settings.dynarec.SmcCheckLevel = FullCheck;
|
||||
settings.aica.LimitFPS = true;
|
||||
settings.aica.NoBatch = false; // This also controls the DSP. Disabled by default
|
||||
settings.aica.NoSound = false;
|
||||
|
@ -563,6 +564,7 @@ void LoadSettings(bool game_specific)
|
|||
settings.dynarec.idleskip = cfgLoadBool(config_section, "Dynarec.idleskip", settings.dynarec.idleskip);
|
||||
settings.dynarec.unstable_opt = cfgLoadBool(config_section, "Dynarec.unstable-opt", settings.dynarec.unstable_opt);
|
||||
settings.dynarec.safemode = cfgLoadBool(config_section, "Dynarec.safe-mode", settings.dynarec.safemode);
|
||||
settings.dynarec.SmcCheckLevel = (SmcCheckEnum)cfgLoadInt(config_section, "Dynarec.SmcCheckLevel", settings.dynarec.SmcCheckLevel);
|
||||
//disable_nvmem can't be loaded, because nvmem init is before cfg load
|
||||
settings.dreamcast.cable = cfgLoadInt(config_section, "Dreamcast.Cable", settings.dreamcast.cable);
|
||||
settings.dreamcast.region = cfgLoadInt(config_section, "Dreamcast.Region", settings.dreamcast.region);
|
||||
|
@ -698,6 +700,8 @@ void SaveSettings()
|
|||
cfgSaveBool("config", "Dynarec.unstable-opt", settings.dynarec.unstable_opt);
|
||||
if (!safemode_game || !settings.dynarec.safemode)
|
||||
cfgSaveBool("config", "Dynarec.safe-mode", settings.dynarec.safemode);
|
||||
cfgSaveInt("config", "Dynarec.SmcCheckLevel", (int)settings.dynarec.SmcCheckLevel);
|
||||
|
||||
cfgSaveInt("config", "Dreamcast.Language", settings.dreamcast.language);
|
||||
cfgSaveBool("config", "aica.LimitFPS", settings.aica.LimitFPS);
|
||||
cfgSaveBool("config", "aica.NoBatch", settings.aica.NoBatch);
|
||||
|
|
|
@ -2082,7 +2082,7 @@ __default:
|
|||
}
|
||||
|
||||
|
||||
void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool staging,bool optimise)
|
||||
void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging,bool optimise)
|
||||
{
|
||||
//printf("Compile: %08X, %d, %d\n",block->addr,staging,optimise);
|
||||
block->code=(DynarecCodeEntryPtr)EMIT_GET_PTR();
|
||||
|
@ -2114,39 +2114,68 @@ void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool st
|
|||
reg.OpBegin(&block->oplist[0],0);
|
||||
|
||||
//scheduler
|
||||
if (force_checks)
|
||||
{
|
||||
s32 sz = block->sh4_code_size;
|
||||
u32 addr = block->addr;
|
||||
MOV32(r0,addr);
|
||||
switch (smc_checks) {
|
||||
case NoCheck:
|
||||
break;
|
||||
|
||||
while (sz > 0)
|
||||
{
|
||||
if (sz > 2)
|
||||
case FastCheck: {
|
||||
MOV32(r0,block->addr);
|
||||
u32* ptr=(u32*)GetMemPtr(block->addr,4);
|
||||
if (ptr != NULL)
|
||||
{
|
||||
u32* ptr=(u32*)GetMemPtr(addr,4);
|
||||
MOV32(r2,(u32)ptr);
|
||||
LDR(r2,r2,0);
|
||||
MOV32(r1,*ptr);
|
||||
CMP(r1,r2);
|
||||
|
||||
JUMP((u32)ngen_blockcheckfail, CC_NE);
|
||||
addr += 4;
|
||||
sz -= 4;
|
||||
}
|
||||
else
|
||||
}
|
||||
break;
|
||||
|
||||
case FullCheck: {
|
||||
s32 sz = block->sh4_code_size;
|
||||
u32 addr = block->addr;
|
||||
MOV32(r0,addr);
|
||||
|
||||
while (sz > 0)
|
||||
{
|
||||
u16* ptr = (u16 *)GetMemPtr(addr, 2);
|
||||
MOV32(r2, (u32)ptr);
|
||||
LDRH(r2, r2, 0, AL);
|
||||
MOVW(r1, *ptr, AL);
|
||||
CMP(r1, r2);
|
||||
if (sz > 2)
|
||||
{
|
||||
u32* ptr=(u32*)GetMemPtr(addr,4);
|
||||
if (ptr != NULL)
|
||||
{
|
||||
MOV32(r2,(u32)ptr);
|
||||
LDR(r2,r2,0);
|
||||
MOV32(r1,*ptr);
|
||||
CMP(r1,r2);
|
||||
|
||||
JUMP((u32)ngen_blockcheckfail, CC_NE);
|
||||
addr += 2;
|
||||
sz -= 2;
|
||||
JUMP((u32)ngen_blockcheckfail, CC_NE);
|
||||
}
|
||||
addr += 4;
|
||||
sz -= 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
u16* ptr = (u16 *)GetMemPtr(addr, 2);
|
||||
if (ptr != NULL)
|
||||
{
|
||||
MOV32(r2, (u32)ptr);
|
||||
LDRH(r2, r2, 0, AL);
|
||||
MOVW(r1, *ptr, AL);
|
||||
CMP(r1, r2);
|
||||
|
||||
JUMP((u32)ngen_blockcheckfail, CC_NE);
|
||||
}
|
||||
addr += 2;
|
||||
sz -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: {
|
||||
die("unhandled smc_checks");
|
||||
}
|
||||
}
|
||||
|
||||
u32 cyc=block->guest_cycles;
|
||||
|
|
|
@ -348,15 +348,15 @@ public:
|
|||
return *ret_reg;
|
||||
}
|
||||
|
||||
void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise)
|
||||
void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise)
|
||||
{
|
||||
//printf("REC-ARM64 compiling %08x\n", block->addr);
|
||||
#ifdef PROFILING
|
||||
SaveFramePointer();
|
||||
#endif
|
||||
this->block = block;
|
||||
if (force_checks)
|
||||
CheckBlock(block);
|
||||
|
||||
CheckBlock(smc_checks, block);
|
||||
|
||||
// run register allocator
|
||||
regalloc.DoAlloc(block);
|
||||
|
@ -1292,49 +1292,72 @@ private:
|
|||
verify (GetCursorAddress<Instruction *>() - start_instruction == code_size * kInstructionSize);
|
||||
}
|
||||
|
||||
void CheckBlock(RuntimeBlockInfo* block)
|
||||
void CheckBlock(SmcCheckEnum smc_checks, RuntimeBlockInfo* block)
|
||||
{
|
||||
s32 sz = block->sh4_code_size;
|
||||
|
||||
Label blockcheck_fail;
|
||||
Label blockcheck_success;
|
||||
|
||||
u8* ptr = GetMemPtr(block->addr, sz);
|
||||
if (ptr == NULL)
|
||||
// FIXME Can a block cross a RAM / non-RAM boundary??
|
||||
return;
|
||||
switch (smc_checks) {
|
||||
case NoCheck:
|
||||
return;
|
||||
|
||||
Ldr(x9, reinterpret_cast<uintptr_t>(ptr));
|
||||
|
||||
while (sz > 0)
|
||||
{
|
||||
if (sz >= 8)
|
||||
{
|
||||
Ldr(x10, MemOperand(x9, 8, PostIndex));
|
||||
Ldr(x11, *(u64*)ptr);
|
||||
Cmp(x10, x11);
|
||||
sz -= 8;
|
||||
ptr += 8;
|
||||
}
|
||||
else if (sz >= 4)
|
||||
{
|
||||
Ldr(w10, MemOperand(x9, 4, PostIndex));
|
||||
case FastCheck: {
|
||||
u8* ptr = GetMemPtr(block->addr, 4);
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
Ldr(x9, reinterpret_cast<uintptr_t>(ptr));
|
||||
Ldr(w10, MemOperand(x9));
|
||||
Ldr(w11, *(u32*)ptr);
|
||||
Cmp(w10, w11);
|
||||
sz -= 4;
|
||||
ptr += 4;
|
||||
B(eq, &blockcheck_success);
|
||||
}
|
||||
else
|
||||
{
|
||||
Ldrh(w10, MemOperand(x9, 2, PostIndex));
|
||||
Mov(w11, *(u16*)ptr);
|
||||
Cmp(w10, w11);
|
||||
sz -= 2;
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case FullCheck: {
|
||||
s32 sz = block->sh4_code_size;
|
||||
|
||||
u8* ptr = GetMemPtr(block->addr, sz);
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
|
||||
Ldr(x9, reinterpret_cast<uintptr_t>(ptr));
|
||||
|
||||
while (sz > 0)
|
||||
{
|
||||
if (sz >= 8)
|
||||
{
|
||||
Ldr(x10, MemOperand(x9, 8, PostIndex));
|
||||
Ldr(x11, *(u64*)ptr);
|
||||
Cmp(x10, x11);
|
||||
sz -= 8;
|
||||
ptr += 8;
|
||||
}
|
||||
else if (sz >= 4)
|
||||
{
|
||||
Ldr(w10, MemOperand(x9, 4, PostIndex));
|
||||
Ldr(w11, *(u32*)ptr);
|
||||
Cmp(w10, w11);
|
||||
sz -= 4;
|
||||
ptr += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
Ldrh(w10, MemOperand(x9, 2, PostIndex));
|
||||
Mov(w11, *(u16*)ptr);
|
||||
Cmp(w10, w11);
|
||||
sz -= 2;
|
||||
ptr += 2;
|
||||
}
|
||||
B(ne, &blockcheck_fail);
|
||||
}
|
||||
B(&blockcheck_success);
|
||||
}
|
||||
B(ne, &blockcheck_fail);
|
||||
break;
|
||||
|
||||
default:
|
||||
die("unhandled smc_checks");
|
||||
}
|
||||
B(&blockcheck_success);
|
||||
|
||||
Bind(&blockcheck_fail);
|
||||
Ldr(w0, block->addr);
|
||||
|
@ -1404,13 +1427,13 @@ private:
|
|||
|
||||
static Arm64Assembler* compiler;
|
||||
|
||||
void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise)
|
||||
void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise)
|
||||
{
|
||||
verify(emit_FreeSpace() >= 16 * 1024);
|
||||
|
||||
compiler = new Arm64Assembler();
|
||||
|
||||
compiler->ngen_Compile(block, force_checks, reset, staging, optimise);
|
||||
compiler->ngen_Compile(block, smc_checks, reset, staging, optimise);
|
||||
|
||||
delete compiler;
|
||||
compiler = NULL;
|
||||
|
|
|
@ -1190,10 +1190,10 @@ public:
|
|||
|
||||
size_t opcode_index;
|
||||
opcodeExec** ptrsg;
|
||||
void compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise) {
|
||||
void compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise) {
|
||||
|
||||
//we need an extra one for the end opcode and optionally one more for block check
|
||||
auto ptrs = fnnCtor_forreal(block->oplist.size() + 1 + (force_checks ? 1 : 0))(block->guest_cycles);
|
||||
auto ptrs = fnnCtor_forreal(block->oplist.size() + 1 + (smc_checks != NoCheck ? 1 : 0))(block->guest_cycles);
|
||||
|
||||
ptrsg = ptrs.ptrs;
|
||||
|
||||
|
@ -1207,9 +1207,16 @@ public:
|
|||
}
|
||||
|
||||
size_t i = 0;
|
||||
if (force_checks)
|
||||
if (smc_checks != NoCheck)
|
||||
{
|
||||
verify (smc_checks == FastCheck || smc_checks == FullCheck)
|
||||
opcodeExec* op;
|
||||
int check_size = block->sh4_code_size;
|
||||
|
||||
if (smc_checks == FastCheck) {
|
||||
check_size = 4;
|
||||
}
|
||||
|
||||
switch (block->sh4_code_size)
|
||||
{
|
||||
case 4:
|
||||
|
@ -1227,6 +1234,7 @@ public:
|
|||
}
|
||||
ptrs.ptrs[i++] = op;
|
||||
}
|
||||
|
||||
for (size_t opnum = 0; opnum < block->oplist.size(); opnum++, i++) {
|
||||
opcode_index = i;
|
||||
shil_opcode& op = block->oplist[opnum];
|
||||
|
@ -1551,14 +1559,14 @@ public:
|
|||
|
||||
BlockCompiler* compiler;
|
||||
|
||||
void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise)
|
||||
void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise)
|
||||
{
|
||||
verify(emit_FreeSpace() >= 16 * 1024);
|
||||
|
||||
compiler = new BlockCompiler();
|
||||
|
||||
|
||||
compiler->compile(block, force_checks, reset, staging, optimise);
|
||||
compiler->compile(block, smc_checks, reset, staging, optimise);
|
||||
|
||||
delete compiler;
|
||||
}
|
||||
|
|
|
@ -239,12 +239,11 @@ public:
|
|||
call_regsxmm.push_back(xmm3);
|
||||
}
|
||||
|
||||
void compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise)
|
||||
void compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise)
|
||||
{
|
||||
//printf("X86_64 compiling %08x to %p\n", block->addr, emit_GetCCPtr());
|
||||
if (force_checks) {
|
||||
CheckBlock(block);
|
||||
}
|
||||
CheckBlock(smc_checks, block);
|
||||
|
||||
regalloc.DoAlloc(block);
|
||||
|
||||
sub(dword[rip + &cycle_counter], block->guest_cycles);
|
||||
|
@ -1086,41 +1085,66 @@ private:
|
|||
typedef void (BlockCompiler::*X64BinaryOp)(const Xbyak::Operand&, const Xbyak::Operand&);
|
||||
typedef void (BlockCompiler::*X64BinaryFOp)(const Xbyak::Xmm&, const Xbyak::Operand&);
|
||||
|
||||
void CheckBlock(RuntimeBlockInfo* block) {
|
||||
mov(call_regs[0], block->addr);
|
||||
void CheckBlock(SmcCheckEnum smc_checks, RuntimeBlockInfo* block) {
|
||||
|
||||
s32 sz=block->sh4_code_size;
|
||||
u32 sa=block->addr;
|
||||
switch (smc_checks) {
|
||||
case NoCheck:
|
||||
return;
|
||||
|
||||
while (sz > 0)
|
||||
{
|
||||
void* ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz);
|
||||
if (ptr)
|
||||
{
|
||||
mov(rax, reinterpret_cast<uintptr_t>(ptr));
|
||||
|
||||
if (sz >= 8) {
|
||||
mov(rdx, *(u64*)ptr);
|
||||
cmp(qword[rax], rdx);
|
||||
sz -= 8;
|
||||
sa += 8;
|
||||
}
|
||||
else if (sz >= 4) {
|
||||
case FastCheck: {
|
||||
void* ptr = (void*)GetMemPtr(block->addr, 4);
|
||||
if (ptr)
|
||||
{
|
||||
mov(call_regs[0], block->addr);
|
||||
mov(rax, reinterpret_cast<uintptr_t>(ptr));
|
||||
mov(edx, *(u32*)ptr);
|
||||
cmp(dword[rax], edx);
|
||||
sz -= 4;
|
||||
sa += 4;
|
||||
jne(reinterpret_cast<const void*>(&ngen_blockcheckfail));
|
||||
}
|
||||
else {
|
||||
mov(edx, *(u16*)ptr);
|
||||
cmp(word[rax],dx);
|
||||
sz -= 2;
|
||||
sa += 2;
|
||||
}
|
||||
jne(reinterpret_cast<const void*>(&ngen_blockcheckfail));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FullCheck: {
|
||||
s32 sz=block->sh4_code_size;
|
||||
u32 sa=block->addr;
|
||||
|
||||
void* ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz);
|
||||
if (ptr)
|
||||
{
|
||||
mov(call_regs[0], block->addr);
|
||||
|
||||
while (sz > 0)
|
||||
{
|
||||
mov(rax, reinterpret_cast<uintptr_t>(ptr));
|
||||
|
||||
if (sz >= 8) {
|
||||
mov(rdx, *(u64*)ptr);
|
||||
cmp(qword[rax], rdx);
|
||||
sz -= 8;
|
||||
sa += 8;
|
||||
}
|
||||
else if (sz >= 4) {
|
||||
mov(edx, *(u32*)ptr);
|
||||
cmp(dword[rax], edx);
|
||||
sz -= 4;
|
||||
sa += 4;
|
||||
}
|
||||
else {
|
||||
mov(edx, *(u16*)ptr);
|
||||
cmp(word[rax],dx);
|
||||
sz -= 2;
|
||||
sa += 2;
|
||||
}
|
||||
jne(reinterpret_cast<const void*>(&ngen_blockcheckfail));
|
||||
ptr = (void*)GetMemPtr(sa, sz > 8 ? 8 : sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
die("unhandled smc_checks");
|
||||
}
|
||||
}
|
||||
|
||||
void GenBinaryOp(const shil_opcode &op, X64BinaryOp natop)
|
||||
|
@ -1262,13 +1286,13 @@ void X64RegAlloc::Writeback_FPU(u32 reg, s8 nreg)
|
|||
|
||||
static BlockCompiler* compiler;
|
||||
|
||||
void ngen_Compile(RuntimeBlockInfo* block, bool force_checks, bool reset, bool staging, bool optimise)
|
||||
void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging, bool optimise)
|
||||
{
|
||||
verify(emit_FreeSpace() >= 16 * 1024);
|
||||
|
||||
compiler = new BlockCompiler();
|
||||
|
||||
compiler->compile(block, force_checks, reset, staging, optimise);
|
||||
compiler->compile(block, smc_checks, reset, staging, optimise);
|
||||
|
||||
delete compiler;
|
||||
}
|
||||
|
|
|
@ -229,29 +229,50 @@ u32 rdmt[6];
|
|||
extern u32 memops_t,memops_l;
|
||||
extern int mips_counter;
|
||||
|
||||
void CheckBlock(RuntimeBlockInfo* block,x86_ptr_imm place)
|
||||
//TODO: Get back validating mode for this
|
||||
void CheckBlock(SmcCheckEnum smc_checks, RuntimeBlockInfo* block)
|
||||
{
|
||||
s32 sz=block->sh4_code_size;
|
||||
u32 sa=block->addr;
|
||||
while(sz>0)
|
||||
{
|
||||
void* ptr=(void*)GetMemPtr(sa,4);
|
||||
if (ptr)
|
||||
{
|
||||
if (sz==2)
|
||||
x86e->Emit(op_cmp16,ptr,*(u16*)ptr);
|
||||
else
|
||||
x86e->Emit(op_cmp32,ptr,*(u32*)ptr);
|
||||
x86e->Emit(op_jne,place);
|
||||
switch (smc_checks) {
|
||||
case NoCheck:
|
||||
break;
|
||||
|
||||
case FastCheck: {
|
||||
void* ptr = (void*)GetMemPtr(block->addr, 4);
|
||||
if (ptr)
|
||||
{
|
||||
x86e->Emit(op_cmp32, ptr, *(u32*)ptr);
|
||||
x86e->Emit(op_jne, x86_ptr_imm(ngen_blockcheckfail));
|
||||
}
|
||||
}
|
||||
sz-=4;
|
||||
sa+=4;
|
||||
break;
|
||||
|
||||
case FullCheck: {
|
||||
s32 sz=block->sh4_code_size;
|
||||
u32 sa=block->addr;
|
||||
while(sz>0)
|
||||
{
|
||||
void* ptr=(void*)GetMemPtr(sa,4);
|
||||
if (ptr)
|
||||
{
|
||||
if (sz==2)
|
||||
x86e->Emit(op_cmp16,ptr,*(u16*)ptr);
|
||||
else
|
||||
x86e->Emit(op_cmp32,ptr,*(u32*)ptr);
|
||||
x86e->Emit(op_jne,x86_ptr_imm(ngen_blockcheckfail));
|
||||
}
|
||||
sz-=4;
|
||||
sa+=4;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
die("unhandled smc_checks");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool staging,bool optimise)
|
||||
void ngen_Compile(RuntimeBlockInfo* block, SmcCheckEnum smc_checks, bool reset, bool staging,bool optimise)
|
||||
{
|
||||
//initialise stuff
|
||||
DetectCpuFeatures();
|
||||
|
@ -282,7 +303,7 @@ void ngen_Compile(RuntimeBlockInfo* block,bool force_checks, bool reset, bool st
|
|||
//block invl. checks
|
||||
x86e->Emit(op_mov32,ECX,block->addr);
|
||||
|
||||
CheckBlock(block,force_checks?x86_ptr_imm(ngen_blockcheckfail):x86_ptr_imm(ngen_blockcheckfail2));
|
||||
CheckBlock(smc_checks, block);
|
||||
|
||||
//Scheduler
|
||||
x86_Label* no_up=x86e->CreateLabel(false,8);
|
||||
|
|
|
@ -1017,10 +1017,33 @@ static void gui_display_settings()
|
|||
ShowHelpMarker("Do not optimize integer division. Recommended");
|
||||
ImGui::Checkbox("Unstable Optimizations", &settings.dynarec.unstable_opt);
|
||||
ImGui::SameLine();
|
||||
ShowHelpMarker("Enable unsafe optimizations. May cause crash or environmental disaster");
|
||||
ShowHelpMarker("Enable unsafe optimizations. Will cause crash or environmental disaster");
|
||||
ImGui::Checkbox("Idle Skip", &settings.dynarec.idleskip);
|
||||
ImGui::SameLine();
|
||||
ShowHelpMarker("Skip wait loops. Recommended");
|
||||
ImGui::PushItemWidth(ImGui::CalcTextSize("Largeenough").x);
|
||||
const char *preview = settings.dynarec.SmcCheckLevel == NoCheck ? "Faster" : settings.dynarec.SmcCheckLevel == FastCheck ? "Fast" : "Full";
|
||||
if (ImGui::BeginCombo("SMC Checks", preview , ImGuiComboFlags_None))
|
||||
{
|
||||
bool is_selected = settings.dynarec.SmcCheckLevel == NoCheck;
|
||||
if (ImGui::Selectable("Faster", &is_selected))
|
||||
settings.dynarec.SmcCheckLevel = NoCheck;
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
is_selected = settings.dynarec.SmcCheckLevel == FastCheck;
|
||||
if (ImGui::Selectable("Fast", &is_selected))
|
||||
settings.dynarec.SmcCheckLevel = FastCheck;
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
is_selected = settings.dynarec.SmcCheckLevel == FullCheck;
|
||||
if (ImGui::Selectable("Full", &is_selected))
|
||||
settings.dynarec.SmcCheckLevel = FullCheck;
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ShowHelpMarker("How to detect self-modifying code. Full check recommended");
|
||||
}
|
||||
if (ImGui::CollapsingHeader("Other", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
|
|
|
@ -601,6 +601,11 @@ struct RegisterStruct
|
|||
u32 flags; //Access flags !
|
||||
};
|
||||
|
||||
enum SmcCheckEnum {
|
||||
FullCheck = 0,
|
||||
FastCheck = 1,
|
||||
NoCheck = 2
|
||||
};
|
||||
|
||||
struct settings_t
|
||||
{
|
||||
|
@ -637,6 +642,7 @@ struct settings_t
|
|||
bool unstable_opt;
|
||||
bool safemode;
|
||||
bool disable_nvmem;
|
||||
SmcCheckEnum SmcCheckLevel;
|
||||
} dynarec;
|
||||
|
||||
struct
|
||||
|
|
Loading…
Reference in New Issue