#include "stdafx.h" #include "ScriptAPI.h" #include #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); }