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 # Java include directory pathnames
# The Windows files need to be copied from a Windows JDK installation # 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 uname -r | sed 's/.*-//'`/include
include_windows = jni-windows-include include_windows = jni-windows-include
@ -10,12 +10,12 @@ default:
@echo @echo
@echo "Planet Virtual Boy Emulator" @echo "Planet Virtual Boy Emulator"
@echo " https://www.planetvb.com/" @echo " https://www.planetvb.com/"
@echo " July 30, 2020" @echo " August 3, 2020"
@echo @echo
@echo "Intended build environment: Debian i386 or amd64" @echo "Intended build environment: Debian i386 or amd64"
@echo " gcc-multilib" @echo " gcc-multilib"
@echo " mingw-w64" @echo " mingw-w64"
@echo " openjdk-13-jdk" @echo " openjdk-14-jdk"
@echo @echo
@echo "Usage: make <recipe>" @echo "Usage: make <recipe>"
@echo " Package recipes:" @echo " Package recipes:"
@ -74,7 +74,7 @@ core:
desktop: clean_desktop desktop: clean_desktop
@echo " Compiling Java desktop application" @echo " Compiling Java desktop application"
@javac -sourcepath src/desktop --release 10 -Xlint:unchecked \ @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 # Build all native modules
.PHONY: native .PHONY: native
@ -112,6 +112,7 @@ clean_most: clean_desktop
src/desktop/native/vue_NativeVUE.h: src/desktop/vue/NativeVUE.java src/desktop/native/vue_NativeVUE.h: src/desktop/vue/NativeVUE.java
@javac -h src/desktop/native -sourcepath src/desktop -d . \ @javac -h src/desktop/native -sourcepath src/desktop -d . \
src/desktop/vue/NativeVUE.java src/desktop/vue/NativeVUE.java
@sleep 3
# linux_x86 # linux_x86
.PHONY: lin32_pre .PHONY: lin32_pre
@ -159,6 +160,6 @@ win64: win64_pre native_common
.PHONY: native_common .PHONY: native_common
native_common: native_common:
@echo " Building native module $(name)" @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 \ -fno-strict-aliasing \
-o native/$(name)$(ext) src/desktop/native/native.c src/core/vue.c -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")) { for (String filename : Util.listFiles("native")) {
File file = null; File file = null;
// Skip .gitignore // Skip any file that isn't a shared object library
if (filename.equals(".gitignore")) if (!filename.endsWith(".dll") && !filename.endsWith(".so"))
continue; continue;
// Write the contents of the native object file // Write the contents of the native object file
@ -33,15 +33,17 @@ public class Main {
try { stream.close(); } catch (Exception e) { } try { stream.close(); } catch (Exception e) { }
// Load the native object file into the JVM // 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) { } catch (Error e) { }
} }
// Begin application operations // Begin application operations
new App(); new App();
var vue = VUE.create(true);
System.out.println(vue.isNative());
} }
} }

View File

