From 952605a8a4d754ad57899b691b10b611ce966c52 Mon Sep 17 00:00:00 2001 From: Guy Perfect Date: Tue, 4 Aug 2020 21:17:56 -0500 Subject: [PATCH] Finishing viewRead()/viewWrite() --- makefile | 20 ++--- src/core/bus.c | 147 ++++++++++++++++++++++++++------- src/core/include/vue.h | 3 +- src/core/vue.c | 61 ++++++++++++-- src/desktop/native/native.c | 24 +++++- src/desktop/vue/Bus.java | 143 ++++++++++++++++++++++++++++++++ src/desktop/vue/JavaVUE.java | 84 +++++++++++++++++-- src/desktop/vue/NativeVUE.java | 8 +- src/desktop/vue/VUE.java | 8 +- 9 files changed, 434 insertions(+), 64 deletions(-) create mode 100644 src/desktop/vue/Bus.java diff --git a/makefile b/makefile index 5e1391c..2830f0c 100644 --- a/makefile +++ b/makefile @@ -10,7 +10,7 @@ default: @echo @echo "Planet Virtual Boy Emulator" @echo " https://www.planetvb.com/" - @echo " August 3, 2020" + @echo " August 4, 2020" @echo @echo "Intended build environment: Debian i386 or amd64" @echo " gcc-multilib" @@ -20,12 +20,12 @@ default: @echo "Usage: make " @echo " Package recipes:" @echo " build Compiles the native modules and desktop application" - @echo " bundle Produces a .jar and deletes all intermediate files" @echo " clean Deletes all output files" @echo " core Check the native core library for style errors" @echo " desktop Compiles the Java desktop application" @echo " native Builds all native modules" @echo " pack Bundles everything in a .jar file" + @echo " release Produces a .jar and deletes all intermediate files" @echo " Native recipes:" @echo " lin32 Builds native module linux_x86" @echo " lin64 Builds native module linux_x86-64" @@ -46,14 +46,6 @@ build: @make -s desktop @make -s native -# Performs a full build and packages it into a .jar -.PHONY: bundle -bundle: - @make -s build - @make -s pack - @echo " Removing temporary files" - @make -s clean_most - # Delete all output files .PHONY: clean clean: clean_most @@ -95,6 +87,14 @@ pack: @jar -cfe $(jarname) Main *.class \ app images locale native src util vue makefile license.txt +# Performs a full build and packages it into a .jar +.PHONY: release +release: + @make -s build + @make -s pack + @echo " Removing temporary files" + @make -s clean_most + # Delete only Java .class files .PHONY: clean_desktop clean_desktop: diff --git a/src/core/bus.c b/src/core/bus.c index 051dfcc..97fead8 100644 --- a/src/core/bus.c +++ b/src/core/bus.c @@ -1,47 +1,134 @@ /* This file is included through vue.c and cannot be built directly. */ #ifdef VUEAPI -/* Read a value directly from host memory */ -static int32_t busReadMemory(uint8_t *data, int type) { +/* Read bytes from host memory */ +static void busReadBytes(uint8_t *src, uint8_t *dest, uint32_t address, + uint32_t size, uint32_t length) { - /* Host memory is little-endian */ - #ifndef VUE_BIGENDIAN - switch (type) { - case VUE_S8 : return *(int8_t *)data; - case VUE_U8 : return *(uint8_t *)data; - case VUE_S16: return *(int16_t *)data; - case VUE_U16: return *(uint16_t *)data; - case VUE_S32: return *(int32_t *)data; - } + /* There is no source data */ + if (src == NULL) + while (length-- > 0) + *dest++ = 0; + + /* Copy bytes from the source as a circular buffer */ + else for (size--; length-- > 0;) + *dest++ = src[address++ & size]; +} + +/* Read a value directly from host memory */ +static int32_t busReadMemory(uint8_t *data, uint32_t offset, int type) { + int32_t value; /* Value read */ + + /* There is no source data */ + if (data == NULL) + return 0; + + /* Locate the value */ + data = &data[offset]; + + /* Read the value */ + switch (type) { + case VUE_S8 : value = *(int8_t *)data; break; + case VUE_U8 : value = *(uint8_t *)data; break; + case VUE_S16: value = *(int16_t *)data; break; + case VUE_U16: value = *(uint16_t *)data; break; + case VUE_S32: value = *(int32_t *)data; break; + } /* Host memory is big-endian */ - #else + #ifdef VUE_BIGENDIAN switch (type) { - case VUE_S8 : return (int8_t ) data[0]; - case VUE_U8 : return (uint8_t ) data[0]; - case VUE_S16: return (int16_t ) data[0] << 8 | data[1]; - case VUE_U16: return (uint16_t) data[0] << 8 | data[1]; - case VUE_S32: return (int32_t ) - data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + case VUE_S32: + value = (value >> 16 & 0x0000FFFF) | value << 16; + /* Fallthrough */ + case VUE_S16: + case VUE_U16: + value = (value >> 8 & 0x00FF00FF) | (value << 8 & 0xFF00FF00); + break; } #endif - return 0; /* Unreachable, but the compiler doesn't know that */ + return value; } -/* Perform a read operation */ -static int32_t busReadValue(VUE *vue, uint32_t address, int type, vbool debug){ - address &= ~TYPE_SIZES[type] + 1; +/* Read a value from the CPU bus */ +static int32_t busReadValue(VUE *vue, uint32_t address, int type) { + address &= -TYPE_SIZES[type]; switch (address >> 24 & 7) { - case 3: /* Unmapped */ - case 4: return 0; /* Cartridge expansion */ - case 5: return busReadMemory(&vue->wram[address & 8191], type); - case 6: return vue->sram == NULL ? 0 : - busReadMemory(&vue->sram[address & (vue->sram_size - 1)], type); - case 7: return vue->rom == NULL ? 0 : - busReadMemory(&vue->rom [address & (vue->rom_size - 1)], type); + case 1: /* Fallthrough */ /* VSU (write-only) */ + case 3: /* Fallthrough */ /* Unmapped */ + case 4: /* Cartridge expansion (not supported) */ + return 0; + case 5: return /* System WRAM */ + busReadMemory(vue->wram, address & 0xFFFF , type); + case 6: return /* Cartridge RAM */ + busReadMemory(vue->sram, address & (vue->sram_size - 1), type); + case 7: return /* Cartridge ROM */ + busReadMemory(vue->rom , address & (vue->rom_size - 1), type); + } + return 0; /* Unreachable */ +} + +/* Write bytes to host memory */ +static void busWriteBytes(uint8_t *dest, uint8_t *src, uint32_t address, + uint32_t size, uint32_t length) { + if (dest != NULL) + for (size--; length-- > 0;) + dest[address++ & size] = *src++; +} + +/* Write a value directly to host memory */ +static void busWriteMemory(uint8_t *data, uint32_t offset, int type, + int32_t value) { + + /* There is no destination data */ + if (data == NULL) + return; + + /* Locate the value */ + data = &data[offset]; + + /* Host memory is big-endian */ + #ifdef VUE_BIGENDIAN + switch (type) { + case VUE_S32: + value = (value >> 16 & 0x0000FFFF) | value << 16; + /* Fallthrough */ + case VUE_S16: + case VUE_U16: + value = (value >> 8 & 0x00FF00FF) | (value << 8 & 0xFF00FF00); + break; + } + #endif + + /* Write the value */ + switch (type) { + case VUE_S8 : /* Fallthrough */ + case VUE_U8 : *(uint8_t *)data = (uint8_t) value; break; + case VUE_S16: /* Fallthrough */ + case VUE_U16: *(uint16_t *)data = (int16_t) value; break; + case VUE_S32: *(int32_t *)data = (int32_t) value; break; + } + +} + +/* Write a value to the CPU bus */ +static void busWriteValue(VUE *vue, uint32_t address, int type, int32_t value){ + address &= -TYPE_SIZES[type]; + switch (address >> 24 & 7) { + case 5: /* System WRAM */ + busWriteMemory(vue->wram, + address & 0xFFFF , type, value); + break; + case 6: /* Cartridge RAM */ + busWriteMemory(vue->sram, + address & (vue->sram_size - 1), type, value); + break; + case 7: /* Cartridge ROM */ + busWriteMemory(vue->rom , + address & (vue->rom_size - 1), type, value); + break; } - return debug * 0; /* Unreachable */ } #endif /* VUEAPI */ diff --git a/src/core/include/vue.h b/src/core/include/vue.h index a4e708d..7a86903 100644 --- a/src/core/include/vue.h +++ b/src/core/include/vue.h @@ -55,8 +55,9 @@ typedef struct { *****************************************************************************/ VUEAPI void vueInitialize(VUE *vue); -VUEAPI vbool vueRead (VUE *vue, uint32_t address, uint8_t *data, uint32_t length); +VUEAPI vbool vueRead (VUE *vue, uint32_t address, uint8_t *dest, uint32_t length); VUEAPI vbool vueSetROM (VUE *vue, uint8_t *rom, uint32_t size); +VUEAPI vbool vueWrite (VUE *vue, uint32_t address, uint8_t *src, uint32_t length); diff --git a/src/core/vue.c b/src/core/vue.c index 5236af4..0851f90 100644 --- a/src/core/vue.c +++ b/src/core/vue.c @@ -30,19 +30,16 @@ void vueInitialize(VUE *vue) { vue->sram = NULL; } -/* Perform a read operation */ -vbool vueRead(VUE *vue, uint32_t address, uint8_t *data, uint32_t length) { +/* Read bytes from the CPU bus */ +vbool vueRead(VUE *vue, uint32_t address, uint8_t *dest, uint32_t length) { uint32_t count; /* Bytes to read in one iteration */ /* Error checking */ - if ( - vue == NULL || - data == NULL || - length > 0x01000000 - ) return VUE_FALSE; + if (vue == NULL || dest == NULL) + return VUE_FALSE; /* Perform the operation */ - for (; length > 0; address += count, length -= count, data += count) { + for (; length > 0; address += count, length -= count, dest += count) { /* Determine the maximum number of bytes to process at once */ count = 0x01000000 - (address & 0x00FFFFFF); @@ -51,7 +48,20 @@ vbool vueRead(VUE *vue, uint32_t address, uint8_t *data, uint32_t length) { /* Process by component */ switch (address >> 24 & 7) { - /* case 7: ROM, etc */ + case 1: /* Fallthrough */ /* VSU (write-only) */ + case 3: /* Fallthrough */ /* Unmapped */ + case 4: /* Cartridge expansion (not supported) */ + busReadBytes(NULL , dest, address, 0 , count); + break; + case 5: /* System WRAM */ + busReadBytes(vue->wram, dest, address, 0x10000 , count); + break; + case 6: /* Cartridge RAM */ + busReadBytes(vue->sram, dest, address, vue->sram_size, count); + break; + case 7: /* Cartridge ROM */ + busReadBytes(vue->rom , dest, address, vue->rom_size , count); + break; } }; @@ -74,3 +84,36 @@ vbool vueSetROM(VUE *vue, uint8_t *rom, uint32_t size) { vue->rom_size = size; return VUE_TRUE; } + +/* Write bytes to the CPU bus */ +vbool vueWrite(VUE *vue, uint32_t address, uint8_t *src, uint32_t length) { + uint32_t count; /* Bytes to write in one iteration */ + + /* Error checking */ + if (vue == NULL || src == NULL) + return VUE_FALSE; + + /* Perform the operation */ + for (; length > 0; address += count, length -= count, src += count) { + + /* Determine the maximum number of bytes to process at once */ + count = 0x01000000 - (address & 0x00FFFFFF); + if (count > length) + count = length; + + /* Process by component */ + switch (address >> 24 & 7) { + case 5: /* System WRAM */ + busWriteBytes(vue->wram, src, address, 0x10000 , count); + break; + case 6: /* Cartridge RAM */ + busWriteBytes(vue->sram, src, address, vue->sram_size, count); + break; + case 7: /* Cartridge ROM */ + busWriteBytes(vue->rom , src, address, vue->rom_size , count); + break; + } + + }; + return VUE_TRUE; +} diff --git a/src/desktop/native/native.c b/src/desktop/native/native.c index d26a2b3..3f0a875 100644 --- a/src/desktop/native/native.c +++ b/src/desktop/native/native.c @@ -96,7 +96,7 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_isNative return JNI_TRUE; } -// Perform a read access +// Read bytes from the CPU bus JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_read (JNIEnv *env, jobject vue, jint address, jbyteArray data, jint offset, jint length) { @@ -106,7 +106,6 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_read data == NULL || offset < 0 || length < 0 || - length < 0 || length > 0x01000000 || offset + length > (*env)->GetArrayLength(env, data) ) return JNI_FALSE; @@ -143,3 +142,24 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_setROM vueSetROM(&core->vue, rom, length); return JNI_TRUE; } + +/* Write bytes to the CPU bus */ +JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_write + (JNIEnv *env, jobject vue, jint address, jbyteArray data, jint offset, + jint length) { + + // Error checking + if ( + data == NULL || + offset < 0 || + length < 0 || + offset + length > (*env)->GetArrayLength(env, data) + ) return JNI_FALSE; + + // Perform the operation + CORE *core = GetCore(env, vue); + jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL); + vueWrite(&core->vue, address, &elems[offset], length); + (*env)->ReleaseByteArrayElements(env, data, elems, 0); + return JNI_TRUE; +} diff --git a/src/desktop/vue/Bus.java b/src/desktop/vue/Bus.java new file mode 100644 index 0000000..adfbb13 --- /dev/null +++ b/src/desktop/vue/Bus.java @@ -0,0 +1,143 @@ +package vue; + +// Java imports +import java.util.*; + +// Simulation of the CPU bus +class Bus { + + // Instance fields + private JavaVUE vue; // Emulation state + + + + /////////////////////////////////////////////////////////////////////////// + // Constructors // + /////////////////////////////////////////////////////////////////////////// + + // Default constructor + Bus(JavaVUE vue) { + this.vue = vue; + } + + // Read bytes from host memory + void readBytes(byte[] src,byte[] dest,int address,int offset,int length) { + + // There is no source data + if (src == null) { + Arrays.fill(dest, offset, offset + length, (byte) 0); + return; + } + + // Copy bytes from the source as a circular buffer + int count = 0; // Bytes to process in one iteration + for (; length > 0; address += count, length -= count, offset += count){ + int from = address & src.length - 1; + count = Math.min(length, src.length - from); + System.arraycopy(src, from, dest, offset, count); + } + + } + + // Read a value directly from host memory + int readMemory(byte[] data, int offset, int type) { + + // There is no source data + if (data == null) + return 0; + + // Processing by type + switch (type) { + case VUE.S8 : return data[offset]; + case VUE.U8 : return data[offset] & 0xFF; + case VUE.S16: return + data[offset+1] << 8 | data[offset ] & 0xFF; + case VUE.U16: return + (data[offset+1] & 0xFF) << 8 | data[offset ] & 0xFF; + case VUE.S32: return + data[offset+3] << 24 | (data[offset+2] & 0xFF) << 16 | + (data[offset+1] & 0xFF) << 8 | (data[offset ] & 0xFF); + } + + return 0; // Unreachable + } + + // Read a value from the CPU bus + int readValue(int address, int type) { + address &= ~JavaVUE.TYPE_SIZES[type] + 1; + switch (address >> 24 & 7) { + case 1: // Fallthrough, VSU (write-only) + case 3: // Fallthrough, unmapped + case 4: // Cartridge expansion (not supported) + return 0; + case 5: return + readMemory(vue.wram, address & 0xFFFF , type); + case 6: return + readMemory(vue.sram, address & vue.sram.length - 1, type); + case 7: return + readMemory(vue.rom , address & vue.rom .length - 1, type); + } + return 0; // Unreachable + } + + // Write bytes to host memory + void writeBytes(byte[] dest,byte[] src,int address,int offset,int length) { + + // There is no destination data + if (dest != null) + return; + + // Copy bytes to the destination as a circular buffer + int count = 0; // Bytes to process in one iteration + for (; length > 0; address += count, length -= count, offset += count){ + int to = address & dest.length - 1; + count = Math.min(length, dest.length - to); + System.arraycopy(src, offset, dest, to, count); + } + + } + + // Write a value directly to host memory + void writeMemory(byte[] data, int offset, int type, int value) { + + // There is no source data + if (data == null) + return; + + // Processing by type + switch (type) { + case VUE.S32: + data[offset + 3] = (byte) (value >> 24); + data[offset + 2] = (byte) (value >> 16); + // Fallthrough + case VUE.S16: // Fallthrough + case VUE.U16: + data[offset + 1] = (byte) (value >> 8); + // Fallthrough + case VUE.S8 : // Fallthrough + case VUE.U8 : + data[offset ] = (byte) value ; + } + + } + + // Write a value to the CPU bus + void writeValue(int address, int type, int value) { + address &= ~JavaVUE.TYPE_SIZES[type] + 1; + switch (address >> 24 & 7) { + case 5: + writeMemory(vue.wram, + address & 0xFFFF , type, value); + break; + case 6: + writeMemory(vue.sram, + address & vue.sram.length - 1, type, value); + break; + case 7: + writeMemory(vue.rom , + address & vue.rom .length - 1, type, value); + break; + } + } + +} diff --git a/src/desktop/vue/JavaVUE.java b/src/desktop/vue/JavaVUE.java index 77d7843..84ee665 100644 --- a/src/desktop/vue/JavaVUE.java +++ b/src/desktop/vue/JavaVUE.java @@ -4,8 +4,12 @@ package vue; class JavaVUE extends VUE { // Instance fields - private byte[] rom; // Cartridge ROM - private byte[] sram; // Cartridge SRAM + byte[] rom; // Cartridge ROM + byte[] sram; // Cartridge SRAM + byte[] wram; // System WRAM + + // Components + Bus bus; // Memory bus @@ -14,7 +18,7 @@ class JavaVUE extends VUE { /////////////////////////////////////////////////////////////////////////// // Memory access type sizes - private static final int[] TYPE_SIZES = { 1, 1, 2, 2, 4 }; + static final int[] TYPE_SIZES = { 1, 1, 2, 2, 4 }; @@ -24,6 +28,8 @@ class JavaVUE extends VUE { // Default constructor JavaVUE() { + bus = new Bus(this); + wram = new byte[0x10000]; } @@ -47,19 +53,46 @@ class JavaVUE extends VUE { return false; } - // Perform a read operation - public boolean read(int address, byte[] data, int offset, int length) { + // Read bytes from the CPU bus + public boolean read(int address, byte[] dest, int offset, int length) { + int count = 0; // Bytes to read in one iteration // Error checking - if (data == null || + if ( + dest == null || offset < 0 || length < 0 || - length < 0 || length > 0x01000000 || - offset + length > data.length + offset + length > dest.length ) return false; // Perform the operation + for (; length > 0; address += count, length -= count, offset += count){ + // Determine the maximum number of bytes to process at once + count = Math.min(length, 0x01000000 - (address & 0x00FFFFFF)); + + // Process by component + switch (address >> 24 & 7) { + case 1: // VSU + // Will support debug reads + bus.readBytes(null, dest, address, 0 , count); + break; + case 3: // Fallthrough, unmapped + case 4: // Cartridge expansion (not supported) + bus.readBytes(null, dest, address, 0 , count); + break; + case 5: /* System WRAM */ + bus.readBytes(wram, dest, address, offset, count); + break; + case 6: /* Cartridge RAM */ + bus.readBytes(sram, dest, address, offset, count); + break; + case 7: /* Cartridge ROM */ + bus.readBytes(rom , dest, address, offset, count); + break; + } + + }; return true; } @@ -81,4 +114,39 @@ class JavaVUE extends VUE { return true; } + // Write bytes to the CPU bus + public boolean write(int address, byte[] src, int offset, int length) { + int count = 0; // Bytes to process in one iteration + + // Error checking + if ( + src == null || + offset < 0 || + length < 0 || + offset + length > src.length + ) return false; + + // Perform the operation + for (; length > 0; address += count, length -= count, offset += count){ + + // Determine the maximum number of bytes to process at once + count = Math.min(length, 0x01000000 - (address & 0x00FFFFFF)); + + // Process by component + switch (address >> 24 & 7) { + case 5: /* System WRAM */ + bus.writeBytes(wram, src, address, offset, count); + break; + case 6: /* Cartridge RAM */ + bus.writeBytes(sram, src, address, offset, count); + break; + case 7: /* Cartridge ROM */ + bus.writeBytes(rom , src, address, offset, count); + break; + } + + }; + return true; + } + } diff --git a/src/desktop/vue/NativeVUE.java b/src/desktop/vue/NativeVUE.java index 5840132..e0e020c 100644 --- a/src/desktop/vue/NativeVUE.java +++ b/src/desktop/vue/NativeVUE.java @@ -32,13 +32,17 @@ class NativeVUE extends VUE { // Determine whether the context is native-backed public native boolean isNative(); - // Perform a read operation - public native boolean read(int address, byte[] data, int offset, + // Read bytes from the CPU bus + public native boolean read(int address, byte[] dest, int offset, int length); // Provide new ROM data public native boolean setROM(byte[] data, int offset, int length); + // Write bytes to the CPU bus + public native boolean write(int address, byte[] src, int offset, + int length); + /////////////////////////////////////////////////////////////////////////// diff --git a/src/desktop/vue/VUE.java b/src/desktop/vue/VUE.java index 6ec6c27..a1c14ce 100644 --- a/src/desktop/vue/VUE.java +++ b/src/desktop/vue/VUE.java @@ -61,11 +61,15 @@ public abstract class VUE { // Determine whether the context is native-backed public abstract boolean isNative(); - // Perform a read operation - public abstract boolean read(int address, byte[] data, int offset, + // Read bytes from the CPU bus + public abstract boolean read(int address, byte[] dest, int offset, int length); // Provide new ROM data public abstract boolean setROM(byte[] data, int offset, int length); + // Write bytes to the CPU bus + public abstract boolean write(int address, byte[] src, int offset, + int length); + }