Laying the groundwork for the CPU

This commit is contained in:
Guy Perfect 2020-08-05 20:40:23 -05:00
parent 61bee38e3d
commit 2e38c1fa49
14 changed files with 620 additions and 96 deletions

View File

@ -59,12 +59,12 @@ static int32_t busReadValue(VUE *vue, uint32_t address, int type) {
case 3: /* Fallthrough */ /* Unmapped */ case 3: /* Fallthrough */ /* Unmapped */
case 4: /* Cartridge expansion (not supported) */ case 4: /* Cartridge expansion (not supported) */
return 0; return 0;
case 5: return /* System WRAM */ case 5: /* System WRAM */
busReadMemory(vue->wram, address & 0xFFFF , type); return busReadMemory(vue->bus.wram, address & 0xFFFF, type);
case 6: return /* Cartridge RAM */ case 6: return busReadMemory(vue->bus.sram, /* Cartridge RAM */
busReadMemory(vue->sram, address & (vue->sram_size - 1), type); address & (vue->bus.sram_size - 1), type);
case 7: return /* Cartridge ROM */ case 7: return busReadMemory(vue->bus.rom , /* Cartridge ROM */
busReadMemory(vue->rom , address & (vue->rom_size - 1), type); address & (vue->bus.rom_size - 1), type);
} }
return 0; /* Unreachable */ return 0; /* Unreachable */
} }
@ -117,16 +117,15 @@ static void busWriteValue(VUE *vue, uint32_t address, int type, int32_t value){
address &= -TYPE_SIZES[type]; address &= -TYPE_SIZES[type];
switch (address >> 24 & 7) { switch (address >> 24 & 7) {
case 5: /* System WRAM */ case 5: /* System WRAM */
busWriteMemory(vue->wram, busWriteMemory(vue->bus.wram, address & 0xFFFF, type, value);
address & 0xFFFF , type, value);
break; break;
case 6: /* Cartridge RAM */ case 6: /* Cartridge RAM */
busWriteMemory(vue->sram, busWriteMemory(vue->bus.sram,
address & (vue->sram_size - 1), type, value); address & (vue->bus.sram_size - 1), type, value);
break; break;
case 7: /* Cartridge ROM */ case 7: /* Cartridge ROM */
busWriteMemory(vue->rom , busWriteMemory(vue->bus.rom ,
address & (vue->rom_size - 1), type, value); address & (vue->bus.rom_size - 1), type, value);
break; break;
} }
} }

156
src/core/cpu.c Normal file
View File

@ -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

View File