@ -5,6 +5,7 @@ import java.util.*;
// Project imports // Project imports
import util.*; import util.*;
import vue.*;
// Top-level software state manager // Top-level software state manager
public class App { public class App {
@ -12,6 +13,7 @@ public class App {
// Instance fields // Instance fields
private Localizer.Locale[] locales; // Language translations private Localizer.Locale[] locales; // Language translations
private Localizer localizer; // UI localization manager private Localizer localizer; // UI localization manager
private boolean useNative; // Produce native core contexts
private ArrayList<Window> windows; // Application windows private ArrayList<Window> windows; // Application windows
@ -28,6 +30,7 @@ public class App {
windows = new ArrayList<Window>(); windows = new ArrayList<Window>();
// Additional processing // Additional processing
setUseNative(true);
initLocales(); initLocales();
addWindow(); addWindow();
} }
@ -49,6 +52,11 @@ public class App {
return localizer.getLocale(); return localizer.getLocale();
} }
// Determine whether the native module is in use
boolean getUseNative() {
return useNative;
}
// Retrieve the localization manager // Retrieve the localization manager
Localizer getLocalizer() { Localizer getLocalizer() {
return localizer; return localizer;
@ -77,6 +85,11 @@ public class App {
localizer.setLocale(locale); 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 // Project imports
import util.*; import util.*;
import vue.*;
// Main application window // Main application window
class Window extends JFrame { class Window extends JFrame {
@ -19,6 +20,7 @@ class Window extends JFrame {
private boolean only; // This is the only application window private boolean only; // This is the only application window
private ROM rom; // Currently loaded ROM private ROM rom; // Currently loaded ROM
private File romFile; // Currently loaded ROM file private File romFile; // Currently loaded ROM file
private VUE vue; // Emulation core context
// UI components // UI components
private File pwd; // Most recent working directory private File pwd; // Most recent working directory
@ -50,6 +52,8 @@ class Window extends JFrame {
// Configure instance fields // Configure instance fields
this.app = app; this.app = app;
pwd = Util.PWD;
vue = VUE.create(app.getUseNative());
// Configure video pane // Configure video pane
video = new JPanel() { video = new JPanel() {
@ -138,6 +142,7 @@ class Window extends JFrame {
private void onClose() { private void onClose() {
app.removeWindow(this); app.removeWindow(this);
dispose(); dispose();
vue.dispose();
} }
// File -> Load ROM // File -> Load ROM
@ -185,11 +190,17 @@ class Window extends JFrame {
return; return;
} }
// Update the emulation state // Update instance fields
this.rom = rom; this.rom = rom;
romFile = file; romFile = file;
loc.put(this, "ctrl.filename", file.getName()); loc.put(this, "ctrl.filename", file.getName());
updateTitle(); updateTitle();
// Update the emulation state
var bytes = rom.toByteArray();
// Pause emulation
vue.setROM(bytes, 0, bytes.length);
// Resume emulation
} }
// File -> New window // File -> New window

View File

@ -1,12 +1,72 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <jni.h> #include <jni.h>
#include "vue_NativeVUE.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 // Retrieve a copy of the ROM data
JNIEXPORT jbyteArray JNICALL Java_vue_NativeVUE_getROM JNIEXPORT jbyteArray JNICALL Java_vue_NativeVUE_getROM
(JNIEnv *env, jobject vue) { (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 // Determine whether the context is native-backed
@ -18,19 +78,20 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_isNative
// Provide new ROM data // Provide new ROM data
JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_setROM JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_setROM
(JNIEnv *env, jobject vue, jbyteArray data, jint offset, jint length) { (JNIEnv *env, jobject vue, jbyteArray data, jint offset, jint length) {
jsize data_length = (*env)->GetArrayLength(env, data);
// Error checking // Error checking
if (data == NULL || offset < 0 || length < 1024 || 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; return JNI_FALSE;
// Accept the new ROM data // Accept the new ROM data
return JNI_TRUE; CORE *core = GetCore(env, vue);
} free(core->rom);
core->rom = calloc(length, 1);
// Invoke native code to ensure the module is loaded jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL);
JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_testNative memcpy(core->rom, &elems[offset], length);
(JNIEnv *env, jclass vue) { (*env)->ReleaseByteArrayElements(env, data, elems, 0);
core->rom_size = (uint32_t) length;
return JNI_TRUE; return JNI_TRUE;
} }

View File

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

View File

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

View File

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

View File

@ -1,16 +1,36 @@
package vue; package vue;
// Template for emulation core implementations // 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 // Produce an emulation core context
public static VUE create(boolean useNative) { public static VUE create(boolean useNative) {
return !useNative ? new JavaVUE() : 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 // // Public Methods //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Release any used resources
public abstract void dispose();
// Retrieve a copy of the ROM data // Retrieve a copy of the ROM data
byte[] getROM(); public abstract byte[] getROM();
// Determine whether the context is native-backed // Determine whether the context is native-backed
boolean isNative(); public abstract boolean isNative();
// Provide new ROM data // Provide new ROM data
boolean setROM(byte[] data, int offset, int length); public abstract boolean setROM(byte[] data, int offset, int length);
} }