827 lines
25 KiB
C++
827 lines
25 KiB
C++
#include "stdafx.h"
|
|
|
|
#include "../OpInfo.h"
|
|
#include "ScriptAPI.h"
|
|
|
|
#pragma warning(disable : 4702) // disable unreachable code warning
|
|
|
|
using namespace ScriptAPI;
|
|
|
|
static bool CbCond_PcBetween(JSAppCallback * cb, void * env);
|
|
static bool CbCond_ReadAddrBetween(JSAppCallback * cb, void * env);
|
|
static bool CbCond_WriteAddrBetween(JSAppCallback * cb, void * env);
|
|
static bool CbCond_PcBetween_OpcodeEquals(JSAppCallback * cb, void * env);
|
|
static bool CbCond_PcBetween_GprValueEquals(JSAppCallback * cb, void * env);
|
|
|
|
static duk_idx_t CbArgs_GenericEventObject(duk_context * ctx, void * env);
|
|
static duk_idx_t CbArgs_EmuStateChangeEventObject(duk_context * ctx, void * env);
|
|
static duk_idx_t CbArgs_ExecEventObject(duk_context * ctx, void * env);
|
|
static duk_idx_t CbArgs_ReadEventObject(duk_context * ctx, void * env);
|
|
static duk_idx_t CbArgs_WriteEventObject(duk_context * ctx, void * env);
|
|
static duk_idx_t CbArgs_OpcodeEventObject(duk_context * ctx, void * env);
|
|
static duk_idx_t CbArgs_RegValueEventObject(duk_context * ctx, void * env);
|
|
static duk_idx_t CbArgs_MouseEventObject(duk_context * ctx, void * env);
|
|
static duk_idx_t CbArgs_SPTaskEventObject(duk_context * ctx, void * env);
|
|
static duk_idx_t CbArgs_PIEventObject(duk_context * ctx, void * env);
|
|
|
|
static duk_ret_t RequireAddressOrAddressRange(duk_context * ctx, duk_idx_t idx, uint32_t * addrStart, uint32_t * addrEnd);
|
|
static duk_ret_t RequireInterpreterCPU(duk_context * ctx);
|
|
|
|
void ScriptAPI::Define_events(duk_context * ctx)
|
|
{
|
|
const DukPropListEntry props[] = {
|
|
{"onstatechange", DukCFunction(js_events_onstatechange)},
|
|
{"onexec", DukCFunction(js_events_onexec)},
|
|
{"onread", DukCFunction(js_events_onread)},
|
|
{"onwrite", DukCFunction(js_events_onwrite)},
|
|
{"ongprvalue", DukCFunction(js_events_ongprvalue)},
|
|
{"onopcode", DukCFunction(js_events_onopcode)},
|
|
{"onpifread", DukCFunction(js_events_onpifread)},
|
|
{"onsptask", DukCFunction(js_events_onsptask)},
|
|
{"onpidma", DukCFunction(js_events_onpidma)},
|
|
{"onmouseup", DukCFunction(js_events_onmouseup)},
|
|
{"onmousedown", DukCFunction(js_events_onmousedown)},
|
|
{"onmousemove", DukCFunction(js_events_onmousemove)},
|
|
{"remove", DukCFunction(js_events_remove)},
|
|
{nullptr},
|
|
};
|
|
|
|
DefineGlobalInterface(ctx, "events", props);
|
|
|
|
DefineGlobalClass(ctx, "GenericEvent", js_DummyConstructor);
|
|
DefineGlobalClass(ctx, "EmuStateChangeEvent", js_DummyConstructor);
|
|
DefineGlobalClass(ctx, "CPUExecEvent", js_DummyConstructor);
|
|
DefineGlobalClass(ctx, "CPUReadWriteEvent", js_DummyConstructor);
|
|
DefineGlobalClass(ctx, "CPUOpcodeEvent", js_DummyConstructor);
|
|
DefineGlobalClass(ctx, "CPURegValueEvent", js_DummyConstructor);
|
|
DefineGlobalClass(ctx, "SPTaskEvent", js_DummyConstructor);
|
|
DefineGlobalClass(ctx, "PIEvent", js_DummyConstructor);
|
|
DefineGlobalClass(ctx, "DrawEvent", js_DummyConstructor);
|
|
|
|
const DukPropListEntry mouseEventStaticProps[] = {
|
|
{"NONE", DukNumber(-1)},
|
|
{"LEFT", DukNumber(0)},
|
|
{"MIDDLE", DukNumber(1)},
|
|
{"RIGHT", DukNumber(2)},
|
|
{nullptr},
|
|
};
|
|
|
|
DefineGlobalClass(ctx, "MouseEvent", js_DummyConstructor, nullptr, mouseEventStaticProps);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_onstatechange(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Function});
|
|
|
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_EMUSTATECHANGE,
|
|
CbArgs_EmuStateChangeEventObject);
|
|
duk_push_uint(ctx, (int)((UINT_PTR)callbackId));
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_onexec(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Any, Arg_Function});
|
|
|
|
uint32_t addrStart, addrEnd;
|
|
RequireAddressOrAddressRange(ctx, 0, &addrStart, &addrEnd);
|
|
|
|
RequireInterpreterCPU(ctx);
|
|
|
|
JSAppCallback cb(GetInstance(ctx), duk_get_heapptr(ctx, 1),
|
|
CbCond_PcBetween, CbArgs_ExecEventObject);
|
|
cb.m_Params.addrStart = addrStart;
|
|
cb.m_Params.addrEnd = addrEnd;
|
|
|
|
JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPU_EXEC, cb);
|
|
|
|
duk_push_uint(ctx, (int)((UINT_PTR)callbackId));
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_onread(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Any, Arg_Function});
|
|
|
|
uint32_t addrStart, addrEnd;
|
|
RequireAddressOrAddressRange(ctx, 0, &addrStart, &addrEnd);
|
|
|
|
RequireInterpreterCPU(ctx);
|
|
|
|
JSAppCallback cb(GetInstance(ctx), duk_get_heapptr(ctx, 1),
|
|
CbCond_ReadAddrBetween, CbArgs_ReadEventObject);
|
|
cb.m_Params.addrStart = addrStart;
|
|
cb.m_Params.addrEnd = addrEnd;
|
|
|
|
JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPU_READ, cb);
|
|
|
|
duk_push_uint(ctx, (int)((UINT_PTR)callbackId));
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_onwrite(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Any, Arg_Function});
|
|
|
|
uint32_t addrStart, addrEnd;
|
|
RequireAddressOrAddressRange(ctx, 0, &addrStart, &addrEnd);
|
|
|
|
RequireInterpreterCPU(ctx);
|
|
|
|
JSAppCallback cb(GetInstance(ctx), duk_get_heapptr(ctx, 1),
|
|
CbCond_WriteAddrBetween, CbArgs_WriteEventObject);
|
|
cb.m_Params.addrStart = addrStart;
|
|
cb.m_Params.addrEnd = addrEnd;
|
|
|
|
JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPU_WRITE, cb);
|
|
|
|
duk_push_uint(ctx, (int)((UINT_PTR)callbackId));
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_onopcode(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Any, Arg_Number, Arg_Number, Arg_Function});
|
|
|
|
uint32_t addrStart, addrEnd;
|
|
RequireAddressOrAddressRange(ctx, 0, &addrStart, &addrEnd);
|
|
|
|
RequireInterpreterCPU(ctx);
|
|
|
|
uint32_t opcode = duk_get_uint(ctx, 1);
|
|
uint32_t mask = duk_get_uint(ctx, 2);
|
|
|
|
JSAppCallback cb(GetInstance(ctx), duk_get_heapptr(ctx, 3),
|
|
CbCond_PcBetween_OpcodeEquals, CbArgs_OpcodeEventObject);
|
|
cb.m_Params.addrStart = addrStart;
|
|
cb.m_Params.addrEnd = addrEnd;
|
|
cb.m_Params.opcode = opcode;
|
|
cb.m_Params.opcodeMask = mask;
|
|
|
|
JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPU_EXEC, cb);
|
|
|
|
duk_push_uint(ctx, (int)((UINT_PTR)callbackId));
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_ongprvalue(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Any, Arg_Number, Arg_Number, Arg_Function});
|
|
|
|
uint32_t addrStart, addrEnd;
|
|
RequireAddressOrAddressRange(ctx, 0, &addrStart, &addrEnd);
|
|
|
|
RequireInterpreterCPU(ctx);
|
|
|
|
JSAppCallback cb(GetInstance(ctx), duk_get_heapptr(ctx, 3),
|
|
CbCond_PcBetween_GprValueEquals, CbArgs_RegValueEventObject);
|
|
cb.m_Params.addrStart = addrStart;
|
|
cb.m_Params.addrEnd = addrEnd;
|
|
cb.m_Params.regIndices = duk_get_uint(ctx, 1);
|
|
cb.m_Params.regValue = duk_get_uint(ctx, 2);
|
|
|
|
JSAppCallbackID callbackId = AddAppCallback(ctx, JS_HOOK_CPU_EXEC, cb);
|
|
|
|
duk_push_uint(ctx, (int)((UINT_PTR)callbackId));
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_onpifread(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Function});
|
|
|
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_PIFREAD, CbArgs_GenericEventObject);
|
|
duk_push_uint(ctx, (int)((UINT_PTR)callbackId));
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_onsptask(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Function});
|
|
|
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_RSPTASK, CbArgs_SPTaskEventObject);
|
|
duk_push_uint(ctx, (int)((UINT_PTR)callbackId));
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_onpidma(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Function});
|
|
|
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_PIDMA, CbArgs_PIEventObject);
|
|
duk_push_uint(ctx, (int)((UINT_PTR)callbackId));
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_onmouseup(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Function});
|
|
|
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_MOUSEUP, CbArgs_MouseEventObject);
|
|
duk_push_uint(ctx, (int)((UINT_PTR)callbackId));
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_onmousedown(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Function});
|
|
|
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_MOUSEDOWN, CbArgs_MouseEventObject);
|
|
duk_push_uint(ctx, (int)((UINT_PTR)callbackId));
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_onmousemove(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Function});
|
|
|
|
JSAppCallbackID callbackId = AddAppCallback(ctx, 0, JS_HOOK_MOUSEMOVE, CbArgs_MouseEventObject);
|
|
duk_push_uint(ctx, (int)((UINT_PTR)callbackId));
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_events_remove(duk_context * ctx)
|
|
{
|
|
CheckArgs(ctx, {Arg_Number});
|
|
|
|
JSAppCallbackID callbackId = (JSAppCallbackID)duk_get_uint(ctx, 0);
|
|
|
|
if (!RemoveAppCallback(ctx, callbackId))
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_REFERENCE_ERROR, "invalid callback ID");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool CbCond_ReadAddrBetween(JSAppCallback * cb, void * _env)
|
|
{
|
|
JSHookCpuStepEnv * env = (JSHookCpuStepEnv *)_env;
|
|
|
|
if (!env->opInfo.IsLoadCommand())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint32_t addr = env->opInfo.GetLoadStoreAddress();
|
|
|
|
return (addr >= cb->m_Params.addrStart && addr <= cb->m_Params.addrEnd);
|
|
}
|
|
|
|
bool CbCond_WriteAddrBetween(JSAppCallback * cb, void * _env)
|
|
{
|
|
JSHookCpuStepEnv * env = (JSHookCpuStepEnv *)_env;
|
|
|
|
if (!env->opInfo.IsStoreCommand())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint32_t addr = env->opInfo.GetLoadStoreAddress();
|
|
|
|
return (addr >= cb->m_Params.addrStart && addr <= cb->m_Params.addrEnd);
|
|
}
|
|
|
|
bool CbCond_PcBetween(JSAppCallback * cb, void * _env)
|
|
{
|
|
JSHookCpuStepEnv * env = (JSHookCpuStepEnv *)_env;
|
|
return (env->pc >= cb->m_Params.addrStart && env->pc <= cb->m_Params.addrEnd);
|
|
}
|
|
|
|
bool CbCond_PcBetween_OpcodeEquals(JSAppCallback * cb, void * _env)
|
|
{
|
|
if (!CbCond_PcBetween(cb, _env))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
JSHookCpuStepEnv * env = (JSHookCpuStepEnv *)_env;
|
|
return cb->m_Params.opcode == (env->opInfo.m_OpCode.Value & cb->m_Params.opcodeMask);
|
|
}
|
|
|
|
static bool CbCond_PcBetween_GprValueEquals(JSAppCallback * cb, void * _env)
|
|
{
|
|
if (!CbCond_PcBetween(cb, _env))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
JSHookCpuStepEnv * env = (JSHookCpuStepEnv *)_env;
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
if (cb->m_Params.regIndices & (1 << i))
|
|
{
|
|
if (g_Reg->m_GPR[i].UW[0] == cb->m_Params.regValue)
|
|
{
|
|
env->outAffectedRegIndex = i;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
duk_idx_t CbArgs_EmuStateChangeEventObject(duk_context * ctx, void * _env)
|
|
{
|
|
CScriptInstance * inst = GetInstance(ctx);
|
|
JSHookEmuStateChangeEnv * env = (JSHookEmuStateChangeEnv *)_env;
|
|
duk_push_object(ctx);
|
|
SetDummyConstructor(ctx, -1, "EmuStateChangeEvent");
|
|
|
|
const DukPropListEntry props[] = {
|
|
{"callbackId", DukUInt((int)((UINT_PTR)inst->CallbackId()))},
|
|
{"state", DukUInt(env->state)},
|
|
{nullptr},
|
|
};
|
|
|
|
DukPutPropList(ctx, -1, props);
|
|
duk_freeze(ctx, -1);
|
|
return 1;
|
|
}
|
|
|
|
duk_idx_t CbArgs_GenericEventObject(duk_context * ctx, void * /*_env*/)
|
|
{
|
|
CScriptInstance * inst = GetInstance(ctx);
|
|
duk_push_object(ctx);
|
|
SetDummyConstructor(ctx, -1, "GenericEvent");
|
|
|
|
const DukPropListEntry props[] = {
|
|
{"callbackId", DukUInt((int)((UINT_PTR)inst->CallbackId()))},
|
|
{nullptr},
|
|
};
|
|
|
|
DukPutPropList(ctx, -1, props);
|
|
duk_freeze(ctx, -1);
|
|
return 1;
|
|
}
|
|
|
|
duk_idx_t CbArgs_ExecEventObject(duk_context * ctx, void * _env)
|
|
{
|
|
CScriptInstance * inst = GetInstance(ctx);
|
|
JSHookCpuStepEnv * env = (JSHookCpuStepEnv *)_env;
|
|
duk_push_object(ctx);
|
|
SetDummyConstructor(ctx, -1, "CPUExecEvent");
|
|
|
|
const DukPropListEntry props[] = {
|
|
{"callbackId", DukUInt((int)((UINT_PTR)inst->CallbackId()))},
|
|
{"pc", DukUInt(env->pc)},
|
|
{nullptr},
|
|
};
|
|
|
|
DukPutPropList(ctx, -1, props);
|
|
duk_freeze(ctx, -1);
|
|
return 1;
|
|
}
|
|
|
|
duk_idx_t CbArgs_ReadEventObject(duk_context * ctx, void * _env)
|
|
{
|
|
CScriptInstance * inst = GetInstance(ctx);
|
|
CDebuggerUI * debugger = inst->Debugger();
|
|
JSHookCpuStepEnv * env = (JSHookCpuStepEnv *)_env;
|
|
|
|
uint32_t address = env->opInfo.GetLoadStoreAddress();
|
|
|
|
uint8_t op = env->opInfo.m_OpCode.op;
|
|
uint8_t rt = env->opInfo.m_OpCode.rt;
|
|
bool bFPU = (op == R4300i_LWC1 || op == R4300i_LDC1);
|
|
|
|
duk_push_object(ctx);
|
|
SetDummyConstructor(ctx, -1, "CPUReadWriteEvent");
|
|
|
|
const DukPropListEntry props[] = {
|
|
{"callbackId", DukUInt((int)((UINT_PTR)inst->CallbackId()))},
|
|
{"pc", DukUInt(env->pc)},
|
|
{"address", DukUInt(address)},
|
|
{"reg", DukUInt(rt)},
|
|
{"fpu", DukBoolean(bFPU)},
|
|
{nullptr},
|
|
};
|
|
|
|
DukPutPropList(ctx, -1, props);
|
|
|
|
union
|
|
{
|
|
uint8_t u8;
|
|
int8_t s8;
|
|
uint16_t u16;
|
|
int16_t s16;
|
|
uint32_t u32;
|
|
int32_t s32;
|
|
float f32;
|
|
double f64;
|
|
uint64_t u64;
|
|
} value = {0};
|
|
|
|
bool bNeedUpper32 = false;
|
|
|
|
switch (env->opInfo.m_OpCode.op)
|
|
{
|
|
case R4300i_LB:
|
|
debugger->DebugLoad_VAddr(address, value.s8);
|
|
duk_push_int(ctx, value.s8);
|
|
duk_push_int(ctx, S8);
|
|
break;
|
|
case R4300i_LBU:
|
|
debugger->DebugLoad_VAddr(address, value.u8);
|
|
duk_push_uint(ctx, value.u8);
|
|
duk_push_int(ctx, U8);
|
|
break;
|
|
case R4300i_LH:
|
|
debugger->DebugLoad_VAddr(address, value.s16);
|
|
duk_push_int(ctx, value.s16);
|
|
duk_push_int(ctx, S16);
|
|
break;
|
|
case R4300i_LHU:
|
|
debugger->DebugLoad_VAddr(address, value.u16);
|
|
duk_push_uint(ctx, value.u16);
|
|
duk_push_int(ctx, U16);
|
|
break;
|
|
case R4300i_LL:
|
|
case R4300i_LW:
|
|
debugger->DebugLoad_VAddr(address, value.s32);
|
|
duk_push_int(ctx, value.s32);
|
|
duk_push_int(ctx, S32);
|
|
break;
|
|
case R4300i_LWU:
|
|
debugger->DebugLoad_VAddr(address, value.u32);
|
|
duk_push_uint(ctx, value.u32);
|
|
duk_push_int(ctx, U32);
|
|
break;
|
|
case R4300i_LWC1:
|
|
debugger->DebugLoad_VAddr(address, value.f32);
|
|
duk_push_number(ctx, value.f32);
|
|
duk_push_int(ctx, F32);
|
|
break;
|
|
case R4300i_LDC1:
|
|
debugger->DebugLoad_VAddr(address, value.f64);
|
|
duk_push_number(ctx, value.f64);
|
|
duk_push_int(ctx, F64);
|
|
break;
|
|
case R4300i_LD:
|
|
debugger->DebugLoad_VAddr(address, value.u64);
|
|
duk_push_number(ctx, (duk_double_t)(value.u64 & 0xFFFFFFFF));
|
|
duk_push_int(ctx, U64);
|
|
bNeedUpper32 = true;
|
|
break;
|
|
case R4300i_LDL:
|
|
{
|
|
int shift = (address & 7) * 8;
|
|
uint64_t mask = ~(((uint64_t)-1) << shift);
|
|
debugger->DebugLoad_VAddr(address & ~7, value.u64);
|
|
value.u64 = (g_Reg->m_GPR[rt].DW & mask) + (value.u64 << shift);
|
|
duk_push_number(ctx, (duk_double_t)(value.u64 & 0xFFFFFFFF));
|
|
duk_push_int(ctx, U64);
|
|
bNeedUpper32 = true;
|
|
break;
|
|
}
|
|
case R4300i_LDR:
|
|
{
|
|
int shift = 56 - ((address & 7) * 8);
|
|
uint64_t mask = ~(((uint64_t)-1) >> shift);
|
|
debugger->DebugLoad_VAddr(address & ~7, value.u64);
|
|
value.u64 = (g_Reg->m_GPR[rt].DW & mask) + (value.u64 >> shift);
|
|
duk_push_number(ctx, (duk_double_t)(value.u64 & 0xFFFFFFFF));
|
|
duk_push_int(ctx, U64);
|
|
bNeedUpper32 = true;
|
|
break;
|
|
}
|
|
case R4300i_LWL:
|
|
{
|
|
int shift = (address & 3) * 8;
|
|
uint32_t mask = ~(((uint32_t)-1) << shift);
|
|
debugger->DebugLoad_VAddr(address & ~3, value.s32);
|
|
value.s32 = (g_Reg->m_GPR[rt].W[0] & mask) + (value.s32 << shift);
|
|
duk_push_number(ctx, value.s32);
|
|
duk_push_int(ctx, S32);
|
|
break;
|
|
}
|
|
case R4300i_LWR:
|
|
{
|
|
int shift = 24 - ((address & 3) * 8);
|
|
uint32_t mask = ~(((uint32_t)-1) >> shift);
|
|
debugger->DebugLoad_VAddr(address & ~3, value.s32);
|
|
value.s32 = (g_Reg->m_GPR[rt].W[0] & mask) + (value.s32 >> shift);
|
|
duk_push_number(ctx, value.s32);
|
|
duk_push_int(ctx, S32);
|
|
break;
|
|
}
|
|
default:
|
|
duk_push_number(ctx, 0);
|
|
duk_push_number(ctx, 0);
|
|
break;
|
|
}
|
|
|
|
duk_put_prop_string(ctx, -3, "valueType");
|
|
duk_put_prop_string(ctx, -2, "value");
|
|
|
|
if (bNeedUpper32)
|
|
{
|
|
duk_push_number(ctx, (duk_double_t)(value.u64 >> 32));
|
|
duk_put_prop_string(ctx, -2, "valueHi");
|
|
}
|
|
|
|
duk_freeze(ctx, -1);
|
|
return 1;
|
|
}
|
|
|
|
duk_idx_t CbArgs_WriteEventObject(duk_context * ctx, void * _env)
|
|
{
|
|
CScriptInstance * inst = GetInstance(ctx);
|
|
CDebuggerUI * debugger = inst->Debugger();
|
|
JSHookCpuStepEnv * env = (JSHookCpuStepEnv *)_env;
|
|
|
|
uint32_t address = env->opInfo.GetLoadStoreAddress();
|
|
|
|
uint8_t op = env->opInfo.m_OpCode.op;
|
|
uint8_t rt = env->opInfo.m_OpCode.rt;
|
|
bool bFPU = (op == R4300i_SWC1 || op == R4300i_SDC1);
|
|
|
|
duk_push_object(ctx);
|
|
SetDummyConstructor(ctx, -1, "CPUReadWriteEvent");
|
|
|
|
const DukPropListEntry props[] = {
|
|
{"callbackId", DukUInt((int)((UINT_PTR)inst->CallbackId()))},
|
|
{"pc", DukUInt(env->pc)},
|
|
{"address", DukUInt(address)},
|
|
{"reg", DukUInt(rt)},
|
|
{"fpu", DukBoolean(bFPU)},
|
|
{nullptr},
|
|
};
|
|
|
|
DukPutPropList(ctx, -1, props);
|
|
|
|
bool bNeedUpper32 = false;
|
|
uint64_t value64 = 0;
|
|
|
|
switch (env->opInfo.m_OpCode.op)
|
|
{
|
|
case R4300i_SB:
|
|
duk_push_int(ctx, g_Reg->m_GPR[rt].B[0]);
|
|
duk_push_int(ctx, S8);
|
|
break;
|
|
case R4300i_SH:
|
|
duk_push_int(ctx, g_Reg->m_GPR[rt].HW[0]);
|
|
duk_push_int(ctx, S16);
|
|
break;
|
|
case R4300i_SW:
|
|
duk_push_int(ctx, g_Reg->m_GPR[rt].W[0]);
|
|
duk_push_int(ctx, S32);
|
|
break;
|
|
case R4300i_SWC1:
|
|
duk_push_number(ctx, *g_Reg->m_FPR_S[rt]);
|
|
duk_push_int(ctx, F32);
|
|
break;
|
|
case R4300i_SDC1:
|
|
duk_push_number(ctx, *g_Reg->m_FPR_D[rt]);
|
|
duk_push_int(ctx, F64);
|
|
break;
|
|
case R4300i_SD:
|
|
duk_push_number(ctx, g_Reg->m_GPR[rt].UW[0]);
|
|
duk_push_int(ctx, U64);
|
|
bNeedUpper32 = true;
|
|
break;
|
|
case R4300i_SWL:
|
|
{
|
|
int shift = (address & 3) * 8;
|
|
uint32_t mask = ~(((uint32_t)-1) >> shift);
|
|
uint32_t value;
|
|
debugger->DebugLoad_VAddr(address & ~3, value);
|
|
value = (value & mask) + (g_Reg->m_GPR[rt].UW[0] >> shift);
|
|
duk_push_number(ctx, value);
|
|
duk_push_int(ctx, S32);
|
|
break;
|
|
}
|
|
case R4300i_SWR:
|
|
{
|
|
int shift = 24 - ((address & 3) * 8);
|
|
uint32_t mask = ~(((uint32_t)-1) << shift);
|
|
uint32_t value;
|
|
debugger->DebugLoad_VAddr(address & ~3, value);
|
|
value = (value & mask) + (g_Reg->m_GPR[rt].UW[0] >> shift);
|
|
duk_push_number(ctx, value);
|
|
duk_push_int(ctx, S32);
|
|
break;
|
|
}
|
|
case R4300i_SDL:
|
|
{
|
|
int shift = (address & 7) * 8;
|
|
uint64_t mask = ~(((uint64_t)-1) >> shift);
|
|
debugger->DebugLoad_VAddr(address & ~7, value64);
|
|
value64 = (value64 & mask) + (g_Reg->m_GPR[rt].UDW >> shift);
|
|
duk_push_number(ctx, (duk_double_t)(value64 & 0xFFFFFFFF));
|
|
duk_push_int(ctx, U64);
|
|
break;
|
|
}
|
|
case R4300i_SDR:
|
|
{
|
|
int shift = 56 - ((address & 7) * 8);
|
|
uint64_t mask = ~(((uint64_t)-1) << shift);
|
|
debugger->DebugLoad_VAddr(address & ~7, value64);
|
|
value64 = (value64 & mask) + (g_Reg->m_GPR[rt].UDW >> shift);
|
|
duk_push_number(ctx, (duk_double_t)(value64 & 0xFFFFFFFF));
|
|
duk_push_int(ctx, U64);
|
|
break;
|
|
}
|
|
default:
|
|
duk_push_number(ctx, 0);
|
|
duk_push_number(ctx, 0);
|
|
break;
|
|
}
|
|
|
|
duk_put_prop_string(ctx, -3, "valueType");
|
|
duk_put_prop_string(ctx, -2, "value");
|
|
|
|
if (bNeedUpper32)
|
|
{
|
|
duk_push_number(ctx, (duk_double_t)(value64 >> 32));
|
|
duk_put_prop_string(ctx, -2, "valueHi");
|
|
}
|
|
|
|
duk_freeze(ctx, -1);
|
|
|
|
return 1;
|
|
}
|
|
|
|
duk_idx_t CbArgs_OpcodeEventObject(duk_context * ctx, void * _env)
|
|
{
|
|
CScriptInstance * inst = GetInstance(ctx);
|
|
JSHookCpuStepEnv * env = (JSHookCpuStepEnv *)_env;
|
|
duk_push_object(ctx);
|
|
SetDummyConstructor(ctx, -1, "CPUOpcodeEvent");
|
|
|
|
const DukPropListEntry props[] = {
|
|
{"callbackId", DukUInt((int)((UINT_PTR)inst->CallbackId()))},
|
|
{"pc", DukUInt(env->pc)},
|
|
{"opcode", DukUInt(env->opInfo.m_OpCode.Value)},
|
|
{nullptr},
|
|
};
|
|
|
|
DukPutPropList(ctx, -1, props);
|
|
duk_freeze(ctx, -1);
|
|
return 1;
|
|
}
|
|
|
|
duk_idx_t CbArgs_RegValueEventObject(duk_context * ctx, void * _env)
|
|
{
|
|
CScriptInstance * inst = GetInstance(ctx);
|
|
JSHookCpuStepEnv * env = (JSHookCpuStepEnv *)_env;
|
|
duk_push_object(ctx);
|
|
SetDummyConstructor(ctx, -1, "CPURegValueEvent");
|
|
|
|
const DukPropListEntry props[] = {
|
|
{"callbackId", DukUInt((int)((UINT_PTR)inst->CallbackId()))},
|
|
{"pc", DukUInt(env->pc)},
|
|
{"value", DukUInt(g_Reg->m_GPR[env->outAffectedRegIndex].UW[0])},
|
|
{"reg", DukUInt(env->outAffectedRegIndex)},
|
|
{nullptr},
|
|
};
|
|
|
|
DukPutPropList(ctx, -1, props);
|
|
duk_freeze(ctx, -1);
|
|
return 1;
|
|
}
|
|
|
|
static duk_idx_t CbArgs_MouseEventObject(duk_context * ctx, void * _env)
|
|
{
|
|
CScriptInstance * inst = GetInstance(ctx);
|
|
JSHookMouseEnv * env = (JSHookMouseEnv *)_env;
|
|
duk_push_object(ctx);
|
|
SetDummyConstructor(ctx, -1, "MouseEvent");
|
|
|
|
const DukPropListEntry props[] = {
|
|
{"callbackId", DukUInt((int)((UINT_PTR)inst->CallbackId()))},
|
|
{"button", DukInt(env->button)},
|
|
{"x", DukInt(env->x)},
|
|
{"y", DukInt(env->y)},
|
|
{nullptr},
|
|
};
|
|
|
|
DukPutPropList(ctx, -1, props);
|
|
duk_freeze(ctx, -1);
|
|
return 1;
|
|
}
|
|
|
|
static duk_idx_t CbArgs_SPTaskEventObject(duk_context * ctx, void * _env)
|
|
{
|
|
CScriptInstance * inst = GetInstance(ctx);
|
|
JSHookSpTaskEnv * env = (JSHookSpTaskEnv *)_env;
|
|
duk_push_object(ctx);
|
|
SetDummyConstructor(ctx, -1, "SPTaskEvent");
|
|
|
|
const DukPropListEntry props[] = {
|
|
{"callbackId", DukUInt((int)((UINT_PTR)inst->CallbackId()))},
|
|
{"taskType", DukUInt(env->taskType)},
|
|
{"taskFlags", DukUInt(env->taskFlags)},
|
|
{"ucodeBootAddress", DukUInt(env->ucodeBootAddress | 0x80000000)},
|
|
{"ucodeBootSize", DukUInt(env->ucodeBootSize)},
|
|
{"ucodeAddress", DukUInt(env->ucodeAddress | 0x80000000)},
|
|
{"ucodeSize", DukUInt(env->ucodeSize)},
|
|
{"ucodeDataAddress", DukUInt(env->ucodeDataAddress | 0x80000000)},
|
|
{"ucodeDataSize", DukUInt(env->ucodeDataSize)},
|
|
{"dramStackAddress", DukUInt(env->dramStackAddress | 0x80000000)},
|
|
{"dramStackSize", DukUInt(env->dramStackSize)},
|
|
{"outputBuffAddress", DukUInt(env->outputBuffAddress | 0x80000000)},
|
|
{"outputBuffSize", DukUInt(env->outputBuffSize)},
|
|
{"dataAddress", DukUInt(env->dataAddress | 0x80000000)},
|
|
{"dataSize", DukUInt(env->dataSize)},
|
|
{"yieldDataAddress", DukUInt(env->yieldDataAddress | 0x80000000)},
|
|
{"yieldDataSize", DukUInt(env->yieldDataSize)},
|
|
{nullptr},
|
|
};
|
|
|
|
DukPutPropList(ctx, -1, props);
|
|
duk_freeze(ctx, -1);
|
|
return 1;
|
|
}
|
|
|
|
static duk_idx_t CbArgs_PIEventObject(duk_context * ctx, void * _env)
|
|
{
|
|
CScriptInstance * inst = GetInstance(ctx);
|
|
JSHookPiDmaEnv * env = (JSHookPiDmaEnv *)_env;
|
|
duk_push_object(ctx);
|
|
SetDummyConstructor(ctx, -1, "PIEvent");
|
|
|
|
const DukPropListEntry props[] = {
|
|
{"callbackId", DukUInt((int)((UINT_PTR)inst->CallbackId()))},
|
|
{"direction", DukUInt(env->direction)},
|
|
{"dramAddress", DukUInt(env->dramAddress | 0x80000000)},
|
|
{"cartAddress", DukUInt(env->cartAddress | 0xA0000000)},
|
|
{"length", DukUInt(env->length + 1)},
|
|
{nullptr},
|
|
};
|
|
|
|
DukPutPropList(ctx, -1, props);
|
|
duk_freeze(ctx, -1);
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t RequireAddressOrAddressRange(duk_context * ctx, duk_idx_t idx, uint32_t * addrStart, uint32_t * addrEnd)
|
|
{
|
|
if (duk_is_number(ctx, idx))
|
|
{
|
|
if (abs(duk_get_number(ctx, idx)) > 0xFFFFFFFF)
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_RANGE_ERROR,
|
|
"address is out of range");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
uint32_t addr = duk_get_uint(ctx, idx);
|
|
*addrStart = addr;
|
|
*addrEnd = addr;
|
|
return 0;
|
|
}
|
|
|
|
if (duk_is_object(ctx, idx))
|
|
{
|
|
if (!duk_has_prop_string(ctx, idx, "start") || !duk_has_prop_string(ctx, idx, "end"))
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_REFERENCE_ERROR,
|
|
"object is missing 'start' or 'end' property");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
duk_get_prop_string(ctx, idx, "start");
|
|
duk_get_prop_string(ctx, idx, "end");
|
|
|
|
if (!duk_is_number(ctx, -2) || !duk_is_number(ctx, -1))
|
|
{
|
|
duk_pop_n(ctx, 2);
|
|
duk_push_error_object(ctx, DUK_ERR_REFERENCE_ERROR,
|
|
"'start' and 'end' properties must be numbers");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
if (abs(duk_get_number(ctx, -2)) > 0xFFFFFFFF || abs(duk_get_number(ctx, -1)) > 0xFFFFFFFF)
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_RANGE_ERROR,
|
|
"'start' or 'end' property out of range");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
*addrStart = duk_get_uint(ctx, -2);
|
|
*addrEnd = duk_get_uint(ctx, -1);
|
|
duk_pop_n(ctx, 2);
|
|
return 0;
|
|
}
|
|
|
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR,
|
|
"argument %d invalid; expected number or object", idx);
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
duk_ret_t RequireInterpreterCPU(duk_context * ctx)
|
|
{
|
|
if (!g_Settings->LoadBool(Setting_ForceInterpreterCPU) && (CPU_TYPE)g_Settings->LoadDword(Game_CpuType) != CPU_Interpreter)
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_ERROR,
|
|
"this feature requires the interpreter core");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
return 0;
|
|
}
|