@ -30,6 +30,19 @@ extern "C" {
#define VUE_U16 3 #define VUE_U16 3
#define VUE_S32 4 #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 */ /* Emulation state */
typedef struct { typedef struct {
/* Memory bus */
struct {
uint8_t *rom; /* Cartridge ROM */ uint8_t *rom; /* Cartridge ROM */
uint32_t rom_size; /* Number of bytes in cartridge ROM */ uint32_t rom_size; /* Number of bytes in cartridge ROM */
uint8_t *sram; /* Cartridge RAM */ uint8_t *sram; /* Cartridge RAM */
uint32_t sram_size; /* Number of bytes in cartridge RAM */ uint32_t sram_size; /* Number of bytes in cartridge RAM */
uint8_t wram[0x10000]; /* System memory */ 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; } VUE;
@ -54,8 +122,10 @@ typedef struct {
* Function Prototypes * * Function Prototypes *
*****************************************************************************/ *****************************************************************************/
VUEAPI int32_t vueGetRegister(VUE *vue, int index, vbool system);
VUEAPI void vueInitialize (VUE *vue); VUEAPI void vueInitialize (VUE *vue);
VUEAPI vbool vueRead (VUE *vue, uint32_t address, uint8_t *dest, uint32_t length); 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 vueSetROM (VUE *vue, uint8_t *rom, uint32_t size);
VUEAPI vbool vueWrite (VUE *vue, uint32_t address, uint8_t *src, uint32_t length); VUEAPI vbool vueWrite (VUE *vue, uint32_t address, uint8_t *src, uint32_t length);

View File

@ -17,6 +17,7 @@ static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
*****************************************************************************/ *****************************************************************************/
#include "bus.c" #include "bus.c"
#include "cpu.c"
@ -24,10 +25,20 @@ static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
* Library Functions * * 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 */ /* Prepare an emulation state context for use */
void vueInitialize(VUE *vue) { void vueInitialize(VUE *vue) {
vue->rom = NULL; vue->bus.rom = NULL;
vue->sram = NULL; vue->bus.sram = NULL;
} }
/* Read bytes from the CPU bus */ /* Read bytes from the CPU bus */
@ -53,14 +64,14 @@ vbool vueRead(VUE *vue, uint32_t address, uint8_t *dest, uint32_t length) {
case 4: /* Cartridge expansion (not supported) */ case 4: /* Cartridge expansion (not supported) */
busReadBytes(NULL, dest, address, 0, count); busReadBytes(NULL, dest, address, 0, count);
break; break;
case 5: /* System WRAM */ case 5: busReadBytes(vue->bus.wram, dest, /* System WRAM */
busReadBytes(vue->wram, dest, address, 0x10000 , count); address, 0x10000 , count);
break; break;
case 6: /* Cartridge RAM */ case 6: busReadBytes(vue->bus.sram, dest, /* Cartridge RAM */
busReadBytes(vue->sram, dest, address, vue->sram_size, count); address, vue->bus.sram_size, count);
break; break;
case 7: /* Cartridge ROM */ case 7: busReadBytes(vue->bus.rom , dest, /* Cartridge ROM */
busReadBytes(vue->rom , dest, address, vue->rom_size , count); address, vue->bus.rom_size , count);
break; break;
} }
@ -68,6 +79,16 @@ vbool vueRead(VUE *vue, uint32_t address, uint8_t *dest, uint32_t length) {
return VUE_TRUE; 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 */ /* Specify a new ROM buffer */
vbool vueSetROM(VUE *vue, uint8_t *rom, uint32_t size) { 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; ) return VUE_FALSE;
/* Accept the new ROM buffer */ /* Accept the new ROM buffer */
vue->rom = rom; vue->bus.rom = rom;
vue->rom_size = size; vue->bus.rom_size = size;
return VUE_TRUE; return VUE_TRUE;
} }
@ -104,13 +125,13 @@ vbool vueWrite(VUE *vue, uint32_t address, uint8_t *src, uint32_t length) {
/* Process by component */ /* Process by component */
switch (address >> 24 & 7) { switch (address >> 24 & 7) {
case 5: /* System WRAM */ case 5: /* System WRAM */
busWriteBytes(vue->wram, src, address, 0x10000 , count); busWriteBytes(vue->bus.wram, src, address, 0x10000, count);
break; break;
case 6: /* Cartridge RAM */ case 6: busWriteBytes(vue->bus.sram, src, /* Cartridge RAM */
busWriteBytes(vue->sram, src, address, vue->sram_size, count); address, vue->bus.sram_size, count);
break; break;
case 7: /* Cartridge ROM */ case 7: busWriteBytes(vue->bus.rom , src, /* Cartridge ROM */
busWriteBytes(vue->rom , src, address, vue->rom_size , count); address, vue->bus.rom_size , count);
break; break;
} }

View File

@ -87,9 +87,7 @@ public class App {
// Specify whether using the native module // Specify whether using the native module
boolean setUseNative(boolean useNative) { boolean setUseNative(boolean useNative) {
this.useNative = useNative && VUE.isNativeLoaded(); return this.useNative = useNative && VUE.isNativeLoaded();
System.out.println("Native: " + (this.useNative ? "Yes" : "No"));
return this.useNative;
} }

View File

@ -41,7 +41,7 @@ class ChildWindow extends JInternalFrame {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Show or hide the component // Show or hide the component
public void setVisible(boolean visible) { public void setVisible(boolean visible) {
// Making visible // Making visible
if (visible) { if (visible) {
@ -56,7 +56,11 @@ class ChildWindow extends JInternalFrame {
} }
// Already visible: bring to front // Already visible: bring to front
else moveToFront(); else {
moveToFront();
try { setSelected(true); } catch (Exception e) { }
}
} }
// Change visibility // Change visibility

View File

@ -64,6 +64,7 @@ class MainWindow extends JFrame {
this.app = app; this.app = app;
pwd = Util.PWD; pwd = Util.PWD;
vue = VUE.create(app.getUseNative()); vue = VUE.create(app.getUseNative());
System.out.println("Native: " + (vue.isNative() ? "Yes" : "No"));
// Configure video pane // Configure video pane
video = new JPanel(null) { video = new JPanel(null) {
@ -109,7 +110,6 @@ class MainWindow extends JFrame {
private JMenuBar initMenus() { private JMenuBar initMenus() {
var bar = new JMenuBar(); var bar = new JMenuBar();
var loc = app.getLocalizer(); var loc = app.getLocalizer();
bar.setBorder(null);
bar.add(initMenuFile (loc)); bar.add(initMenuFile (loc));
bar.add(initMenuDebug(loc)); bar.add(initMenuDebug(loc));
return bar; return bar;

View File

@ -227,6 +227,7 @@ class Memory extends ChildWindow {
var label = row.bytes[x] = new JLabel(); var label = row.bytes[x] = new JLabel();
label.setFont(font); label.setFont(font);
label.setForeground(SystemColor.windowText); label.setForeground(SystemColor.windowText);
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setVisible(false); label.setVisible(false);
client.add(label); client.add(label);
} }

View File

@ -59,7 +59,6 @@ JNIEXPORT void JNICALL Java_vue_NativeVUE_construct
// Produce and initialize s new core context // Produce and initialize s new core context
CORE *core = calloc(sizeof (CORE), 1); CORE *core = calloc(sizeof (CORE), 1);
vueInitialize(&core->vue); vueInitialize(&core->vue);
vueSetROM(&core->vue, calloc(1024, 1), 1024);
// Encode the context handle into a byte array // Encode the context handle into a byte array
jbyteArray pointer = (*env)->NewByteArray(env, sizeof (void *)); 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 JNIEXPORT void JNICALL Java_vue_NativeVUE_dispose
(JNIEnv *env, jobject vue) { (JNIEnv *env, jobject vue) {
CORE *core = GetCore(env, vue); CORE *core = GetCore(env, vue);
free(core->vue.rom); if (core->vue.bus.rom != NULL)
if (core->vue.sram != NULL) free(core->vue.bus.rom);
free(core->vue.sram); if (core->vue.bus.sram != NULL)
free(core->vue.bus.sram);
free(core); 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 // 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); 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); 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); (*env)->ReleaseByteArrayElements(env, ret, elems, 0);
return ret; return ret;
} }
@ -117,6 +130,13 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_read
return JNI_TRUE; 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 // 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) {
@ -138,7 +158,8 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_setROM
// Transfer the ROM data to the emulation state // Transfer the ROM data to the emulation state
CORE *core = GetCore(env, vue); 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); vueSetROM(&core->vue, rom, length);
return JNI_TRUE; return JNI_TRUE;
} }

View File

@ -6,9 +6,14 @@ import java.util.*;
// Simulation of the CPU bus // Simulation of the CPU bus
class Bus { class Bus {
// Instance fields // Private fields
private JavaVUE vue; // Emulation state 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 // Default constructor
Bus(JavaVUE vue) { Bus(JavaVUE vue) {
this.vue = vue; this.vue = vue;
wram = new byte[0x10000];
} }
// Read bytes from host memory // Read bytes from host memory
@ -40,23 +46,24 @@ class Bus {
} }
// Read a value directly from host memory // 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 // There is no source data
if (data == null) if (data == null)
return 0; return 0;
// Processing by type // Processing by type
address &= data.length - 1;
switch (type) { switch (type) {
case VUE.S8 : return data[offset]; case VUE.S8 : return data[address];
case VUE.U8 : return data[offset] & 0xFF; case VUE.U8 : return data[address] & 0xFF;
case VUE.S16: return case VUE.S16: return
data[offset+1] << 8 | data[offset ] & 0xFF; data[address+1] << 8 | data[address ] & 0xFF;
case VUE.U16: return case VUE.U16: return
(data[offset+1] & 0xFF) << 8 | data[offset ] & 0xFF; (data[address+1] & 0xFF)<< 8 | data[address ] & 0xFF;
case VUE.S32: return case VUE.S32: return
data[offset+3] << 24 | (data[offset+2] & 0xFF) << 16 | data[address+3] <<24 | (data[address+2] & 0xFF)<<16 |
(data[offset+1] & 0xFF) << 8 | (data[offset ] & 0xFF); (data[address+1] & 0xFF)<< 8 | (data[address ] & 0xFF);
} }
return 0; // Unreachable return 0; // Unreachable
@ -71,11 +78,11 @@ class Bus {
case 4: // Cartridge expansion (not supported) case 4: // Cartridge expansion (not supported)
return 0; return 0;
case 5: return case 5: return
readMemory(vue.wram, address & 0xFFFF , type); readMemory(wram, address & 0xFFFF , type);
case 6: return case 6: return
readMemory(vue.sram, address & vue.sram.length - 1, type); readMemory(sram, address & sram.length - 1, type);
case 7: return case 7: return
readMemory(vue.rom , address & vue.rom .length - 1, type); readMemory(rom , address & rom .length - 1, type);
} }
return 0; // Unreachable return 0; // Unreachable
} }
@ -98,25 +105,26 @@ class Bus {
} }
// Write a value directly to host memory // 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 // There is no source data
if (data == null) if (data == null)
return; return;
// Processing by type // Processing by type
address &= data.length - 1;
switch (type) { switch (type) {
case VUE.S32: case VUE.S32:
data[offset + 3] = (byte) (value >> 24); data[address + 3] = (byte) (value >> 24);
data[offset + 2] = (byte) (value >> 16); data[address + 2] = (byte) (value >> 16);
// Fallthrough // Fallthrough
case VUE.S16: // Fallthrough case VUE.S16: // Fallthrough
case VUE.U16: case VUE.U16:
data[offset + 1] = (byte) (value >> 8); data[address + 1] = (byte) (value >> 8);
// Fallthrough // Fallthrough
case VUE.S8 : // Fallthrough case VUE.S8 : // Fallthrough
case VUE.U8 : 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) { void writeValue(int address, int type, int value) {
address &= ~JavaVUE.TYPE_SIZES[type] + 1; address &= ~JavaVUE.TYPE_SIZES[type] + 1;
switch (address >> 24 & 7) { switch (address >> 24 & 7) {
case 5: case 5: writeMemory(wram, address, type, value); break;
writeMemory(vue.wram, case 6: writeMemory(sram, address, type, value); break;
address & 0xFFFF , type, value); case 7: writeMemory(rom , address, type, value); break;
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;
} }
} }

208
src/desktop/vue/CPU.java Normal file
View File

@ -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;
}
}

View File

@ -3,13 +3,9 @@ package vue;
// Java emulation core implementation // Java emulation core implementation
class JavaVUE extends VUE { class JavaVUE extends VUE {
// Instance fields
byte[] rom; // Cartridge ROM
byte[] sram; // Cartridge SRAM
byte[] wram; // System WRAM
// Components // Components
Bus bus; // Memory bus Bus bus; // Memory bus
CPU cpu; // Processor
@ -29,7 +25,7 @@ class JavaVUE extends VUE {
// Default constructor // Default constructor
JavaVUE() { JavaVUE() {
bus = new Bus(this); bus = new Bus(this);
wram = new byte[0x10000]; cpu = new CPU(this);
} }
@ -41,10 +37,26 @@ class JavaVUE extends VUE {
// Release any used resources // Release any used resources
public void dispose() { }; // No action needed 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 // Retrieve a copy of the ROM data
public byte[] getROM() { 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; return ret;
} }
@ -82,13 +94,13 @@ class JavaVUE extends VUE {
bus.readBytes(null , dest, address, 0 , count); bus.readBytes(null , dest, address, 0 , count);
break; break;
case 5: /* System WRAM */ case 5: /* System WRAM */
bus.readBytes(wram, dest, address, offset, count); bus.readBytes(bus.wram, dest, address, offset, count);
break; break;
case 6: /* Cartridge RAM */ case 6: /* Cartridge RAM */
bus.readBytes(sram, dest, address, offset, count); bus.readBytes(bus.sram, dest, address, offset, count);
break; break;
case 7: /* Cartridge ROM */ case 7: /* Cartridge ROM */
bus.readBytes(rom , dest, address, offset, count); bus.readBytes(bus.rom , dest, address, offset, count);
break; break;
} }
@ -96,6 +108,16 @@ class JavaVUE extends VUE {
return true; 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 // Provide new ROM data
public boolean setROM(byte[] data, int offset, int length) { public boolean setROM(byte[] data, int offset, int length) {
@ -109,8 +131,8 @@ class JavaVUE extends VUE {
) return false; ) return false;
// Accept the new ROM data // Accept the new ROM data
rom = new byte[length]; bus.rom = new byte[length];
System.arraycopy(data, offset, rom, 0, length); System.arraycopy(data, offset, bus.rom, 0, length);
return true; return true;
} }
@ -135,13 +157,13 @@ class JavaVUE extends VUE {
// Process by component // Process by component
switch (address >> 24 & 7) { switch (address >> 24 & 7) {
case 5: /* System WRAM */ case 5: /* System WRAM */
bus.writeBytes(wram, src, address, offset, count); bus.writeBytes(bus.wram, src, address, offset, count);
break; break;
case 6: /* Cartridge RAM */ case 6: /* Cartridge RAM */
bus.writeBytes(sram, src, address, offset, count); bus.writeBytes(bus.sram, src, address, offset, count);
break; break;
case 7: /* Cartridge ROM */ case 7: /* Cartridge ROM */
bus.writeBytes(rom , src, address, offset, count); bus.writeBytes(bus.rom , src, address, offset, count);
break; break;
} }

View File

@ -26,6 +26,9 @@ class NativeVUE extends VUE {
// Release any used resources // Release any used resources
public native void dispose(); public native void dispose();
// Retrieve a register value
public native int getRegister(int index, boolean system);
// Retrieve a copy of the ROM data // Retrieve a copy of the ROM data
public native byte[] getROM(); public native byte[] getROM();
@ -36,6 +39,9 @@ class NativeVUE extends VUE {
public native boolean read(int address, byte[] dest, int offset, public native boolean read(int address, byte[] dest, int offset,
int length); int length);
// Specify a register value
public native int setRegister(int index, boolean system, int value);
// Provide new ROM data // Provide new ROM data
public native boolean setROM(byte[] data, int offset, int length); public native boolean setROM(byte[] data, int offset, int length);

View File

@ -19,6 +19,19 @@ public abstract class VUE {
public static final int U16 = 3; public static final int U16 = 3;
public static final int S32 = 4; 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 // Release any used resources
public abstract void dispose(); public abstract void dispose();
// Retrieve a register value
public abstract int getRegister(int index, boolean system);
// Retrieve a copy of the ROM data // Retrieve a copy of the ROM data
public abstract byte[] getROM(); public abstract byte[] getROM();
@ -65,6 +81,9 @@ public abstract class VUE {
public abstract boolean read(int address, byte[] dest, int offset, public abstract boolean read(int address, byte[] dest, int offset,
int length); int length);
// Specify a register value
public abstract int setRegister(int index, boolean system, int value);
// Provide new ROM data // Provide new ROM data
public abstract boolean setROM(byte[] data, int offset, int length); public abstract boolean setROM(byte[] data, int offset, int length);