diff --git a/Source/Project64/UserInterface/Debugger/Debugger.cpp b/Source/Project64/UserInterface/Debugger/Debugger.cpp index bcfca5204..40d32d774 100644 --- a/Source/Project64/UserInterface/Debugger/Debugger.cpp +++ b/Source/Project64/UserInterface/Debugger/Debugger.cpp @@ -579,34 +579,55 @@ void CDebuggerUI::HandleCartToRamDMA(void) // Called from the interpreter core at the beginning of every CPU step void CDebuggerUI::CPUStepStarted() { - uint32_t pc = g_Reg->m_PROGRAM_COUNTER; - COpInfo opInfo(R4300iOp::m_Opcode); - bool bStoreOp = opInfo.IsStoreCommand(); - uint32_t storeAddress = bStoreOp ? opInfo.GetLoadStoreAddress() : 0; - if (isStepping() && bCPULoggingEnabled()) { Debug_RefreshCPULogWindow(); } - if(bStoreOp && m_Breakpoints->NumMemLocks() > 0) + if(m_Breakpoints->NumMemLocks() > 0) { - if (m_Breakpoints->MemLockExists(storeAddress, opInfo.NumBytesToStore())) + COpInfo opInfo(R4300iOp::m_Opcode); + bool bStoreOp = opInfo.IsStoreCommand(); + + if (bStoreOp) { - // Memory is locked, skip op - g_Settings->SaveBool(Debugger_SkipOp, true); - return; + uint32_t storeAddress = bStoreOp ? opInfo.GetLoadStoreAddress() : 0; + if (m_Breakpoints->MemLockExists(storeAddress, opInfo.NumBytesToStore())) + { + // Memory is locked, skip op + g_Settings->SaveBool(Debugger_SkipOp, true); + return; + } } } - JSHookCpuStepEnv hookEnv = { 0 }; - hookEnv.pc = pc; - hookEnv.opInfo = opInfo; - - m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_CPUSTEP, (void*)&hookEnv); + if (m_ScriptSystem->HaveAppCallbacks()) + { + JSHookCpuStepEnv hookEnv; + hookEnv.pc = g_Reg->m_PROGRAM_COUNTER; + hookEnv.opInfo = COpInfo(R4300iOp::m_Opcode); + + if(m_ScriptSystem->HaveCpuExecCallbacks(hookEnv.pc)) + { + m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_CPU_EXEC, (void*)&hookEnv); + } + + if (hookEnv.opInfo.IsLoadCommand() && + m_ScriptSystem->HaveCpuReadCallbacks(hookEnv.opInfo.GetLoadStoreAddress())) + { + m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_CPU_READ, (void*)&hookEnv); + } + else if (hookEnv.opInfo.IsStoreCommand() && + m_ScriptSystem->HaveCpuWriteCallbacks(hookEnv.opInfo.GetLoadStoreAddress())) + { + m_ScriptSystem->InvokeAppCallbacks(JS_HOOK_CPU_WRITE, (void*)&hookEnv); + } + } if (CDebugSettings::ExceptionBreakpoints() != 0) { + uint32_t pc = g_Reg->m_PROGRAM_COUNTER; + if (pc == 0x80000000 || pc == 0x80000080 || pc == 0xA0000100 || pc == 0x80000180) { @@ -619,6 +640,8 @@ void CDebuggerUI::CPUStepStarted() if (m_Breakpoints->HaveRegBP()) { + COpInfo opInfo(R4300iOp::m_Opcode); + if (m_Breakpoints->HaveAnyGPRWriteBP()) { int nReg = 0; diff --git a/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI.cpp b/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI.cpp index 793d4df04..137692725 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI.cpp +++ b/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI.cpp @@ -375,7 +375,7 @@ JSAppCallbackID ScriptAPI::AddAppCallback(duk_context* ctx, duk_idx_t callbackId JSAppCallbackID ScriptAPI::AddAppCallback(duk_context* ctx, JSAppHookID hookId, JSAppCallback& callback) { CScriptInstance* inst = GetInstance(ctx); - JSAppCallbackID callbackId = inst->System()->RawAddAppCallback(hookId, callback); + JSAppCallbackID callbackId = inst->System()->QueueAddAppCallback(hookId, callback); if(callbackId == JS_INVALID_CALLBACK) { @@ -400,8 +400,6 @@ JSAppCallbackID ScriptAPI::AddAppCallback(duk_context* ctx, JSAppHookID hookId, duk_pop(ctx); - inst->IncRefCount(); - return callbackId; } @@ -431,13 +429,8 @@ duk_ret_t ScriptAPI::js__AppCallbackFinalizer(duk_context* ctx) JSAppCallbackID callbackId = (JSAppCallbackID)duk_get_uint(ctx, -1); duk_pop_n(ctx, 2); - bool bRemoved = inst->System()->RawRemoveAppCallback(hookId, callbackId); + inst->System()->QueueRemoveAppCallback(hookId, callbackId); - if(bRemoved) - { - inst->DecRefCount(); - } - return 0; } diff --git a/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI_events.cpp b/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI_events.cpp index 23ee79761..1403c3276 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI_events.cpp +++ b/Source/Project64/UserInterface/Debugger/ScriptAPI/ScriptAPI_events.cpp @@ -92,7 +92,7 @@ duk_ret_t ScriptAPI::js_events_onexec(duk_context* ctx) cb.m_Params.addrStart = addrStart; cb.m_Params.addrEnd = addrEnd; - JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPUSTEP, cb); + JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPU_EXEC, cb); duk_push_uint(ctx, callbackId); return 1; @@ -112,7 +112,7 @@ duk_ret_t ScriptAPI::js_events_onread(duk_context* ctx) cb.m_Params.addrStart = addrStart; cb.m_Params.addrEnd = addrEnd; - JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPUSTEP, cb); + JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPU_READ, cb); duk_push_uint(ctx, callbackId); return 1; @@ -132,7 +132,7 @@ duk_ret_t ScriptAPI::js_events_onwrite(duk_context* ctx) cb.m_Params.addrStart = addrStart; cb.m_Params.addrEnd = addrEnd; - JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPUSTEP, cb); + JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPU_WRITE, cb); duk_push_uint(ctx, callbackId); return 1; @@ -157,7 +157,7 @@ duk_ret_t ScriptAPI::js_events_onopcode(duk_context* ctx) cb.m_Params.opcode = opcode; cb.m_Params.opcodeMask = mask; - JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPUSTEP, cb); + JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPU_EXEC, cb); duk_push_uint(ctx, callbackId); return 1; @@ -179,7 +179,7 @@ duk_ret_t ScriptAPI::js_events_ongprvalue(duk_context* ctx) cb.m_Params.regIndices = duk_get_uint(ctx, 1); cb.m_Params.regValue = duk_get_uint(ctx, 2); - JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPUSTEP, cb); + JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPU_EXEC, cb); duk_push_uint(ctx, callbackId); return 1; @@ -258,7 +258,7 @@ bool CbCond_ReadAddrBetween(JSAppCallback* cb, void* _env) { JSHookCpuStepEnv* env = (JSHookCpuStepEnv*)_env; - if(!env->opInfo.IsLoadCommand()) + if (!env->opInfo.IsLoadCommand()) { return false; } @@ -273,7 +273,7 @@ bool CbCond_WriteAddrBetween(JSAppCallback* cb, void* _env) { JSHookCpuStepEnv* env = (JSHookCpuStepEnv*)_env; - if(!env->opInfo.IsStoreCommand()) + if (!env->opInfo.IsStoreCommand()) { return false; } diff --git a/Source/Project64/UserInterface/Debugger/ScriptInstance.cpp b/Source/Project64/UserInterface/Debugger/ScriptInstance.cpp index b9e5633fe..87c4972f6 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptInstance.cpp +++ b/Source/Project64/UserInterface/Debugger/ScriptInstance.cpp @@ -112,11 +112,6 @@ error_cleanup: return false; } -size_t CScriptInstance::GetRefCount() -{ - return m_RefCount; -} - void CScriptInstance::IncRefCount() { m_RefCount++; @@ -135,27 +130,6 @@ void CScriptInstance::SetStopping(bool bStopping) m_bStopping = bStopping; } -bool CScriptInstance::IsStopping() -{ - return m_bStopping; -} - -void CScriptInstance::RawCall(void *dukFuncHeapPtr, JSDukArgSetupFunc argSetupFunc, void *param) -{ - m_ExecStartTime = Timestamp(); - duk_push_heapptr(m_Ctx, dukFuncHeapPtr); - duk_idx_t nargs = argSetupFunc ? argSetupFunc(m_Ctx, param) : 0; - - if(duk_pcall(m_Ctx, nargs) == DUK_EXEC_ERROR) - { - duk_get_prop_string(m_Ctx, -1, "stack"); - m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); - duk_pop(m_Ctx); - } - - duk_pop(m_Ctx); -} - void CScriptInstance::RawCMethodCall(void* dukThisHeapPtr, duk_c_function func, JSDukArgSetupFunc argSetupFunc, void *argSetupParam) { m_ExecStartTime = Timestamp(); @@ -179,25 +153,6 @@ void CScriptInstance::PostCMethodCall(void* dukThisHeapPtr, duk_c_function func, m_System->PostCMethodCall(m_InstanceName.c_str(), dukThisHeapPtr, func, argSetupFunc, argSetupParam, argSetupParamSize); } -void CScriptInstance::RawInvokeAppCallback(JSAppCallback& cb, void* _hookEnv) -{ - if (cb.m_ConditionFunc != nullptr && !cb.m_ConditionFunc(&cb, _hookEnv)) - { - return; - } - - m_CurExecCallbackId = cb.m_CallbackId; - - RawCall(cb.m_DukFuncHeapPtr, cb.m_DukArgSetupFunc, _hookEnv); - - if (cb.m_CleanupFunc != nullptr) - { - cb.m_CleanupFunc(m_Ctx, _hookEnv); - } - - m_CurExecCallbackId = JS_INVALID_CALLBACK; -} - void CScriptInstance::RawConsoleInput(const char* code) { m_System->ConsoleLog("> %s", code); diff --git a/Source/Project64/UserInterface/Debugger/ScriptInstance.h b/Source/Project64/UserInterface/Debugger/ScriptInstance.h index da4e7f525..2bf2bf6f9 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptInstance.h +++ b/Source/Project64/UserInterface/Debugger/ScriptInstance.h @@ -32,21 +32,45 @@ public: void SetExecTimeout(uint64_t timeout); bool IsTimedOut(); - size_t GetRefCount(); + inline size_t GetRefCount() { return m_RefCount; } void IncRefCount(); void DecRefCount(); void SetStopping(bool bStopping); - bool IsStopping(); + inline bool IsStopping() { return m_bStopping; } bool RegisterWorker(CScriptWorker* worker); void UnregisterWorker(CScriptWorker* worker); void StopRegisteredWorkers(); - void RawInvokeAppCallback(JSAppCallback& cb, void *_hookEnv); + inline void RawInvokeAppCallback(JSAppCallback& cb, void* _hookEnv) + { + m_CurExecCallbackId = cb.m_CallbackId; - void RawConsoleInput(const char* code); + RawCall(cb.m_DukFuncHeapPtr, cb.m_DukArgSetupFunc, _hookEnv); - void RawCall(void* dukFuncHeapPtr, JSDukArgSetupFunc argSetupFunc, void* param = nullptr); + if (cb.m_CleanupFunc != nullptr) + { + cb.m_CleanupFunc(m_Ctx, _hookEnv); + } + + m_CurExecCallbackId = JS_INVALID_CALLBACK; + } + + inline void RawCall(void *dukFuncHeapPtr, JSDukArgSetupFunc argSetupFunc, void *param = nullptr) + { + m_ExecStartTime = Timestamp(); + duk_push_heapptr(m_Ctx, dukFuncHeapPtr); + duk_idx_t nargs = argSetupFunc ? argSetupFunc(m_Ctx, param) : 0; + + if (duk_pcall(m_Ctx, nargs) == DUK_EXEC_ERROR) + { + duk_get_prop_string(m_Ctx, -1, "stack"); + m_System->ConsoleLog("%s", duk_safe_to_string(m_Ctx, -1)); + duk_pop(m_Ctx); + } + + duk_pop(m_Ctx); + } void RawCMethodCall(void* dukThisHeapPtr, duk_c_function func, JSDukArgSetupFunc argSetupFunc = nullptr, @@ -56,6 +80,8 @@ public: JSDukArgSetupFunc argSetupFunc = nullptr, void* argSetupParam = nullptr, size_t argSetupParamSize = 0); + void RawConsoleInput(const char* code); + private: static uint64_t Timestamp(); void Cleanup(); diff --git a/Source/Project64/UserInterface/Debugger/ScriptSystem.cpp b/Source/Project64/UserInterface/Debugger/ScriptSystem.cpp index 5d0b05a88..af6ce7c6b 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptSystem.cpp +++ b/Source/Project64/UserInterface/Debugger/ScriptSystem.cpp @@ -11,7 +11,10 @@ CScriptSystem::CScriptSystem(CDebuggerUI *debugger) : m_Debugger(debugger), m_NextAppCallbackId(0), - m_AppCallbackCount(0) + m_AppCallbackCount(0), + m_CpuExecCbInfo({}), + m_CpuReadCbInfo({}), + m_CpuWriteCbInfo({}) { InitDirectories(); @@ -158,7 +161,7 @@ bool CScriptSystem::HaveAppCallbacks(JSAppHookID hookId) { CGuard guard(m_InstancesCS); - return (m_AppCallbackHooks.count(hookId) > 0 && + return (hookId < JS_NUM_APP_HOOKS && m_AppCallbackHooks[hookId].size() > 0); } @@ -166,35 +169,27 @@ void CScriptSystem::InvokeAppCallbacks(JSAppHookID hookId, void* env) { CGuard guard(m_InstancesCS); - if (m_AppCallbackHooks.count(hookId) == 0 || - m_AppCallbackHooks[hookId].size() == 0) - { - return; - } + JSAppCallbackList& callbacks = m_AppCallbackHooks[hookId]; bool bNeedSweep = false; - - // note: have to copy the map so iterator doesn't break if a callback makes changes - // todo: use reference, queue callback additions/removals? - // JSAppCallbackMap& callbacks = m_AppCallbackHooks[hookId]; - JSAppCallbackMap callbacks = m_AppCallbackHooks[hookId]; - - JSAppCallbackMap::iterator it; - for (it = callbacks.begin(); it != callbacks.end(); it++) + for (JSAppCallback& callback : callbacks) { - JSAppCallback& callback = it->second; - - if (callback.m_Instance->IsStopping()) + if (!callback.m_ConditionFunc(&callback, env)) { continue; } - callback.m_Instance->RawInvokeAppCallback(callback, env); + CScriptInstance* instance = callback.m_Instance; - if (callback.m_Instance->GetRefCount() == 0) + if (!instance->IsStopping()) { - bNeedSweep = true; + instance->RawInvokeAppCallback(callback, env); + + if (instance->GetRefCount() == 0) + { + bNeedSweep = true; + } } } @@ -204,6 +199,126 @@ void CScriptSystem::InvokeAppCallbacks(JSAppHookID hookId, void* env) } } +void CScriptSystem::UpdateCpuCbListInfo(volatile JSCpuCbListInfo& info, JSAppCallbackList& callbacks) +{ + uint32_t minAddrStart = 0; + uint32_t maxAddrEnd = 0; + int numCacheEntries = 0; + bool bCacheExceeded = false; + + for (JSAppCallback& callback : callbacks) + { + size_t i; + for (i = 0; i < numCacheEntries; i++) + { + // combine adjacent/overlapping ranges + if ((callback.m_Params.addrStart >= info.rangeCache[i].addrStart && + callback.m_Params.addrStart <= info.rangeCache[i].addrEnd + 1) || + (callback.m_Params.addrEnd >= info.rangeCache[i].addrStart - 1 && + callback.m_Params.addrEnd <= info.rangeCache[i].addrEnd)) + { + info.rangeCache[i].addrStart = min(info.rangeCache[i].addrStart, callback.m_Params.addrStart); + info.rangeCache[i].addrEnd = max(info.rangeCache[i].addrEnd, callback.m_Params.addrEnd); + break; + } + } + + if (i == numCacheEntries) + { + if (i == JS_CPU_CB_RANGE_CACHE_SIZE) + { + bCacheExceeded = true; + } + else + { + info.rangeCache[i].addrStart = callback.m_Params.addrStart; + info.rangeCache[i].addrEnd = callback.m_Params.addrEnd; + numCacheEntries++; + } + } + + if (callback.m_Params.addrStart < minAddrStart) + { + minAddrStart = callback.m_Params.addrStart; + } + + if (callback.m_Params.addrEnd > maxAddrEnd) + { + maxAddrEnd = callback.m_Params.addrEnd; + } + } + + info.numRangeCacheEntries = numCacheEntries; + info.bRangeCacheExceeded = bCacheExceeded; + info.numCallbacks = callbacks.size(); + info.minAddrStart = minAddrStart; + info.maxAddrEnd = maxAddrEnd; +} + +void CScriptSystem::RefreshCallbackMaps() +{ + for (JSQueuedCallbackRemove& cbRemove : m_CbRemoveQueue) + { + RawRemoveAppCallback(cbRemove.hookId, cbRemove.callbackId); + } + + for (JSQueuedCallbackAdd& cbAdd : m_CbAddQueue) + { + RawAddAppCallback(cbAdd.hookId, cbAdd.callback); + } + + m_CbRemoveQueue.clear(); + m_CbAddQueue.clear(); + + UpdateCpuCbListInfo(m_CpuExecCbInfo, m_AppCallbackHooks[JS_HOOK_CPU_EXEC]); + UpdateCpuCbListInfo(m_CpuReadCbInfo, m_AppCallbackHooks[JS_HOOK_CPU_READ]); + UpdateCpuCbListInfo(m_CpuWriteCbInfo, m_AppCallbackHooks[JS_HOOK_CPU_WRITE]); +} + +JSAppCallbackID CScriptSystem::QueueAddAppCallback(JSAppHookID hookId, JSAppCallback callback) +{ + if (hookId >= JS_NUM_APP_HOOKS) + { + return JS_INVALID_CALLBACK; + } + + callback.m_CallbackId = m_NextAppCallbackId++; + m_CbAddQueue.push_back({ hookId, callback }); + callback.m_Instance->IncRefCount(); + return callback.m_CallbackId; +} + +void CScriptSystem::QueueRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId) +{ + // todo also remove from addqueue + + for (size_t i = 0; i < m_CbRemoveQueue.size(); i++) + { + if (m_CbRemoveQueue[i].hookId == hookId && + m_CbRemoveQueue[i].callbackId == callbackId) + { + return; + } + } + + JSAppCallbackList::iterator it; + for (it = m_AppCallbackHooks[hookId].begin(); it != m_AppCallbackHooks[hookId].end(); it++) + { + if (it->m_CallbackId == callbackId) + { + break; + } + } + + if (it == m_AppCallbackHooks[hookId].end()) + { + return; + } + + m_CbRemoveQueue.push_back({ hookId, callbackId }); + it->m_Instance->DecRefCount(); +} + void CScriptSystem::DoMouseEvent(JSAppHookID hookId, int x, int y, DWORD uMsg) { int button = -1; @@ -394,29 +509,29 @@ bool CScriptSystem::RawRemoveInstance(const char *name) return true; } -JSAppCallbackID CScriptSystem::RawAddAppCallback(JSAppHookID hookId, JSAppCallback& callback) +void CScriptSystem::RawAddAppCallback(JSAppHookID hookId, JSAppCallback& callback) { if(hookId >= JS_NUM_APP_HOOKS) { - return JS_INVALID_CALLBACK; + return; } - callback.m_CallbackId = m_NextAppCallbackId; - m_AppCallbackHooks[hookId][m_NextAppCallbackId] = callback; + m_AppCallbackHooks[hookId].push_back(callback); m_AppCallbackCount++; - return m_NextAppCallbackId++; } -bool CScriptSystem::RawRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId) +void CScriptSystem::RawRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId) { - if(m_AppCallbackHooks[hookId].count(callbackId) == 0) + JSAppCallbackList::iterator it; + for (it = m_AppCallbackHooks[hookId].begin(); it != m_AppCallbackHooks[hookId].end(); it++) { - return false; + if (it->m_CallbackId == callbackId) + { + m_AppCallbackHooks[hookId].erase(it); + m_AppCallbackCount--; + return; + } } - - m_AppCallbackHooks[hookId].erase(callbackId); - m_AppCallbackCount--; - return true; } void CScriptSystem::ExecAutorunList() @@ -529,6 +644,7 @@ bool CScriptSystem::ProcessCommand(JSSysCommand& cmd) return false; } + RefreshCallbackMaps(); return true; } diff --git a/Source/Project64/UserInterface/Debugger/ScriptSystem.h b/Source/Project64/UserInterface/Debugger/ScriptSystem.h index f5e0ee3ee..97bfdf15c 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptSystem.h +++ b/Source/Project64/UserInterface/Debugger/ScriptSystem.h @@ -13,11 +13,37 @@ class CScriptSystem { typedef std::map JSInstanceMap; - typedef std::map JSAppCallbackMap; - typedef std::map JSAppHookMap; + typedef std::vector JSAppCallbackList; typedef std::map JSInstanceStatusMap; typedef std::vector JSSysCommandQueue; + struct JSQueuedCallbackAdd + { + JSAppHookID hookId; + JSAppCallback callback; + }; + + struct JSQueuedCallbackRemove + { + JSAppHookID hookId; + JSAppCallbackID callbackId; + }; + + enum { JS_CPU_CB_RANGE_CACHE_SIZE = 256 }; + + struct JSCpuCbListInfo + { + size_t numCallbacks; + uint32_t minAddrStart; + uint32_t maxAddrEnd; + size_t numRangeCacheEntries; + bool bRangeCacheExceeded; + struct { + uint32_t addrStart; + uint32_t addrEnd; + } rangeCache[JS_CPU_CB_RANGE_CACHE_SIZE]; + }; + HANDLE m_hThread; CriticalSection m_CmdQueueCS; @@ -26,10 +52,18 @@ class CScriptSystem CriticalSection m_InstancesCS; JSInstanceMap m_Instances; - JSAppHookMap m_AppCallbackHooks; + JSAppCallbackList m_AppCallbackHooks[JS_NUM_APP_HOOKS]; JSAppCallbackID m_NextAppCallbackId; - size_t m_AppCallbackCount; + + std::vector m_CbRemoveQueue; + std::vector m_CbAddQueue; + volatile size_t m_AppCallbackCount; + + volatile JSCpuCbListInfo m_CpuExecCbInfo; + volatile JSCpuCbListInfo m_CpuReadCbInfo; + volatile JSCpuCbListInfo m_CpuWriteCbInfo; + CriticalSection m_UIStateCS; JSInstanceStatusMap m_UIInstanceStatus; stdstr m_UILog; @@ -69,17 +103,74 @@ public: size_t argSetupParamSize = 0); bool HaveAppCallbacks(JSAppHookID hookId); - void InvokeAppCallbacks(JSAppHookID hookId, void* env = nullptr); + + // Note: Unguarded for speed, shouldn't matter + inline bool HaveAppCallbacks() { return m_AppCallbackCount != 0; } + + inline bool HaveCpuExecCallbacks(uint32_t address) + { + return HaveCpuCallbacks(m_CpuExecCbInfo, m_AppCallbackHooks[JS_HOOK_CPU_EXEC], address); + } + + inline bool HaveCpuReadCallbacks(uint32_t address) + { + return HaveCpuCallbacks(m_CpuReadCbInfo, m_AppCallbackHooks[JS_HOOK_CPU_READ], address); + } + + inline bool HaveCpuWriteCallbacks(uint32_t address) + { + return HaveCpuCallbacks(m_CpuWriteCbInfo, m_AppCallbackHooks[JS_HOOK_CPU_WRITE], address); + } + + static void UpdateCpuCbListInfo(volatile JSCpuCbListInfo& info, JSAppCallbackList& callbacks); + void DoMouseEvent(JSAppHookID hookId, int x, int y, DWORD uMsg = (DWORD)-1); - JSAppCallbackID RawAddAppCallback(JSAppHookID hookId, JSAppCallback& callback); - bool RawRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId); + + JSAppCallbackID QueueAddAppCallback(JSAppHookID hookId, JSAppCallback callback); + void QueueRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId); + void InvokeAppCallbacks(JSAppHookID hookId, void* env = nullptr); void ExecAutorunList(); std::set& AutorunList(); void LoadAutorunList(); void SaveAutorunList(); - + private: + inline bool HaveCpuCallbacks(volatile JSCpuCbListInfo& info, JSAppCallbackList& callbacks, uint32_t address) + { + if (info.numCallbacks == 0 || address < info.minAddrStart || address > info.maxAddrEnd) + { + return false; + } + + if (!info.bRangeCacheExceeded) + { + for (size_t i = 0; i < info.numRangeCacheEntries; i++) + { + if (address >= info.rangeCache[i].addrStart && + address <= info.rangeCache[i].addrEnd) + { + return true; + } + } + + return false; + } + + CGuard guard(m_InstancesCS); + + for (JSAppCallback& callback : callbacks) + { + if (address >= callback.m_Params.addrStart && + address <= callback.m_Params.addrEnd) + { + return true; + } + } + + return false; + } + void InitDirectories(); void PostCommand(JSSysCommandID id, stdstr paramA = "", stdstr paramB = "", void* paramC = nullptr); @@ -98,6 +189,10 @@ private: void OnSweep(bool bIfDone); bool RawRemoveInstance(const char* key); + + void RawAddAppCallback(JSAppHookID hookId, JSAppCallback& callback); + void RawRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId); + void RefreshCallbackMaps(); static stdstr FixStringReturns(const char* str); }; diff --git a/Source/Project64/UserInterface/Debugger/ScriptTypes.h b/Source/Project64/UserInterface/Debugger/ScriptTypes.h index 451939985..e5c06f827 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptTypes.h +++ b/Source/Project64/UserInterface/Debugger/ScriptTypes.h @@ -14,7 +14,9 @@ enum enum JSAppHookID { - JS_HOOK_CPUSTEP, + JS_HOOK_CPU_EXEC, + JS_HOOK_CPU_READ, + JS_HOOK_CPU_WRITE, JS_HOOK_PIFREAD, JS_HOOK_PIDMA, JS_HOOK_GFXUPDATE, @@ -90,6 +92,8 @@ struct JSAppCallback }; } m_Params; + static bool CbCondTrue(JSAppCallback*, void*) { return true; } + JSAppCallback(CScriptInstance* instance, void* dukFuncHeapPtr, JSAppCallbackCondFunc condFunc = nullptr, JSDukArgSetupFunc argSetupFunc = nullptr, @@ -101,6 +105,11 @@ struct JSAppCallback m_CleanupFunc(cleanupFunc), m_CallbackId(JS_INVALID_CALLBACK) { + if (m_ConditionFunc == nullptr) + { + m_ConditionFunc = CbCondTrue; + } + m_Params = {}; } @@ -112,6 +121,11 @@ struct JSAppCallback m_CleanupFunc(nullptr), m_CallbackId(JS_INVALID_CALLBACK) { + if (m_ConditionFunc == nullptr) + { + m_ConditionFunc = CbCondTrue; + } + m_Params = {}; } };