501 lines
11 KiB
C++
501 lines
11 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, 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", &g_Reg->INDEX_REGISTER },
|
|
{ "random", &g_Reg->RANDOM_REGISTER },
|
|
{ "entrylo0", &g_Reg->ENTRYLO0_REGISTER },
|
|
{ "entrylo1", &g_Reg->ENTRYLO1_REGISTER },
|
|
{ "context", &g_Reg->CONTEXT_REGISTER },
|
|
{ "pagemask", &g_Reg->PAGE_MASK_REGISTER },
|
|
{ "wired", &g_Reg->WIRED_REGISTER },
|
|
{ "badvaddr", &g_Reg->BAD_VADDR_REGISTER },
|
|
{ "count", &g_Reg->COUNT_REGISTER },
|
|
{ "entryhi", &g_Reg->ENTRYHI_REGISTER },
|
|
{ "compare", &g_Reg->COMPARE_REGISTER },
|
|
{ "status", &g_Reg->STATUS_REGISTER },
|
|
//{ "cause", &g_Reg->CAUSE_REGISTER },
|
|
{ "epc", &g_Reg->EPC_REGISTER },
|
|
{ "config", &g_Reg->CONFIG_REGISTER },
|
|
{ "taglo", &g_Reg->TAGLO_REGISTER },
|
|
{ "taghi", &g_Reg->TAGHI_REGISTER },
|
|
{ "errorepc", &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);
|
|
}
|