349 lines
12 KiB
C
349 lines
12 KiB
C
// Native-backed emulation core implementation
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <jni.h>
|
|
#include <vue.h>
|
|
#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);
|
|
}
|