diff --git a/core/hw/sh4/dyna/blockmanager.cpp b/core/hw/sh4/dyna/blockmanager.cpp index dd0a3b818..e64cd0d60 100644 --- a/core/hw/sh4/dyna/blockmanager.cpp +++ b/core/hw/sh4/dyna/blockmanager.cpp @@ -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]); diff --git a/core/hw/sh4/dyna/driver.cpp b/core/hw/sh4/dyna/driver.cpp index d398a6f70..56e66724e 100644 --- a/core/hw/sh4/dyna/driver.cpp +++ b/core/hw/sh4/dyna/driver.cpp @@ -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); diff --git a/core/hw/sh4/dyna/ngen.h b/core/hw/sh4/dyna/ngen.h index 0e7d9cd3b..e7d2aff35 100644 --- a/core/hw/sh4/dyna/ngen.h +++ b/core/hw/sh4/dyna/ngen.h @@ -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(); diff --git a/core/hw/sh4/modules/ccn.cpp b/core/hw/sh4/modules/ccn.cpp index 49b7c574f..99d694c8d 100644 --- a/core/hw/sh4/modules/ccn.cpp +++ b/core/hw/sh4/modules/ccn.cpp @@ -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; diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 00309f0dc..e770b3c08 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -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); diff --git a/core/rec-ARM/rec_arm.cpp b/core/rec-ARM/rec_arm.cpp index 9e61af8ac..933ff19d2 100644 --- a/core/rec-ARM/rec_arm.cpp +++ b/core/rec-ARM/rec_arm.cpp @@ -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; diff --git a/core/rec-ARM64/rec_arm64.cpp b/core/rec-ARM64/rec_arm64.cpp index cc35f57b6..b30720796 100644 --- a/core/rec-ARM64/rec_arm64.cpp +++ b/core/rec-ARM64/rec_arm64.cpp @@ -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() - 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(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(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(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; diff --git a/core/rec-cpp/rec_cpp.cpp b/core/rec-cpp/rec_cpp.cpp index a3d155679..caa403bdd 100644 --- a/core/rec-cpp/rec_cpp.cpp +++ b/core/rec-cpp/rec_cpp.cpp @@ -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; } diff --git a/core/rec-x64/rec_x64.cpp b/core/rec-x64/rec_x64.cpp index 19e9ba835..b80df57ee 100644 --- a/core/rec-x64/rec_x64.cpp +++ b/core/rec-x64/rec_x64.cpp @@ -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(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(ptr)); mov(edx, *(u32*)ptr); cmp(dword[rax], edx); - sz -= 4; - sa += 4; + jne(reinterpret_cast(&ngen_blockcheckfail)); } - else { - mov(edx, *(u16*)ptr); - cmp(word[rax],dx); - sz -= 2; - sa += 2; - } - jne(reinterpret_cast(&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(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(&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; } diff --git a/core/rec-x86/rec_x86_driver.cpp b/core/rec-x86/rec_x86_driver.cpp index 1c14a6924..1d19a46fa 100644 --- a/core/rec-x86/rec_x86_driver.cpp +++ b/core/rec-x86/rec_x86_driver.cpp @@ -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); diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 4b53795fb..03e976d85 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -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)) { diff --git a/core/types.h b/core/types.h index b3f809dc3..9d2e54d4d 100644 --- a/core/types.h +++ b/core/types.h @@ -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