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 { rom
events
debug
+ asm
console
fs
fs.Stats
@@ -336,7 +337,7 @@ events.onwrite(ADDR_ANY_CART_ROM_UNC, function(addr) interpreter mode only
events.onopcode(address, opcode, callback)
- Adds a CPU executions callback for a virtual address or AddressRange and returns a callback ID. + Adds a CPU execution callback for a virtual address or AddressRange and returns a callback ID. callback will be invoked at the beginning of a CPU step if the program counter is at address and opcode is equal to the opcode to be executed. callback receives the program counter address at which the event is fired.
@@ -354,7 +355,7 @@ events.onopcode(ADDR_ANY, JR_RA, function(pc)
 		interpreter mode only
 		
events.onopcode(address, opcode, mask, callback)
- Adds a CPU executions callback for a virtual address or AddressRange and returns a callback ID. + Adds a CPU execution callback for a virtual address or AddressRange and returns a callback ID. callback will be invoked at the beginning of a CPU step if the program counter is at address and opcode is equal to the opcode to be executed ANDed with mask. callback receives the program counter address at which the event is fired.
@@ -379,6 +380,42 @@ events.onopcode(ADDR_ANY, JAL, NO_TARGET, function(pc)
 
+
+ emulation thread + interpreter mode only +
events.ongprvalue(address, registers, value, callback)
+
+ Adds a CPU execution callback for a virtual address or AddressRange and returns a callback ID. + callback will be invoked at the beginning of a CPU step if the program counter is at address and the lower 32 bits of the general purpose registers specified by registers are equal to value. + callback receives the program counter address at which the event is fired, and the index of the first register that contains the value. +
+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))
+})
+
+
+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))
+})
+
+ Valid registers: +
+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
+
+
+
emulation thread
events.ondraw(callback)
@@ -437,6 +474,19 @@ events.onexec(0x802CB1C0, function()
+
+
asm
+
+
asm.gprname(regIndex)
+
+ Returns the name of the general purpose register specified by regIndex. +
+asm.gprname(4) // 'a0'
+
+
+
+
+
fs