pvbemu/src/desktop/vue/NativeVue.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);
}