From 2e38c1fa49a65f479911414cd7495d4178d51ef3 Mon Sep 17 00:00:00 2001 From: Guy Perfect Date: Wed, 5 Aug 2020 20:40:23 -0500 Subject: [PATCH] Laying the groundwork for the CPU --- src/core/bus.c | 23 ++-- src/core/cpu.c | 156 +++++++++++++++++++++++ src/core/include/vue.h | 88 +++++++++++-- src/core/vue.c | 53 +++++--- src/desktop/app/App.java | 4 +- src/desktop/app/ChildWindow.java | 8 +- src/desktop/app/MainWindow.java | 2 +- src/desktop/app/Memory.java | 1 + src/desktop/native/native.c | 35 ++++-- src/desktop/vue/Bus.java | 55 ++++---- src/desktop/vue/CPU.java | 208 +++++++++++++++++++++++++++++++ src/desktop/vue/JavaVUE.java | 58 ++++++--- src/desktop/vue/NativeVUE.java | 6 + src/desktop/vue/VUE.java | 19 +++ 14 files changed, 620 insertions(+), 96 deletions(-) create mode 100644 src/core/cpu.c create mode 100644 src/desktop/vue/CPU.java diff --git a/src/core/bus.c b/src/core/bus.c index 97fead8..9ace5dd 100644 --- a/src/core/bus.c +++ b/src/core/bus.c @@ -59,12 +59,12 @@ static int32_t busReadValue(VUE *vue, uint32_t address, int type) { 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); + case 5: /* System WRAM */ + return busReadMemory(vue->bus.wram, address & 0xFFFF, type); + case 6: return busReadMemory(vue->bus.sram, /* Cartridge RAM */ + address & (vue->bus.sram_size - 1), type); + case 7: return busReadMemory(vue->bus.rom , /* Cartridge ROM */ + address & (vue->bus.rom_size - 1), type); } return 0; /* Unreachable */ } @@ -117,16 +117,15 @@ 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); + busWriteMemory(vue->bus.wram, address & 0xFFFF, type, value); break; case 6: /* Cartridge RAM */ - busWriteMemory(vue->sram, - address & (vue->sram_size - 1), type, value); + busWriteMemory(vue->bus.sram, + address & (vue->bus.sram_size - 1), type, value); break; case 7: /* Cartridge ROM */ - busWriteMemory(vue->rom , - address & (vue->rom_size - 1), type, value); + busWriteMemory(vue->bus.rom , + address & (vue->bus.rom_size - 1), type, value); break; } } diff --git a/src/core/cpu.c b/src/core/cpu.c new file mode 100644 index 0000000..f62ed58 --- /dev/null +++ b/src/core/cpu.c @@ -0,0 +1,156 @@ +/* This file is included through vue.c and cannot be built directly. */ +#ifdef VUEAPI + + + +/***************************************************************************** + * Constants * + *****************************************************************************/ + +/* Stages */ +#define CPU_FETCH 0 +#define CPU_EXECUTE 1 +#define CPU_HALT 2 +#define CPU_EXCEPTION 3 +#define CPU_FATAL 4 +#define CPU_CLEAR 5 +#define CPU_DUMP 6 +#define CPU_RESTORE 7 + + + +/***************************************************************************** + * Module Functions * + *****************************************************************************/ + +/* Read a system register */ +static int32_t cpuGetSystemRegister(VUE *vue, int index) { + switch (index) { + case VUE_ADTRE: return vue->cpu.adtre; + case VUE_EIPC : return vue->cpu.eipc; + case VUE_EIPSW: return vue->cpu.eipsw; + case VUE_FEPC : return vue->cpu.fepc; + case VUE_FEPSW: return vue->cpu.fepsw; + case VUE_ECR : return vue->cpu.ecr_fecc << 16 | vue->cpu.ecr_eicc; + case VUE_PIR : return 0x00005346; + case VUE_TKCW : return 0x000000E0; + case VUE_CHCW : return vue->cpu.chcw_ice << 1; + case 29 : return vue->cpu.sr29; + case 30 : return 0x00000004; + case 31 : return vue->cpu.sr31; + case VUE_PSW : return + vue->cpu.psw_i << 16 | vue->cpu.psw_fov << 6 | + vue->cpu.psw_np << 15 | vue->cpu.psw_fud << 5 | + vue->cpu.psw_ep << 14 | vue->cpu.psw_fpr << 4 | + vue->cpu.psw_ae << 13 | vue->cpu.psw_cy << 3 | + vue->cpu.psw_id << 12 | vue->cpu.psw_ov << 2 | + vue->cpu.psw_fro << 9 | vue->cpu.psw_s << 1 | + vue->cpu.psw_fiv << 8 | vue->cpu.psw_z | + vue->cpu.psw_fzd << 7 + ; + + /* Remaining cases to encourage tableswitch */ + case 8: case 9: case 10: case 11: case 12: case 13: case 14: + case 15: case 16: case 17: case 18: case 19: case 20: case 21: + case 22: case 23: case 26: case 27: case 28: + return 0; + } + return 1; /* Unreachable */ +} + +/* Write a system register */ +static int32_t cpuSetSystemRegister(VUE *vue, int index, int32_t value, + vbool debug) { + switch (index) { + case VUE_ADTRE: return vue->cpu.adtre = value & 0xFFFFFFFE; + case VUE_EIPC : return vue->cpu.eipc = value & 0xFFFFFFFE; + case VUE_EIPSW: return vue->cpu.eipsw = value & 0x000FF3FF; + case VUE_FEPC : return vue->cpu.fepc = value & 0xFFFFFFFE; + case VUE_FEPSW: return vue->cpu.fepsw = value & 0x000FF3FF; + case 29 : return vue->cpu.sr29 = value; + case 31 : return vue->cpu.sr31 = + debug || value >= 0 ? value : -value; + + case VUE_CHCW : + vue->cpu.chcw_cen = value >> 20 & 0x00000FFF; + vue->cpu.chcw_cec = value >> 8 & 0x00000FFF; + vue->cpu.chcw_sa = value >> 8 & 0x00FFFFFF; + vue->cpu.chcw_icr = value >> 5 & 1; + vue->cpu.chcw_icd = value >> 4 & 1; + vue->cpu.chcw_ice = value >> 1 & 1; + vue->cpu.chcw_icc = value & 1; + + /* Only one of ICC, ICD or ICR is set */ + value &= 0x00000031; + if ((value & (value - 1)) == 0) { + /* Clear */ + /* Dump */ + /* Restore */ + } + + return vue->cpu.chcw_ice << 1; + + case VUE_PSW : + vue->cpu.psw_i = value >> 16 & 15; + vue->cpu.psw_np = value >> 15 & 1; + vue->cpu.psw_ep = value >> 14 & 1; + vue->cpu.psw_ae = value >> 13 & 1; + vue->cpu.psw_id = value >> 12 & 1; + vue->cpu.psw_fro = value >> 9 & 1; + vue->cpu.psw_fiv = value >> 8 & 1; + vue->cpu.psw_fzd = value >> 7 & 1; + vue->cpu.psw_fov = value >> 6 & 1; + vue->cpu.psw_fud = value >> 5 & 1; + vue->cpu.psw_fpr = value >> 4 & 1; + vue->cpu.psw_cy = value >> 3 & 1; + vue->cpu.psw_ov = value >> 2 & 1; + vue->cpu.psw_s = value >> 1 & 1; + vue->cpu.psw_z = value & 1; + return value & 0x000FF3FF; + + /* Remaining cases to encourage tableswitch */ + case 4: case 6: case 7: case 8: case 9: case 10: case 11: + case 12: case 13: case 14: case 15: case 16: case 17: case 18: + case 19: case 20: case 21: case 22: case 23: case 26: case 27: + case 28: case 30: + return 0; + } + return 1; /* Unreachable */ +} + +/* System reset */ +static void cpuReset(VUE *vue) { + int x; + + /* Configure instance fields */ + vue->cpu.cycles = 0; + vue->cpu.fetch = 0; + vue->cpu.stage = CPU_FETCH; + + /* Reset program counter */ + vue->cpu.pc = 0xFFFFFFF0; + + /* Clear all registers (hardware only sets ECR, PC and PSW) */ + for (x = 0; x < 32; x++) { + vue->cpu.program[x] = 0; + cpuSetSystemRegister(vue, x, 0, VUE_TRUE); + } + +} + +/* Test a condition */ +static int8_t cpuTest(VUE *vue, int condition) { + switch (condition) { + case 0: return vue->cpu.psw_ov; + case 1: return vue->cpu.psw_cy; + case 2: return vue->cpu.psw_z; + case 3: return vue->cpu.psw_cy | vue->cpu.psw_z; + case 4: return vue->cpu.psw_s; + case 5: return 1; + case 6: return vue->cpu.psw_ov | vue->cpu.psw_s; + case 7: return (vue->cpu.psw_ov ^ vue->cpu.psw_s) | vue->cpu.psw_z; + } + return cpuTest(vue, condition & 7) ^ 1; +} + +#endif diff --git a/src/core/include/vue.h b/src/core/include/vue.h index 7a86903..9cfbb84 100644 --- a/src/core/include/vue.h +++ b/src/core/include/vue.h @@ -30,6 +30,19 @@ extern "C" { #define VUE_U16 3 #define VUE_S32 4 +/* System register indexes */ +#define VUE_ADTRE 25 +#define VUE_CHCW 24 +#define VUE_ECR 4 +#define VUE_EIPC 0 +#define VUE_EIPSW 1 +#define VUE_FEPC 2 +#define VUE_FEPSW 3 +#define VUE_PC -1 +#define VUE_PIR 6 +#define VUE_PSW 5 +#define VUE_TKCW 7 + /***************************************************************************** @@ -41,11 +54,66 @@ typedef int vbool; /* Emulation state */ typedef struct { - uint8_t *rom; /* Cartridge ROM */ - uint32_t rom_size; /* Number of bytes in cartridge ROM */ - uint8_t *sram; /* Cartridge RAM */ - uint32_t sram_size; /* Number of bytes in cartridge RAM */ - uint8_t wram[0x10000]; /* System memory */ + + /* Memory bus */ + struct { + uint8_t *rom; /* Cartridge ROM */ + uint32_t rom_size; /* Number of bytes in cartridge ROM */ + uint8_t *sram; /* Cartridge RAM */ + uint32_t sram_size; /* Number of bytes in cartridge RAM */ + uint8_t wram[0x10000]; /* System memory */ + } bus; + + /* CPU state */ + struct { + uint32_t cycles; /* Cycles until next stage */ + int fetch; /* Fetch unit index */ + int stage; /* Current processing stage */ + + /* Program registers */ + int32_t program[32]; + + /* System registers */ + int32_t adtre; /* Address Trap Register for Execution */ + int32_t eipc; /* Exception/interrupt PC */ + int32_t eipsw; /* Exception/interrupt PSW */ + int32_t fepc; /* Duplexed exception PC */ + int32_t fepsw; /* Duplexed exception PSW */ + int32_t pc; /* Program Counter */ + int32_t sr29; /* System register 29 */ + int32_t sr31; /* System register 31 */ + + /* Program Status Word */ + int8_t psw_ae; /* Address Trap Enable */ + int8_t psw_ep; /* Exception Pending */ + int8_t psw_id; /* Interrupt Disable */ + int8_t psw_cy; /* Carry */ + int8_t psw_fiv; /* Floating Reserved Operand */ + int8_t psw_fov; /* Floating Overflow */ + int8_t psw_fpr; /* Floating Precision */ + int8_t psw_fro; /* Floating Reserved Operand */ + int8_t psw_fud; /* Floating Underflow */ + int8_t psw_fzd; /* Floating Zero Divide */ + int8_t psw_i; /* Interrupt Level */ + int8_t psw_np; /* NMI Pending */ + int8_t psw_ov; /* Overflow */ + int8_t psw_s; /* Sign */ + int8_t psw_z; /* Zero */ + + /* Cache Control Word */ + int32_t chcw_cec; /* Clear Entry Count */ + int32_t chcw_cen; /* Clear Entry Number */ + int8_t chcw_icc; /* Instruction Cache Clear */ + int8_t chcw_icd; /* Instruction Cache Dump */ + int8_t chcw_ice; /* Instruction Cache Enable */ + int8_t chcw_icr; /* Instruction Cache Restore */ + int32_t chcw_sa; /* Spill-Out Base Address */ + + /* Exception Cause Register */ + int8_t ecr_eicc; /* Exception/Interrupt Cause Code */ + int8_t ecr_fecc; /* Fatal Error Cause Code */ + } cpu; + } VUE; @@ -54,10 +122,12 @@ typedef struct { * Function Prototypes * *****************************************************************************/ -VUEAPI void vueInitialize(VUE *vue); -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); +VUEAPI int32_t vueGetRegister(VUE *vue, int index, vbool system); +VUEAPI void vueInitialize (VUE *vue); +VUEAPI vbool vueRead (VUE *vue, uint32_t address, uint8_t *dest, uint32_t length); +VUEAPI int32_t vueSetRegister(VUE *vue, int index, vbool system, int32_t value); +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 0851f90..fbf8208 100644 --- a/src/core/vue.c +++ b/src/core/vue.c @@ -17,6 +17,7 @@ static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 }; *****************************************************************************/ #include "bus.c" +#include "cpu.c" @@ -24,10 +25,20 @@ static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 }; * Library Functions * *****************************************************************************/ +/* Retrieve the value of a register */ +int32_t vueGetRegister(VUE *vue, int index, vbool system) { + return + index == VUE_PC && system ? vue->cpu.pc : + index < 0 || index > 31 ? 0 : + system ? cpuGetSystemRegister(vue, index) : + vue->cpu.program[index] + ; +} + /* Prepare an emulation state context for use */ void vueInitialize(VUE *vue) { - vue->rom = NULL; - vue->sram = NULL; + vue->bus.rom = NULL; + vue->bus.sram = NULL; } /* Read bytes from the CPU bus */ @@ -51,16 +62,16 @@ vbool vueRead(VUE *vue, uint32_t address, uint8_t *dest, uint32_t length) { case 1: /* Fallthrough */ /* VSU (write-only) */ case 3: /* Fallthrough */ /* Unmapped */ case 4: /* Cartridge expansion (not supported) */ - busReadBytes(NULL , dest, address, 0 , count); + busReadBytes(NULL, dest, address, 0, count); break; - case 5: /* System WRAM */ - busReadBytes(vue->wram, dest, address, 0x10000 , count); + case 5: busReadBytes(vue->bus.wram, dest, /* System WRAM */ + address, 0x10000 , count); break; - case 6: /* Cartridge RAM */ - busReadBytes(vue->sram, dest, address, vue->sram_size, count); + case 6: busReadBytes(vue->bus.sram, dest, /* Cartridge RAM */ + address, vue->bus.sram_size, count); break; - case 7: /* Cartridge ROM */ - busReadBytes(vue->rom , dest, address, vue->rom_size , count); + case 7: busReadBytes(vue->bus.rom , dest, /* Cartridge ROM */ + address, vue->bus.rom_size , count); break; } @@ -68,6 +79,16 @@ vbool vueRead(VUE *vue, uint32_t address, uint8_t *dest, uint32_t length) { return VUE_TRUE; } +/* Specify a value for a register */ +int32_t vueSetRegister(VUE *vue, int index, vbool system, int32_t value) { + return + index == VUE_PC && system ? vue->cpu.pc = value & 0xFFFFFFFE : + index < 0 || index > 31 ? 0 : + system ? cpuSetSystemRegister(vue, index, value, VUE_TRUE) : + index == 0 ? 0 : vue->cpu.program[index] + ; +} + /* Specify a new ROM buffer */ vbool vueSetROM(VUE *vue, uint8_t *rom, uint32_t size) { @@ -80,8 +101,8 @@ vbool vueSetROM(VUE *vue, uint8_t *rom, uint32_t size) { ) return VUE_FALSE; /* Accept the new ROM buffer */ - vue->rom = rom; - vue->rom_size = size; + vue->bus.rom = rom; + vue->bus.rom_size = size; return VUE_TRUE; } @@ -104,13 +125,13 @@ vbool vueWrite(VUE *vue, uint32_t address, uint8_t *src, uint32_t length) { /* Process by component */ switch (address >> 24 & 7) { case 5: /* System WRAM */ - busWriteBytes(vue->wram, src, address, 0x10000 , count); + busWriteBytes(vue->bus.wram, src, address, 0x10000, count); break; - case 6: /* Cartridge RAM */ - busWriteBytes(vue->sram, src, address, vue->sram_size, count); + case 6: busWriteBytes(vue->bus.sram, src, /* Cartridge RAM */ + address, vue->bus.sram_size, count); break; - case 7: /* Cartridge ROM */ - busWriteBytes(vue->rom , src, address, vue->rom_size , count); + case 7: busWriteBytes(vue->bus.rom , src, /* Cartridge ROM */ + address, vue->bus.rom_size , count); break; } diff --git a/src/desktop/app/App.java b/src/desktop/app/App.java index 316991e..a39f8a4 100644 --- a/src/desktop/app/App.java +++ b/src/desktop/app/App.java @@ -87,9 +87,7 @@ public class App { // Specify whether using the native module boolean setUseNative(boolean useNative) { - this.useNative = useNative && VUE.isNativeLoaded(); - System.out.println("Native: " + (this.useNative ? "Yes" : "No")); - return this.useNative; + return this.useNative = useNative && VUE.isNativeLoaded(); } diff --git a/src/desktop/app/ChildWindow.java b/src/desktop/app/ChildWindow.java index 2dfd246..9f90a8d 100644 --- a/src/desktop/app/ChildWindow.java +++ b/src/desktop/app/ChildWindow.java @@ -41,7 +41,7 @@ class ChildWindow extends JInternalFrame { /////////////////////////////////////////////////////////////////////////// // Show or hide the component - public void setVisible​(boolean visible) { + public void setVisible(boolean visible) { // Making visible if (visible) { @@ -56,7 +56,11 @@ class ChildWindow extends JInternalFrame { } // Already visible: bring to front - else moveToFront(); + else { + moveToFront(); + try { setSelected(true); } catch (Exception e) { } + } + } // Change visibility diff --git a/src/desktop/app/MainWindow.java b/src/desktop/app/MainWindow.java index d30d9b5..5882330 100644 --- a/src/desktop/app/MainWindow.java +++ b/src/desktop/app/MainWindow.java @@ -64,6 +64,7 @@ class MainWindow extends JFrame { this.app = app; pwd = Util.PWD; vue = VUE.create(app.getUseNative()); + System.out.println("Native: " + (vue.isNative() ? "Yes" : "No")); // Configure video pane video = new JPanel(null) { @@ -109,7 +110,6 @@ class MainWindow extends JFrame { private JMenuBar initMenus() { var bar = new JMenuBar(); var loc = app.getLocalizer(); - bar.setBorder(null); bar.add(initMenuFile (loc)); bar.add(initMenuDebug(loc)); return bar; diff --git a/src/desktop/app/Memory.java b/src/desktop/app/Memory.java index 80319d1..d8da350 100644 --- a/src/desktop/app/Memory.java +++ b/src/desktop/app/Memory.java @@ -227,6 +227,7 @@ class Memory extends ChildWindow { var label = row.bytes[x] = new JLabel(); label.setFont(font); label.setForeground(SystemColor.windowText); + label.setHorizontalAlignment(SwingConstants.CENTER); label.setVisible(false); client.add(label); } diff --git a/src/desktop/native/native.c b/src/desktop/native/native.c index 3f0a875..5f43a72 100644 --- a/src/desktop/native/native.c +++ b/src/desktop/native/native.c @@ -59,7 +59,6 @@ JNIEXPORT void JNICALL Java_vue_NativeVUE_construct // Produce and initialize s new core context CORE *core = calloc(sizeof (CORE), 1); vueInitialize(&core->vue); - vueSetROM(&core->vue, calloc(1024, 1), 1024); // Encode the context handle into a byte array jbyteArray pointer = (*env)->NewByteArray(env, sizeof (void *)); @@ -73,19 +72,33 @@ JNIEXPORT void JNICALL Java_vue_NativeVUE_construct JNIEXPORT void JNICALL Java_vue_NativeVUE_dispose (JNIEnv *env, jobject vue) { CORE *core = GetCore(env, vue); - free(core->vue.rom); - if (core->vue.sram != NULL) - free(core->vue.sram); + if (core->vue.bus.rom != NULL) + free(core->vue.bus.rom); + if (core->vue.bus.sram != NULL) + free(core->vue.bus.sram); free(core); } +// Retrieve a register value +JNIEXPORT jint JNICALL Java_vue_NativeVUE_getRegister + (JNIEnv *env, jobject vue, jint index, jboolean system) { + CORE *core = GetCore(env, vue); + return vueGetRegister(&core->vue, index, system); +} + // 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->vue.rom_size); + + // No ROM data + if (core->vue.bus.rom == NULL) + return NULL; + + // Copy the ROM data + jbyteArray ret = (*env)->NewByteArray(env, (jint)core->vue.bus.rom_size); jbyte *elems = (*env)->GetByteArrayElements(env, ret, NULL); - memcpy(elems, core->vue.rom, core->vue.rom_size); + memcpy(elems, core->vue.bus.rom, core->vue.bus.rom_size); (*env)->ReleaseByteArrayElements(env, ret, elems, 0); return ret; } @@ -117,6 +130,13 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_read return JNI_TRUE; } +// Specify a register value +JNIEXPORT jint JNICALL Java_vue_NativeVUE_setRegister + (JNIEnv *env, jobject vue, jint index, jboolean system, jint value) { + CORE *core = GetCore(env, vue); + return vueSetRegister(&core->vue, index, system, value); +} + // Provide new ROM data JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_setROM (JNIEnv *env, jobject vue, jbyteArray data, jint offset, jint length) { @@ -138,7 +158,8 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_setROM // Transfer the ROM data to the emulation state CORE *core = GetCore(env, vue); - free(core->vue.rom); + if (core->vue.bus.rom != NULL) + free(core->vue.bus.rom); vueSetROM(&core->vue, rom, length); return JNI_TRUE; } diff --git a/src/desktop/vue/Bus.java b/src/desktop/vue/Bus.java index adfbb13..12602d4 100644 --- a/src/desktop/vue/Bus.java +++ b/src/desktop/vue/Bus.java @@ -6,9 +6,14 @@ import java.util.*; // Simulation of the CPU bus class Bus { - // Instance fields + // Private fields private JavaVUE vue; // Emulation state + // Package fields + byte[] rom; // Cartridge ROM + byte[] sram; // Cartridge SRAM + byte[] wram; // System WRAM + /////////////////////////////////////////////////////////////////////////// @@ -18,6 +23,7 @@ class Bus { // Default constructor Bus(JavaVUE vue) { this.vue = vue; + wram = new byte[0x10000]; } // Read bytes from host memory @@ -40,23 +46,24 @@ class Bus { } // Read a value directly from host memory - int readMemory(byte[] data, int offset, int type) { + int readMemory(byte[] data, int address, int type) { // There is no source data if (data == null) return 0; // Processing by type + address &= data.length - 1; switch (type) { - case VUE.S8 : return data[offset]; - case VUE.U8 : return data[offset] & 0xFF; + case VUE.S8 : return data[address]; + case VUE.U8 : return data[address] & 0xFF; case VUE.S16: return - data[offset+1] << 8 | data[offset ] & 0xFF; + data[address+1] << 8 | data[address ] & 0xFF; case VUE.U16: return - (data[offset+1] & 0xFF) << 8 | data[offset ] & 0xFF; + (data[address+1] & 0xFF)<< 8 | data[address ] & 0xFF; case VUE.S32: return - data[offset+3] << 24 | (data[offset+2] & 0xFF) << 16 | - (data[offset+1] & 0xFF) << 8 | (data[offset ] & 0xFF); + data[address+3] <<24 | (data[address+2] & 0xFF)<<16 | + (data[address+1] & 0xFF)<< 8 | (data[address ] & 0xFF); } return 0; // Unreachable @@ -71,11 +78,11 @@ class Bus { case 4: // Cartridge expansion (not supported) return 0; case 5: return - readMemory(vue.wram, address & 0xFFFF , type); + readMemory(wram, address & 0xFFFF , type); case 6: return - readMemory(vue.sram, address & vue.sram.length - 1, type); + readMemory(sram, address & sram.length - 1, type); case 7: return - readMemory(vue.rom , address & vue.rom .length - 1, type); + readMemory(rom , address & rom .length - 1, type); } return 0; // Unreachable } @@ -98,25 +105,26 @@ class Bus { } // Write a value directly to host memory - void writeMemory(byte[] data, int offset, int type, int value) { + void writeMemory(byte[] data, int address, int type, int value) { // There is no source data if (data == null) return; // Processing by type + address &= data.length - 1; switch (type) { case VUE.S32: - data[offset + 3] = (byte) (value >> 24); - data[offset + 2] = (byte) (value >> 16); + data[address + 3] = (byte) (value >> 24); + data[address + 2] = (byte) (value >> 16); // Fallthrough case VUE.S16: // Fallthrough case VUE.U16: - data[offset + 1] = (byte) (value >> 8); + data[address + 1] = (byte) (value >> 8); // Fallthrough case VUE.S8 : // Fallthrough case VUE.U8 : - data[offset ] = (byte) value ; + data[address ] = (byte) value ; } } @@ -125,18 +133,9 @@ class 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; + case 5: writeMemory(wram, address, type, value); break; + case 6: writeMemory(sram, address, type, value); break; + case 7: writeMemory(rom , address, type, value); break; } } diff --git a/src/desktop/vue/CPU.java b/src/desktop/vue/CPU.java new file mode 100644 index 0000000..1234e5d --- /dev/null +++ b/src/desktop/vue/CPU.java @@ -0,0 +1,208 @@ +package vue; + +// CPU state +class CPU { + + // Private fields + private JavaVUE vue; // Emulation state + + // Package fields + int cycles; // Cycles until next stage + int fetch; // Fetch unit index + int stage; // Current processing stage + + // Program registers + int[] program; + + // System registers + int adtre; // Address Trap Register for Execution + int eipc; // Exception/interrupt PC + int eipsw; // Exception/interrupt PSW + int fepc; // Duplexed exception PC + int fepsw; // Duplexed exception PSW + int pc; // Program Counter + int sr29; // System register 29 + int sr31; // System register 31 + + // Program Status Word + int psw_ae; // Address Trap Enable + int psw_ep; // Exception Pending + int psw_id; // Interrupt Disable + int psw_cy; // Carry + int psw_fiv; // Floating Reserved Operand + int psw_fov; // Floating Overflow + int psw_fpr; // Floating Precision + int psw_fro; // Floating Reserved Operand + int psw_fud; // Floating Underflow + int psw_fzd; // Floating Zero Divide + int psw_i; // Interrupt Level + int psw_np; // NMI Pending + int psw_ov; // Overflow + int psw_s; // Sign + int psw_z; // Zero + + // Cache Control Word + int chcw_cec; // Clear Entry Count + int chcw_cen; // Clear Entry Number + int chcw_icc; // Instruction Cache Clear + int chcw_icd; // Instruction Cache Dump + int chcw_ice; // Instruction Cache Enable + int chcw_icr; // Instruction Cache Restore + int chcw_sa; // Spill-Out Base Address + + // Exception Cause Register + int ecr_eicc; // Exception/Interrupt Cause Code + int ecr_fecc; // Fatal Error Cause Code + + + + /////////////////////////////////////////////////////////////////////////// + // Constants // + /////////////////////////////////////////////////////////////////////////// + + // Stages + static final int FETCH = 0; + static final int EXECUTE = 1; + static final int HALT = 2; + static final int EXCEPTION = 3; + static final int FATAL = 4; + static final int CLEAR = 5; + static final int DUMP = 6; + static final int RESTORE = 7; + + + + /////////////////////////////////////////////////////////////////////////// + // Constructors // + /////////////////////////////////////////////////////////////////////////// + + // Default constructor + CPU(JavaVUE vue) { + program = new int[32]; + this.vue = vue; + } + + + + /////////////////////////////////////////////////////////////////////////// + // Package Methods // + /////////////////////////////////////////////////////////////////////////// + + // Read a system register + int getSystemRegister(int index) { + switch (index) { + case VUE.ADTRE: return adtre; + case VUE.EIPC : return eipc; + case VUE.EIPSW: return eipsw; + case VUE.FEPC : return fepc; + case VUE.FEPSW: return fepsw; + case VUE.ECR : return ecr_fecc << 16 | ecr_eicc; + case VUE.PIR : return 0x00005346; + case VUE.TKCW : return 0x000000E0; + case VUE.CHCW : return chcw_ice << 1; + case 29 : return sr29; + case 30 : return 0x00000004; + case 31 : return sr31; + case VUE.PSW : return + psw_i << 16 | psw_fro << 9 | psw_fpr << 4 | + psw_np << 15 | psw_fiv << 8 | psw_cy << 3 | + psw_ep << 14 | psw_fzd << 7 | psw_ov << 2 | + psw_ae << 13 | psw_fov << 6 | psw_s << 1 | + psw_id << 12 | psw_fud << 5 | psw_z + ; + + // Remaining cases to encourage tableswitch + case 8: case 9: case 10: case 11: case 12: case 13: case 14: + case 15: case 16: case 17: case 18: case 19: case 20: case 21: + case 22: case 23: case 26: case 27: case 28: + return 0; + } + return 1; // Unreachable + } + + // System reset + void reset() { + + // Configure instance fields + cycles = 0; + fetch = 0; + stage = FETCH; + + // Reset program counter + pc = 0xFFFFFFF0; + + // Clear all registers (hardware only sets ECR, PC and PSW) + for (int x = 0; x < 32; x++) { + program[x] = 0; + setSystemRegister(x, 0, true); + } + + } + + // Write a system register + int setSystemRegister(int index, int value, boolean debug) { + switch (index) { + case VUE.ADTRE: return adtre = value & 0xFFFFFFFE; + case VUE.EIPC : return eipc = value & 0xFFFFFFFE; + case VUE.EIPSW: return eipsw = value & 0x000FF3FF; + case VUE.FEPC : return fepc = value & 0xFFFFFFFE; + case VUE.FEPSW: return fepsw = value & 0x000FF3FF; + case 29 : return sr29 = value; + case 31 : return sr31 = debug || value >= 0 ? value : -value; + + case VUE.CHCW : + chcw_cen = value >> 20 & 0x00000FFF; + chcw_cec = value >> 8 & 0x00000FFF; + chcw_sa = value >> 8 & 0x00FFFFFF; + chcw_icr = value >> 5 & 1; + chcw_icd = value >> 4 & 1; + chcw_ice = value >> 1 & 1; + chcw_icc = value & 1; + + // Only one of ICC, ICD or ICR is set + value &= 0x00000031; + if ((value & value - 1) == 0) { + // Clear + // Dump + // Restore + } + + return chcw_ice << 1; + + case VUE.PSW : + psw_i = value >> 16 & 15; psw_fov = value >> 6 & 1; + psw_np = value >> 15 & 1; psw_fud = value >> 5 & 1; + psw_ep = value >> 14 & 1; psw_fpr = value >> 4 & 1; + psw_ae = value >> 13 & 1; psw_cy = value >> 3 & 1; + psw_id = value >> 12 & 1; psw_ov = value >> 2 & 1; + psw_fro = value >> 9 & 1; psw_s = value >> 1 & 1; + psw_fiv = value >> 8 & 1; psw_z = value & 1; + psw_fzd = value >> 7 & 1; + return value & 0x000FF3FF; + + // Remaining cases to encourage tableswitch + case 4: case 6: case 7: case 8: case 9: case 10: case 11: + case 12: case 13: case 14: case 15: case 16: case 17: case 18: + case 19: case 20: case 21: case 22: case 23: case 26: case 27: + case 28: case 30: + return 0; + } + return 1; // Unreachable + } + + // Test a condition + int test(int condition) { + switch (condition) { + case 0: return psw_ov; + case 1: return psw_cy; + case 2: return psw_z; + case 3: return psw_cy | psw_z; + case 4: return psw_s; + case 5: return 1; + case 6: return psw_ov | psw_s; + case 7: return psw_ov ^ psw_s | psw_z; + } + return test(condition & 7) ^ 1; + } + +} diff --git a/src/desktop/vue/JavaVUE.java b/src/desktop/vue/JavaVUE.java index 84ee665..ba8ffd8 100644 --- a/src/desktop/vue/JavaVUE.java +++ b/src/desktop/vue/JavaVUE.java @@ -3,13 +3,9 @@ package vue; // Java emulation core implementation class JavaVUE extends VUE { - // Instance fields - byte[] rom; // Cartridge ROM - byte[] sram; // Cartridge SRAM - byte[] wram; // System WRAM - // Components Bus bus; // Memory bus + CPU cpu; // Processor @@ -28,8 +24,8 @@ class JavaVUE extends VUE { // Default constructor JavaVUE() { - bus = new Bus(this); - wram = new byte[0x10000]; + bus = new Bus(this); + cpu = new CPU(this); } @@ -41,10 +37,26 @@ class JavaVUE extends VUE { // Release any used resources public void dispose() { }; // No action needed + // Retrieve a register value + public int getRegister(int index, boolean system) { + return + index == VUE.PC && system ? cpu.pc : + index < 0 || index > 31 ? 0 : + system ? cpu.getSystemRegister(index) : + cpu.program[index] + ; + } + // Retrieve a copy of the ROM data public byte[] getROM() { - var ret = new byte[rom.length]; - System.arraycopy(rom, 0, ret, 0, ret.length); + + // No ROM data + if (bus.rom == null) + return null; + + // Copy the ROM data + var ret = new byte[bus.rom.length]; + System.arraycopy(bus.rom, 0, ret, 0, ret.length); return ret; } @@ -79,16 +91,16 @@ class JavaVUE extends VUE { break; case 3: // Fallthrough, unmapped case 4: // Cartridge expansion (not supported) - bus.readBytes(null, dest, address, 0 , count); + bus.readBytes(null , dest, address, 0 , count); break; case 5: /* System WRAM */ - bus.readBytes(wram, dest, address, offset, count); + bus.readBytes(bus.wram, dest, address, offset, count); break; case 6: /* Cartridge RAM */ - bus.readBytes(sram, dest, address, offset, count); + bus.readBytes(bus.sram, dest, address, offset, count); break; case 7: /* Cartridge ROM */ - bus.readBytes(rom , dest, address, offset, count); + bus.readBytes(bus.rom , dest, address, offset, count); break; } @@ -96,6 +108,16 @@ class JavaVUE extends VUE { return true; } + // Specify a register value + public int setRegister(int index, boolean system, int value) { + return + index == VUE.PC && system ? cpu.pc = value & 0xFFFFFFFE : + index < 0 || index > 31 ? 0 : + system ? cpu.setSystemRegister(index, value, true) : + index == 0 ? 0 : cpu.program[index] + ; + } + // Provide new ROM data public boolean setROM(byte[] data, int offset, int length) { @@ -109,8 +131,8 @@ class JavaVUE extends VUE { ) return false; // Accept the new ROM data - rom = new byte[length]; - System.arraycopy(data, offset, rom, 0, length); + bus.rom = new byte[length]; + System.arraycopy(data, offset, bus.rom, 0, length); return true; } @@ -135,13 +157,13 @@ class JavaVUE extends VUE { // Process by component switch (address >> 24 & 7) { case 5: /* System WRAM */ - bus.writeBytes(wram, src, address, offset, count); + bus.writeBytes(bus.wram, src, address, offset, count); break; case 6: /* Cartridge RAM */ - bus.writeBytes(sram, src, address, offset, count); + bus.writeBytes(bus.sram, src, address, offset, count); break; case 7: /* Cartridge ROM */ - bus.writeBytes(rom , src, address, offset, count); + bus.writeBytes(bus.rom , src, address, offset, count); break; } diff --git a/src/desktop/vue/NativeVUE.java b/src/desktop/vue/NativeVUE.java index e0e020c..960ddc3 100644 --- a/src/desktop/vue/NativeVUE.java +++ b/src/desktop/vue/NativeVUE.java @@ -26,6 +26,9 @@ class NativeVUE extends VUE { // Release any used resources public native void dispose(); + // Retrieve a register value + public native int getRegister(int index, boolean system); + // Retrieve a copy of the ROM data public native byte[] getROM(); @@ -36,6 +39,9 @@ class NativeVUE extends VUE { public native boolean read(int address, byte[] dest, int offset, int length); + // Specify a register value + public native int setRegister(int index, boolean system, int value); + // Provide new ROM data public native boolean setROM(byte[] data, int offset, int length); diff --git a/src/desktop/vue/VUE.java b/src/desktop/vue/VUE.java index a1c14ce..4c3065d 100644 --- a/src/desktop/vue/VUE.java +++ b/src/desktop/vue/VUE.java @@ -19,6 +19,19 @@ public abstract class VUE { public static final int U16 = 3; public static final int S32 = 4; + // System register indexes + public static final int ADTRE = 25; + public static final int CHCW = 24; + public static final int ECR = 4; + public static final int EIPC = 0; + public static final int EIPSW = 1; + public static final int FEPC = 2; + public static final int FEPSW = 3; + public static final int PC = -1; + public static final int PIR = 6; + public static final int PSW = 5; + public static final int TKCW = 7; + /////////////////////////////////////////////////////////////////////////// @@ -55,6 +68,9 @@ public abstract class VUE { // Release any used resources public abstract void dispose(); + // Retrieve a register value + public abstract int getRegister(int index, boolean system); + // Retrieve a copy of the ROM data public abstract byte[] getROM(); @@ -65,6 +81,9 @@ public abstract class VUE { public abstract boolean read(int address, byte[] dest, int offset, int length); + // Specify a register value + public abstract int setRegister(int index, boolean system, int value); + // Provide new ROM data public abstract boolean setROM(byte[] data, int offset, int length);