// Native-backed emulation core implementation #include #include #include #include #include #include "vue_NativeVue.h" /////////////////////////////////////////////////////////////////////////////// // Constants // /////////////////////////////////////////////////////////////////////////////// // Memory access type sizes static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 }; // Word data type limits #define MAX_WORD 2147483648.0f #define MAX_UWORD 4294967296.0f /////////////////////////////////////////////////////////////////////////////// // Types // /////////////////////////////////////////////////////////////////////////////// // Breakpoint condition token typedef struct { int32_t type; // Token category int32_t id; // Operator or symbol ID, or literal value } Token; // Precompiled breakpoint typedef struct { int32_t enabled; // The breakpoint is enabled int32_t fetch; // Breakpoint traps fetch reads int32_t hooks; // Events hooked by the breakpoint int32_t numRanges; // Number of address ranges int32_t numTokens; // Number of condition tokens uint32_t *ranges; // Address ranges Token *tokens; // Condition tokens in RPN order } Breakpoint; // Core context state type typedef struct { Vue vue; // Context into the generic C library int32_t breakType; // Most recent breakpoint scenario int32_t numBreakpoints; // Number of breakpoints Breakpoint *breakpoints; // Application breakpoints int32_t *stack; // Expression stack for breakpoints } Core; /////////////////////////////////////////////////////////////////////////////// // Macros // /////////////////////////////////////////////////////////////////////////////// /* Sign-extend a value */ #define SIGN_EXTEND(bits, value) \ ((value) & 1 << (bits - 1) ? (value) | (~(1 << bits) + 1) : (value)) /////////////////////////////////////////////////////////////////////////////// // Component Includes // /////////////////////////////////////////////////////////////////////////////// #define NATIVEVUE #include "Breakpoint.c" /////////////////////////////////////////////////////////////////////////////// // Constructors // /////////////////////////////////////////////////////////////////////////////// // Native constructor JNIEXPORT jlong JNICALL Java_vue_NativeVue_construct (JNIEnv *env, jobject uve) { Core *mem[] = { NULL, NULL }; Core *core = mem[0] = calloc(sizeof (Core), 1); Vue *vue = &core->vue; vueInitialize (vue); vueOnException(vue, &brkOnException); vueOnExecute (vue, &brkOnExecute ); vueOnFrame (vue, &brkOnFrame ); vueOnRead (vue, &brkOnRead ); vueOnWrite (vue, &brkOnWrite ); vueReset (vue); vueSetTag (vue, core); return *(jlong *)&core; } /////////////////////////////////////////////////////////////////////////////// // Public Methods // /////////////////////////////////////////////////////////////////////////////// // Produce a new breakpoint and add it to the collection JNIEXPORT void JNICALL Java_vue_NativeVue_breakpoint (JNIEnv *env, jobject vue, jlong handle) { Core *core = *(Core **)&handle; core->numBreakpoints++; core->breakpoints = realloc(core->breakpoints,core->numBreakpoints * sizeof (Breakpoint)); Breakpoint *brk = &core->breakpoints[core->numBreakpoints - 1]; memset(brk, 0 ,sizeof (Breakpoint)); brk->fetch = VUE_TRUE; } // Release any used resources JNIEXPORT void JNICALL Java_vue_NativeVue_dispose (JNIEnv *env, jobject vue, jlong handle) { Core *core = *(Core **)&handle; Breakpoint *brk; // Handle to breakpoint int x; // Iterator // Delete breakpoints for (x = 0; x < core->numBreakpoints; x++) { brk = &core->breakpoints[x]; free(brk->ranges); free(brk->tokens); } // Delete core free(core->breakpoints); free(core->stack ); free(core->vue.pak.rom); free(core->vue.pak.ram); free(core); } // Process the simulation JNIEXPORT jint JNICALL Java_vue_NativeVue_emulate (JNIEnv *env, jobject vue, jlong handle, jint maxCycles) { Core *core = *(Core **)&handle; return vueEmulate(&core->vue, maxCycles); } // Retrieve the most recent exception code JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode (JNIEnv *env, jobject vue, jlong handle) { Core *core = *(Core **)&handle; return vueGetBreakCode(&core->vue); } // Retrieve the most recent exception code JNIEXPORT jint JNICALL Java_vue_NativeVue_getExceptionCode (JNIEnv *env, jobject vue, jlong handle) { Core *core = *(Core **)&handle; return vueGetExceptionCode(&core->vue); } // Retrieve a register value JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister (JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) { Core *core = *(Core **)&handle; return vueGetRegister(&core->vue, index, system); } // Retrieve a copy of the ROM data JNIEXPORT jbyteArray JNICALL Java_vue_NativeVue_getROM (JNIEnv *env, jobject vue, jlong handle) { Core *core = *(Core **)&handle; // No ROM data if (core->vue.pak.rom == NULL) return NULL; // Copy the ROM data jbyteArray ret = (*env)->NewByteArray(env, (jint)core->vue.pak.romSize); jbyte *elems = (*env)->GetByteArrayElements(env, ret, NULL); memcpy(elems, core->vue.pak.rom, core->vue.pak.romSize); (*env)->ReleaseByteArrayElements(env, ret, elems, 0); return ret; } // Read a value from the CPU bus JNIEXPORT jint JNICALL Java_vue_NativeVue_read (JNIEnv *env, jobject vue, jlong handle, jint address, jint type) { Core *core = *(Core **)&handle; return vueRead(&core->vue, address, type); } // Read bytes from the CPU bus JNIEXPORT jboolean JNICALL Java_vue_NativeVue_readBytes (JNIEnv *env, jobject vue, jlong handle, jint address, jbyteArray data, jint offset, jint length) { // Error checking if ( data == NULL || offset < 0 || length < 0 || offset + length > (*env)->GetArrayLength(env, data) ) return JNI_FALSE; // Perform the operation Core *core = *(Core **)&handle; jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL); vueReadBytes(&core->vue, address, &elems[offset], length); (*env)->ReleaseByteArrayElements(env, data, elems, 0); return JNI_TRUE; } // Remove a breakpoint from the collection JNIEXPORT void JNICALL Java_vue_NativeVue_remove (JNIEnv *env, jobject vue, jlong handle, jint index) { Core *core = *(Core **)&handle; Breakpoint *brk = &core->breakpoints[index]; free(brk->ranges); free(brk->tokens); memmove(brk, &core->breakpoints[index + 1], (core->numBreakpoints - index - 1) * sizeof (Breakpoint)); core->numBreakpoints--; } // Initialize all system components JNIEXPORT void JNICALL Java_vue_NativeVue_reset (JNIEnv *env, jobject vue, jlong handle) { Core *core = *(Core **)&handle; vueReset(&core->vue); } // Specify a register value JNIEXPORT jint JNICALL Java_vue_NativeVue_setRegister (JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system, jint value) { Core *core = *(Core **)&handle; return vueSetRegister(&core->vue, index, system, value); } // Provide new ROM data JNIEXPORT jboolean JNICALL Java_vue_NativeVue_setROM (JNIEnv *env, jobject vue, jlong handle, jbyteArray data, jint offset, jint length) { // Error checking if ( data == NULL || offset < 0 || length < 1024 || length > 0x01000000 || (length & length - 1) != 0 || offset + length > (*env)->GetArrayLength(env, data) ) return JNI_FALSE; // Accept the new ROM data uint8_t *rom = malloc(length); jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL); memcpy(rom, &elems[offset], length); (*env)->ReleaseByteArrayElements(env, data, elems, 0); // Transfer the ROM data to the emulation state Core *core = *(Core **)&handle; free(core->vue.pak.rom); vueSetROM(&core->vue, rom, length); return JNI_TRUE; } // Write a value to the CPU bus JNIEXPORT void JNICALL Java_vue_NativeVue_write (JNIEnv *env, jobject vue, jlong handle, jint address, jint type, jint value) { Core *core = *(Core **)&handle; vueWrite(&core->vue, address, type, value); } // Write bytes to the CPU bus JNIEXPORT jboolean JNICALL Java_vue_NativeVue_writeBytes (JNIEnv *env, jobject vue, jlong handle, jint address, jbyteArray data, jint offset, jint length) { // Error checking if ( data == NULL || offset < 0 || length < 0 || offset + length > (*env)->GetArrayLength(env, data) ) return JNI_FALSE; // Perform the operation Core *core = *(Core **)&handle; jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL); vueWriteBytes(&core->vue, address, &elems[offset], length); (*env)->ReleaseByteArrayElements(env, data, elems, 0); return JNI_TRUE; } /////////////////////////////////////////////////////////////////////////////// // Package Methods // /////////////////////////////////////////////////////////////////////////////// // Retrieve the current state's breakpoint scenario JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakType (JNIEnv *env, jobject vue, jlong handle) { Core *core = *(Core **)&handle; return core->breakType; } // Retrieve the current instruction fetch index JNIEXPORT jint JNICALL Java_vue_NativeVue_getFetch (JNIEnv *env, jobject vue, jlong handle) { Core *core = *(Core **)&handle; return core->vue.cpu.fetch; } // A breakpoint's address ranges have changed JNIEXPORT void JNICALL Java_vue_NativeVue_updateRanges (JNIEnv *env, jobject vue, jlong handle, jint index, jintArray ranges) { Core *core = *(Core **)&handle; Breakpoint *brk = &core->breakpoints[index]; brk->numRanges = (*env)->GetArrayLength(env, ranges); brk->ranges = realloc(brk->ranges, brk->numRanges * 8); jint *elems = (*env)->GetIntArrayElements(env, ranges, NULL); memcpy(brk->ranges, elems, brk->numRanges * 8); (*env)->ReleaseIntArrayElements(env, ranges, elems, 0); } // A breakpoint's enabled/hook state has changed JNIEXPORT void JNICALL Java_vue_NativeVue_updateState (JNIEnv *env, jobject vue, jlong handle, jint index, jboolean enabled, jboolean fetch, jint hooks) { Core *core = *(Core **)&handle; Breakpoint *brk = &core->breakpoints[index]; brk->enabled = enabled; brk->fetch = fetch; brk->hooks = hooks; } // A breakpoint's condition tokens have changed JNIEXPORT void JNICALL Java_vue_NativeVue_updateTokens (JNIEnv *env, jobject vue, jlong handle, jint index, jintArray tokens, jint depth, jint maxDepth) { Core *core = *(Core **)&handle; core->stack = realloc(core->stack, maxDepth * 4); Breakpoint *brk = &core->breakpoints[index]; brk->numTokens = (*env)->GetArrayLength(env, tokens) / 2; brk->tokens = realloc(brk->tokens, brk->numTokens * sizeof (Token)); jint *elems = (*env)->GetIntArrayElements(env, tokens, NULL); memcpy(brk->tokens, elems, brk->numTokens * sizeof (Token)); (*env)->ReleaseIntArrayElements(env, tokens, elems, 0); }