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
|
# 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
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ),
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue