Merge pull request #1511 from shygoo2/onexecopcode

[Debugger] Add events.onopcode to JS API
This commit is contained in:
zilmar 2018-12-09 05:01:10 +10:30 committed by GitHub
commit be73dcd459
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 736 additions and 628 deletions

File diff suppressed because it is too large Load Diff

View File

@ -391,6 +391,7 @@ void CDebuggerUI::CPUStepStarted()
uint32_t JumpToLocation = R4300iOp::m_JumpToLocation;
m_ScriptSystem->HookCPUExec()->InvokeByParamInRange(PROGRAM_COUNTER);
m_ScriptSystem->HookCPUExecOpcode()->InvokeByParamInRangeWithMaskedValue(PROGRAM_COUNTER, R4300iOp::m_Opcode.Hex);
// Memory breakpoints

View File

@ -4,7 +4,8 @@
#include "ScriptInstance.h"
#include "ScriptSystem.h"
int CScriptHook::Add(CScriptInstance* scriptInstance, void* heapptr, uint32_t param, uint32_t param2, bool bOnce)
int CScriptHook::Add(CScriptInstance* scriptInstance, void* heapptr, uint32_t param, uint32_t param2,
uint32_t param3, uint32_t param4, bool bOnce)
{
JSCALLBACK jsCallback;
jsCallback.scriptInstance = scriptInstance;
@ -12,6 +13,8 @@ int CScriptHook::Add(CScriptInstance* scriptInstance, void* heapptr, uint32_t pa
jsCallback.callbackId = m_ScriptSystem->GetNextCallbackId();
jsCallback.param = param;
jsCallback.param2 = param2;
jsCallback.param3 = param3;
jsCallback.param4 = param4;
jsCallback.bOnce = bOnce;
m_Callbacks.push_back(jsCallback);
return jsCallback.callbackId;
@ -56,6 +59,22 @@ void CScriptHook::InvokeByParamInRange(uint32_t param)
}
}
void CScriptHook::InvokeByParamInRangeWithMaskedValue(uint32_t param, uint32_t value)
{
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 ((m_Callbacks[i].param3 & m_Callbacks[i].param4) == (value & m_Callbacks[i].param4))
{
m_Callbacks[i].scriptInstance->Invoke(m_Callbacks[i].heapptr, param);
return;
}
}
}
}
void CScriptHook::InvokeAll()
{
int nCallbacks = m_Callbacks.size();

View File

@ -13,6 +13,8 @@ private:
void* heapptr;
uint32_t param;
uint32_t param2;
uint32_t param3;
uint32_t param4;
int callbackId;
bool bOnce;
} JSCALLBACK;
@ -25,12 +27,15 @@ private:
public:
CScriptHook(CScriptSystem* scriptSystem);
~CScriptHook();
int Add(CScriptInstance* scriptInstance, void* heapptr, uint32_t param = 0, uint32_t param2 = 0, bool bOnce = false);
int Add(CScriptInstance* scriptInstance, void* heapptr, uint32_t param = 0, uint32_t param2 = 0,
uint32_t param3 = 0, uint32_t param4 = 0, bool bOnce = false);
void InvokeAll();
void InvokeById(int callbackId);
void InvokeByParam(uint32_t param);
/* invoke if param >= cb.param && param < cb.param2*/
void InvokeByParamInRange(uint32_t param);
/* invoke if param >= cb.param && param < cb.param2 && (value & cb.param4) == cb.param3 */
void InvokeByParamInRangeWithMaskedValue(uint32_t param, uint32_t value);
void RemoveById(int callbackId);
void RemoveByParam(uint32_t tag);
void RemoveByInstance(CScriptInstance* scriptInstance);

View File

@ -741,6 +741,8 @@ duk_ret_t CScriptInstance::js_AddCallback(duk_context* ctx)
void* heapptr;
uint32_t param = 0;
uint32_t param2 = 0;
uint32_t param3 = 0;
uint32_t param4 = 0;
bool bOnce = false;
int argc = duk_get_top(ctx);
@ -748,25 +750,18 @@ duk_ret_t CScriptInstance::js_AddCallback(duk_context* ctx)
hookId = duk_get_string(ctx, 0);
heapptr = duk_get_heapptr(ctx, 1);
if (argc >= 3)
{
param = duk_get_uint(ctx, 2);
if (argc > 3)
{
param2 = duk_get_uint(ctx, 3);
if (argc > 4)
{
bOnce = duk_get_boolean(ctx, 4) != 0;
}
}
}
if (argc >= 3) param = duk_get_uint(ctx, 2);
if (argc >= 4) param2 = duk_get_uint(ctx, 3);
if (argc >= 5) param3 = duk_get_uint(ctx, 4);
if (argc >= 6) param4 = duk_get_uint(ctx, 5);
if (argc >= 7) bOnce = (duk_get_boolean(ctx, 6) != 0);
int callbackId = -1;
CScriptHook* hook = _this->m_ScriptSystem->GetHook(hookId);
if (hook != NULL)
{
callbackId = hook->Add(_this, heapptr, param, param2, bOnce);
callbackId = hook->Add(_this, heapptr, param, param2, param3, param4, bOnce);
}
duk_pop_n(ctx, argc);

View File

@ -25,6 +25,7 @@ CScriptSystem::CScriptSystem(CDebuggerUI* debugger)
m_Debugger = debugger;
m_HookCPUExec = new CScriptHook(this);
m_HookCPUExecOpcode = new CScriptHook(this);
m_HookCPURead = new CScriptHook(this);
m_HookCPUWrite = new CScriptHook(this);
m_HookFrameDrawn = new CScriptHook(this);
@ -32,6 +33,7 @@ CScriptSystem::CScriptSystem(CDebuggerUI* debugger)
RegisterHook("exec", m_HookCPUExec);
RegisterHook("read", m_HookCPURead);
RegisterHook("write", m_HookCPUWrite);
RegisterHook("onopcode", m_HookCPUExecOpcode);
RegisterHook("draw", m_HookFrameDrawn);
HMODULE hInst = GetModuleHandle(NULL);

View File

