Refining core interface, setROM() behavior validated

This commit is contained in:
Guy Perfect 2020-08-03 13:51:46 -05:00
parent b86c046dd5
commit 030fc96fc7
9 changed files with 154 additions and 46 deletions

View File

@ -1,6 +1,6 @@
# Java include directory pathnames
# The Windows files need to be copied from a Windows JDK installation
include_linux = /usr/lib/jvm/java-13-openjdk-`\
include_linux = /usr/lib/jvm/java-14-openjdk-`\
uname -r | sed 's/.*-//'`/include
include_windows = jni-windows-include
@ -10,12 +10,12 @@ default:
@echo
@echo "Planet Virtual Boy Emulator"
@echo " https://www.planetvb.com/"
@echo " July 30, 2020"
@echo " August 3, 2020"
@echo
@echo "Intended build environment: Debian i386 or amd64"
@echo " gcc-multilib"
@echo " mingw-w64"
@echo " openjdk-13-jdk"
@echo " openjdk-14-jdk"
@echo
@echo "Usage: make <recipe>"
@echo " Package recipes:"
@ -74,7 +74,7 @@ core:
desktop: clean_desktop
@echo " Compiling Java desktop application"
@javac -sourcepath src/desktop --release 10 -Xlint:unchecked \
-d . src/desktop/Main.java
-h src/desktop/native -d . src/desktop/Main.java
# Build all native modules
.PHONY: native
@ -112,6 +112,7 @@ clean_most: clean_desktop
src/desktop/native/vue_NativeVUE.h: src/desktop/vue/NativeVUE.java
@javac -h src/desktop/native -sourcepath src/desktop -d . \
src/desktop/vue/NativeVUE.java
@sleep 3
# linux_x86
.PHONY: lin32_pre
@ -159,6 +160,6 @@ win64: win64_pre native_common
.PHONY: native_common
native_common:
@echo " Building native module $(name)"
@$(prefix)gcc $(include) -Isrc/core/include $(gccargs) -s -shared -Os \
@$(prefix)gcc $(include) -Isrc/core/include $(gccargs) -s -shared -O2 \
-fno-strict-aliasing \
-o native/$(name)$(ext) src/desktop/native/native.c src/core/vue.c

View File

@ -19,8 +19,8 @@ public class Main {
for (String filename : Util.listFiles("native")) {
File file = null;
// Skip .gitignore
if (filename.equals(".gitignore"))
// Skip any file that isn't a shared object library
if (!filename.endsWith(".dll") && !filename.endsWith(".so"))
continue;
// Write the contents of the native object file
@ -33,15 +33,17 @@ public class Main {
try { stream.close(); } catch (Exception e) { }
// Load the native object file into the JVM
try { System.load(file.getAbsolutePath()); break; }
try {
System.load(file.getAbsolutePath());
VUE.setNativeID(filename.substring(0,
filename.lastIndexOf(".")));
break;
}
catch (Error e) { }
}
// Begin application operations
new App();
var vue = VUE.create(true);
System.out.println(vue.isNative());
}
}

View File

@ -5,6 +5,7 @@ import java.util.*;
// Project imports
import util.*;
import vue.*;
// Top-level software state manager
public class App {
@ -12,6 +13,7 @@ public class App {
// Instance fields
private Localizer.Locale[] locales; // Language translations
private Localizer localizer; // UI localization manager
private boolean useNative; // Produce native core contexts
private ArrayList<Window> windows; // Application windows
@ -28,6 +30,7 @@ public class App {
windows = new ArrayList<Window>();
// Additional processing
setUseNative(true);
initLocales();
addWindow();
}
@ -49,6 +52,11 @@ public class App {
return localizer.getLocale();
}
// Determine whether the native module is in use
boolean getUseNative() {
return useNative;
}
// Retrieve the localization manager
Localizer getLocalizer() {
return localizer;
@ -77,6 +85,11 @@ public class App {
localizer.setLocale(locale);
}
// Specify whether using the native module
boolean setUseNative(boolean useNative) {
return this.useNative = useNative && VUE.isNativeLoaded();
}
///////////////////////////////////////////////////////////////////////////

View File

@ -9,6 +9,7 @@ import javax.swing.filechooser.*;
// Project imports
import util.*;
import vue.*;
// Main application window
class Window extends JFrame {
@ -19,6 +20,7 @@ class Window extends JFrame {
private boolean only; // This is the only application window
private ROM rom; // Currently loaded ROM
private File romFile; // Currently loaded ROM file
private VUE vue; // Emulation core context
// UI components
private File pwd; // Most recent working directory
@ -50,6 +52,8 @@ class Window extends JFrame {
// Configure instance fields
this.app = app;
pwd = Util.PWD;
vue = VUE.create(app.getUseNative());
// Configure video pane
video = new JPanel() {
@ -138,6 +142,7 @@ class Window extends JFrame {
private void onClose() {
app.removeWindow(this);
dispose();
vue.dispose();
}
// File -> Load ROM
@ -185,11 +190,17 @@ class Window extends JFrame {
return;
}
// Update the emulation state
// Update instance fields
this.rom = rom;
romFile = file;
loc.put(this, "ctrl.filename", file.getName());
updateTitle();
// Update the emulation state
var bytes = rom.toByteArray();
// Pause emulation
vue.setROM(bytes, 0, bytes.length);
// Resume emulation
}
// File -> New window

View File

@ -1,12 +1,72 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include "vue_NativeVUE.h"
// NativeVUE class lookup data
static struct {
jclass clazz; // Handle to NativeVUE class
jfieldID pointer; // ID of NativeVUE.pointer field
} NativeVUE;
// Core context state type
typedef struct {
uint8_t *rom;
uint32_t rom_size;
} CORE;
// Retrieve the handle to a NativeVUE's core context
static CORE* GetCore(JNIEnv *env, jobject vue) {
jbyteArray pointer =
(*env)->GetObjectField(env, vue, NativeVUE.pointer);
jbyte *elems = (*env)->GetByteArrayElements(env, pointer, NULL);
CORE *core = *(CORE **)elems;
(*env)->ReleaseByteArrayElements(env, pointer, elems, 0);
return core;
}
// Native constructor
JNIEXPORT void JNICALL Java_vue_NativeVUE_construct
(JNIEnv *env, jobject vue) {
// Initialize the class data
if (NativeVUE.clazz == NULL) {
NativeVUE.clazz = (*env)->GetObjectClass(env, vue);
NativeVUE.pointer =
(*env)->GetFieldID(env, NativeVUE.clazz, "pointer", "[B");
}
// Produce and initialize s new core context
CORE *core = calloc(sizeof (CORE), 1);
core->rom = calloc(1024, 1);
// Encode the context handle into a byte array
jbyteArray pointer = (*env)->NewByteArray(env, sizeof (void *));
jbyte *elems = (*env)->GetByteArrayElements(env, pointer, NULL);
*(CORE **)elems = core;
(*env)->ReleaseByteArrayElements(env, pointer, elems, 0);
(*env)->SetObjectField(env, vue, NativeVUE.pointer, pointer);
}
// Release any used resources
JNIEXPORT void JNICALL Java_vue_NativeVUE_dispose
(JNIEnv *env, jobject vue) {
CORE *core = GetCore(env, vue);
free(core->rom);
free(core);
}
// Retrieve a copy of the ROM data
JNIEXPORT jbyteArray JNICALL Java_vue_NativeVUE_getROM
(JNIEnv *env, jobject vue) {
CORE *core = GetCore(env, vue);
jbyteArray ret = (*env)->NewByteArray(env, (jint) core->rom_size);
jbyte *elems = (*env)->GetByteArrayElements(env, ret, NULL);
memcpy(elems, core->rom, core->rom_size);
(*env)->ReleaseByteArrayElements(env, ret, elems, 0);
return ret;
}
// Determine whether the context is native-backed
@ -18,19 +78,20 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_isNative
// Provide new ROM data
JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_setROM
(JNIEnv *env, jobject vue, jbyteArray data, jint offset, jint length) {
jsize data_length = (*env)->GetArrayLength(env, data);
// Error checking
if (data == NULL || offset < 0 || length < 1024 ||
offset + length > data_length || (length & length - 1) != 0)
offset + length > (*env)->GetArrayLength(env, data) ||
(length & length - 1) != 0)
return JNI_FALSE;
// Accept the new ROM data
return JNI_TRUE;
}
// Invoke native code to ensure the module is loaded
JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_testNative
(JNIEnv *env, jclass vue) {
CORE *core = GetCore(env, vue);
free(core->rom);
core->rom = calloc(length, 1);
jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL);
memcpy(core->rom, &elems[offset], length);
(*env)->ReleaseByteArrayElements(env, data, elems, 0);
core->rom_size = (uint32_t) length;
return JNI_TRUE;
}

View File

@ -48,6 +48,9 @@ public final class Util {
// Constants //
///////////////////////////////////////////////////////////////////////////
// Directory from which the JVM was initialized
public static final File PWD = new File(System.getProperty("user.dir"));
// Text file byte-order marks
private static final BOM[] BOMS = {
new BOM(new byte[] { -17, -69, -65 }, StandardCharsets.UTF_8 ),

View File

@ -1,8 +1,7 @@
package vue;
// Java emulation core implementation
// Native-backed emulation core implementation
class JavaVUE implements VUE {
class JavaVUE extends VUE {
// Instance fields
private byte[] rom; // Cartridge ROM
@ -24,6 +23,9 @@ class JavaVUE implements VUE {
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Release any used resources
public void dispose() { }; // No action needed
// Retrieve a copy of the ROM data
public byte[] getROM() {
var ret = new byte[rom.length];

View File

@ -1,10 +1,10 @@
package vue;
// Native-backed emulation core implementation
class NativeVUE implements VUE {
class NativeVUE extends VUE {
// Instance fields
private byte[] pointer; // Instance address in native memory
private byte[] pointer; // Context address in native memory
@ -14,18 +14,7 @@ class NativeVUE implements VUE {
// Default constructor
NativeVUE() {
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Determine whether the native library has been loaded
static boolean isLoaded() {
try { return testNative(); }
catch (Exception e) { return false; }
construct();
}
@ -34,6 +23,9 @@ class NativeVUE implements VUE {
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Release any used resources
public native void dispose();
// Retrieve a copy of the ROM data
public native byte[] getROM();
@ -49,7 +41,7 @@ class NativeVUE implements VUE {
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Invoke native code to ensure the module is loaded
private static native boolean testNative();
// Native constructor
private native void construct();
}

View File

@ -1,16 +1,36 @@
package vue;
// Template for emulation core implementations
public interface VUE {
public abstract class VUE {
// Static fields
private static String nativeID = null; // ID of loaded native library
///////////////////////////////////////////////////////////////////////////
// Factory Methods //
// Static Methods //
///////////////////////////////////////////////////////////////////////////
// Produce an emulation core context
public static VUE create(boolean useNative) {
return !useNative ? new JavaVUE() :
NativeVUE.isLoaded() ? new NativeVUE() : null;
isNativeLoaded() ? new NativeVUE() : null;
}
// Retrieve the ID of the loaded native library, if any
public static String getNativeID() {
return nativeID;
}
// Determine whether the native module is loaded
public static boolean isNativeLoaded() {
return nativeID != null;
}
// Specify the ID of the loaded native library
public static void setNativeID(String nativeID) {
VUE.nativeID = nativeID;
}
@ -19,13 +39,16 @@ public interface VUE {
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Release any used resources
public abstract void dispose();
// Retrieve a copy of the ROM data
byte[] getROM();
public abstract byte[] getROM();
// Determine whether the context is native-backed
boolean isNative();
public abstract boolean isNative();
// Provide new ROM data
boolean setROM(byte[] data, int offset, int length);
public abstract boolean setROM(byte[] data, int offset, int length);
}