560 lines
12 KiB
C++
560 lines
12 KiB
C++
#include "stdafx.h"
|
|
|
|
#include "ScriptAPI.h"
|
|
#include <Project64-core/N64System/Mips/Register.h>
|
|
|
|
#pragma warning(disable : 4702) // disable unreachable code warning
|
|
|
|
static duk_ret_t GPRGetImpl(duk_context * ctx, bool bUpper);
|
|
static duk_ret_t GPRSetImpl(duk_context * ctx, bool bUpper);
|
|
static duk_ret_t FPRGetImpl(duk_context * ctx, bool bDouble);
|
|
static duk_ret_t FPRSetImpl(duk_context * ctx, bool bDouble);
|
|
|
|
static int FPRIndex(const char * regName);
|
|
static int GPRIndex(const char * regName);
|
|
static uint32_t * COP0RegPtr(const char * regName);
|
|
static uint32_t * CPURegPtr(const char * regName);
|
|
|
|
static duk_ret_t ThrowRegInvalidError(duk_context * ctx);
|
|
static duk_ret_t ThrowRegContextUnavailableError(duk_context * ctx);
|
|
static duk_ret_t ThrowRegAssignmentTypeError(duk_context * ctx);
|
|
|
|
void ScriptAPI::Define_cpu(duk_context * ctx)
|
|
{
|
|
// todo cleanup
|
|
|
|
#define REG_PROXY_FUNCTIONS(getter, setter) { \
|
|
{ "get", getter, 2 }, \
|
|
{ "set", setter, 3 }, \
|
|
{ nullptr, nullptr, 0 } \
|
|
}
|
|
|
|
const struct
|
|
{
|
|
const char * key;
|
|
const duk_function_list_entry functions[3];
|
|
} proxies[] = {
|
|
{"gpr", REG_PROXY_FUNCTIONS(js_cpu_gpr_get, js_cpu_gpr_set)},
|
|
{"ugpr", REG_PROXY_FUNCTIONS(js_cpu_ugpr_get, js_cpu_ugpr_set)},
|
|
{"fpr", REG_PROXY_FUNCTIONS(js_cpu_fpr_get, js_cpu_fpr_set)},
|
|
{"dfpr", REG_PROXY_FUNCTIONS(js_cpu_dfpr_get, js_cpu_dfpr_set)},
|
|
{"cop0", REG_PROXY_FUNCTIONS(js_cpu_cop0_get, js_cpu_cop0_set)},
|
|
{nullptr, nullptr},
|
|
};
|
|
|
|
const duk_function_list_entry cpufuncs[] = REG_PROXY_FUNCTIONS(js_cpu_get, js_cpu_set);
|
|
|
|
duk_push_global_object(ctx);
|
|
duk_push_object(ctx);
|
|
|
|
for (size_t i = 0; proxies[i].key != nullptr; i++)
|
|
{
|
|
duk_push_string(ctx, proxies[i].key);
|
|
duk_push_object(ctx);
|
|
duk_push_object(ctx);
|
|
duk_put_function_list(ctx, -1, proxies[i].functions);
|
|
duk_push_proxy(ctx, 0);
|
|
duk_freeze(ctx, -1);
|
|
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_ENUMERABLE);
|
|
}
|
|
|
|
duk_push_object(ctx);
|
|
duk_put_function_list(ctx, -1, cpufuncs);
|
|
duk_push_proxy(ctx, 0);
|
|
duk_freeze(ctx, -1);
|
|
|
|
duk_put_prop_string(ctx, -2, "cpu");
|
|
duk_pop(ctx);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_cpu_get(duk_context * ctx)
|
|
{
|
|
if (g_Reg == nullptr)
|
|
{
|
|
return ThrowRegContextUnavailableError(ctx);
|
|
}
|
|
|
|
const char * key = duk_get_string(ctx, 1);
|
|
uint32_t * pReg = CPURegPtr(key);
|
|
|
|
if (pReg == nullptr)
|
|
{
|
|
duk_get_prop_string(ctx, 0, key);
|
|
return 1;
|
|
}
|
|
|
|
duk_push_uint(ctx, *pReg);
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_cpu_set(duk_context * ctx)
|
|
{
|
|
if (g_Reg == nullptr)
|
|
{
|
|
return ThrowRegContextUnavailableError(ctx);
|
|
}
|
|
|
|
uint32_t * pReg = CPURegPtr(duk_get_string(ctx, 1));
|
|
|
|
if (!duk_is_number(ctx, 2) || pReg == nullptr)
|
|
{
|
|
return ThrowRegAssignmentTypeError(ctx);
|
|
}
|
|
|
|
*pReg = duk_get_uint(ctx, 2);
|
|
duk_push_true(ctx);
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_cpu_gpr_get(duk_context * ctx)
|
|
{
|
|
return GPRGetImpl(ctx, false);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_cpu_gpr_set(duk_context * ctx)
|
|
{
|
|
return GPRSetImpl(ctx, false);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_cpu_ugpr_get(duk_context * ctx)
|
|
{
|
|
return GPRGetImpl(ctx, true);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_cpu_ugpr_set(duk_context * ctx)
|
|
{
|
|
return GPRSetImpl(ctx, true);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_cpu_fpr_get(duk_context * ctx)
|
|
{
|
|
return FPRGetImpl(ctx, false);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_cpu_fpr_set(duk_context * ctx)
|
|
{
|
|
return FPRSetImpl(ctx, false);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_cpu_dfpr_get(duk_context * ctx)
|
|
{
|
|
return FPRGetImpl(ctx, true);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_cpu_dfpr_set(duk_context * ctx)
|
|
{
|
|
return FPRSetImpl(ctx, true);
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_cpu_cop0_get(duk_context * ctx)
|
|
{
|
|
if (g_Reg == nullptr)
|
|
{
|
|
return ThrowRegContextUnavailableError(ctx);
|
|
}
|
|
|
|
if (!duk_is_string(ctx, 1))
|
|
{
|
|
return ThrowRegInvalidError(ctx);
|
|
}
|
|
|
|
const char * name = duk_get_string(ctx, 1);
|
|
|
|
if (strcmp(name, "cause") == 0)
|
|
{
|
|
duk_push_uint(ctx, (uint32_t)(g_Reg->FAKE_CAUSE_REGISTER | g_Reg->CAUSE_REGISTER));
|
|
return 1;
|
|
}
|
|
|
|
uint32_t * reg = COP0RegPtr(name);
|
|
|
|
if (reg == nullptr)
|
|
{
|
|
return ThrowRegInvalidError(ctx);
|
|
}
|
|
|
|
duk_push_uint(ctx, *reg);
|
|
return 1;
|
|
}
|
|
|
|
duk_ret_t ScriptAPI::js_cpu_cop0_set(duk_context * ctx)
|
|
{
|
|
if (g_Reg == nullptr)
|
|
{
|
|
return ThrowRegContextUnavailableError(ctx);
|
|
}
|
|
|
|
if (!duk_is_string(ctx, 1))
|
|
{
|
|
return ThrowRegInvalidError(ctx);
|
|
}
|
|
|
|
const char * name = duk_get_string(ctx, 1);
|
|
|
|
if (!duk_is_number(ctx, 2))
|
|
{
|
|
return ThrowRegAssignmentTypeError(ctx);
|
|
}
|
|
|
|
if (strcmp(name, "cause") == 0)
|
|
{
|
|
uint32_t value = duk_get_uint(ctx, 2);
|
|
g_Reg->FAKE_CAUSE_REGISTER = value;
|
|
g_Reg->CAUSE_REGISTER = value;
|
|
g_Reg->CheckInterrupts();
|
|
|
|
duk_push_true(ctx);
|
|
return 1;
|
|
}
|
|
|
|
uint32_t * reg = COP0RegPtr(name);
|
|
|
|
if (reg == nullptr)
|
|
{
|
|
return ThrowRegInvalidError(ctx);
|
|
}
|
|
|
|
*reg = duk_get_uint(ctx, 2);
|
|
duk_push_true(ctx);
|
|
return 1;
|
|
}
|
|
|
|
static duk_ret_t GPRGetImpl(duk_context * ctx, bool bUpper)
|
|
{
|
|
int regIndex = -1;
|
|
|
|
if (g_Reg == nullptr)
|
|
{
|
|
return ThrowRegContextUnavailableError(ctx);
|
|
}
|
|
|
|
if (duk_is_number(ctx, 1))
|
|
{
|
|
regIndex = duk_get_int(ctx, 1);
|
|
}
|
|
else if (duk_is_string(ctx, 1))
|
|
{
|
|
regIndex = GPRIndex(duk_get_string(ctx, 1));
|
|
}
|
|
|
|
if (regIndex < 0 || regIndex > 31)
|
|
{
|
|
return ThrowRegInvalidError(ctx);
|
|
}
|
|
|
|
duk_push_uint(ctx, g_Reg->m_GPR[regIndex].UW[bUpper ? 1 : 0]);
|
|
return 1;
|
|
}
|
|
|
|
static duk_ret_t GPRSetImpl(duk_context * ctx, bool bUpper)
|
|
{
|
|
int regIndex = -1;
|
|
|
|
if (g_Reg == nullptr)
|
|
{
|
|
return ThrowRegContextUnavailableError(ctx);
|
|
}
|
|
|
|
if (!duk_is_number(ctx, 2))
|
|
{
|
|
return ThrowRegAssignmentTypeError(ctx);
|
|
}
|
|
|
|
uint32_t value = duk_get_uint(ctx, 2);
|
|
|
|
if (duk_is_number(ctx, 1))
|
|
{
|
|
regIndex = duk_get_int(ctx, 1);
|
|
}
|
|
else if (duk_is_string(ctx, 1))
|
|
{
|
|
regIndex = GPRIndex(duk_get_string(ctx, 1));
|
|
}
|
|
|
|
if (regIndex == 0)
|
|
{
|
|
duk_push_true(ctx);
|
|
return 1;
|
|
}
|
|
|
|
if (regIndex < 0 || regIndex > 31)
|
|
{
|
|
return ThrowRegInvalidError(ctx);
|
|
}
|
|
|
|
g_Reg->m_GPR[regIndex].UW[bUpper ? 1 : 0] = value;
|
|
duk_push_true(ctx);
|
|
return 1;
|
|
}
|
|
|
|
static duk_ret_t FPRGetImpl(duk_context * ctx, bool bDouble)
|
|
{
|
|
int regIndex = -1;
|
|
|
|
if (g_Reg == nullptr)
|
|
{
|
|
return ThrowRegContextUnavailableError(ctx);
|
|
}
|
|
|
|
if (duk_is_number(ctx, 1))
|
|
{
|
|
regIndex = duk_get_int(ctx, 1);
|
|
}
|
|
else if (duk_is_string(ctx, 1))
|
|
{
|
|
regIndex = FPRIndex(duk_get_string(ctx, 1));
|
|
}
|
|
|
|
if (regIndex < 0 || regIndex > 31)
|
|
{
|
|
return ThrowRegInvalidError(ctx);
|
|
}
|
|
|
|
if (bDouble)
|
|
{
|
|
duk_push_number(ctx, (duk_double_t)*g_Reg->m_FPR_D[regIndex & 0x1E]);
|
|
}
|
|
else
|
|
{
|
|
duk_push_number(ctx, (duk_double_t)*g_Reg->m_FPR_S[regIndex]);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static duk_ret_t FPRSetImpl(duk_context * ctx, bool bDouble)
|
|
{
|
|
int regIndex = -1;
|
|
|
|
if (g_Reg == nullptr)
|
|
{
|
|
return ThrowRegContextUnavailableError(ctx);
|
|
}
|
|
|
|
if (!duk_is_number(ctx, 2))
|
|
{
|
|
return ThrowRegAssignmentTypeError(ctx);
|
|
}
|
|
|
|
if (duk_is_number(ctx, 1))
|
|
{
|
|
regIndex = duk_get_int(ctx, 1);
|
|
}
|
|
else if (duk_is_string(ctx, 1))
|
|
{
|
|
regIndex = FPRIndex(duk_get_string(ctx, 1));
|
|
}
|
|
|
|
if (regIndex < 0 || regIndex > 31)
|
|
{
|
|
return ThrowRegInvalidError(ctx);
|
|
}
|
|
|
|
duk_double_t value = duk_get_number(ctx, 2);
|
|
|
|
if (bDouble)
|
|
{
|
|
*g_Reg->m_FPR_D[regIndex & 0x1E] = value;
|
|
}
|
|
else
|
|
{
|
|
*g_Reg->m_FPR_S[regIndex] = (float)value;
|
|
}
|
|
|
|
duk_push_true(ctx);
|
|
return 1;
|
|
}
|
|
|
|
static int GPRIndex(const char * regName)
|
|
{
|
|
const char * names[] = {
|
|
"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",
|
|
};
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
if (strcmp(names[i], regName) == 0)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int FPRIndex(const char * regName)
|
|
{
|
|
const char * names[32] = {
|
|
"f0",
|
|
"f1",
|
|
"f2",
|
|
"f3",
|
|
"f4",
|
|
"f5",
|
|
"f6",
|
|
"f7",
|
|
"f8",
|
|
"f9",
|
|
"f10",
|
|
"f11",
|
|
"f12",
|
|
"f13",
|
|
"f14",
|
|
"f15",
|
|
"f16",
|
|
"f17",
|
|
"f18",
|
|
"f19",
|
|
"f20",
|
|
"f21",
|
|
"f22",
|
|
"f23",
|
|
"f24",
|
|
"f25",
|
|
"f26",
|
|
"f27",
|
|
"f28",
|
|
"f29",
|
|
"f30",
|
|
"f31",
|
|
};
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
if (strcmp(names[i], regName) == 0)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static uint32_t * COP0RegPtr(const char * regName)
|
|
{
|
|
if (g_Reg == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
struct
|
|
{
|
|
const char * name;
|
|
uint32_t * ptr;
|
|
} names[] = {
|
|
{"index", (uint32_t *)&g_Reg->INDEX_REGISTER},
|
|
{"random", (uint32_t *)&g_Reg->RANDOM_REGISTER},
|
|
{"entrylo0", (uint32_t *)&g_Reg->ENTRYLO0_REGISTER},
|
|
{"entrylo1", (uint32_t *)&g_Reg->ENTRYLO1_REGISTER},
|
|
{"context", (uint32_t *)&g_Reg->CONTEXT_REGISTER},
|
|
{"pagemask", (uint32_t *)&g_Reg->PAGE_MASK_REGISTER},
|
|
{"wired", (uint32_t *)&g_Reg->WIRED_REGISTER},
|
|
{"badvaddr", (uint32_t *)&g_Reg->BAD_VADDR_REGISTER},
|
|
{"count", (uint32_t *)&g_Reg->COUNT_REGISTER},
|
|
{"entryhi", (uint32_t *)&g_Reg->ENTRYHI_REGISTER},
|
|
{"compare", (uint32_t *)&g_Reg->COMPARE_REGISTER},
|
|
{"status", (uint32_t *)&g_Reg->STATUS_REGISTER},
|
|
//{ "cause", (uint32_t*)&g_Reg->CAUSE_REGISTER },
|
|
{"epc", (uint32_t *)&g_Reg->EPC_REGISTER},
|
|
{"config", (uint32_t *)&g_Reg->CONFIG_REGISTER},
|
|
{"taglo", (uint32_t *)&g_Reg->TAGLO_REGISTER},
|
|
{"taghi", (uint32_t *)&g_Reg->TAGHI_REGISTER},
|
|
{"errorepc", (uint32_t *)&g_Reg->ERROREPC_REGISTER},
|
|
{nullptr, nullptr},
|
|
};
|
|
|
|
for (int i = 0; names[i].name != nullptr; i++)
|
|
{
|
|
if (strcmp(regName, names[i].name) == 0)
|
|
{
|
|
return names[i].ptr;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static uint32_t * CPURegPtr(const char * key)
|
|
{
|
|
if (g_Reg == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if (strcmp(key, "pc") == 0)
|
|
{
|
|
return &g_Reg->m_PROGRAM_COUNTER;
|
|
}
|
|
else if (strcmp(key, "hi") == 0)
|
|
{
|
|
return &g_Reg->m_HI.UW[0];
|
|
}
|
|
else if (strcmp(key, "uhi") == 0)
|
|
{
|
|
return &g_Reg->m_HI.UW[1];
|
|
}
|
|
else if (strcmp(key, "lo") == 0)
|
|
{
|
|
return &g_Reg->m_LO.UW[0];
|
|
}
|
|
else if (strcmp(key, "ulo") == 0)
|
|
{
|
|
return &g_Reg->m_LO.UW[1];
|
|
}
|
|
else if (strcmp(key, "fcr31") == 0)
|
|
{
|
|
return &g_Reg->m_FPCR[31];
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static duk_ret_t ThrowRegInvalidError(duk_context * ctx)
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_REFERENCE_ERROR, "invalid register name or number");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
static duk_ret_t ThrowRegContextUnavailableError(duk_context * ctx)
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_ERROR, "CPU register context is unavailable");
|
|
return duk_throw(ctx);
|
|
}
|
|
|
|
static duk_ret_t ThrowRegAssignmentTypeError(duk_context * ctx)
|
|
{
|
|
duk_push_error_object(ctx, DUK_ERR_TYPE_ERROR, "invalid register value assignment");
|
|
return duk_throw(ctx);
|
|
}
|