Implementing CPU emulation logic

This commit is contained in:
Guy Perfect 2020-08-10 20:24:00 -05:00
parent a98c35ee8e
commit d1b8e293c3
9 changed files with 369 additions and 39 deletions

View File

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

View File

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

View File

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

View File

@ -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() { }
}

View File

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

View File

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

View File

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

View File

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

View File

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