From 030fc96fc7d607bfd8589728816f1db5455ccdd2 Mon Sep 17 00:00:00 2001 From: Guy Perfect Date: Mon, 3 Aug 2020 13:51:46 -0500 Subject: [PATCH] Refining core interface, setROM() behavior validated --- makefile | 11 ++--- src/desktop/Main.java | 14 ++++--- src/desktop/app/App.java | 17 +++++++- src/desktop/app/Window.java | 13 +++++- src/desktop/native/native.c | 77 ++++++++++++++++++++++++++++++---- src/desktop/util/Util.java | 3 ++ src/desktop/vue/JavaVUE.java | 6 ++- src/desktop/vue/NativeVUE.java | 24 ++++------- src/desktop/vue/VUE.java | 35 +++++++++++++--- 9 files changed, 154 insertions(+), 46 deletions(-) diff --git a/makefile b/makefile index 0a46fc7..fe7eb2c 100644 --- a/makefile +++ b/makefile @@ -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 " @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 diff --git a/src/desktop/Main.java b/src/desktop/Main.java index dab1032..ca5d6a7 100644 --- a/src/desktop/Main.java +++ b/src/desktop/Main.java @@ -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()); } } diff --git a/src/desktop/app/App.java b/src/desktop/app/App.java index 97ed65f..192673a 100644 --- a/src/desktop/app/App.java +++ b/src/desktop/app/App.java @@ -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 windows; // Application windows @@ -24,10 +26,11 @@ public class App { public App() { // Instance fields - localizer = new Localizer(); - windows = new ArrayList(); + localizer = new Localizer(); + windows = new ArrayList(); // 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(); + } + /////////////////////////////////////////////////////////////////////////// diff --git a/src/desktop/app/Window.java b/src/desktop/app/Window.java index 1cd0ced..26e246f 100644 --- a/src/desktop/app/Window.java +++ b/src/desktop/app/Window.java @@ -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 diff --git a/src/desktop/native/native.c b/src/desktop/native/native.c index bbdb98f..2a70b3c 100644 --- a/src/desktop/native/native.c +++ b/src/desktop/native/native.c @@ -1,12 +1,72 @@ #include #include #include +#include #include #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; } diff --git a/src/desktop/util/Util.java b/src/desktop/util/Util.java index ce2c894..5e9263b 100644 --- a/src/desktop/util/Util.java +++ b/src/desktop/util/Util.java @@ -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 ), diff --git a/src/desktop/vue/JavaVUE.java b/src/desktop/vue/JavaVUE.java index 9e31b9a..f12b1d2 100644 --- a/src/desktop/vue/JavaVUE.java +++ b/src/desktop/vue/JavaVUE.java @@ -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]; diff --git a/src/desktop/vue/NativeVUE.java b/src/desktop/vue/NativeVUE.java index 295a1d6..ee3cfef 100644 --- a/src/desktop/vue/NativeVUE.java +++ b/src/desktop/vue/NativeVUE.java @@ -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(); } diff --git a/src/desktop/vue/VUE.java b/src/desktop/vue/VUE.java index 21e002d..7749f45 100644 --- a/src/desktop/vue/VUE.java +++ b/src/desktop/vue/VUE.java @@ -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); }