Implementing CPU emulation logic
This commit is contained in:
parent
a98c35ee8e
commit
d1b8e293c3
|
@ -234,6 +234,7 @@ typedef struct {
|
|||
*****************************************************************************/
|
||||
|
||||
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);
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() { }
|
||||
|
||||
}
|
|
@ -10,9 +10,12 @@ class CPU {
|
|||
private JavaVUE vue; // Emulation state
|
||||
|
||||
// Package fields
|
||||
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
|
||||
|
@ -67,14 +70,23 @@ class CPU {
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Stages
|
||||
static final int FATAL = -1;
|
||||
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 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) {
|
||||
|
@ -132,7 +173,9 @@ class CPU {
|
|||
|
||||
// Configure instance fields
|
||||
cycles = 0; // Duration of first fetch
|
||||
exception = 0;
|
||||
fetch = 0;
|
||||
irq = 0;
|
||||
stage = FETCH;
|
||||
|
||||
// Clear all registers (hardware only sets ECR, PC and PSW)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue