From d1b8e293c30ef8b9bfd76bc701fb7e226fff96be Mon Sep 17 00:00:00 2001 From: Guy Perfect Date: Mon, 10 Aug 2020 20:24:00 -0500 Subject: [PATCH] Implementing CPU emulation logic --- src/core/include/vue.h | 21 +-- src/core/vue.c | 5 + src/desktop/app/CPUWindow.java | 2 +- src/desktop/vue/Access.java | 21 +++ src/desktop/vue/CPU.java | 300 +++++++++++++++++++++++++++++++-- src/desktop/vue/JavaVUE.java | 35 ++-- src/desktop/vue/NativeVUE.c | 7 + src/desktop/vue/NativeVUE.java | 3 + src/desktop/vue/VUE.java | 14 ++ 9 files changed, 369 insertions(+), 39 deletions(-) create mode 100644 src/desktop/vue/Access.java diff --git a/src/core/include/vue.h b/src/core/include/vue.h index 2685c5b..899b4cf 100644 --- a/src/core/include/vue.h +++ b/src/core/include/vue.h @@ -233,16 +233,17 @@ typedef struct { * Function Prototypes * *****************************************************************************/ -VUEAPI int32_t vueEmulate (VUE *vue, int32_t maxCycles); -VUEAPI int32_t vueGetRegister(VUE *vue, int32_t index, vbool system); -VUEAPI void vueInitialize (VUE *vue); -VUEAPI int32_t vueRead (VUE *vue, uint32_t address, int32_t type); -VUEAPI vbool vueReadBytes (VUE *vue, uint32_t address, uint8_t *dest, uint32_t length); -VUEAPI void vueReset (VUE *vue); -VUEAPI int32_t vueSetRegister(VUE *vue, int32_t index, vbool system, int32_t value); -VUEAPI vbool vueSetROM (VUE *vue, uint8_t *rom, uint32_t size); -VUEAPI void vueWrite (VUE *vue, uint32_t address, int32_t type, int32_t value); -VUEAPI vbool vueWriteBytes (VUE *vue, uint32_t address, uint8_t *src, uint32_t length); +VUEAPI int32_t vueEmulate (VUE *vue, int32_t maxCycles); +VUEAPI int32_t vueGetBreakCode(VUE *vue); +VUEAPI int32_t vueGetRegister (VUE *vue, int32_t index, vbool system); +VUEAPI void vueInitialize (VUE *vue); +VUEAPI int32_t vueRead (VUE *vue, uint32_t address, int32_t type); +VUEAPI vbool vueReadBytes (VUE *vue, uint32_t address, uint8_t *dest, uint32_t length); +VUEAPI void vueReset (VUE *vue); +VUEAPI int32_t vueSetRegister (VUE *vue, int32_t index, vbool system, int32_t value); +VUEAPI vbool vueSetROM (VUE *vue, uint8_t *rom, uint32_t size); +VUEAPI void vueWrite (VUE *vue, uint32_t address, int32_t type, int32_t value); +VUEAPI vbool vueWriteBytes (VUE *vue, uint32_t address, uint8_t *src, uint32_t length); diff --git a/src/core/vue.c b/src/core/vue.c index a8fa671..5b98f32 100644 --- a/src/core/vue.c +++ b/src/core/vue.c @@ -198,6 +198,11 @@ int32_t vueEmulate(VUE *vue, int32_t maxCycles) { return maxCycles; } +/* Retrieve the application break code */ +int32_t vueGetBreakCode(VUE *vue) { + return vue == NULL ? 0 : vue->breakCode; +} + /* Retrieve the value of a register */ int32_t vueGetRegister(VUE *vue, int32_t index, vbool system) { diff --git a/src/desktop/app/CPUWindow.java b/src/desktop/app/CPUWindow.java index 050c9a6..7fec2ce 100644 --- a/src/desktop/app/CPUWindow.java +++ b/src/desktop/app/CPUWindow.java @@ -169,7 +169,7 @@ class CPUWindow extends ChildWindow { // Private Methods // /////////////////////////////////////////////////////////////////////////// - // Determine the maximum character width of a hex character in a font + // Determine the maximum width of a hex character in a font private static Dimension measureHex(Font font) { int ret = 0; Dimension size = null; diff --git a/src/desktop/vue/Access.java b/src/desktop/vue/Access.java new file mode 100644 index 0000000..1e1cfff --- /dev/null +++ b/src/desktop/vue/Access.java @@ -0,0 +1,21 @@ +package vue; + +// Access state +public class Access { + + // Instance fields + public int address; // CPU bus address + public int fetch; // Index of machine code unit + public int type; // Data type + public int value; // Value read/to write + + + + /////////////////////////////////////////////////////////////////////////// + // Constructors // + /////////////////////////////////////////////////////////////////////////// + + // Default constructor + Access() { } + +} diff --git a/src/desktop/vue/CPU.java b/src/desktop/vue/CPU.java index 0eee643..a162ee4 100644 --- a/src/desktop/vue/CPU.java +++ b/src/desktop/vue/CPU.java @@ -10,12 +10,15 @@ class CPU { private JavaVUE vue; // Emulation state // Package fields - int cycles; // Cycles until next stage - int fetch; // Fetch unit index - Instruction inst; // Instruction state - int[] jumpFrom; // Source PCs of most recent jumps - int[] jumpTo; // Destination PCs of most recent jumps - int stage; // Current processing stage + Access access; // Access state + int cycles; // Cycles until next stage + int exception; // Exception code + int fetch; // Fetch unit index + Instruction inst; // Instruction state + int irq; // Interrupt lines + int[] jumpFrom; // Source PCs of most recent jumps + int[] jumpTo; // Destination PCs of most recent jumps + int stage; // Current processing stage // Program registers int[] program; @@ -67,14 +70,23 @@ class CPU { /////////////////////////////////////////////////////////////////////////// // 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; + static final int FATAL = -1; + static final int FETCH = 0; + static final int EXECUTE = 1; + static final int EXCEPTION = 2; + static final int HALT = 3; + static final int CLEAR = 4; + static final int DUMP = 5; + static final int RESTORE = 6; + + // Instruction cycle counts + private static final byte[] CYCLES = { + 1, 1, 28, 1, 1, 1, 1, 1, 1, 26, 12, 1, 1, 10, 14, 16, + 38, 44, 36, 1, 5, 5, 5, 3, 3, 3, 5, 5, 5, 8, 1, 1, + 1, 1, 1, 9, 13, 30, 13, 1, 1, 1, 1, 1, 1, 4, 4, 4, + 10, 22, 1, 1, 1, 1, 1, 1, 12, 1, 1, 1, 1, 1, 4, 4, + 4, 8, 1, 28, 15, 14, 6, 1, 1, 1, 1, 1 + }; @@ -84,6 +96,8 @@ class CPU { // Default constructor CPU(JavaVUE vue) { + access = new Access(); + inst = new Instruction(); jumpFrom = new int[3]; jumpTo = new int[3]; program = new int[32]; @@ -96,6 +110,33 @@ class CPU { // Package Methods // /////////////////////////////////////////////////////////////////////////// + // Process the simulation + void emulate(int cycles) { + + // The CPU is in permanent halt + if (stage == FATAL) + return; + + // Process for the given number of cycles + do { + + // The next event occurs after the given number of cycles + if (this.cycles > cycles) { + this.cycles -= cycles; + return; + } + + // Processing by stage + switch (stage) { + case FETCH : if (fetch ()) return; + case EXECUTE : if (execute ()) return; + case HALT : testException(); break; + case EXCEPTION: if (exception()) return; + } + + } while (cycles > 0); + } + // Read a system register int getSystemRegister(int index) { switch (index) { @@ -131,9 +172,11 @@ class CPU { void reset() { // Configure instance fields - cycles = 0; // Duration of first fetch - fetch = 0; - stage = FETCH; + cycles = 0; // Duration of first fetch + exception = 0; + fetch = 0; + irq = 0; + stage = FETCH; // Clear all registers (hardware only sets ECR, PC and PSW) for (int x = 0; x < 32; x++) { @@ -224,4 +267,227 @@ class CPU { return test(condition & 7) ^ 1; } + // Determine the number of CPU cycles until something can happen + int until(int cycles) { + if (stage == FATAL || stage == HALT) + return cycles; + return cycles < 0 ? this.cycles : Math.min(cycles, this.cycles); + } + + + + /////////////////////////////////////////////////////////////////////////// + // Private Methods // + /////////////////////////////////////////////////////////////////////////// + + // Operations for exception stage + private boolean exception() { + + // Application callback + if (vue.onException != null) { + vue.breakCode = vue.onException.call(vue, exception); + if (vue.breakCode != 0) + return true; + } + + exception &= 0xFFFF; + boolean isIRQ = (exception & 0xFF00) == 0xFE00; + int psw = getSystemRegister(VUE.PSW); + + // Fatal exception + if (psw_np != 0) { + vue.write(0x00000000, VUE.S32, 0xFFFF0000 | exception); + vue.write(0x00000004, VUE.S32, psw); + vue.write(0x00000008, VUE.S32, pc); + } + + // Duplexed exception + if (psw_ep != 0) { + ecr_fecc = exception; + fepc = pc; + fepsw = psw; + pc = 0xFFFFFFD0; + } + + // Regular exception + else { + ecr_eicc = exception; + eipc = pc; + eipsw = psw; + pc = 0xFFFF0000 | exception & 0xFFF0; + if (pc == 0xFFFFFF70) // FIV + pc = 0xFFFFFF60; + } + + // Interrupt + if (isIRQ) + psw_i = Math.min(15, exception >> 4 & 15); + + // Common processing + exception = 0; + psw_ae = 0; + psw_id = 1; + stage = FETCH; + return false; + } + + // Operations for execute stage + private boolean execute() { + + // Application callback + if (vue.onExecute != null) { + vue.breakCode = vue.onExecute.call(vue, inst); + if (vue.breakCode != 0) + return true; + } + + // Determine the default number of cycles for the instruction + if (inst.id >= 0 && inst.id <= 75) + cycles = CYCLES[inst.id]; + + // Processing by instruction ID + switch (inst.id) { + //case VUE.ADD_IMM: ADD_IMM(); break; + //case VUE.ADD_REG: ADD_REG(); break; + //case VUE.ADDF_S : ADDF_S (); break; + //case VUE.ADDI : ADDI (); break; + //case VUE.AND : AND (); break; + //case VUE.ANDBSU : ANDBSU (); break; + //case VUE.ANDI : ANDI (); break; + //case VUE.ANDNBSU: ANDNBSU(); break; + //case VUE.BCOND : BCOND (); break; + //case VUE.CAXI : CAXI (); break; + //case VUE.CLI : CLI (); break; + //case VUE.CMP_IMM: CMP_IMM(); break; + //case VUE.CMP_REG: CMP_REG(); break; + //case VUE.CMPF_S : CMPF_S (); break; + //case VUE.CVT_SW : CVT_SW (); break; + //case VUE.CVT_WS : CVT_WS (); break; + //case VUE.DIV : DIV (); break; + //case VUE.DIVF_S : DIVF_S (); break; + //case VUE.DIVU : DIVU (); break; + //case VUE.HALT : HALT (); break; + //case VUE.IN_B : IN_B (); break; + //case VUE.IN_H : IN_H (); break; + //case VUE.IN_W : IN_W (); break; + //case VUE.JAL : JAL (); break; + //case VUE.JMP : JMP (); break; + //case VUE.JR : JR (); break; + //case VUE.LD_B : LD_B (); break; + //case VUE.LD_H : LD_H (); break; + //case VUE.LD_W : LD_W (); break; + //case VUE.LDSR : LDSR (); break; + //case VUE.MOV_IMM: MOV_IMM(); break; + //case VUE.MOV_REG: MOV_REG(); break; + //case VUE.MOVBSU : MOVBSU (); break; + //case VUE.MOVEA : MOVEA (); break; + //case VUE.MOVHI : MOVHI (); break; + //case VUE.MPYHW : MPYHW (); break; + //case VUE.MUL : MUL (); break; + //case VUE.MULF_S : MULF_S (); break; + //case VUE.MULU : MULU (); break; + //case VUE.NOT : NOT (); break; + //case VUE.NOTBSU : NOTBSU (); break; + //case VUE.OR : OR (); break; + //case VUE.ORBSU : ORBSU (); break; + //case VUE.ORI : ORI (); break; + //case VUE.ORNBSU : ORNBSU (); break; + //case VUE.OUT_B : OUT_B (); break; + //case VUE.OUT_H : OUT_H (); break; + //case VUE.OUT_W : OUT_W (); break; + //case VUE.RETI : RETI (); break; + //case VUE.REV : REV (); break; + //case VUE.SAR_IMM: SAR_IMM(); break; + //case VUE.SAR_REG: SAR_REG(); break; + //case VUE.SCH0BSD: SCH0BSD(); break; + //case VUE.SCH0BSU: SCH0BSU(); break; + //case VUE.SCH1BSD: SCH1BSD(); break; + //case VUE.SCH1BSU: SCH1BSU(); break; + //case VUE.SEI : SEI (); break; + //case VUE.SETF : SETF (); break; + //case VUE.SHL_IMM: SHL_IMM(); break; + //case VUE.SHL_REG: SHL_REG(); break; + //case VUE.SHR_IMM: SHR_IMM(); break; + //case VUE.SHR_REG: SHR_REG(); break; + //case VUE.ST_B : ST_B (); break; + //case VUE.ST_H : ST_H (); break; + //case VUE.ST_W : ST_W (); break; + //case VUE.STSR : STSR (); break; + //case VUE.SUB : SUB (); break; + //case VUE.SUBF_S : SUBF_S (); break; + //case VUE.TRAP : TRAP (); break; + //case VUE.TRNC_SW: TRNC_SW(); break; + //case VUE.XB : XB (); break; + //case VUE.XH : XH (); break; + //case VUE.XOR : XOR (); break; + //case VUE.XORBSU : XORBSU (); break; + //case VUE.XORI : XORI (); break; + //case VUE.XORNBSU: XORNBSU(); break; + default: exception = 0xFF90; // Invalid instruction + } + + // Common processing + pc += inst.size; + program[0] = 0; + testException(); + return false; + } + + // Operations for fetch stage + private boolean fetch() { + + // Read the bits from the bus + access.address = pc + (fetch << 1); + access.fetch = fetch; + access.type = VUE.U16; + access.value = vue.read(access.address, VUE.U16); + + // Application callback + if (vue.onRead != null) { + vue.breakCode = vue.onRead.call(vue, access); + if (vue.breakCode != 0) + return true; + } + + // First unit + if (fetch == 0) { + inst.bits = access.value << 16; + if (Instruction.size(access.value & 0x3F) == 4) { + fetch = 1; + return false; + } + } + + // Second unit + else { + inst.bits |= access.value & 0xFFFF; + fetch = 0; + } + + // Decode the instruction and advance to execute stage + inst.decode(inst.bits); + stage = EXECUTE; + return false; + } + + // Check for an exception or interrupt + private void testException() { + + // Check for an interrupt + if (irq != 0 && (exception | psw_id | psw_ep | psw_np) == 0) { + int level; + for (level = 4; level >= 0; level--) + if ((irq >> level & 1) != 0) + break; + exception = 0xFE00 | level << 4; + } + + // Check for an exception + if (exception != 0) { + cycles = 0; + stage = EXCEPTION; + } + + } + } diff --git a/src/desktop/vue/JavaVUE.java b/src/desktop/vue/JavaVUE.java index c61d0b2..f112a66 100644 --- a/src/desktop/vue/JavaVUE.java +++ b/src/desktop/vue/JavaVUE.java @@ -6,13 +6,15 @@ import java.util.*; // Java emulation core implementation class JavaVUE extends VUE { - // State fields - int breakCode; // Application break code - byte[] wram; // System WRAM - // Package fields - CPU cpu; // Processor - GamePak pak; // Game pak + int breakCode; // Application break code + CPU cpu; // Processor + OnException onException; // Exception callback handler + OnExecute onExecute; // Execute callback handler + OnRead onRead; // Read callback handler + OnWrite onWrite; // Write callback handler + GamePak pak; // Game pak + byte[] wram; // System WRAM @@ -53,13 +55,19 @@ class JavaVUE extends VUE { do { // Determine the number of cycles during which nothing will happen - int cycles = maxCycles; - //cycles = cpu .until(cycles); + int cycles = -1; //cycles = pad .until(cycles); //cycles = link .until(cycles); //cycles = timer.until(cycles); //cycles = vip .until(cycles); //cycles = vsu .until(cycles); + cycles = cpu .until(cycles); + + // Range checking + if (cycles == -1) // No activity on any component + break; + if (maxCycles >= 0) // Restrict to given number of cycles + cycles = Math.min(cycles, maxCycles); // Process all system components breakCode = 0; @@ -68,7 +76,7 @@ class JavaVUE extends VUE { //timer.emulate(cycles); //vip .emulate(cycles); //vsu .emulate(cycles); - //cpu .emulate(cycles); + cpu .emulate(cycles); // An application break was requested if (breakCode != 0) @@ -80,7 +88,12 @@ class JavaVUE extends VUE { } while (maxCycles != 0); // A break condition has occurred - return maxCycles; + return Math.max(0, maxCycles); + } + + // Retrieve the application break code + public int getBreakCode() { + return breakCode; } // Retrieve a register value @@ -293,7 +306,7 @@ class JavaVUE extends VUE { // Write a value to a byte buffer static void writeBuffer(byte[] data, int address, int type, int value) { - // Error checking + // The destination does not exist if (data == null) return; diff --git a/src/desktop/vue/NativeVUE.c b/src/desktop/vue/NativeVUE.c index fd0a432..fa0e732 100644 --- a/src/desktop/vue/NativeVUE.c +++ b/src/desktop/vue/NativeVUE.c @@ -89,6 +89,13 @@ JNIEXPORT jint JNICALL Java_vue_NativeVUE_emulate return vueEmulate(&core->vue, maxCycles); } +// Retrieve the application break code +JNIEXPORT jint JNICALL Java_vue_NativeVUE_getRegister + (JNIEnv *env, jobject vue) { + CORE *core = GetCore(env, vue); + return vueGetBreakCode(&core->vue); +} + // Retrieve a register value JNIEXPORT jint JNICALL Java_vue_NativeVUE_getRegister (JNIEnv *env, jobject vue, jint index, jboolean system) { diff --git a/src/desktop/vue/NativeVUE.java b/src/desktop/vue/NativeVUE.java index 4dd0c23..37cbb08 100644 --- a/src/desktop/vue/NativeVUE.java +++ b/src/desktop/vue/NativeVUE.java @@ -29,6 +29,9 @@ class NativeVUE extends VUE { // Process the simulation public native int emulate(int maxCycles); + // Retrieve the application break code + public native int getBreakCode(); + // Retrieve a register value public native int getRegister(int index, boolean system); diff --git a/src/desktop/vue/VUE.java b/src/desktop/vue/VUE.java index d6beae3..0213457 100644 --- a/src/desktop/vue/VUE.java +++ b/src/desktop/vue/VUE.java @@ -8,6 +8,17 @@ public abstract class VUE { + /////////////////////////////////////////////////////////////////////////// + // Types // + /////////////////////////////////////////////////////////////////////////// + + public interface OnException { int call(VUE vue, int code ); } + public interface OnExecute { int call(VUE vue, Instruction inst ); } + public interface OnRead { int call(VUE vue, Access access); } + public interface OnWrite { int call(VUE vue, Access access); } + + + /////////////////////////////////////////////////////////////////////////// // Constants // /////////////////////////////////////////////////////////////////////////// @@ -161,6 +172,9 @@ public abstract class VUE { // Process the simulation public abstract int emulate(int maxCycles); + // Retrieve the application break code + public abstract int getBreakCode(); + // Retrieve a register value public abstract int getRegister(int index, boolean system);