@ -54,6 +54,7 @@ private:
CScriptHook* m_HookCPUExec;
CScriptHook* m_HookCPURead;
CScriptHook* m_HookCPUWrite;
CScriptHook* m_HookCPUExecOpcode;
CScriptHook* m_HookFrameDrawn;
@ -120,6 +121,11 @@ public:
return m_HookCPUWrite;
}
CScriptHook* HookCPUExecOpcode()
{
return m_HookCPUExecOpcode;
}
CScriptHook* HookFrameDrawn()
{
return m_HookFrameDrawn;

View File

@ -75,7 +75,7 @@ span.tag {
color: #885;
font-weight: bold;
padding: 0px 2px;
font-size: 12px;
font-size: 11px;
}
span.tag2 {
float: right;
@ -86,7 +86,7 @@ span.tag2 {
color: #588;
font-weight: bold;
padding: 0px 2px;
font-size: 12px;
font-size: 11px;
}
</style>
</head>
@ -213,7 +213,7 @@ Player.prototype.move = function(x, y, z)
Player.prototype.heal = function()
{
this.health = 100;
this.health = 100
}
var player = new Player(0x8033B1AC)
@ -266,10 +266,10 @@ console.log('Internal ROM name: ' + romName)</div>
</div>-->
<div class="property">
<span class="tag2">emulation thread</span>
<span class="tag">interpreter mode</span>
<span class="tag">interpreter mode only</span>
<div class="propertyname">events.onexec(address, 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.
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>.
<span class="snip">callback</span> receives the program counter address at which the event is fired.
<pre class="example">
@ -279,60 +279,106 @@ events.onexec(0x802CB1C0, function()
})
</pre>
<pre class="example">
events.onexec(ADDR_ANY, function(addr))
events.onexec(ADDR_ANY, function(pc))
{
// Log every step!
console.log('CPU is executing ' + addr.hex())
console.log('CPU is executing 0x' + pc.hex())
})
</pre>
</div>
</div>
<div class="property">
<span class="tag2">emulation thread</span>
<span class="tag">interpreter mode</span>
<span class="tag">interpreter mode only</span>
<div class="propertyname">events.onread(address, callback)</div>
<div class="propertydesc">
Adds a CPU read callback for a virtual address or <a href="AddressRange">AddressRange</a> and returns a callback ID.
Adds a CPU read 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 current instruction is going to read from <span class="snip">address</span>.
<span class="snip">callback</span> receives the virtual address that the CPU is going to read.
<pre class="example">
events.onread(0x8033B1B0, function()
{
console.log('CPU is reading 8033B1B0')
console.log('CPU is reading 0x8033B1B0')
})
</pre>
<pre class="example">
const addr_range_rom = {start: 0xB0000000, end: 0xB6000000}
events.onread(addr_range_rom, function(addr)
events.onread(ADDR_ANY_CART_ROM_UNC, function(addr)
{
console.log('CPU is reading ROM ' + addr)
console.log('CPU is reading ROM 0x' + addr.hex())
})
</pre>
</div>
</div>
<div class="property">
<span class="tag2">emulation thread</span>
<span class="tag">interpreter mode</span>
<span class="tag">interpreter mode only</span>
<div class="propertyname">events.onwrite(address, callback)</div>
<div class="propertydesc">
Adds a CPU write callback for a virtual address or <a href="AddressRange">AddressRange</a> and returns a callback ID.
Adds a CPU write 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 current instruction is going to write to <span class="snip">address</span>.
<span class="snip">callback</span> receives the virtual address that the CPU is going to write to.
<pre class="example">
events.onwrite(0x8033B1B0, function()
{
console.log('CPU is modifying 8033B1B0')
console.log('CPU is modifying 0x8033B1B0')
})
</pre>
<pre class="example">
events.onwrite({0xB0000000, 0x90000000}, function(addr)
events.onwrite(ADDR_ANY_CART_ROM_UNC, function(addr)
{
console.log(gpr.pc.hex() + ': wrote to cartridge ' + addr.hex());
console.log(gpr.pc.hex() + ': wrote to cartridge ' + addr.hex())
})
</pre>
</div>
</div>
<div class="property">
<span class="tag2">emulation thread</span>
<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.
<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">
const JR_RA = 0x03E00008 // 'jr ra' command
events.onopcode(ADDR_ANY, JR_RA, function(pc)
{
console.log(pc.hex()) // log pc at every 'jr ra'
})
</pre>
</div>
</div>
<div class="property">
<span class="tag2">emulation thread</span>
<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.
<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">
const ADDIU_SP_SP = 0x27BD0000 // 'addiu sp, sp, 0x0000'
const NO_IMM16 = 0xFFFF0000 // mask off immediate field
events.onopcode(ADDR_ANY, ADDIU_SP_SP, NO_IMM16, function(pc)
{
// log pc at every 'addiu sp, sp, x' regardless of the immediate value
console.log(pc.hex())
})
</pre>
<pre class="example">
const JAL = 0x0C000000 // 'jal 0x00000000'
const NO_TARGET = 0xFC000000 // mask off target field
events.onopcode(ADDR_ANY, JAL, NO_TARGET, function(pc)
{
// log pc at every 'jal' regardless of the target address
console.log(pc.hex())
})
</pre>
</div>
</div>
<div class="property">
<span class="tag2">emulation thread</span>
<div class="propertyname">events.ondraw(callback)</div>
@ -468,7 +514,7 @@ console.log('size: ' + stats.size)
<div class="property">
<div class="propertyname">fs.stat(path)</div>
<div class="propertydesc">
Returns an <a href="#fs.Stats">fs.Stats</a> object describing the file or directory pointed to by <span class="snip">path</span>. Returns <span class="snip">false</span> if the operation failed..
Returns an <a href="#fs.Stats">fs.Stats</a> object describing the file or directory pointed to by <span class="snip">path</span>. Returns <span class="snip">false</span> if the operation failed.
</div>
</div>
<div class="property">