diff --git a/Source/Project64/UserInterface/API.js b/Source/Project64/UserInterface/API.js index bec3727ca..2dc524ece 100644 --- a/Source/Project64/UserInterface/API.js +++ b/Source/Project64/UserInterface/API.js @@ -39,6 +39,51 @@ const _regNums = { f28: 28, f29: 29, f30: 30, f31: 31 } +const _gprNames = [ + 'r0', 'at', 'v0', 'v1', 'a0', 'a1', 'a2', 'a3', + 't0', 't1', 't2', 't3', 't4', 't5', 't6', 't7', + 's0', 's1', 's2', 's3', 's4', 's5', 's6', 's7', + 't8', 't9', 'k0', 'k1', 'gp', 'sp', 'fp', 'ra' +] + +const GPR_R0 = (1 << 0) +const GPR_AT = (1 << 1) +const GPR_V0 = (1 << 2) +const GPR_V1 = (1 << 3) +const GPR_A0 = (1 << 4) +const GPR_A1 = (1 << 5) +const GPR_A2 = (1 << 6) +const GPR_A3 = (1 << 7) +const GPR_T0 = (1 << 8) +const GPR_T1 = (1 << 9) +const GPR_T2 = (1 << 10) +const GPR_T3 = (1 << 11) +const GPR_T4 = (1 << 12) +const GPR_T5 = (1 << 13) +const GPR_T6 = (1 << 14) +const GPR_T7 = (1 << 15) +const GPR_S0 = (1 << 16) +const GPR_S1 = (1 << 17) +const GPR_S2 = (1 << 18) +const GPR_S3 = (1 << 19) +const GPR_S4 = (1 << 20) +const GPR_S5 = (1 << 21) +const GPR_S6 = (1 << 22) +const GPR_S7 = (1 << 23) +const GPR_T8 = (1 << 24) +const GPR_T9 = (1 << 25) +const GPR_K0 = (1 << 26) +const GPR_K1 = (1 << 27) +const GPR_GP = (1 << 28) +const GPR_SP = (1 << 29) +const GPR_FP = (1 << 30) +const GPR_RA = (1 << 31) >>> 0 + +const GPR_ANY_ARG = (GPR_A0 | GPR_A1 | GPR_A2 | GPR_A3) +const GPR_ANY_TEMP = (GPR_T0 | GPR_T1 | GPR_T2 | GPR_T3 | GPR_T4 | GPR_T5 | GPR_T6 | GPR_T7 | GPR_T8 | GPR_T9) +const GPR_ANY_SAVE = (GPR_S0 | GPR_S1 | GPR_S2 | GPR_S3 | GPR_S4 | GPR_S5 | GPR_S6 | GPR_S7) +const GPR_ANY = (GPR_R0 | GPR_AT | GPR_V0 | GPR_V1 | GPR_ANY_ARG | GPR_ANY_TEMP | GPR_ANY_SAVE | GPR_K0 | GPR_K1 | GPR_GP | GPR_SP | GPR_FP | GPR_RA) >>> 0 + function AddressRange(start, end) { this.start = start >>> 0 @@ -56,6 +101,17 @@ const ADDR_ANY_RDRAM_UNC = new AddressRange(0xA0000000, 0xA0800000) const ADDR_ANY_CART_ROM = new AddressRange(0x90000000, 0x96000000) const ADDR_ANY_CART_ROM_UNC = new AddressRange(0xB0000000, 0xB6000000) +const asm = { + gprname: function(num) + { + if(num in _gprNames) + { + return _gprNames[num] + } + return "" + } +} + const fs = { open: function(path, mode) { @@ -331,7 +387,25 @@ const events = (function() } this._stashCallback(callback); - return _native.addCallback('onopcode', callback, start, end, value, mask) + return _native.addCallback('opcode', callback, start, end, value, mask) + }, + ongprvalue: function(addr, registers, value, callback) + { + var start = 0; + var end = 0; + + if(typeof(addr) == "object") + { + start = addr.start; + end = addr.end; + } + else if(typeof(addr) == "number") + { + start = addr; + } + + this._stashCallback(callback); + return _native.addCallback('gprvalue', callback, start, end, registers, value) }, onread: function(addr, callback) { @@ -754,7 +828,6 @@ function alert(text, caption){ _native.msgBox(text, caption) } - function Socket(fd) { var connected = false; diff --git a/Source/Project64/UserInterface/Debugger/Debugger.cpp b/Source/Project64/UserInterface/Debugger/Debugger.cpp index cc5a2944b..747e7f0f6 100644 --- a/Source/Project64/UserInterface/Debugger/Debugger.cpp +++ b/Source/Project64/UserInterface/Debugger/Debugger.cpp @@ -576,8 +576,9 @@ void CDebuggerUI::CPUStepStarted() uint32_t PROGRAM_COUNTER = g_Reg->m_PROGRAM_COUNTER; uint32_t JumpToLocation = R4300iOp::m_JumpToLocation; - m_ScriptSystem->HookCPUExec()->InvokeByParamInRange(PROGRAM_COUNTER); - m_ScriptSystem->HookCPUExecOpcode()->InvokeByParamInRangeWithMaskedValue(PROGRAM_COUNTER, R4300iOp::m_Opcode.Hex); + m_ScriptSystem->HookCPUExec()->InvokeByAddressInRange(PROGRAM_COUNTER); + m_ScriptSystem->HookCPUExecOpcode()->InvokeByAddressInRange_MaskedOpcode(PROGRAM_COUNTER, R4300iOp::m_Opcode.Hex); + m_ScriptSystem->HookCPUGPRValue()->InvokeByAddressInRange_GPRValue(PROGRAM_COUNTER); // Memory breakpoints @@ -589,11 +590,11 @@ void CDebuggerUI::CPUStepStarted() if (opInfo.IsLoadCommand()) // Read instructions { - m_ScriptSystem->HookCPURead()->InvokeByParamInRange(memoryAddress); + m_ScriptSystem->HookCPURead()->InvokeByAddressInRange(memoryAddress); } else // Write instructions { - m_ScriptSystem->HookCPUWrite()->InvokeByParamInRange(memoryAddress); + m_ScriptSystem->HookCPUWrite()->InvokeByAddressInRange(memoryAddress); // Catch cart -> rdram dma if (memoryAddress == 0xA460000C) // PI_WR_LEN_REG diff --git a/Source/Project64/UserInterface/Debugger/ScriptHook.cpp b/Source/Project64/UserInterface/Debugger/ScriptHook.cpp index 2d0b8ed6f..fecf459a6 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptHook.cpp +++ b/Source/Project64/UserInterface/Debugger/ScriptHook.cpp @@ -46,35 +46,65 @@ void CScriptHook::InvokeByParam(uint32_t param) } } -void CScriptHook::InvokeByParamInRange(uint32_t param) +void CScriptHook::InvokeByAddressInRange(uint32_t address) { int nCallbacks = m_Callbacks.size(); for (int i = 0; i < nCallbacks; i++) { - if (param == m_Callbacks[i].param || (param >= m_Callbacks[i].param && param < m_Callbacks[i].param2)) + if (address == m_Callbacks[i].param || (address >= m_Callbacks[i].param && address < m_Callbacks[i].param2)) { - m_Callbacks[i].scriptInstance->Invoke(m_Callbacks[i].heapptr, param); + m_Callbacks[i].scriptInstance->Invoke(m_Callbacks[i].heapptr, address); return; } } } -void CScriptHook::InvokeByParamInRangeWithMaskedValue(uint32_t param, uint32_t value) +void CScriptHook::InvokeByAddressInRange_MaskedOpcode(uint32_t pc, uint32_t opcode) { int nCallbacks = m_Callbacks.size(); for (int i = 0; i < nCallbacks; i++) { - if (param == m_Callbacks[i].param || (param >= m_Callbacks[i].param && param < m_Callbacks[i].param2)) + if (pc == m_Callbacks[i].param || (pc >= m_Callbacks[i].param && pc < m_Callbacks[i].param2)) { - if ((m_Callbacks[i].param3 & m_Callbacks[i].param4) == (value & m_Callbacks[i].param4)) + if ((m_Callbacks[i].param3 & m_Callbacks[i].param4) == (opcode & m_Callbacks[i].param4)) { - m_Callbacks[i].scriptInstance->Invoke(m_Callbacks[i].heapptr, param); + m_Callbacks[i].scriptInstance->Invoke(m_Callbacks[i].heapptr, pc); return; } } } } +void CScriptHook::InvokeByAddressInRange_GPRValue(uint32_t pc) +{ + int nCallbacks = m_Callbacks.size(); + + for (int i = 0; i < nCallbacks; i++) + { + uint32_t startAddress = m_Callbacks[i].param; + uint32_t endAddress = m_Callbacks[i].param2; + uint32_t registers = m_Callbacks[i].param3; + uint32_t value = m_Callbacks[i].param4; + + if (!(pc == startAddress || (pc >= startAddress && pc < endAddress))) + { + continue; + } + + for (int nReg = 0; nReg < 32; nReg++) + { + if (registers & (1 << nReg)) + { + if (value == g_Reg->m_GPR[nReg].UW[0]) + { + m_Callbacks[i].scriptInstance->Invoke2(m_Callbacks[i].heapptr, pc, nReg); + break; + } + } + } + } +} + void CScriptHook::InvokeAll() { int nCallbacks = m_Callbacks.size(); diff --git a/Source/Project64/UserInterface/Debugger/ScriptHook.h b/Source/Project64/UserInterface/Debugger/ScriptHook.h index f82de1968..4b111e385 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptHook.h +++ b/Source/Project64/UserInterface/Debugger/ScriptHook.h @@ -33,9 +33,10 @@ public: void InvokeById(int callbackId); void InvokeByParam(uint32_t param); /* invoke if param >= cb.param && param < cb.param2*/ - void InvokeByParamInRange(uint32_t param); + void InvokeByAddressInRange(uint32_t address); /* invoke if param >= cb.param && param < cb.param2 && (value & cb.param4) == cb.param3 */ - void InvokeByParamInRangeWithMaskedValue(uint32_t param, uint32_t value); + void InvokeByAddressInRange_MaskedOpcode(uint32_t pc, uint32_t value); + void InvokeByAddressInRange_GPRValue(uint32_t pc); void RemoveById(int callbackId); void RemoveByParam(uint32_t tag); void RemoveByInstance(CScriptInstance* scriptInstance); diff --git a/Source/Project64/UserInterface/Debugger/ScriptInstance.cpp b/Source/Project64/UserInterface/Debugger/ScriptInstance.cpp index d04276c7a..9cb311665 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptInstance.cpp +++ b/Source/Project64/UserInterface/Debugger/ScriptInstance.cpp @@ -577,6 +577,26 @@ void CScriptInstance::Invoke(void* heapptr, uint32_t param) LeaveCriticalSection(&m_CriticalSection); } +void CScriptInstance::Invoke2(void* heapptr, uint32_t param, uint32_t param2) +{ + EnterCriticalSection(&m_CriticalSection); + duk_push_heapptr(m_Ctx, heapptr); + duk_push_uint(m_Ctx, param); + duk_push_uint(m_Ctx, param2); + + duk_int_t status = duk_pcall(m_Ctx, 2); + + if (status != DUK_EXEC_SUCCESS) + { + const char* errorText = duk_safe_to_string(m_Ctx, -1); + m_Debugger->Debug_LogScriptsWindow(errorText); + m_Debugger->Debug_LogScriptsWindow("\r\n"); + } + + duk_pop(m_Ctx); + LeaveCriticalSection(&m_CriticalSection); +} + void CScriptInstance::QueueAPC(PAPCFUNC userProc, ULONG_PTR param) { if (m_hThread != NULL) diff --git a/Source/Project64/UserInterface/Debugger/ScriptInstance.h b/Source/Project64/UserInterface/Debugger/ScriptInstance.h index 48cd1eb44..afc926daa 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptInstance.h +++ b/Source/Project64/UserInterface/Debugger/ScriptInstance.h @@ -68,6 +68,7 @@ public: void Start(char* path); void ForceStop(); void Invoke(void* heapptr, uint32_t param = 0); + void Invoke2(void* heapptr, uint32_t param = 0, uint32_t param2 = 0); INSTANCE_STATE GetState(); friend class PendingEval; diff --git a/Source/Project64/UserInterface/Debugger/ScriptSystem.cpp b/Source/Project64/UserInterface/Debugger/ScriptSystem.cpp index cf4bb70c0..db7c34c42 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptSystem.cpp +++ b/Source/Project64/UserInterface/Debugger/ScriptSystem.cpp @@ -28,12 +28,14 @@ CScriptSystem::CScriptSystem(CDebuggerUI* debugger) m_HookCPUExecOpcode = new CScriptHook(this); m_HookCPURead = new CScriptHook(this); m_HookCPUWrite = new CScriptHook(this); + m_HookCPUGPRValue = new CScriptHook(this); m_HookFrameDrawn = new CScriptHook(this); RegisterHook("exec", m_HookCPUExec); RegisterHook("read", m_HookCPURead); RegisterHook("write", m_HookCPUWrite); - RegisterHook("onopcode", m_HookCPUExecOpcode); + RegisterHook("opcode", m_HookCPUExecOpcode); + RegisterHook("gprvalue", m_HookCPUGPRValue); RegisterHook("draw", m_HookFrameDrawn); HMODULE hInst = GetModuleHandle(NULL); diff --git a/Source/Project64/UserInterface/Debugger/ScriptSystem.h b/Source/Project64/UserInterface/Debugger/ScriptSystem.h index ea2625c44..52d872ccf 100644 --- a/Source/Project64/UserInterface/Debugger/ScriptSystem.h +++ b/Source/Project64/UserInterface/Debugger/ScriptSystem.h @@ -55,7 +55,7 @@ private: CScriptHook* m_HookCPURead; CScriptHook* m_HookCPUWrite; CScriptHook* m_HookCPUExecOpcode; - + CScriptHook* m_HookCPUGPRValue; CScriptHook* m_HookFrameDrawn; void RegisterHook(const char* hookId, CScriptHook* cbList); // associate string id with callback list @@ -126,6 +126,11 @@ public: return m_HookCPUExecOpcode; } + CScriptHook* HookCPUGPRValue() + { + return m_HookCPUGPRValue; + } + CScriptHook* HookFrameDrawn() { return m_HookFrameDrawn; diff --git a/apidoc.htm b/apidoc.htm index 0220bf90e..57f7a1c64 100644 --- a/apidoc.htm +++ b/apidoc.htm @@ -98,6 +98,7 @@ span.tag2 { <a href="#rom">rom</a><br> <a href="#events">events</a><br> <a href="#debug">debug</a><br> + <a href="#asm">asm</a><br> <a href="#console">console</a><br> <a href="#fs">fs</a><br> <a href="#fs.Stats">fs.Stats</a><br> @@ -336,7 +337,7 @@ events.onwrite(ADDR_ANY_CART_ROM_UNC, function(addr) <span class="tag">interpreter mode only</span> <div class="propertyname">events.onopcode(address, opcode, callback)</div> <div class="propertydesc"> - Adds a CPU executions callback for a virtual address or <a href="#AddressRange">AddressRange</a> and returns a callback ID. + Adds a CPU execution callback for a virtual address or <a href="#AddressRange">AddressRange</a> and returns a callback ID. <span class="snip">callback</span> will be invoked at the beginning of a CPU step if the program counter is at <span class="snip">address</span> and <span class="snip">opcode</span> is equal to the opcode to be executed. <span class="snip">callback</span> receives the program counter address at which the event is fired. <pre class="example"> @@ -354,7 +355,7 @@ events.onopcode(ADDR_ANY, JR_RA, function(pc) <span class="tag">interpreter mode only</span> <div class="propertyname">events.onopcode(address, opcode, mask, callback)</div> <div class="propertydesc"> - Adds a CPU executions callback for a virtual address or <a href="#AddressRange">AddressRange</a> and returns a callback ID. + Adds a CPU execution callback for a virtual address or <a href="#AddressRange">AddressRange</a> and returns a callback ID. <span class="snip">callback</span> will be invoked at the beginning of a CPU step if the program counter is at <span class="snip">address</span> and <span class="snip">opcode</span> is equal to the opcode to be executed ANDed with <span class="snip">mask</span>. <span class="snip">callback</span> receives the program counter address at which the event is fired. <pre class="example"> @@ -379,6 +380,42 @@ events.onopcode(ADDR_ANY, JAL, NO_TARGET, function(pc) </pre> </div> </div> + <div class="property"> + <span class="tag2">emulation thread</span> + <span class="tag">interpreter mode only</span> + <div class="propertyname">events.ongprvalue(address, registers, value, callback)</div> + <div class="propertydesc"> + Adds a CPU execution callback for a virtual address or <a href="#AddressRange">AddressRange</a> and returns a callback ID. + <span class="snip">callback</span> will be invoked at the beginning of a CPU step if the program counter is at <span class="snip">address</span> and the lower 32 bits of the general purpose registers specified by <span class="snip">registers</span> are equal to <span class="snip">value</span>. + <span class="snip">callback</span> receives the program counter address at which the event is fired, and the index of the first register that contains the value. + <pre class="example"> +events.ongprvalue(ADDR_ANY, GPR_ANY, 0x49533634, function(pc, reg) +{ + // log pc whenever any general purpose register contains 0x49533634 + console.log(pc.hex(), asm.gprname(reg)) +}) +</pre> + <pre class="example"> +events.ongprvalue(ADDR_ANY, GPR_A0 | GPR_A1, 0x00001234, function(pc, reg) +{ + // log pc whenever A0 or A1 contains 0x00001234 + console.log(pc.hex(), asm.gprname(reg)) +}) +</pre> + Valid registers: + <pre style="font-size: 13px;"> +GPR_R0 GPR_AT GPR_V0 GPR_V1 GPR_A0 GPR_A1 GPR_A2 GPR_A3 +GPR_T0 GPR_T1 GPR_T2 GPR_T3 GPR_T4 GPR_T5 GPR_T6 GPR_T7 +GPR_S0 GPR_S1 GPR_S2 GPR_S3 GPR_S4 GPR_S5 GPR_S6 GPR_S7 +GPR_T8 GPR_T9 GPR_K0 GPR_K1 GPR_GP GPR_SP GPR_FP GPR_RA + +GPR_ANY_ARG -- Any of the 'A' registers +GPR_ANY_TEMP -- Any of the 'T' registers +GPR_ANY_SAVE -- Any of the 'S' registers +GPR_ANY -- Any register +</pre> + </div> + </div> <div class="property"> <span class="tag2">emulation thread</span> <div class="propertyname">events.ondraw(callback)</div> @@ -437,6 +474,19 @@ events.onexec(0x802CB1C0, function() </div> </div> +<div class="panel" id="asm"> + <div class="classname">asm</div> + <div class="property"> + <div class="propertyname">asm.gprname(regIndex)</div> + <div class="propertydesc"> + Returns the name of the general purpose register specified by <span class="snip">regIndex</span>. + <pre class="example"> +asm.gprname(4) // 'a0' +</pre> + </div> + </div> + </div> + <div class="panel" id="fs"> <div class="classname">fs</div> <div class="property">