From f240472f5e216babccf7a958b62fb79869e85c8c Mon Sep 17 00:00:00 2001 From: rogerman Date: Sat, 8 Jul 2023 19:52:10 -0700 Subject: [PATCH] Cheat System: Standardize memory writes for all cheat types. Most notably, Internal cheats now reset the JIT in the same way as Action Replay cheats do. --- desmume/src/MMU.cpp | 93 ++++++++++++++++++ desmume/src/MMU.h | 6 +- desmume/src/NDSSystem.cpp | 11 ++- desmume/src/cheatSystem.cpp | 187 ++++++++++++++++++++---------------- desmume/src/cheatSystem.h | 12 ++- 5 files changed, 217 insertions(+), 92 deletions(-) diff --git a/desmume/src/MMU.cpp b/desmume/src/MMU.cpp index be6a874c3..bb0ba58d8 100644 --- a/desmume/src/MMU.cpp +++ b/desmume/src/MMU.cpp @@ -3216,6 +3216,99 @@ bool validateIORegsRead(u32 addr, u8 size) #define VALIDATE_IO_REGS_READ(PROC, SIZE) ; #endif +template +bool MMU_WriteFromExternal(const int targetProc, const u32 targetAddress, T newValue) +{ + u32 oldValue32; + + switch (LENGTH) + { + case 1: + if (sizeof(T) > LENGTH) + newValue &= 0x000000FF; + break; + + case 2: + if (sizeof(T) > LENGTH) + newValue &= 0x0000FFFF; + break; + + case 3: + oldValue32 = _MMU_read32(targetProc, MMU_AT_DEBUG, targetAddress); + if (sizeof(T) > LENGTH) + newValue = (oldValue32 & 0xFF000000) | (newValue & 0x00FFFFFF); + break; + + case 4: + oldValue32 = _MMU_read32(targetProc, MMU_AT_DEBUG, targetAddress); + if (sizeof(T) > LENGTH) + newValue &= 0xFFFFFFFF; + break; + + default: + break; + } + + bool needsJitReset = ( (targetAddress >= 0x02000000) && (targetAddress < 0x02400000) ); + if (needsJitReset) + { + bool willValueChange = false; + + switch (LENGTH) + { + case 1: + willValueChange = (_MMU_read08(targetProc, MMU_AT_DEBUG, targetAddress) != (u8)newValue); + break; + + case 2: + willValueChange = (_MMU_read16(targetProc, MMU_AT_DEBUG, targetAddress) != (u16)newValue); + break; + + case 3: + case 4: + willValueChange = (oldValue32 != (u32)newValue); + break; + + default: + break; + } + + if (!willValueChange) + { + needsJitReset = false; + return needsJitReset; + } + } + + switch (LENGTH) + { + case 1: + _MMU_write08(targetProc, MMU_AT_DEBUG, targetAddress, (u8)newValue); + break; + + case 2: + _MMU_write16(targetProc, MMU_AT_DEBUG, targetAddress, (u16)newValue); + break; + + case 3: + case 4: + _MMU_write32(targetProc, MMU_AT_DEBUG, targetAddress, (u32)newValue); + break; + + default: + break; + } + + return needsJitReset; +} + +template bool MMU_WriteFromExternal< u8, 1>(const int targetProc, const u32 targetAddress, u8 newValue); +template bool MMU_WriteFromExternal(const int targetProc, const u32 targetAddress, u16 newValue); +template bool MMU_WriteFromExternal(const int targetProc, const u32 targetAddress, u32 newValue); +template bool MMU_WriteFromExternal(const int targetProc, const u32 targetAddress, u32 newValue); +template bool MMU_WriteFromExternal(const int targetProc, const u32 targetAddress, u32 newValue); +template bool MMU_WriteFromExternal(const int targetProc, const u32 targetAddress, u32 newValue); + //================================================================================================== ARM9 * //========================================================================================================= //========================================================================================================= diff --git a/desmume/src/MMU.h b/desmume/src/MMU.h index 35c981556..2fc5d0af9 100644 --- a/desmume/src/MMU.h +++ b/desmume/src/MMU.h @@ -1,7 +1,7 @@ /* Copyright (C) 2006 yopyop Copyright (C) 2007 shash - Copyright (C) 2007-2017 DeSmuME team + Copyright (C) 2007-2023 DeSmuME team This file is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -626,6 +626,10 @@ FORCEINLINE void* MMU_gpu_map(const u32 vram_addr) return MMU.ARM9_LCD + (vram_page << 14) + ofs; } +// Call MMU_WriteFromExternal() when modifying memory outside of the normal execution process, such +// as when using cheats or when the client wants to write to memory directly. This function returns +// true if memory is modified in such a way that requires the JIT execution be reset. +template bool MMU_WriteFromExternal(const int targetProc, const u32 targetAddress, T newValue); template u8 _MMU_read08(u32 addr); template u16 _MMU_read16(u32 addr); diff --git a/desmume/src/NDSSystem.cpp b/desmume/src/NDSSystem.cpp index 58cbb57d9..55513259b 100644 --- a/desmume/src/NDSSystem.cpp +++ b/desmume/src/NDSSystem.cpp @@ -1454,8 +1454,11 @@ static void execHardware_hstart_vblankStart() //for ARM7, cheats process when a vblank IRQ fires. necessary for AR compatibility and to stop cheats from breaking game boot-ups. //note that how we process raw cheats is up to us. so we'll do it the same way we used to, elsewhere - if (i==1 && cheats) + if ( (i == 1) && (cheats != NULL) ) + { cheats->process(CHEAT_TYPE_AR); + CHEATS::ResetJitIfNeeded(); + } } } @@ -2197,7 +2200,11 @@ void NDS_exec(s32 nb) } currFrameCounter++; DEBUG_Notify.NextFrame(); - if(cheats) cheats->process(CHEAT_TYPE_INTERNAL); + if (cheats != NULL) + { + cheats->process(CHEAT_TYPE_INTERNAL); + CHEATS::ResetJitIfNeeded(); + } GDBSTUB_MUTEX_UNLOCK(); } diff --git a/desmume/src/cheatSystem.cpp b/desmume/src/cheatSystem.cpp index 39213aa3f..811536a5e 100755 --- a/desmume/src/cheatSystem.cpp +++ b/desmume/src/cheatSystem.cpp @@ -33,7 +33,7 @@ static const char hexValid[23] = {"0123456789ABCDEFabcdef"}; CHEATS *cheats = NULL; CHEATSEARCH *cheatSearch = NULL; -static bool cheatsResetJit; +static bool cheatsResetJit = false; void CHEATS::clear() { @@ -143,34 +143,34 @@ bool CHEATS::move(size_t srcPos, size_t dstPos) #define CHEATLOG(...) //#define CHEATLOG(...) printf(__VA_ARGS__) -static void CheatWrite(int size, int proc, u32 addr, u32 val) +template +bool CHEATS::DirectWrite(const int targetProc, const u32 targetAddress, u32 newValue) { - bool dirty = true; - - bool isDangerous = false; - if(addr >= 0x02000000 && addr < 0x02400000) - isDangerous = true; - - if(isDangerous) - { - //test dirtiness - if(size == 8) dirty = _MMU_read08(proc, MMU_AT_DEBUG, addr) != val; - if(size == 16) dirty = _MMU_read16(proc, MMU_AT_DEBUG, addr) != val; - if(size == 32) dirty = _MMU_read32(proc, MMU_AT_DEBUG, addr) != val; - } - - if(!dirty) return; - - if(size == 8) _MMU_write08(proc, MMU_AT_DEBUG, addr, val); - if(size == 16) _MMU_write16(proc, MMU_AT_DEBUG, addr, val); - if(size == 32) _MMU_write32(proc, MMU_AT_DEBUG, addr, val); - - if(isDangerous) - cheatsResetJit = true; + return MMU_WriteFromExternal(targetProc, targetAddress, newValue); } +template bool CHEATS::DirectWrite<1>(const int targetProc, const u32 targetAddress, u32 newValue); +template bool CHEATS::DirectWrite<2>(const int targetProc, const u32 targetAddress, u32 newValue); +template bool CHEATS::DirectWrite<3>(const int targetProc, const u32 targetAddress, u32 newValue); +template bool CHEATS::DirectWrite<4>(const int targetProc, const u32 targetAddress, u32 newValue); -void CHEATS::ARparser(const CHEATS_LIST &theList) +bool CHEATS::DirectWrite(const size_t newValueLength, const int targetProc, const u32 targetAddress, u32 newValue) +{ + switch (newValueLength) + { + case 1: return CHEATS::DirectWrite<1>(targetProc, targetAddress, newValue); + case 2: return CHEATS::DirectWrite<2>(targetProc, targetAddress, newValue); + case 3: return CHEATS::DirectWrite<3>(targetProc, targetAddress, newValue); + case 4: return CHEATS::DirectWrite<4>(targetProc, targetAddress, newValue); + + default: + break; + } + + return false; +} + +bool CHEATS::ARparser(const CHEATS_LIST &theList) { //primary organizational source (seems to be referenced by cheaters the most) - http://doc.kodewerx.org/hacking_nds.html //secondary clarification and details (for programmers) - http://problemkaputt.de/gbatek.htm#dscartcheatactionreplayds @@ -184,6 +184,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) bool v154 = true; //on advice of power users, v154 is so old, we can assume all cheats use it bool vEmulator = true; + bool needsJitReset = false; struct { //LSB is @@ -218,6 +219,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) for (u32 i = 0; i < theList.num; i++) { + bool shouldResetJit = false; const u32 hi = theList.code[i][0]; const u32 lo = theList.code[i][1]; @@ -273,7 +275,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) x = hi & 0x0FFFFFFF; y = lo; addr = x + st.offset; - CheatWrite(32,st.proc,addr, y); + shouldResetJit = CHEATS::DirectWrite<4>(st.proc, addr, y); break; case 0x01: @@ -283,7 +285,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) x = hi & 0x0FFFFFFF; y = lo & 0xFFFF; addr = x + st.offset; - CheatWrite(16,st.proc,addr, y); + shouldResetJit = CHEATS::DirectWrite<2>(st.proc, addr, y); break; case 0x02: @@ -293,7 +295,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) x = hi & 0x0FFFFFFF; y = lo & 0xFF; addr = x + st.offset; - CheatWrite(8,st.proc,addr, y); + shouldResetJit = CHEATS::DirectWrite<1>(st.proc, addr, y); break; case 0x03: @@ -443,7 +445,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) // C6000000 XXXXXXXX [XXXXXXXX]=offset if(!v154) break; x = lo; - CheatWrite(32,st.proc,x, st.offset); + shouldResetJit = CHEATS::DirectWrite<4>(st.proc, x, st.offset); break; case 0xD0: @@ -524,7 +526,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) // word[XXXXXXXX+offset]=datareg, offset=offset+4 x = lo; addr = x + st.offset; - CheatWrite(32,st.proc,addr, st.data); + shouldResetJit = CHEATS::DirectWrite<4>(st.proc, addr, st.data); st.offset += 4; break; @@ -535,7 +537,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) // half[XXXXXXXX+offset]=datareg, offset=offset+2 x = lo; addr = x + st.offset; - CheatWrite(16,st.proc,addr, st.data); + shouldResetJit = CHEATS::DirectWrite<2>(st.proc, addr, st.data); st.offset += 2; break; @@ -546,7 +548,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) // byte[XXXXXXXX+offset]=datareg, offset=offset+1 x = lo; addr = x + st.offset; - CheatWrite(8,st.proc,addr, st.data); + shouldResetJit = CHEATS::DirectWrite<1>(st.proc, addr, st.data); st.offset += 1; break; @@ -623,7 +625,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) u32 tmp = theList.code[i][t]; if (t == 1) i++; t ^= 1; - CheatWrite(32,st.proc,addr,tmp); + shouldResetJit = CHEATS::DirectWrite<4>(st.proc, addr, tmp); addr += 4; y -= 4; } @@ -631,7 +633,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) { if (i == theList.num) break; //if we erroneously went off the end, bail u32 tmp = theList.code[i][t]>>b; - CheatWrite(8,st.proc,addr,tmp); + shouldResetJit = CHEATS::DirectWrite<1>(st.proc, addr, tmp); addr += 1; y -= 1; b += 4; @@ -657,7 +659,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) { if (i == theList.num) break; //if we erroneously went off the end, bail u32 tmp = _MMU_read32(st.proc,MMU_AT_DEBUG,addr); - CheatWrite(32, st.proc,operand,tmp); + shouldResetJit = CHEATS::DirectWrite<4>(st.proc, operand, tmp); addr += 4; operand += 4; y -= 4; @@ -666,7 +668,7 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) { if (i == theList.num) break; //if we erroneously went off the end, bail u8 tmp = _MMU_read08(st.proc,MMU_AT_DEBUG,addr); - CheatWrite(8,st.proc,operand,tmp); + shouldResetJit = CHEATS::DirectWrite<1>(st.proc, operand, tmp); addr += 1; operand += 1; y -= 1; @@ -677,8 +679,14 @@ void CHEATS::ARparser(const CHEATS_LIST &theList) printf("AR: ERROR unknown command %08X %08X\n", hi, lo); break; } + + if (shouldResetJit) + { + needsJitReset = true; + } } - + + return needsJitReset; } size_t CHEATS::add_AR_Direct(const CHEATS_LIST &srcCheat) @@ -1140,73 +1148,82 @@ bool CHEATS::load() return didLoadAllItems; } -void CHEATS::process(int targetType) const +bool CHEATS::process(int targetType) const { - if (CommonSettings.cheatsDisable) return; - - if (this->_list.size() == 0) return; - - cheatsResetJit = false; - + bool needsJitReset = false; + + if (CommonSettings.cheatsDisable || (this->_list.size() == 0)) + return needsJitReset; + size_t num = this->_list.size(); for (size_t i = 0; i < num; i++) { - if (this->_list[i].enabled == 0) continue; + bool shouldResetJit = false; + + if (this->_list[i].enabled == 0) + continue; int type = this->_list[i].type; - if(type != targetType) - continue; + if (type != targetType) + continue; - switch(type) + switch (type) { - case 0: // internal cheat system - { + case CHEAT_TYPE_INTERNAL: //INFO("list at 0x0|%07X value %i (size %i)\n",list[i].code[0], list[i].lo[0], list[i].size); - u32 addr = this->_list[i].code[0][0]; - u32 val = this->_list[i].code[0][1]; - switch (this->_list[i].size) - { - case 0: - _MMU_write08(addr,val); - break; - case 1: - _MMU_write16(addr,val); - break; - case 2: - { - u32 tmp = _MMU_read32(addr); - tmp &= 0xFF000000; - tmp |= (val & 0x00FFFFFF); - _MMU_write32(addr,tmp); - break; - } - case 3: - _MMU_write32(addr,val); - break; - } + shouldResetJit = CHEATS::DirectWrite(this->_list[i].size + 1, ARMCPU_ARM9, this->_list[i].code[0][0], this->_list[i].code[0][1]); break; - } //end case 0 internal cheat system - - case 1: // Action Replay - CHEATS::ARparser(this->_list[i]); + + case CHEAT_TYPE_AR: + shouldResetJit = CHEATS::ARparser(this->_list[i]); break; - case 2: // Codebreaker + + case CHEAT_TYPE_CODEBREAKER: break; - default: continue; + + default: + continue; + } + + if (shouldResetJit) + { + needsJitReset = true; } } -#ifdef HAVE_JIT - if(cheatsResetJit) + if (needsJitReset) { - if(CommonSettings.use_jit) + CHEATS::JitNeedsReset(); + } + + return needsJitReset; +} + +void CHEATS::JitNeedsReset() +{ + cheatsResetJit = true; +} + +bool CHEATS::ResetJitIfNeeded() +{ + bool didJitReset = false; + +#ifdef HAVE_JIT + if (cheatsResetJit) + { + if (CommonSettings.use_jit) { printf("Cheat code operation potentially not compatible with JIT operations. Resetting JIT...\n"); arm_jit_reset(true, true); } + + cheatsResetJit = false; + didJitReset = true; } #endif + + return didJitReset; } void CHEATS::StringFromXXCode(const CHEATS_LIST &srcCheatItem, char *outCStringBuffer) @@ -1441,7 +1458,7 @@ u32 CHEATSEARCH::search(u8 comp) this->_amount = 0; - switch (_size) + switch (this->_size) { case 0: // 1 byte for (u32 i = 0; i < (4 * 1024 * 1024); i++) @@ -1561,10 +1578,10 @@ u32 CHEATSEARCH::getAmount() bool CHEATSEARCH::getList(u32 *address, u32 *curVal) { bool didGetValue = false; - u8 step = (_size+1); + u8 step = this->_size + 1; u8 stepMem = 1; - switch (_size) + switch (this->_size) { case 1: stepMem = 0x3; break; case 2: stepMem = 0x7; break; @@ -1580,7 +1597,7 @@ bool CHEATSEARCH::getList(u32 *address, u32 *curVal) *address = i; this->_lastRecord = i+step; - switch (_size) + switch (this->_size) { case 0: *curVal = (u32)T1ReadByte(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i); break; case 1: *curVal = (u32)T1ReadWord(MMU.MMU_MEM[ARMCPU_ARM9][0x20], i); break; diff --git a/desmume/src/cheatSystem.h b/desmume/src/cheatSystem.h index dfaa90cdd..4c74451c0 100755 --- a/desmume/src/cheatSystem.h +++ b/desmume/src/cheatSystem.h @@ -110,9 +110,15 @@ public: void setDescription(const char *description, const size_t pos); bool save(); bool load(); - void process(int targetType) const; + bool process(int targetType) const; - static void ARparser(const CHEATS_LIST &cheat); + static void JitNeedsReset(); + static bool ResetJitIfNeeded(); + + template static bool DirectWrite(const int targetProc, const u32 targetAddress, u32 newValue); + static bool DirectWrite(const size_t newValueLength, const int targetProc, const u32 targetAddress, u32 newValue); + + static bool ARparser(const CHEATS_LIST &cheat); static void StringFromXXCode(const CHEATS_LIST &srcCheatItem, char *outCStringBuffer); static bool XXCodeFromString(const std::string codeString, CHEATS_LIST &outCheatItem); @@ -213,5 +219,3 @@ public: extern CHEATS *cheats; extern CHEATSEARCH *cheatSearch; - -