Refining core interface, setROM() behavior validated
This commit is contained in:
parent
b86c046dd5
commit
030fc96fc7
11
makefile
11
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 <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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
@ -24,10 +26,11 @@ public class App {
|
|||
public App() {
|
||||
|
||||
// Instance fields
|
||||
localizer = new Localizer();
|
||||
windows = new ArrayList<Window>();
|
||||
localizer = new Localizer();
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 ),
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue