#include "stdafx.h" #include "ScriptAPI.h" #include #include #include #include #pragma warning(disable : 4702) // disable unreachable code warning using namespace ScriptAPI; static size_t MemTypeSize(MemType t); static duk_ret_t ThrowMemoryError(duk_context * ctx, uint32_t address); void ScriptAPI::Define_mem(duk_context * ctx) { #define MEM_PROXY_FUNCS(T) js_mem__get, js_mem__set const DukPropListEntry props[] = { {"getblock", DukCFunction(js_mem_getblock)}, {"setblock", DukCFunction(js_mem_setblock)}, {"getstring", DukCFunction(js_mem_getstring)}, {"setstring", DukCFunction(js_mem_setblock)}, {"bindvar", DukCFunction(js_mem_bindvar)}, {"bindvars", DukCFunction(js_mem_bindvars)}, {"bindstruct", DukCFunction(js_mem_bindstruct)}, {"typedef", DukCFunction(js_mem_typedef)}, {"ramSize", DukGetter(js_mem__get_ramsize)}, {"romSize", DukGetter(js_mem__get_romsize)}, {"ptr", DukGetter(js_mem__get_ptr)}, {"u32", DukProxy(MEM_PROXY_FUNCS(uint32_t))}, {"u16", DukProxy(MEM_PROXY_FUNCS(uint16_t))}, {"u8", DukProxy(MEM_PROXY_FUNCS(uint8_t))}, {"s32", DukProxy(MEM_PROXY_FUNCS(int32_t))}, {"s16", DukProxy(MEM_PROXY_FUNCS(int16_t))}, {"s8", DukProxy(MEM_PROXY_FUNCS(int8_t))}, {"f64", DukProxy(MEM_PROXY_FUNCS(double))}, {"f32", DukProxy(MEM_PROXY_FUNCS(float))}, {nullptr}, }; DefineGlobalInterface(ctx, "mem", props); } template duk_ret_t ScriptAPI::js_mem__get(duk_context * ctx) { CScriptInstance * inst = GetInstance(ctx); uint32_t addr = (uint32_t)duk_to_number(ctx, 1); T value; if (inst->Debugger()->DebugLoad_VAddr(addr, value)) { duk_push_number(ctx, value); return 1; } return ThrowMemoryError(ctx, addr); } template duk_ret_t ScriptAPI::js_mem__set(duk_context * ctx) { CScriptInstance * inst = GetInstance(ctx); uint32_t addr = (uint32_t)duk_to_number(ctx, 1); T value = (T)duk_to_number(ctx, 2); if (inst->Debugger()->DebugStore_VAddr(addr, value)) { duk_push_true(ctx); return 1; } return ThrowMemoryError(ctx, addr); } duk_ret_t ScriptAPI::js_mem_getblock(duk_context * ctx) { CheckArgs(ctx, {Arg_Number, Arg_Number}); CScriptInstance * inst = GetInstance(ctx); duk_uint_t addr = duk_to_uint(ctx, 0); duk_uint_t length = duk_to_uint(ctx, 1); uint8_t * data = (uint8_t *)duk_push_fixed_buffer(ctx, length); duk_push_buffer_object(ctx, -1, 0, length, DUK_BUFOBJ_NODEJS_BUFFER); uint32_t paddr; uint8_t * memsrc = nullptr; uint32_t offsetStart = 0; if (addr < 0x80000000 || addr >= 0xC0000000) { if (!g_MMU || !g_MMU->VAddrToPAddr(addr, paddr)) { return ThrowMemoryError(ctx, addr); } } else { paddr = addr & 0x1FFFFFFF; } if (g_MMU && paddr >= 0x00000000 && (paddr + length) <= g_MMU->RdramSize()) { memsrc = g_MMU->Rdram(); offsetStart = paddr; } else if (g_Rom && paddr >= 0x10000000 && ((paddr - 0x10000000) + length) <= g_Rom->GetRomSize()) { memsrc = g_Rom->GetRomAddress(); offsetStart = paddr - 0x10000000; } if (memsrc != nullptr) { uint32_t offsetEnd = offsetStart + length; uint32_t alignedOffsetStart = (offsetStart + 15) & ~15; uint32_t alignedOffsetEnd = offsetEnd & ~15; uint32_t prefixLen = alignedOffsetStart - offsetStart; uint32_t middleLen = alignedOffsetEnd - alignedOffsetStart; uint32_t suffixLen = offsetEnd - alignedOffsetEnd; uint32_t * middleDst = (uint32_t *)&data[0 + prefixLen]; uint32_t * middleDstEnd = (uint32_t *)&data[0 + prefixLen + middleLen]; uint32_t * middleSrc = (uint32_t *)&memsrc[alignedOffsetStart]; for (size_t i = 0; i < prefixLen; i++) { data[i] = memsrc[(offsetStart + i) ^ 3]; } while (middleDst < middleDstEnd) { *middleDst++ = _byteswap_ulong(*middleSrc++); *middleDst++ = _byteswap_ulong(*middleSrc++); *middleDst++ = _byteswap_ulong(*middleSrc++); *middleDst++ = _byteswap_ulong(*middleSrc++); } for (size_t i = 0; i < suffixLen; i++) { data[(length - suffixLen) + i] = memsrc[(alignedOffsetEnd + i) ^ 3]; } } else { for (size_t i = 0; i < length; i++) { uint8_t byte; if (inst->Debugger()->DebugLoad_VAddr((uint32_t)((UINT_PTR)addr + i), byte)) { data[i] = byte; } else { return ThrowMemoryError(ctx, (uint32_t)((UINT_PTR)addr + i)); } } } return 1; } duk_ret_t ScriptAPI::js_mem_getstring(duk_context * ctx) { CheckArgs(ctx, {Arg_Number, Arg_OptNumber}); CScriptInstance * inst = GetInstance(ctx); duk_idx_t nargs = duk_get_top(ctx); duk_uint_t addr = duk_to_uint(ctx, 0); duk_uint_t maxLength = nargs > 1 ? duk_to_uint(ctx, 1) : 0xFFFFFFFF; size_t length = 0; for (size_t i = 0; i < maxLength; i++) { char c; if (!inst->Debugger()->DebugLoad_VAddr((uint32_t)((UINT_PTR)addr + i), c)) { return ThrowMemoryError(ctx, addr); } if (c == 0) { break; } length++; } char * str = new char[length + 1]; str[length] = '\0'; for (size_t i = 0; i < length; i++) { if (!inst->Debugger()->DebugLoad_VAddr((uint32_t)((UINT_PTR)addr + i), str[i])) { delete[] str; return ThrowMemoryError(ctx, addr); } } duk_push_string(ctx, str); delete[] str; return 1; } duk_ret_t ScriptAPI::js_mem_setblock(duk_context * ctx) { CheckArgs(ctx, {Arg_Number, Arg_Any, Arg_OptNumber}); CScriptInstance * inst = GetInstance(ctx); CDebuggerUI * debugger = inst->Debugger(); duk_idx_t nargs = duk_get_top(ctx); char * data; duk_size_t dataSize, length; uint32_t address = duk_get_uint(ctx, 0); if (duk_is_buffer_data(ctx, 1)) { data = (char *)duk_get_buffer_data(ctx, 1, &dataSize); } else if (duk_is_string(ctx, 1)) { data = (char *)duk_get_lstring(ctx, 1, &dataSize); } else { return ThrowInvalidArgsError(ctx); } if (nargs == 3) { duk_double_t l = duk_get_number(ctx, 2); if (l < 0 || l > dataSize) { return DUK_RET_RANGE_ERROR; } length = (duk_size_t)l; } else { length = dataSize; } for (size_t i = 0; i < length; i++) { if (!debugger->DebugStore_VAddr((uint32_t)((UINT_PTR)address + i), data[i])) { return ThrowMemoryError(ctx, (uint32_t)((UINT_PTR)address + i)); } } return 0; } duk_ret_t ScriptAPI::js_mem__boundget(duk_context * ctx) { CDebuggerUI * debugger = GetInstance(ctx)->Debugger(); uint32_t addr = duk_get_uint(ctx, 0); duk_int_t type = duk_get_int(ctx, 1); union { uint8_t u8; uint16_t u16; uint32_t u32; int8_t s8; int16_t s16; int32_t s32; float f32; double f64; } retval; #define MEM_BOUNDGET_TRY(addr, T, result, dukpush) \ if (debugger->DebugLoad_VAddr(addr, result)) \ { \ dukpush(ctx, result); \ } \ else \ { \ goto memory_error; \ } switch (type) { case U8: MEM_BOUNDGET_TRY(addr, uint8_t, retval.u8, duk_push_uint); return 1; case U16: MEM_BOUNDGET_TRY(addr, uint16_t, retval.u16, duk_push_uint); return 1; case U32: MEM_BOUNDGET_TRY(addr, uint32_t, retval.u32, duk_push_uint); return 1; case S8: MEM_BOUNDGET_TRY(addr, int8_t, retval.s8, duk_push_int); return 1; case S16: MEM_BOUNDGET_TRY(addr, int16_t, retval.s16, duk_push_int); return 1; case S32: MEM_BOUNDGET_TRY(addr, int32_t, retval.s32, duk_push_int); return 1; case F32: MEM_BOUNDGET_TRY(addr, float, retval.f32, duk_push_number); return 1; case F64: MEM_BOUNDGET_TRY(addr, double, retval.f64, duk_push_number); return 1; } memory_error: return ThrowMemoryError(ctx, addr); } duk_ret_t ScriptAPI::js_mem__boundset(duk_context * ctx) { CDebuggerUI * debugger = GetInstance(ctx)->Debugger(); uint32_t addr = duk_get_uint(ctx, 0); duk_int_t type = duk_get_int(ctx, 1); #define MEM_BOUNDSET_TRY(addr, T, value) \ if (debugger->DebugStore_VAddr(addr, value)) \ { \ return 1; \ } \ else \ { \ goto memory_error; \ } switch (type) { case U8: MEM_BOUNDSET_TRY(addr, uint8_t, duk_get_uint(ctx, 2) & 0xFF); break; case U16: MEM_BOUNDSET_TRY(addr, uint16_t, duk_get_uint(ctx, 2) & 0xFFFF); break; case U32: MEM_BOUNDSET_TRY(addr, uint32_t, duk_get_uint(ctx, 2)); break; case S8: MEM_BOUNDSET_TRY(addr, int8_t, duk_get_int(ctx, 2) & 0xFF); break; case S16: MEM_BOUNDSET_TRY(addr, int16_t, duk_get_int(ctx, 2) & 0xFFFF); break; case S32: MEM_BOUNDSET_TRY(addr, int32_t, duk_get_int(ctx, 2)); break; case F32: MEM_BOUNDSET_TRY(addr, float, (float)duk_get_number(ctx, 2)); break; case F64: MEM_BOUNDSET_TRY(addr, double, duk_get_number(ctx, 2)); break; } return 0; memory_error: return ThrowMemoryError(ctx, addr); } duk_ret_t ScriptAPI::js_mem_bindvar(duk_context * ctx) { CheckArgs(ctx, {Arg_Object, Arg_Number, Arg_String, Arg_Number}); duk_uint_t addr = duk_get_uint(ctx, 1); const char * key = duk_get_string(ctx, 2); duk_int_t type = duk_get_int(ctx, 3); duk_push_string(ctx, key); // [ ... js_mem__boundget ] -> [ ... js_mem__boundget.bind(obj, addr, type) ] duk_push_c_function(ctx, js_mem__boundget, DUK_VARARGS); duk_push_string(ctx, "bind"); duk_dup(ctx, 0); duk_push_uint(ctx, addr); duk_push_int(ctx, type); duk_call_prop(ctx, -5, 3); duk_remove(ctx, -2); duk_push_c_function(ctx, js_mem__boundset, DUK_VARARGS); duk_push_string(ctx, "bind"); duk_dup(ctx, 0); duk_push_uint(ctx, addr); duk_push_int(ctx, type); duk_call_prop(ctx, -5, 3); duk_remove(ctx, -2); duk_def_prop(ctx, 0, DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); return 0; } duk_ret_t ScriptAPI::js_mem_bindvars(duk_context * ctx) { CheckArgs(ctx, {Arg_Object, Arg_Array}); duk_size_t length = duk_get_length(ctx, 1); for (duk_uarridx_t i = 0; i < length; i++) { duk_get_prop_index(ctx, 1, i); if (!duk_is_array(ctx, -1) || duk_get_length(ctx, -1) != 3) { return DUK_RET_TYPE_ERROR; } duk_push_c_function(ctx, js_mem_bindvar, 4); duk_dup(ctx, 0); duk_get_prop_index(ctx, -3, 0); duk_get_prop_index(ctx, -4, 1); duk_get_prop_index(ctx, -5, 2); if (duk_pcall(ctx, 4) != DUK_EXEC_SUCCESS) { return duk_throw(ctx); } duk_pop_n(ctx, 1); } duk_dup(ctx, 0); return 1; } duk_ret_t ScriptAPI::js_mem_bindstruct(duk_context * ctx) { CheckArgs(ctx, {Arg_Object, Arg_Number, Arg_Object}); uint32_t curAddr = duk_get_uint(ctx, 1); duk_enum(ctx, 2, DUK_ENUM_OWN_PROPERTIES_ONLY); while (duk_next(ctx, -1, 1)) { MemType type = (MemType)duk_get_int(ctx, -1); duk_push_c_function(ctx, js_mem_bindvar, 4); duk_dup(ctx, 0); duk_push_uint(ctx, curAddr); duk_pull(ctx, -5); duk_pull(ctx, -5); if (duk_pcall(ctx, 4) != DUK_EXEC_SUCCESS) { return duk_throw(ctx); } duk_pop(ctx); curAddr += (uint32_t)((UINT_PTR)MemTypeSize(type)); } duk_dup(ctx, 0); return 1; } duk_ret_t ScriptAPI::js_mem__type_constructor(duk_context * ctx) { CheckArgs(ctx, {Arg_Object, Arg_Number}); duk_push_c_function(ctx, js_mem_bindstruct, 3); duk_push_this(ctx); duk_pull(ctx, 1); duk_pull(ctx, 0); if (duk_pcall(ctx, 3) != DUK_EXEC_SUCCESS) { return duk_throw(ctx); } return 0; } duk_ret_t ScriptAPI::js_mem_typedef(duk_context * ctx) { CheckArgs(ctx, {Arg_Object}); duk_push_c_function(ctx, js_mem__type_constructor, DUK_VARARGS); duk_push_string(ctx, "bind"); duk_push_null(ctx); duk_dup(ctx, 0); duk_call_prop(ctx, -4, 2); return 1; } duk_ret_t ScriptAPI::js_mem__get_ramsize(duk_context * ctx) { duk_push_number(ctx, g_MMU ? g_MMU->RdramSize() : 0); return 1; } duk_ret_t ScriptAPI::js_mem__get_romsize(duk_context * ctx) { duk_push_number(ctx, g_Rom ? g_Rom->GetRomSize() : 0); return 1; } duk_ret_t ScriptAPI::js_mem__get_ptr(duk_context * ctx) { duk_push_pointer(ctx, g_MMU ? g_MMU->Rdram() : nullptr); return 1; } size_t MemTypeSize(MemType t) { switch (t) { case U8: case S8: return 1; case U16: case S16: return 2; case U32: case S32: case F32: return 4; case F64: return 8; } return 0; } duk_ret_t ThrowMemoryError(duk_context * ctx, uint32_t address) { duk_push_error_object(ctx, DUK_ERR_ERROR, "memory error (can't access 0x%08X)", address); return duk_throw(ctx); }