pvbemu/src/desktop/vue/CPU.java

494 lines
17 KiB
Java

package vue;
// Java imports
import java.util.*;
// CPU state
class CPU {
// Private fields
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
// 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 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
};
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
CPU(JavaVUE vue) {
access = new Access();
inst = new Instruction();
jumpFrom = new int[3];
jumpTo = new int[3];
program = new int[32];
this.vue = vue;
}
///////////////////////////////////////////////////////////////////////////
// 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) {
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:
return 0;
}
return 1; // Unreachable
}
// System reset
void reset() {
// 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)
for (int x = 0; x < 32; x++) {
program[x] = 0;
setSystemRegister(x, 0, true);
}
// Configure jump histories
Arrays.fill(jumpFrom, 0xFFFFFFF0);
Arrays.fill(jumpTo , 0xFFFFFFF0);
// Configure registers
ecr_eicc = 0xFFF0;
pc = 0xFFFFFFF0;
psw_np = 1;
}
// 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.ECR:
if (debug) {
ecr_fecc = value >> 16 & 0xFFFF;
ecr_eicc = value & 0xFFFF;
}
return ecr_fecc << 16 | ecr_eicc;
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 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;
}
// 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;
}
}
}