275 lines
8.6 KiB
C
275 lines
8.6 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 };
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Types //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Breakpoint token
|
|
typedef struct {
|
|
int32_t type;
|
|
int32_t value;
|
|
} Token;
|
|
|
|
// Precompiled breakpoint
|
|
typedef struct {
|
|
int32_t enabled; // The breakpoint is enabled
|
|
int32_t numAddresses; // Number of address ranges
|
|
int32_t numCondition; // Number of tokens in condition
|
|
int32_t *addresses; // Address ranges
|
|
Token *condition; // Condition tokens in RPN order
|
|
} Breakpoint;
|
|
|
|
// Core context state type
|
|
typedef struct {
|
|
Vue vue; // Context into the generic C library
|
|
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"
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Internal Functions //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Method Functions //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Native constructor
|
|
JNIEXPORT jlong JNICALL Java_vue_NativeVue_construct
|
|
(JNIEnv *env, jobject vue) {
|
|
Core *mem[] = { NULL, NULL };
|
|
Core *core = mem[0] = calloc(sizeof (Core), 1);
|
|
vueInitialize(&core->vue);
|
|
vueReset(&core->vue);
|
|
return *(jlong *)&core;
|
|
}
|
|
|
|
// Release any used resources
|
|
JNIEXPORT void JNICALL Java_vue_NativeVue_dispose
|
|
(JNIEnv *env, jobject vue, jlong handle) {
|
|
Core *core = *(Core **)&handle;
|
|
if (core->vue.pak.rom != NULL)
|
|
free(core->vue.pak.rom);
|
|
if (core->vue.pak.ram != NULL)
|
|
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);
|
|
}
|
|
|
|
// Evaluate the condition in a breakpoint
|
|
JNIEXPORT jboolean JNICALL Java_vue_NativeVue_evaluate
|
|
(JNIEnv *env, jobject vue, jlong handle, jobject brk) {
|
|
Core *core = *(Core **)&handle;
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
// Retrieve the application break code
|
|
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode
|
|
(JNIEnv *env, jobject vue, jlong handle) {
|
|
Core *core = *(Core **)&handle;
|
|
return vueGetBreakCode(&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;
|
|
}
|
|
|
|
// Determine whether the context is native-backed
|
|
JNIEXPORT jboolean JNICALL Java_vue_NativeVue_isNative
|
|
(JNIEnv *env, jobject vue) {
|
|
return JNI_TRUE;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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 an exception breakpoint callback
|
|
JNIEXPORT void JNICALL Java_vue_NativeVue_setException
|
|
(JNIEnv *env, jobject vue, jlong handle, jobject onException) {
|
|
Core *core = *(Core **)&handle;
|
|
}
|
|
|
|
// Specify an execute breakpoint callback
|
|
JNIEXPORT void JNICALL Java_vue_NativeVue_setExecute
|
|
(JNIEnv *env, jobject vue, jlong handle, jobject onExecute) {
|
|
Core *core = *(Core **)&handle;
|
|
}
|
|
|
|
// Specify a read breakpoint callback
|
|
JNIEXPORT void JNICALL Java_vue_NativeVue_setRead
|
|
(JNIEnv *env, jobject vue, jlong handle, jobject onRead) {
|
|
Core *core = *(Core **)&handle;
|
|
}
|
|
|
|
// 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;
|
|
if (core->vue.pak.rom != NULL)
|
|
free(core->vue.pak.rom);
|
|
vueSetROM(&core->vue, rom, length);
|
|
return JNI_TRUE;
|
|
}
|
|
|
|
// Specify a write breakpoint callback
|
|
JNIEXPORT void JNICALL Java_vue_NativeVue_setWrite
|
|
(JNIEnv *env, jobject vue, jlong handle, jobject onWrite) {
|
|
Core *core = *(Core **)&handle;
|
|
}
|
|
|
|
// 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;
|
|
}
|