diff --git a/desmume/src/MMU.h b/desmume/src/MMU.h index 6091a2723..35c981556 100644 --- a/desmume/src/MMU.h +++ b/desmume/src/MMU.h @@ -30,6 +30,10 @@ #include "lua-engine.h" #endif +#ifdef TARGET_INTERFACE +#include "frontend/interface/interface.h" +#endif + #ifdef HAVE_JIT #include "arm_jit.h" #endif @@ -699,6 +703,9 @@ FORCEINLINE u8 _MMU_read08(const int PROCNUM, const MMU_ACCESS_TYPE AT, const u3 #ifdef HAVE_LUA CallRegisteredLuaMemHook(addr, 1, /*FIXME*/ 0, LUAMEMHOOK_READ); #endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(addr, 1, HOOK_READ); +#endif // break points, wheee for (size_t i = 0; i < memReadBreakPoints.size(); ++i) @@ -744,6 +751,9 @@ FORCEINLINE u16 _MMU_read16(const int PROCNUM, const MMU_ACCESS_TYPE AT, const u #ifdef HAVE_LUA CallRegisteredLuaMemHook(addr, 2, /*FIXME*/ 0, LUAMEMHOOK_READ); #endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(addr, 2, HOOK_READ); +#endif // break points, wheee for (size_t i = 0; i < memReadBreakPoints.size(); ++i) @@ -801,6 +811,9 @@ FORCEINLINE u32 _MMU_read32(const int PROCNUM, const MMU_ACCESS_TYPE AT, const u #ifdef HAVE_LUA CallRegisteredLuaMemHook(addr, 4, /*FIXME*/ 0, LUAMEMHOOK_READ); +#endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(addr, 4, HOOK_READ); #endif // break points, wheee for (size_t i = 0; i < memReadBreakPoints.size(); ++i) @@ -883,6 +896,9 @@ FORCEINLINE void _MMU_write08(const int PROCNUM, const MMU_ACCESS_TYPE AT, const T1WriteByte(MMU.ARM9_DTCM, addr & 0x3FFF, val); #ifdef HAVE_LUA CallRegisteredLuaMemHook(addr, 1, val, LUAMEMHOOK_WRITE); +#endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(addr, 1, HOOK_READ); #endif return; } @@ -894,6 +910,9 @@ FORCEINLINE void _MMU_write08(const int PROCNUM, const MMU_ACCESS_TYPE AT, const T1WriteByte( MMU.MAIN_MEM, addr & _MMU_MAIN_MEM_MASK, val); #ifdef HAVE_LUA CallRegisteredLuaMemHook(addr, 1, val, LUAMEMHOOK_WRITE); +#endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(addr, 1, HOOK_WRITE); #endif return; } @@ -903,6 +922,9 @@ FORCEINLINE void _MMU_write08(const int PROCNUM, const MMU_ACCESS_TYPE AT, const #ifdef HAVE_LUA CallRegisteredLuaMemHook(addr, 1, val, LUAMEMHOOK_WRITE); #endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(addr, 1, HOOK_WRITE); +#endif } FORCEINLINE void _MMU_write16(const int PROCNUM, const MMU_ACCESS_TYPE AT, const u32 addr, u16 val) @@ -932,6 +954,9 @@ FORCEINLINE void _MMU_write16(const int PROCNUM, const MMU_ACCESS_TYPE AT, const T1WriteWord(MMU.ARM9_DTCM, addr & 0x3FFE, val); #ifdef HAVE_LUA CallRegisteredLuaMemHook(addr, 2, val, LUAMEMHOOK_WRITE); +#endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(addr, 2, HOOK_WRITE); #endif return; } @@ -952,6 +977,9 @@ FORCEINLINE void _MMU_write16(const int PROCNUM, const MMU_ACCESS_TYPE AT, const #ifdef HAVE_LUA CallRegisteredLuaMemHook(addr, 2, val, LUAMEMHOOK_WRITE); #endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(addr, 2, HOOK_WRITE); +#endif } FORCEINLINE void _MMU_write32(const int PROCNUM, const MMU_ACCESS_TYPE AT, const u32 addr, u32 val) @@ -981,6 +1009,9 @@ FORCEINLINE void _MMU_write32(const int PROCNUM, const MMU_ACCESS_TYPE AT, const T1WriteLong(MMU.ARM9_DTCM, addr & 0x3FFC, val); #ifdef HAVE_LUA CallRegisteredLuaMemHook(addr, 4, val, LUAMEMHOOK_WRITE); +#endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(addr, 4, HOOK_WRITE); #endif return; } @@ -993,6 +1024,9 @@ FORCEINLINE void _MMU_write32(const int PROCNUM, const MMU_ACCESS_TYPE AT, const T1WriteLong( MMU.MAIN_MEM, addr & _MMU_MAIN_MEM_MASK32, val); #ifdef HAVE_LUA CallRegisteredLuaMemHook(addr, 4, val, LUAMEMHOOK_WRITE); +#endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(addr, 4, HOOK_WRITE); #endif return; } @@ -1002,6 +1036,9 @@ FORCEINLINE void _MMU_write32(const int PROCNUM, const MMU_ACCESS_TYPE AT, const #ifdef HAVE_LUA CallRegisteredLuaMemHook(addr, 4, val, LUAMEMHOOK_WRITE); #endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(addr, 4, HOOK_WRITE); +#endif } diff --git a/desmume/src/armcpu.cpp b/desmume/src/armcpu.cpp index 9595ede53..b3182045a 100644 --- a/desmume/src/armcpu.cpp +++ b/desmume/src/armcpu.cpp @@ -32,6 +32,9 @@ #ifdef HAVE_LUA #include "lua-engine.h" #endif +#ifdef TARGET_INTERFACE +#include "frontend/interface/interface.h" +#endif #ifdef HAVE_JIT #include "arm_jit.h" #endif @@ -674,6 +677,9 @@ u32 armcpu_exec() { #ifdef HAVE_LUA CallRegisteredLuaMemHook(ARMPROC.instruct_adr, 4, ARMPROC.instruction, LUAMEMHOOK_EXEC); // should report even if condition=false? +#endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(ARMPROC.instruct_adr, 4, HOOK_EXEC); #endif #ifdef DEVELOPER DEBUG_statistics.instructionHits[PROCNUM].arm[INSTRUCTION_INDEX(ARMPROC.instruction)]++; @@ -695,6 +701,9 @@ u32 armcpu_exec() #ifdef HAVE_LUA CallRegisteredLuaMemHook(ARMPROC.instruct_adr, 2, ARMPROC.instruction, LUAMEMHOOK_EXEC); +#endif +#ifdef TARGET_INTERFACE + call_registered_interface_mem_hook(ARMPROC.instruct_adr, 2, HOOK_EXEC); #endif #ifdef DEVELOPER DEBUG_statistics.instructionHits[PROCNUM].thumb[ARMPROC.instruction>>6]++; diff --git a/desmume/src/frontend/interface/configure.ac b/desmume/src/frontend/interface/configure.ac index db5b57b9c..69e45d729 100644 --- a/desmume/src/frontend/interface/configure.ac +++ b/desmume/src/frontend/interface/configure.ac @@ -32,6 +32,8 @@ if ( test -d ../../../../.git; ) && test "x${GIT}" != "x" -a -x "${GIT}" ; then fi AC_DEFINE_UNQUOTED([GIT_COMMIT], ["$COMMIT"], [git commit hash]) +AC_DEFINE([TARGET_INTERFACE]) + dnl - Check for zlib AC_CHECK_LIB(z, gzopen, [], [AC_MSG_ERROR([zlib was not found, we can't go further. Please install it or specify the location where it's installed.])]) diff --git a/desmume/src/frontend/interface/interface.cpp b/desmume/src/frontend/interface/interface.cpp index 6832b92de..47b18b226 100644 --- a/desmume/src/frontend/interface/interface.cpp +++ b/desmume/src/frontend/interface/interface.cpp @@ -25,11 +25,13 @@ // TODO: OSD Support isn't really done yet! Test! #include "../modules/osd/agg/agg_osd.h" #include "../../SPU.h" +#include "../../MMU.h" #include "../../rasterize.h" #include "../../saves.h" #include "../../movie.h" #include "../../mc.h" #include "../../firmware.h" +#include "../../armcpu.h" #include "../posix/shared/sndsdl.h" #include "../posix/shared/ctrlssdl.h" #include @@ -38,6 +40,8 @@ #define SCREENS_PIXEL_SIZE 98304 volatile bool execute = false; +TieredRegion hooked_regions [HOOK_COUNT]; +std::map hooks[HOOK_COUNT]; SoundInterface_struct *SNDCoreList[] = { @@ -311,29 +315,198 @@ EXPORTED void desmume_volume_set(int volume) SNDSDLSetAudioVolume(volume); } -// -//EXPORTED unsigned char desmume_memory_read_byte(int address); -//EXPORTED signed char desmume_memory_read_byte_signed(int address); -//EXPORTED unsigned short desmume_memory_read_short(int address); -//EXPORTED signed short desmume_memory_read_short_signed(int address); -//EXPORTED unsigned long desmume_memory_read_long(int address); -//EXPORTED signed long desmume_memory_read_long_signed(int address); -//EXPORTED unsigned char *desmume_memory_read_byterange(int address, int length); -// -//EXPORTED void desmume_memory_write_byte(int address, unsigned char value); -//EXPORTED void desmume_memory_write_byte_signed(int address, signed char value); -//EXPORTED void desmume_memory_write_short(int address, unsigned short value); -//EXPORTED void desmume_memory_write_short_signed(int address, signed short value); -//EXPORTED void desmume_memory_write_long(int address, unsigned long value); -//EXPORTED void desmume_memory_write_long_signed(int address, signed long value); -//EXPORTED void desmume_memory_write_byterange(int address, const unsigned char *bytes); -// -//EXPORTED long desmume_memory_read_register(char* register_name); -//EXPORTED void desmume_memory_write_register(char* register_name, long value); -// -//EXPORTED void desmume_memory_register_write(int address, int size, memory_cb_fnc cb); -//EXPORTED void desmume_memory_register_read(int address, int size, memory_cb_fnc cb); -//EXPORTED void desmume_memory_register_exec(int address, int size, memory_cb_fnc cb); +EXPORTED unsigned char desmume_memory_read_byte(int address) +{ + return (unsigned char)(_MMU_read08(address) & 0xFF); +} + +EXPORTED signed char desmume_memory_read_byte_signed(int address) +{ + return (signed char)(_MMU_read08(address) & 0xFF); +} + +EXPORTED unsigned short desmume_memory_read_short(int address) +{ + return (unsigned short)(_MMU_read16(address) & 0xFFFF); +} + +EXPORTED signed short desmume_memory_read_short_signed(int address) +{ + return (signed short)(_MMU_read16(address) & 0xFFFF); +} + +EXPORTED unsigned long desmume_memory_read_long(int address) +{ + return (unsigned long)(_MMU_read32(address)); +} + +EXPORTED signed long desmume_memory_read_long_signed(int address) +{ + return (signed long)(_MMU_read32(address)); +} + +EXPORTED void desmume_memory_write_byte(int address, unsigned char value) +{ + _MMU_write08(address, value); +} + +EXPORTED void desmume_memory_write_short(int address, unsigned short value) +{ + _MMU_write16(address, value); +} + +EXPORTED void desmume_memory_write_long(int address, unsigned long value) +{ + _MMU_write32(address, value); +} + +struct registerPointerMap +{ + const char* registerName; + unsigned int* pointer; + int dataSize; +}; + +#define RPM_ENTRY(name,var) {name, (unsigned int*)&var, sizeof(var)}, + +registerPointerMap arm9PointerMap [] = { + RPM_ENTRY("r0", NDS_ARM9.R[0]) + RPM_ENTRY("r1", NDS_ARM9.R[1]) + RPM_ENTRY("r2", NDS_ARM9.R[2]) + RPM_ENTRY("r3", NDS_ARM9.R[3]) + RPM_ENTRY("r4", NDS_ARM9.R[4]) + RPM_ENTRY("r5", NDS_ARM9.R[5]) + RPM_ENTRY("r6", NDS_ARM9.R[6]) + RPM_ENTRY("r7", NDS_ARM9.R[7]) + RPM_ENTRY("r8", NDS_ARM9.R[8]) + RPM_ENTRY("r9", NDS_ARM9.R[9]) + RPM_ENTRY("r10", NDS_ARM9.R[10]) + RPM_ENTRY("r11", NDS_ARM9.R[11]) + RPM_ENTRY("r12", NDS_ARM9.R[12]) + RPM_ENTRY("r13", NDS_ARM9.R[13]) + RPM_ENTRY("r14", NDS_ARM9.R[14]) + RPM_ENTRY("r15", NDS_ARM9.R[15]) + RPM_ENTRY("cpsr", NDS_ARM9.CPSR.val) + RPM_ENTRY("spsr", NDS_ARM9.SPSR.val) + {} +}; +registerPointerMap arm7PointerMap [] = { + RPM_ENTRY("r0", NDS_ARM7.R[0]) + RPM_ENTRY("r1", NDS_ARM7.R[1]) + RPM_ENTRY("r2", NDS_ARM7.R[2]) + RPM_ENTRY("r3", NDS_ARM7.R[3]) + RPM_ENTRY("r4", NDS_ARM7.R[4]) + RPM_ENTRY("r5", NDS_ARM7.R[5]) + RPM_ENTRY("r6", NDS_ARM7.R[6]) + RPM_ENTRY("r7", NDS_ARM7.R[7]) + RPM_ENTRY("r8", NDS_ARM7.R[8]) + RPM_ENTRY("r9", NDS_ARM7.R[9]) + RPM_ENTRY("r10", NDS_ARM7.R[10]) + RPM_ENTRY("r11", NDS_ARM7.R[11]) + RPM_ENTRY("r12", NDS_ARM7.R[12]) + RPM_ENTRY("r13", NDS_ARM7.R[13]) + RPM_ENTRY("r14", NDS_ARM7.R[14]) + RPM_ENTRY("r15", NDS_ARM7.R[15]) + RPM_ENTRY("cpsr", NDS_ARM7.CPSR.val) + RPM_ENTRY("spsr", NDS_ARM7.SPSR.val) + {} +}; + +struct cpuToRegisterMap +{ + const char* cpuName; + registerPointerMap* rpmap; +} +cpuToRegisterMaps [] = +{ + {"arm9.", arm9PointerMap}, + {"main.", arm9PointerMap}, + {"arm7.", arm7PointerMap}, + {"sub.", arm7PointerMap}, + {"", arm9PointerMap}, +}; + +EXPORTED int desmume_memory_read_register(char* register_name) +{ + for(int cpu = 0; cpu < sizeof(cpuToRegisterMaps)/sizeof(*cpuToRegisterMaps); cpu++) + { + cpuToRegisterMap ctrm = cpuToRegisterMaps[cpu]; + int cpuNameLen = strlen(ctrm.cpuName); + if(!strncasecmp(register_name, ctrm.cpuName, cpuNameLen)) + { + register_name += cpuNameLen; + for(int reg = 0; ctrm.rpmap[reg].dataSize; reg++) + { + registerPointerMap rpm = ctrm.rpmap[reg]; + if(!strcasecmp(register_name, rpm.registerName)) + { + switch(rpm.dataSize) + { default: + case 1: return *(unsigned char*)rpm.pointer; + case 2: return *(unsigned short*)rpm.pointer; + case 4: return *(unsigned long*)rpm.pointer; + } + } + } + return 0; + } + } + return 0; +} + +EXPORTED void desmume_memory_write_register(char* register_name, long value) +{ + for(int cpu = 0; cpu < sizeof(cpuToRegisterMaps)/sizeof(*cpuToRegisterMaps); cpu++) + { + cpuToRegisterMap ctrm = cpuToRegisterMaps[cpu]; + int cpuNameLen = strlen(ctrm.cpuName); + if(!strncasecmp(register_name, ctrm.cpuName, cpuNameLen)) + { + register_name += cpuNameLen; + for(int reg = 0; ctrm.rpmap[reg].dataSize; reg++) + { + registerPointerMap rpm = ctrm.rpmap[reg]; + if(!strcasecmp(register_name, rpm.registerName)) + { + switch(rpm.dataSize) + { default: + case 1: *(unsigned char*)rpm.pointer = (unsigned char)(value & 0xFF); break; + case 2: *(unsigned short*)rpm.pointer = (unsigned short)(value & 0xFFFF); break; + case 4: *(unsigned long*)rpm.pointer = value; break; + } + } + } + } + } +} + +INLINE void memory_register_hook(int addr, MemHookType hook_type, int size, memory_cb_fnc cb) +{ + for(unsigned int i = addr; i != addr+size; i++) + { + hooks[hook_type][i] = cb; + } + std::vector hooked_bytes; + for(std::map::iterator it = hooks[hook_type].begin(); it != hooks[hook_type].end(); ++it) { + hooked_bytes.push_back(it->first); + } + hooked_regions[hook_type].Calculate(hooked_bytes); +} + +EXPORTED void desmume_memory_register_write(int address, int size, memory_cb_fnc cb) +{ + memory_register_hook(address, HOOK_WRITE, size, cb); +} + +EXPORTED void desmume_memory_register_read(int address, int size, memory_cb_fnc cb) +{ + memory_register_hook(address, HOOK_READ, size, cb); +} + +EXPORTED void desmume_memory_register_exec(int address, int size, memory_cb_fnc cb) +{ + memory_register_hook(address, HOOK_EXEC, size, cb); +} EXPORTED void desmume_screenshot(char *screenshot_buffer) { diff --git a/desmume/src/frontend/interface/interface.h b/desmume/src/frontend/interface/interface.h index a7fa6a3eb..31a91284f 100755 --- a/desmume/src/frontend/interface/interface.h +++ b/desmume/src/frontend/interface/interface.h @@ -44,9 +44,18 @@ # define EXPORTED #endif +enum MemHookType +{ + HOOK_WRITE, + HOOK_READ, + HOOK_EXEC, + + HOOK_COUNT +}; + extern "C" { -// callback for memory hooks: if it returns false, remove it. -typedef BOOL (*memory_cb_fnc)(void); +// callback for memory hooks (get's two values: address and size of operation that triggered the hook) +typedef BOOL (*memory_cb_fnc)(unsigned int, int); struct SimpleDate { int year; @@ -68,6 +77,7 @@ EXPORTED void desmume_set_savetype(int type); EXPORTED void desmume_pause(void); EXPORTED void desmume_resume(void); EXPORTED void desmume_reset(void); +EXPORTED void desmume_stop(void); EXPORTED BOOL desmume_running(void); EXPORTED void desmume_skip_next_frame(void); EXPORTED void desmume_cycle(void); @@ -115,17 +125,14 @@ EXPORTED unsigned short desmume_memory_read_short(int address); EXPORTED signed short desmume_memory_read_short_signed(int address); EXPORTED unsigned long desmume_memory_read_long(int address); EXPORTED signed long desmume_memory_read_long_signed(int address); -EXPORTED unsigned char *desmume_memory_read_byterange(int address, int length); +//EXPORTED unsigned char *desmume_memory_read_byterange(int address, int length); EXPORTED void desmume_memory_write_byte(int address, unsigned char value); -EXPORTED void desmume_memory_write_byte_signed(int address, signed char value); EXPORTED void desmume_memory_write_short(int address, unsigned short value); -EXPORTED void desmume_memory_write_short_signed(int address, signed short value); EXPORTED void desmume_memory_write_long(int address, unsigned long value); -EXPORTED void desmume_memory_write_long_signed(int address, signed long value); -EXPORTED void desmume_memory_write_byterange(int address, const unsigned char *bytes); +//EXPORTED void desmume_memory_write_byterange(int address, int length, const unsigned char *bytes); -EXPORTED long desmume_memory_read_register(char* register_name); +EXPORTED int desmume_memory_read_register(char* register_name); EXPORTED void desmume_memory_write_register(char* register_name, long value); EXPORTED void desmume_memory_register_write(int address, int size, memory_cb_fnc cb); @@ -168,4 +175,118 @@ EXPORTED void desmume_movie_replay(); EXPORTED void desmume_movie_stop(); }; + +// TODO: Below is mostly just from lua-engine.h, might think about how to get rid of the code duplication. + +#include +#include + +// the purpose of this structure is to provide a way of +// QUICKLY determining whether a memory address range has a hook associated with it, +// with a bias toward fast rejection because the majority of addresses will not be hooked. +// (it must not use any part of Lua or perform any per-script operations, +// otherwise it would definitely be too slow.) +// calculating the regions when a hook is added/removed may be slow, +// but this is an intentional tradeoff to obtain a high speed of checking during later execution +struct TieredRegion +{ + template + struct Region + { + struct Island + { + unsigned int start; + unsigned int end; + FORCEINLINE bool Contains(unsigned int address, int size) const { return address < end && address+size > start; } + }; + std::vector islands; + + void Calculate(const std::vector& bytes) + { + islands.clear(); + + unsigned int lastEnd = ~0; + + std::vector::const_iterator iter = bytes.begin(); + std::vector::const_iterator end = bytes.end(); + for(; iter != end; ++iter) + { + unsigned int addr = *iter; + if(addr < lastEnd || addr > lastEnd + (long long)maxGap) + { + islands.push_back(Island()); + islands.back().start = addr; + } + islands.back().end = addr+1; + lastEnd = addr+1; + } + } + + bool Contains(unsigned int address, int size) const + { + typename std::vector::const_iterator iter = islands.begin(); + typename std::vector::const_iterator end = islands.end(); + for(; iter != end; ++iter) + if(iter->Contains(address, size)) + return true; + return false; + } + }; + + Region<0xFFFFFFFF> broad; + Region<0x1000> mid; + Region<0> narrow; + + void Calculate(std::vector& bytes) + { + std::sort(bytes.begin(), bytes.end()); + + broad.Calculate(bytes); + mid.Calculate(bytes); + narrow.Calculate(bytes); + } + + TieredRegion() + { + std::vector somevector; + Calculate(somevector); + } + + FORCEINLINE int NotEmpty() + { + return broad.islands.size(); + } + + // note: it is illegal to call this if NotEmpty() returns 0 + FORCEINLINE bool Contains(unsigned int address, int size) + { + return broad.islands[0].Contains(address,size) && + mid.Contains(address,size) && + narrow.Contains(address,size); + } +}; +extern TieredRegion hooked_regions [HOOK_COUNT]; + +extern std::map hooks[HOOK_COUNT]; + +FORCEINLINE void call_registered_interface_mem_hook(unsigned int address, int size, MemHookType hook_type) +{ + // See notes for CallRegisteredLuaMemHook! + if(hooked_regions[hook_type].NotEmpty()) + { + if(hooked_regions[hook_type].Contains(address, size)) + { + for(int i = address; i != address+size; i++) + { + memory_cb_fnc hook = hooks[hook_type][i]; + if(hook != 0) + { + (*hook)(address, size); + break; + } + } + } + } +} + #endif //DESMUME_INTERFACE_H