Adjusting top-level emulation logic, integrating breakpoints

This commit is contained in:
2020-12-19 19:51:42 -06:00
parent e13c56e1e7
commit 167bc4f8bf
17 changed files with 1504 additions and 704 deletions
+106 -102
View File
@@ -116,8 +116,8 @@ static vbool cpuFloatReserved(Vue *vue, vbool left) {
)) continue;
/* The value is a reserved operand */
vue->cpu.exception.code = 0xFF60;
vue->cpu.psw_fro = 1;
vue->cpu.exception = 0xFF60;
vue->cpu.psw_fro = 1;
return VUE_TRUE;
}
@@ -150,7 +150,7 @@ static void cpuFloatConvert(Vue *vue, vbool round) {
/* Invalid operation (word overflow) */
else if (bits >= 8) {
vue->cpu.exception.code = 0xFF70;
vue->cpu.exception = 0xFF70;
return;
}
@@ -193,8 +193,8 @@ static void cpuFloatResult(Vue *vue, vbool compare, double full) {
/* Overflow */
if (full > result || full < -result) {
vue->cpu.exception.code = 0xFF64;
vue->cpu.psw_fov = 1;
vue->cpu.exception = 0xFF64;
vue->cpu.psw_fov = 1;
return;
}
@@ -578,7 +578,7 @@ static void cpuDIV(Vue *vue) {
/* Zero division */
if (right == 0) {
vue->cpu.exception.code = 0xFF80;
vue->cpu.exception = 0xFF80;
return;
}
@@ -612,7 +612,7 @@ static void cpuDIVF_S(Vue *vue) {
/* An exception has occurred */
if (!right) {
vue->cpu.exception.code = left ? 0xFF68 : 0xFF70;
vue->cpu.exception = left ? 0xFF68 : 0xFF70;
return;
}
@@ -628,7 +628,7 @@ static void cpuDIVU(Vue *vue) {
/* Zero division */
if (right == 0) {
vue->cpu.exception.code = 0xFF80;
vue->cpu.exception = 0xFF80;
return;
}
@@ -848,7 +848,7 @@ static void cpuREV(Vue *vue) {
/* Trap */
#define cpuTRAP(vue) \
vue->cpu.exception.code = 0xFFA0 | (vue->cpu.inst.imm & 15), \
vue->cpu.exception = 0xFFA0 | (vue->cpu.inst.imm & 15), \
vue->cpu.pc += 2
/* Truncate Short Floating to Word */
@@ -908,8 +908,12 @@ static void cpuDecode(VueInstruction *inst) {
x = inst->bits >> 16 & 31;
inst->reg2 = inst->bits >> 21 & 31;
inst->imm = extend < 0 ? SIGN_EXTEND(5, x) : x;
if (inst->id == BITSTRING)
if (inst->id == BITSTRING) {
inst->id = x >= 16 ? VUE_ILLEGAL : LOOKUP_BITSTRING[x];
inst->subopcode = x;
}
if (inst->id == VUE_SETF)
inst->cond = x & 15;
break;
case 3:
x = inst->bits >> 16 & 0x1FF;
@@ -944,21 +948,84 @@ static void cpuDecode(VueInstruction *inst) {
}
/* Operations for exception stage */
static vbool cpuException(Vue *vue) {
vbool isIRQ; /* The exception is an interrupt */
int32_t psw; /* Current value of PSW */
/* Application callback */
if (vue->onException != NULL) {
vue->breakCode = vue->onException(vue, vue->cpu.exception);
if (vue->breakCode != 0)
return VUE_TRUE;
}
/* Configure working variables */
vue->cpu.exception &= 0xFFFF;
isIRQ = (vue->cpu.exception & 0xFF00) == 0xFE00;
psw = cpuGetSystemRegister(vue, VUE_PSW);
/* Fatal exception */
if (vue->cpu.psw_np != 0) {
vueWrite(vue, 0x00000000, VUE_S32, 0xFFFF0000 | vue->cpu.exception);
vueWrite(vue, 0x00000004, VUE_S32, psw);
vueWrite(vue, 0x00000008, VUE_S32, vue->cpu.pc);
vue->cpu.stage = CPU_FATAL;
return VUE_TRUE;
}
/* Duplexed exception */
if (vue->cpu.psw_ep != 0) {
vue->cpu.ecr_fecc = vue->cpu.exception;
vue->cpu.fepc = vue->cpu.pc;
vue->cpu.fepsw = psw;
vue->cpu.psw_np = 1;
vue->cpu.pc = 0xFFFFFFD0;
}
/* Regular exception */
else {
vue->cpu.ecr_eicc = vue->cpu.exception;
vue->cpu.eipc = vue->cpu.pc;
vue->cpu.eipsw = psw;
vue->cpu.psw_ep = 1;
vue->cpu.pc = 0xFFFF0000 | (vue->cpu.exception & 0xFFF0);
if (vue->cpu.pc == (int32_t) 0xFFFFFF70) /* FIV */
vue->cpu.pc = 0xFFFFFF60;
}
/* Interrupt */
if (isIRQ) {
psw = vue->cpu.exception >> 4 & 15;
vue->cpu.psw_i = psw < 15 ? psw : 15;
if (vue->cpu.stage == CPU_HALT)
vue->cpu.pc += 2;
}
/* Common processing */
vue->cpu.cycles = 0; /* TODO: Determine the actual number */
vue->cpu.exception = 0;
vue->cpu.psw_ae = 0;
vue->cpu.psw_id = 1;
vue->cpu.stage = CPU_FETCH;
return VUE_FALSE;
}
/* Check for an exception or interrupt */
static vbool cpuTestException(Vue *vue) {
int32_t level; /* Interrupt level */
/* Check for an interrupt */
if (vue->cpu.irq != 0 && (vue->cpu.exception.code |
if (vue->cpu.irq != 0 && (vue->cpu.exception |
vue->cpu.psw_id | vue->cpu.psw_ep | vue->cpu.psw_np) == 0) {
for (level = 4; level >= 0; level--)
if ((vue->cpu.irq >> level & 1) != 0)
break;
vue->cpu.exception.code = 0xFE00 | level << 4;
vue->cpu.exception = 0xFE00 | level << 4;
}
/* There is no exception */
if (vue->cpu.exception.code == 0) /* No further processing */
if (vue->cpu.exception == 0) /* No further processing */
return vue->cpu.stage == CPU_HALT && vue->cpu.cycles == 0;
/* An exception has occurred */
@@ -975,8 +1042,6 @@ static vbool cpuExecute(Vue *vue) {
vue->breakCode = vue->onExecute(vue, &vue->cpu.inst);
if (vue->breakCode != 0)
return VUE_TRUE;
vue->cpu.inst.reg1 &= 31;
vue->cpu.inst.reg2 &= 31;
}
/* Processing by instruction ID */
@@ -1058,11 +1123,11 @@ static vbool cpuExecute(Vue *vue) {
case VUE_XORI : cpuXORI (vue); break;
/*case VUE_XORNBSU: cpuXORNBSU(vue); break;*/
default: /* Invalid instruction */
vue->cpu.exception.code = 0xFF90;
vue->cpu.exception = 0xFF90;
}
/* Common processing */
if (vue->cpu.exception.code == 0) {
if (vue->cpu.exception == 0) {
vue->cpu.cycles += CYCLES[vue->cpu.inst.id];
vue->cpu.pc += vue->cpu.inst.size;
}
@@ -1082,10 +1147,15 @@ static int32_t cpuSize(int32_t opcode) {
/* Operations for fetch stage */
static vbool cpuFetch(Vue *vue) {
/* Entering the fetch stage */
if (vue->cpu.fetch == -1)
vue->cpu.fetch = 0;
/* Read the bits from the bus */
if (cpuRead(vue, vue->cpu.pc + (vue->cpu.fetch << 1),
VUE_U16, vue->cpu.fetch))
return VUE_TRUE;
/* TODO: Determine how many cycles this takes */
/* First unit */
if (vue->cpu.fetch == 0) {
@@ -1099,7 +1169,7 @@ static vbool cpuFetch(Vue *vue) {
/* Second unit */
else {
vue->cpu.inst.bits |= vue->cpu.access.value & 0xFFFF;
vue->cpu.fetch = 0;
vue->cpu.fetch = -1;
}
/* Decode the instruction and advance to execute stage */
@@ -1108,94 +1178,30 @@ static vbool cpuFetch(Vue *vue) {
return VUE_FALSE;
}
/* Operations for exception stage */
static vbool cpuException(Vue *vue) {
vbool isIRQ; /* The exception is an interrupt */
int32_t psw; /* Current value of PSW */
/* Application callback */
if (vue->onException != NULL) {
vue->breakCode = vue->onException(vue, &vue->cpu.exception);
if (vue->breakCode != 0)
return VUE_TRUE;
}
/* Configure working variables */
vue->cpu.exception.code &= 0xFFFF;
isIRQ = (vue->cpu.exception.code & 0xFF00) == 0xFE00;
psw = cpuGetSystemRegister(vue, VUE_PSW);
/* Fatal exception */
if (vue->cpu.psw_np != 0) {
vueWrite(vue, 0x00000000, VUE_S32, 0xFFFF0000|vue->cpu.exception.code);
vueWrite(vue, 0x00000004, VUE_S32, psw);
vueWrite(vue, 0x00000008, VUE_S32, vue->cpu.pc);
vue->cpu.stage = CPU_FATAL;
return VUE_TRUE;
}
/* Duplexed exception */
if (vue->cpu.psw_ep != 0) {
vue->cpu.ecr_fecc = vue->cpu.exception.code;
vue->cpu.fepc = vue->cpu.pc;
vue->cpu.fepsw = psw;
vue->cpu.psw_np = 1;
vue->cpu.pc = 0xFFFFFFD0;
}
/* Regular exception */
else {
vue->cpu.ecr_eicc = vue->cpu.exception.code;
vue->cpu.eipc = vue->cpu.pc;
vue->cpu.eipsw = psw;
vue->cpu.psw_ep = 1;
vue->cpu.pc = 0xFFFF0000 | (vue->cpu.exception.code & 0xFFF0);
if (vue->cpu.pc == (int32_t) 0xFFFFFF70) /* FIV */
vue->cpu.pc = 0xFFFFFF60;
}
/* Interrupt */
if (isIRQ) {
psw = vue->cpu.exception.code >> 4 & 15;
vue->cpu.psw_i = psw < 15 ? psw : 15;
if (vue->cpu.stage == CPU_HALT)
vue->cpu.pc += 2;
}
/* Common processing */
vue->cpu.cycles = 0; /* TODO: Determine the actual number */
vue->cpu.exception.code = 0;
vue->cpu.psw_ae = 0;
vue->cpu.psw_id = 1;
vue->cpu.stage = CPU_FETCH;
return VUE_FALSE;
}
/* Process the simulation */
static void cpuEmulate(Vue *vue, int32_t cycles) {
static void cpuEmulate(Vue *vue, uint32_t cycles) {
/* The CPU is halting */
if (vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT)
/* The CPU is in fatal halt status */
if (vue->cpu.stage == CPU_FATAL)
return;
vue->cpu.cycles = 0; /* DEBUG: Stop processing after execute */
/* Process for the given number of cycles */
for (;;) {
/* The next event occurs after the given number of cycles */
/* The next stage occurs after the given number of cycles */
if (vue->cpu.cycles > cycles) {
vue->cpu.cycles -= cycles;
return;
}
/* Processing by stage */
cycles -= vue->cpu.cycles;
vue->cpu.cycles = 0;
switch (vue->cpu.stage) {
case CPU_EXCEPTION: if (cpuException (vue)) return; break;
case CPU_EXECUTE : if (cpuExecute (vue)) return; break;
case CPU_FETCH : if (cpuFetch (vue)) return; break;
case CPU_HALT : if (cpuTestException(vue)) return; break;
case CPU_EXCEPTION: if (cpuException(vue)) return; break;
case CPU_EXECUTE : if (cpuExecute (vue)) return; break;
case CPU_FETCH : if (cpuFetch (vue)) return; break;
case CPU_HALT : cpuTestException(vue); return;
}
}
@@ -1207,11 +1213,11 @@ static void cpuReset(Vue *vue) {
int32_t x;
/* Configure instance fields */
vue->cpu.cycles = 0;
vue->cpu.exception.code = 0;
vue->cpu.fetch = 0;
vue->cpu.irq = 0;
vue->cpu.stage = CPU_FETCH;
vue->cpu.cycles = 0;
vue->cpu.exception = 0;
vue->cpu.fetch = -1;
vue->cpu.irq = 0;
vue->cpu.stage = CPU_FETCH;
/* Clear all registers (hardware only sets ECR, PC and PSW) */
for (x = 0; x < 32; x++) {
@@ -1229,12 +1235,10 @@ static void cpuReset(Vue *vue) {
vue->cpu.psw_np = 1;
}
/* Determine the number of CPU cycles until something can happen */
static int32_t cpuUntil(Vue *vue, int32_t cycles) {
if (vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT)
return cycles;
return cycles < 0 ? vue->cpu.cycles :
cycles < vue->cpu.cycles ? cycles : vue->cpu.cycles;
/* Determine the number of CPU cycles until a breakpoint could trigger */
static uint32_t cpuUntil(Vue *vue, uint32_t cycles) {
return vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT ?
cycles : cycles < vue->cpu.cycles ? cycles : vue->cpu.cycles;
}
+27 -29
View File
@@ -154,11 +154,6 @@ typedef struct {
int8_t type; /* Data type */
} VueAccess;
/* Exception state */
typedef struct {
uint16_t code; /* Exception code */
} VueException;
/* Instruction state */
typedef struct {
int32_t bits; /* Binary encoding */
@@ -174,31 +169,32 @@ typedef struct {
uint8_t subopcode; /* Instruction subopcode */
} VueInstruction;
/* Callbacks */
typedef int32_t (*VueOnException)(Vue *, VueException *);
/* Breakpoint handler callbacks */
typedef int32_t (*VueOnAccess )(Vue *, VueAccess *);
typedef int32_t (*VueOnException)(Vue *, uint16_t );
typedef int32_t (*VueOnExecute )(Vue *, VueInstruction *);
typedef int32_t (*VueOnRead )(Vue *, VueAccess *);
typedef int32_t (*VueOnWrite )(Vue *, VueAccess *);
typedef int32_t (*VueOnFrame )(Vue * );
/* Emulation state */
struct Vue {
int32_t breakCode; /* Application break code */
uint8_t wram[0x10000]; /* System memory */
/* Callback handlers */
/* Breakpoint handlers */
VueOnException onException;
VueOnExecute onExecute;
VueOnRead onRead;
VueOnWrite onWrite;
VueOnFrame onFrame;
VueOnAccess onRead;
VueOnAccess onWrite;
/* CPU state */
struct {
VueAccess access; /* Access state */
VueException exception; /* Exception state */
VueInstruction inst; /* Instruction state */
int32_t cycles; /* Cycles until next stage */
uint32_t cycles; /* Cycles until next stage */
int32_t jumpFrom[3]; /* Source PCs of most recent jumps */
int32_t jumpTo [3]; /* Destination PCs of most recent jumps */
uint16_t exception; /* Exception code */
uint16_t irq; /* Interrupt lines */
int fetch; /* Fetch unit index */
int stage; /* Current processing stage */
@@ -265,21 +261,23 @@ struct Vue {
* Function Prototypes *
*****************************************************************************/
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 void vueSetException(Vue *vue, VueOnException callback);
VUEAPI void vueSetExecute (Vue *vue, VueOnExecute callback);
VUEAPI void vueSetRead (Vue *vue, VueOnRead callback);
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 vueSetWrite (Vue *vue, VueOnWrite callback);
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 uint32_t vueEmulate (Vue *vue, uint32_t maxCycles);
VUEAPI int32_t vueGetBreakCode (Vue *vue);
VUEAPI uint16_t vueGetExceptionCode (Vue *vue);
VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system);
VUEAPI void vueInitialize (Vue *vue);
VUEAPI VueOnException vueOnException (Vue *vue, VueOnException callback);
VUEAPI VueOnExecute vueOnExecute (Vue *vue, VueOnExecute callback);
VUEAPI VueOnFrame vueOnFrame (Vue *vue, VueOnFrame callback);
VUEAPI VueOnAccess vueOnRead (Vue *vue, VueOnAccess callback);
VUEAPI VueOnAccess vueOnWrite (Vue *vue, VueOnAccess callback);
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);
+68 -49
View File
@@ -155,55 +155,46 @@ static void writeBytes(uint8_t *data, uint32_t datlen, uint32_t address,
*****************************************************************************/
/* Process the simulation */
int32_t vueEmulate(Vue *vue, int32_t maxCycles) {
int32_t cycles; /* Number of cycles to process */
uint32_t vueEmulate(Vue *vue, uint32_t maxCycles) {
uint32_t cycles; /* Number of cycles to process */
/* Process up to the given number of cycles */
do {
/* Determine the number of cycles during which nothing will happen */
cycles = -1;
/* Determine the number of cycles where no breakpoint will occur */
cycles = maxCycles; /* min(maxCycles, nextFrameCycles) */
cycles = cpuUntil (vue, cycles);
/*cycles = padUntil (vue, cycles);*/
/*cycles = linkUntil (vue, cycles);*/
/*cycles = timerUntil(vue, cycles);*/
/*cycles = vipUntil (vue, cycles);*/
/*cycles = vsuUntil (vue, cycles);*/
cycles = cpuUntil (vue, cycles);
/* Range checking */
if (cycles == -1) /* No activity on any component */
break;
if (maxCycles >= 0) /* Restrict to given number of cycles */
cycles = cycles < maxCycles ? cycles : maxCycles;
/* Process all system components */
vue->breakCode = 0;
cpuEmulate (vue, cycles);
/*padEmulate (vue, cycles);*/
/*pakEmulate (vue, cycles);*/
/*linkEmulate (vue, cycles);*/
/*timerEmulate(vue, cycles);*/
/*vipEmulate (vue, cycles);*/
/*vsuEmulate (vue, cycles);*/
cpuEmulate (vue, cycles);
/* An application break was requested */
if (vue->breakCode != 0)
break;
/* Update the number of cycles remaining */
if (maxCycles >= 0)
maxCycles -= cycles;
} while (maxCycles != 0);
maxCycles -= cycles;
} while (vue->breakCode == 0 && maxCycles != 0);
/* A break condition has occurred */
return maxCycles;
}
/* Retrieve the application break code */
/* Retrieve the most recent applicaiton break code */
int32_t vueGetBreakCode(Vue *vue) {
return vue == NULL ? 0 : vue->breakCode;
}
/* Retrieve the most recent exception code */
uint16_t vueGetExceptionCode(Vue *vue) {
return vue == NULL ? 0 : vue->cpu.exception;
}
/* Retrieve the value of a register */
int32_t vueGetRegister(Vue *vue, int32_t index, vbool system) {
@@ -234,17 +225,69 @@ void vueInitialize(Vue *vue) {
return;
vue->onException = NULL;
vue->onExecute = NULL;
vue->onFrame = NULL;
vue->onRead = NULL;
vue->onWrite = NULL;
vue->pak.ram = NULL;
vue->pak.rom = NULL;
vueReset(vue);
}
/* Specify an exception breakpoint callback */
VueOnException vueOnException(Vue *vue, VueOnException callback) {
VueOnException ret;
if (vue == NULL)
return NULL;
ret = vue->onException;
vue->onException = callback;
return ret;
}
/* Specify an execute breakpoint callback */
VueOnExecute vueOnExecute(Vue *vue, VueOnExecute callback) {
VueOnExecute ret;
if (vue == NULL)
return NULL;
ret = vue->onExecute;
vue->onExecute = callback;
return ret;
}
/* Specify a frame breakpoint callback */
VueOnFrame vueOnFrame(Vue *vue, VueOnFrame callback) {
VueOnFrame ret;
if (vue == NULL)
return NULL;
ret = vue->onFrame;
vue->onFrame = callback;
return ret;
}
/* Specify a read breakpoint callback */
VueOnAccess vueSetRead(Vue *vue, VueOnAccess callback) {
VueOnAccess ret;
if (vue == NULL)
return NULL;
ret = vue->onRead;
vue->onRead = callback;
return ret;
}
/* Specify a write breakpoint callback */
VueOnAccess vueOnWrite(Vue *vue, VueOnAccess callback) {
VueOnAccess ret;
if (vue == NULL)
return NULL;
ret = vue->onWrite;
vue->onWrite = callback;
return ret;
}
/* Read a value from the CPU bus */
int32_t vueRead(Vue *vue, uint32_t address, int32_t type) {
/* Error checking */
if (vue == NULL)
if (vue == NULL || type < 0 || type > 4)
return 0;
/* Perform the operation */
@@ -299,28 +342,10 @@ void vueReset(Vue *vue) {
/* Reset state */
cpuReset(vue);
pakReset(vue);
for (x = 0; x < 0x1000; x++)
for (x = 0; x < 0x10000; x++)
vue->wram[x] = 0;
}
/* Specify an exception breakpoint callback */
void vueSetException(Vue *vue, VueOnException callback) {
if (vue != NULL)
vue->onException = callback;
}
/* Specify an execute breakpoint callback */
void vueSetExecute(Vue *vue, VueOnExecute callback) {
if (vue != NULL)
vue->onExecute = callback;
}
/* Specify a read breakpoint callback */
void vueSetRead(Vue *vue, VueOnRead callback) {
if (vue != NULL)
vue->onRead = callback;
}
/* Specify a value for a register */
int32_t vueSetRegister(Vue *vue, int32_t index, vbool system, int32_t value) {
return vue == NULL ? 0 :
@@ -348,12 +373,6 @@ vbool vueSetROM(Vue *vue, uint8_t *rom, uint32_t size) {
return VUE_TRUE;
}
/* Specify a write breakpoint callback */
void vueSetWrite(Vue *vue, VueOnRead callback) {
if (vue != NULL)
vue->onWrite = callback;
}
/* Write a value to the CPU bus */
void vueWrite(Vue *vue, uint32_t address, int32_t type, int32_t value) {
+1 -37
View File
@@ -49,43 +49,7 @@ public class Main {
useNative = true;
// Begin application operations
//new App(useNative);
var brk = new Breakpoint(null);
/*
//String exp =
// "([sp - 8] & 3) << 6 != 12.0 + r6 * ecr && [0x0500008C + r8] == 0";
//String exp = "float true + -3 * 4";
String exp = "1100 ^ uword 1010";
System.out.println("\n" + exp);
if (brk.setCondition(exp))
System.out.println(brk.debugTokens());
else {
var err = brk.getConditionError();
System.out.println("Error " +
err.code + "\t" +
err.position + ":" +
err.text
);
}
*/
/*
exp = "123, 456 - 987 , f0-fffffff2";
System.out.println("\n" + exp);
if (brk.setAddresses(exp))
System.out.println(brk.debugRanges());
else {
var err = brk.getAddressError();
System.out.println("Error " +
err.code + "\t" +
err.position + ":" +
err.text
);
}
*/
new App(useNative);
}
}
+1 -1
View File
@@ -74,7 +74,7 @@ public class App {
// Specify whether using the native module
boolean setUseNative(boolean useNative) {
return this.useNative = useNative && Vue.isNativeLoaded();
return this.useNative = useNative && Vue.getNativeID() != null;
}
+1
View File
@@ -31,6 +31,7 @@ class ChildWindow extends JInternalFrame {
addInternalFrameListener(Util.onClose2(e->setVisible(false)));
setClosable(true);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setMaximizable(true);
setResizable(true);
}
+30 -2
View File
@@ -63,6 +63,7 @@ class DisassemblerPane extends JScrollPane {
Util.onFocus(e->client.repaint(), e->client.repaint()));
client.addKeyListener(Util.onKey(e->onKeyDown(e), null));
client.addMouseListener(Util.onMouse(e->client.requestFocus(), null));
client.addMouseWheelListener(e->onMouseWheel(e));
client.setBackground(SystemColor.window);
client.setFocusable(true);
@@ -98,6 +99,7 @@ class DisassemblerPane extends JScrollPane {
int code = e.getKeyCode();
int count = tall(false);
int mods = e.getModifiersEx();
var vue = parent.parent.vue;
boolean alt = (mods & InputEvent.ALT_DOWN_MASK ) != 0;
boolean ctrl = (mods & InputEvent.CTRL_DOWN_MASK) != 0;
@@ -116,16 +118,37 @@ class DisassemblerPane extends JScrollPane {
}
// Processing by key code
int pc = vue.getRegister(Vue.PC, true);
var step = parent.parent.brkStep;
switch (code) {
case KeyEvent.VK_UP : seek(address, 1); break;
case KeyEvent.VK_DOWN : seek(address, -1); break;
case KeyEvent.VK_PAGE_UP : seek(address, count); break;
case KeyEvent.VK_PAGE_DOWN: seek(address, -count); break;
// Single Step
case KeyEvent.VK_F11:
parent.parent.vue.emulate(0);
step.setCondition("pc!=" + pc);
step.setEnabled(true);
vue.emulate(20000000);
if (step.evaluate())
step.setEnabled(false);
parent.parent.refreshDebug();
int pc = parent.parent.vue.getRegister(Vue.PC, true);
pc = vue.getRegister(Vue.PC, true);
if (!isVisible(pc))
seek(pc, count / 3);
break;
// Run to Next
case KeyEvent.VK_F12:
step.setCondition("pc==" +
(pc + Instruction.size(vue.read(pc, Vue.U16) >> 10)));
step.setEnabled(true);
vue.emulate(20000000);
if (step.evaluate())
step.setEnabled(false);
parent.parent.refreshDebug();
pc = vue.getRegister(Vue.PC, true);
if (!isVisible(pc))
seek(pc, count / 3);
break;
@@ -133,6 +156,11 @@ class DisassemblerPane extends JScrollPane {
}
// Mouse wheel
private void onMouseWheel(MouseWheelEvent e) {
seek(address, -e.getUnitsToScroll());
}
// Client paint
private void onPaint(Graphics2D g, int width, int height) {
var vue = parent.parent.vue;
+7 -2
View File
@@ -15,8 +15,9 @@ import vue.*;
class MainWindow extends JFrame {
// Instance fields
App app; // Containing application
Vue vue; // Emulation core context
App app; // Containing application
Breakpoint brkStep; // Single step internal breakpoint
Vue vue; // Emulation core context
// Private fields
private boolean debugMode; // Window is in debug mode
@@ -97,6 +98,10 @@ class MainWindow extends JFrame {
desktop.add(cpu = new CPUWindow (this));
desktop.add(memory = new MemoryWindow (this));
// Configure internal breakpoints
brkStep = vue.breakpoint();
brkStep.setExecute(true);
// Display window
refreshDebug();
pack();
+351 -280
View File
@@ -63,20 +63,23 @@ public class Breakpoint {
// Functional symbol IDs
private static final int ADDRESS = 0;
private static final int CODE = 1;
private static final int COND = 2;
private static final int DISP = 3;
private static final int FORMAT = 4;
private static final int ID = 5;
private static final int IMM = 6;
private static final int OPCODE = 7;
private static final int REG1 = 8;
private static final int REG2 = 9;
private static final int REGID = 10;
private static final int SIZE = 11;
private static final int SUBOPCODE = 12;
private static final int VALUE = 13;
private static final int VECTOR = 14;
private static final int BREAK = 1;
private static final int CODE = 2;
private static final int COND = 3;
private static final int DISP = 4;
private static final int FETCH = 5;
private static final int FORMAT = 6;
private static final int ID = 7;
private static final int IMM = 8;
private static final int OPCODE = 9;
private static final int REG1 = 10;
private static final int REG2 = 11;
private static final int REGID = 12;
private static final int SIZE = 13;
private static final int SUBOPCODE = 14;
private static final int TYPE = 15;
private static final int VALUE = 16;
private static final int VECTOR = 17;
// Evaluation operator IDs
private static final int GROUP = 0; // ()
@@ -190,6 +193,12 @@ public class Breakpoint {
SYMDEFS.put("false", new Def(BOOL, 0));
SYMDEFS.put("true" , new Def(BOOL, 1));
// Break type symbl definitions
SYMDEFS.put("exception", new Def(SIGNED, EXCEPTION));
SYMDEFS.put("execute" , new Def(SIGNED, EXECUTE ));
SYMDEFS.put("read" , new Def(SIGNED, READ ));
SYMDEFS.put("write" , new Def(SIGNED, WRITE ));
// Condition code symbol definitions
SYMDEFS.put("c" , new Def(SIGNED, 1));
SYMDEFS.put("e" , new Def(SIGNED, 2));
@@ -293,9 +302,11 @@ public class Breakpoint {
// Functional symbol definitions
SYMDEFS.put("address" , new Def(SYMBOL, ADDRESS ));
SYMDEFS.put("break" , new Def(SYMBOL, BREAK ));
SYMDEFS.put("code" , new Def(SYMBOL, CODE ));
SYMDEFS.put("cond" , new Def(SYMBOL, COND ));
SYMDEFS.put("disp" , new Def(SYMBOL, DISP ));
SYMDEFS.put("fetch" , new Def(SYMBOL, FETCH ));
SYMDEFS.put("format" , new Def(SYMBOL, FORMAT ));
SYMDEFS.put("id" , new Def(SYMBOL, ID ));
SYMDEFS.put("imm" , new Def(SYMBOL, IMM ));
@@ -305,6 +316,7 @@ public class Breakpoint {
SYMDEFS.put("regid" , new Def(SYMBOL, REGID ));
SYMDEFS.put("size" , new Def(SYMBOL, SIZE ));
SYMDEFS.put("subopcode", new Def(SYMBOL, SUBOPCODE));
SYMDEFS.put("type" , new Def(SYMBOL, TYPE ));
SYMDEFS.put("value" , new Def(SYMBOL, VALUE ));
SYMDEFS.put("vector" , new Def(SYMBOL, VECTOR ));
@@ -454,9 +466,34 @@ public class Breakpoint {
// Evaluate the condition against the emulation context
public boolean evaluate() {
return vue == null || conditionError.code != NONE ?
false : tokens.length == 0 ? true :
evaluate(null, null, null) != 0;
// Error checking
if (vue == null)
return false;
// Retrieve state objects
Access acc = vue.getAccess ();
int breakType = vue.getBreakType ();
Instruction inst = vue.getInstruction();
// Error checking
if (!appliesTo(breakType))
return false;
// Check address ranges for execute
if (breakType == EXECUTE) {
int pc = vue.getRegister(Vue.PC, true);
if (!inRange(pc, pc + inst.size - 1))
return false;
}
// Check address ranges for read and write
else if (breakType == READ || breakType == WRITE &&
!inRange(acc.address, acc.address+JavaVue.TYPE_SIZES[acc.type]-1))
return false;
// Evaluate the condition
return isTrue(new int[depth], breakType, inst, acc);
}
// Retrieve the most recent address text
@@ -504,6 +541,15 @@ public class Breakpoint {
return name;
}
// Determine whether the breakpoint can be used
boolean isActive() {
return
isEnabled &&
addressError .code == NONE &&
conditionError.code == NONE
;
}
// Determine whether the breakpoint is enabled
public boolean isEnabled() {
return isEnabled;
@@ -738,12 +784,16 @@ public class Breakpoint {
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Cast a value to the condition evaluation's data type
Object cast(int value) {
if (conditionError.code != NONE)
return conditionError;
// Determine whether the breakpoint applies to a break scenario
boolean appliesTo(int breakType) {
return isActive() && (breakType == 0 || (breakType & hooks) != 0);
}
// Perform a typed evaluation of the condition expression
Object evaluateTyped(int[]stack,int breakType,Instruction inst,Access acc){
if (tokens.length == 0)
return null;
int value = evaluate(stack, breakType, inst, acc);
switch (dataType) {
case BOOL : return (Boolean) (value != 0);
case FLOAT : return (Float ) Float.intBitsToFloat(value);
@@ -752,54 +802,28 @@ public class Breakpoint {
return (Integer) value;
}
// Evaluate the condition, returning only the computed value
int evaluate(int[] stack, Instruction inst, Access acc) {
// Determine whether the breakpoint applies to a given address range
boolean inRange(int start, int end) {
// Error checking
if (conditionError.code != NONE)
return 0;
if (tokens.length == 0)
return 1;
if (stack == null)
stack = new int[depth];
// Implicit inclusion
if (ranges.length == 0)
return true;
// Process all tokens
int size = 0;
for (var tok : tokens) switch (tok.type) {
// Check all ranges
for (var range : ranges)
if (
Integer.compareUnsigned(
start - range[0], range[1] - range[0]) <= 0 ||
Integer.compareUnsigned(
range[0] - start , end - start ) <= 0
) return true;
return false;
}
// Binary operator
case BINARY:
stack[size - 2] =
evalBinary(tok.id, stack[size - 2], stack[size - 1]);
break;
// Literal
case BOOL:
case FLOAT:
case SIGNED:
case UNSIGNED:
stack[size++] = tok.id;
break;
// CPU register
case PROREG:
case SYSREG:
stack[size++] = vue.getRegister(tok.id, tok.type == SYSREG);
break;
// Symbol
case SYMBOL:
stack[size++] = 0;
break;
// Unary operator
case UNARY:
stack[size - 1] = evalUnary(tok.id, stack[size - 1]);
break;
}
// Return the remaining stack value
return stack[0];
// Determine whether the breakpoint's condition is truthy
boolean isTrue(int[] stack, int breakType, Instruction inst, Access acc) {
return tokens.length == 0 ? true :
evaluate(stack, breakType, inst, acc) != 0;
}
// Produce a one-dimensional array from the address ranges
@@ -829,21 +853,6 @@ public class Breakpoint {
return depth;
}
// Retrieve the bit mask for enabled hooks
int getHooks() {
return hooks;
}
// Retrieve the list of address ranges
int[][] getRanges() {
return ranges;
}
// Retrieve the list of condition tokens
Token[] getTokens() {
return tokens;
}
// The breakpoint is being removed from its emulation context
void remove() {
vue = null;
@@ -1216,10 +1225,11 @@ public class Breakpoint {
// Binary operators
if (tok.type == BINARY) {
int id = tok.id;
boolean literal = isLiteral(tok.left) && isLiteral(tok.right);
tok.dataType = Math.max(tok.left.dataType, tok.right.dataType);
tok.id = -id * 4 + tok.dataType;
int dataType = Math.max(tok.left.dataType, tok.right.dataType);
int id = tok.id;
boolean literal = isLiteral(tok.left) && isLiteral(tok.right);
tok.dataType = dataType;
tok.id = -id * 4 + tok.dataType;
// Process by ID
switch (id) {
@@ -1269,7 +1279,7 @@ public class Breakpoint {
var top = op == 0 ? tok.left : tok.right;
// The operand is already the result type
if (top.dataType == tok.dataType)
if (top.dataType == dataType)
continue;
// Select the ID of the conversion operation
@@ -1284,7 +1294,7 @@ public class Breakpoint {
// Convert the literal operand directly
if (isLiteral(top)) {
top.id = evalUnary(cvt, top);
top.type = top.dataType = tok.dataType;
top.type = top.dataType = dataType;
}
// Insert a conversion token
@@ -1296,7 +1306,7 @@ public class Breakpoint {
case SIGNED : imp.text = "s32" ; break;
case UNSIGNED: imp.text = "u32" ; break;
}
imp.dataType = tok.dataType;
imp.dataType = dataType;
imp.id = -cvt * 4 + top.dataType;
imp.parent = tok;
imp.right = top;
@@ -1444,202 +1454,71 @@ public class Breakpoint {
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Adjust a float value as needed
private static float adjust(float value) {
int bits = Float.floatToRawIntBits(value);
int exp = bits & 0x7F800000;
return
(bits & 0x7FFFFFFF) == 0 || // Zero
exp == 0x7F800000 || // Indefinite
exp == 0 // Denormal
? 0 : value;
}
// Determine the required stack size to evaluate the expression
private int depth() {
// Error checking
if (conditionError.code != NONE)
return 0;
// Count the maximum size of the stack
int max = 0;
int size = 0;
for (var tok : tokens) switch (tok.type) {
case BINARY: size--; break;
case UNARY : break;
default : max = Math.max(max, ++size);
}
return max;
}
// Remove the fraction part of a float
private static float trunc(float x) {
return (float) (x < 0 ? Math.ceil(x) : Math.floor(x));
}
///////////////////////////////////////////////////////////////////////////
// Debugging Methods //
///////////////////////////////////////////////////////////////////////////
// Produce a string representation of the condition token list
public String debugTokens() {
var ret = new StringBuilder();
// The condition was not successfully parsed
if (conditionError.code != NONE)
return "Error";
// Determine the maximum width of the text fields
int max = 0;
for (var tok : tokens)
max = Math.max(max, tok.text.length());
// Output all tokens
var last = tokens[tokens.length - 1];
for (var tok : tokens) {
// Text
ret.append(String.format("%-" + max + "s ", tok.text));
// Type
String type = null;
switch (tok.type) {
case BINARY : type = "Binary" ; break;
case BOOL : type = "Bool" ; break;
case FLOAT : type = "Float" ; break;
case PROREG : type = "ProReg" ; break;
case SYSREG : type = "SysReg" ; break;
case SIGNED : type = "Signed" ; break;
case SYMBOL : type = "Symbol" ; break;
case UNARY : type = "Unary" ; break;
case UNSIGNED: type = "Unsigned"; break;
default: type = Integer.toString(tok.type);
}
ret.append(String.format("%-8s ", type));
// Data type
switch (tok.dataType) {
case BOOL : type = "Bool" ; break;
case FLOAT : type = "Float" ; break;
case SIGNED : type = "Signed" ; break;
case UNSIGNED: type = "Unsigned"; break;
default: type = Integer.toString(tok.dataType);
}
ret.append(String.format("%-8s ", type));
// Operator
if (tok.type == BINARY || tok.type == UNARY) {
String id = Integer.toString(tok.id);
switch (-tok.id / 4) {
case 0 : id = "REINTERPRET"; break;
case READ8 : id = "READ8" ; break;
case READ16 : id = "READ16" ; break;
case READ32 : id = "READ32" ; break;
case ADD : id = "ADD" ; break;
case AND_B : id = "AND_B" ; break;
case AND_L : id = "AND_L" ; break;
case BOOL_ : id = "BOOL_" ; break;
case CEIL : id = "CEIL" ; break;
case DIVIDE : id = "DIVIDE" ; break;
case EQUAL : id = "EQUAL" ; break;
case FLOAT_ : id = "FLOAT_" ; break;
case FLOOR : id = "FLOOR" ; break;
case GREATER : id = "GREATER" ; break;
case GREQUAL : id = "GREQUAL" ; break;
case LEFT_L : id = "LEFT_L" ; break;
case LEQUAL : id = "LEQUAL" ; break;
case LESS : id = "LESS" ; break;
case MULTIPLY : id = "MULTIPLY" ; break;
case NEQUAL : id = "NEQUAL" ; break;
case NOT_B : id = "NOT_B" ; break;
case NOT_L : id = "NOT_L" ; break;
case NEGATE : id = "NEGATE" ; break;
case OR_B : id = "OR_B" ; break;
case OR_L : id = "OR_L" ; break;
case REMAINDER: id = "REMAINDER" ; break;
case RIGHT_A : id = "RIGHT_A" ; break;
case RIGHT_L : id = "RIGHT_L" ; break;
case ROUND : id = "ROUND" ; break;
case S8 : id = "S8" ; break;
case S16 : id = "S16" ; break;
case S32 : id = "S32" ; break;
case SUBTRACT : id = "SUBTRACT" ; break;
case TRUNC : id = "TRUNC" ; break;
case U8 : id = "U8" ; break;
case U16 : id = "U16" ; break;
case U32 : id = "U32" ; break;
case XFLOAT : id = "XFLOAT" ; break;
case XOR_B : id = "XOR_B" ; break;
case XOR_L : id = "XOR_L" ; break;
case XS32 : id = "XS32" ; break;
case XU32 : id = "XU32" ; break;
default: ret.append(Integer.toString(tok.id));
}
ret.append(String.format("%-11s", id));
}
// Symbol or literal
else switch (tok.type) {
case PROREG:
case SYSREG:
case SYMBOL:
ret.append(Integer.toString(tok.id));
break;
case FLOAT:
ret.append(String.format("%.6f",
Float.intBitsToFloat(tok.id)));
break;
case BOOL:
case SIGNED:
ret.append(Integer.toString(tok.id));
break;
case UNSIGNED:
ret.append(Long.toString(tok.id & 0xFFFFFFFFL));
break;
default: ret.append("Error");
}
// Advance to the next line
if (tok != last)
ret.append("\n");
}
return ret.toString();
}
// Produce a string representation of the internal address ranges
public String debugRanges() {
var ret = new StringBuilder();
// The address ranges were not successfully parsed
if (addressError.code != NONE)
return "Error";
// Process ranges
for (int x = 0; x < ranges.length; x++) {
var range = ranges[x];
if (x > 0)
ret.append("\n");
ret.append(String.format("%08X", range[0]));
if (range[0] != range[1])
ret.append(String.format("-%08X", range[1]));
}
return ret.toString();
}
///////////////////////////////////////////////////////////////////////////
// Evaluation Methods //
///////////////////////////////////////////////////////////////////////////
// Evaluate the condition, returning only the computed value
private int evaluate(int[]stack,int breakType,Instruction inst,Access acc){
// Process all tokens
int size = 0;
for (var tok : tokens) switch (tok.type) {
// Binary operator
case BINARY:
stack[size - 2] =
evalBinary(tok.id, stack[size - 2], stack[size - 1]);
break;
// Literal
case BOOL:
case FLOAT:
case SIGNED:
case UNSIGNED:
stack[size++] = tok.id;
break;
// CPU register
case PROREG:
case SYSREG:
stack[size++] = vue.getRegister(tok.id, tok.type == SYSREG);
break;
// Unary operator
case UNARY:
stack[size - 1] = evalUnary(tok.id, stack[size - 1]);
break;
// Symbol
case SYMBOL:
int sym = inst.imm; // IMM, REGID, VECTOR
switch (tok.id) {
case ADDRESS : sym = acc.address ; break;
case BREAK : sym = breakType ; break;
case CODE : sym = vue.getExceptionCode(); break;
case COND : sym = inst.cond ; break;
case DISP : sym = inst.disp ; break;
case FETCH : sym = vue.getFetch (); break;
case FORMAT : sym = inst.format ; break;
case ID : sym = inst.id ; break;
case OPCODE : sym = inst.opcode ; break;
case REG1 : sym = inst.reg1 ; break;
case REG2 : sym = inst.reg2 ; break;
case SIZE : sym = inst.size ; break;
case SUBOPCODE: sym = inst.subopcode ; break;
case TYPE : sym = acc.type ; break;
case VALUE : sym = acc.value ; break;
}
stack[size++] = sym;
break;
}
// Return the remaining stack value
return stack[0];
}
// Evaluate a unary operator given a token
private int evalUnary(int id, Token tok) {
return evalUnary(-id * 4 + tok.dataType, tok.id);
@@ -1988,4 +1867,196 @@ public class Breakpoint {
return 0;
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Adjust a float value as needed
private static float adjust(float value) {
int bits = Float.floatToRawIntBits(value);
int exp = bits & 0x7F800000;
return
(bits & 0x7FFFFFFF) == 0 || // Zero
exp == 0x7F800000 || // Indefinite
exp == 0 // Denormal
? 0 : value;
}
// Determine the required stack size to evaluate the expression
private int depth() {
// Error checking
if (conditionError.code != NONE)
return 0;
// Count the maximum size of the stack
int max = 0;
int size = 0;
for (var tok : tokens) switch (tok.type) {
case BINARY: size--; break;
case UNARY : break;
default : max = Math.max(max, ++size);
}
return max;
}
// Remove the fraction part of a float
private static float trunc(float x) {
return (float) (x < 0 ? Math.ceil(x) : Math.floor(x));
}
///////////////////////////////////////////////////////////////////////////
// Debugging Methods //
///////////////////////////////////////////////////////////////////////////
// Produce a string representation of the condition token list
public String debugTokens() {
var ret = new StringBuilder();
// The condition was not successfully parsed
if (conditionError.code != NONE)
return "Error";
// Determine the maximum width of the text fields
int max = 0;
for (var tok : tokens)
max = Math.max(max, tok.text.length());
// Output all tokens
var last = tokens[tokens.length - 1];
for (var tok : tokens) {
// Text
ret.append(String.format("%-" + max + "s ", tok.text));
// Type
String type = null;
switch (tok.type) {
case BINARY : type = "Binary" ; break;
case BOOL : type = "Bool" ; break;
case FLOAT : type = "Float" ; break;
case PROREG : type = "ProReg" ; break;
case SYSREG : type = "SysReg" ; break;
case SIGNED : type = "Signed" ; break;
case SYMBOL : type = "Symbol" ; break;
case UNARY : type = "Unary" ; break;
case UNSIGNED: type = "Unsigned"; break;
default: type = Integer.toString(tok.type);
}
ret.append(String.format("%-8s ", type));
// Data type
switch (tok.dataType) {
case BOOL : type = "Bool" ; break;
case FLOAT : type = "Float" ; break;
case SIGNED : type = "Signed" ; break;
case UNSIGNED: type = "Unsigned"; break;
default: type = Integer.toString(tok.dataType);
}
ret.append(String.format("%-8s ", type));
// Operator
if (tok.type == BINARY || tok.type == UNARY) {
String id = Integer.toString(tok.id);
switch (-tok.id / 4) {
case 0 : id = "REINTERPRET"; break;
case READ8 : id = "READ8" ; break;
case READ16 : id = "READ16" ; break;
case READ32 : id = "READ32" ; break;
case ADD : id = "ADD" ; break;
case AND_B : id = "AND_B" ; break;
case AND_L : id = "AND_L" ; break;
case BOOL_ : id = "BOOL_" ; break;
case CEIL : id = "CEIL" ; break;
case DIVIDE : id = "DIVIDE" ; break;
case EQUAL : id = "EQUAL" ; break;
case FLOAT_ : id = "FLOAT_" ; break;
case FLOOR : id = "FLOOR" ; break;
case GREATER : id = "GREATER" ; break;
case GREQUAL : id = "GREQUAL" ; break;
case LEFT_L : id = "LEFT_L" ; break;
case LEQUAL : id = "LEQUAL" ; break;
case LESS : id = "LESS" ; break;
case MULTIPLY : id = "MULTIPLY" ; break;
case NEQUAL : id = "NEQUAL" ; break;
case NOT_B : id = "NOT_B" ; break;
case NOT_L : id = "NOT_L" ; break;
case NEGATE : id = "NEGATE" ; break;
case OR_B : id = "OR_B" ; break;
case OR_L : id = "OR_L" ; break;
case REMAINDER: id = "REMAINDER" ; break;
case RIGHT_A : id = "RIGHT_A" ; break;
case RIGHT_L : id = "RIGHT_L" ; break;
case ROUND : id = "ROUND" ; break;
case S8 : id = "S8" ; break;
case S16 : id = "S16" ; break;
case S32 : id = "S32" ; break;
case SUBTRACT : id = "SUBTRACT" ; break;
case TRUNC : id = "TRUNC" ; break;
case U8 : id = "U8" ; break;
case U16 : id = "U16" ; break;
case U32 : id = "U32" ; break;
case XFLOAT : id = "XFLOAT" ; break;
case XOR_B : id = "XOR_B" ; break;
case XOR_L : id = "XOR_L" ; break;
case XS32 : id = "XS32" ; break;
case XU32 : id = "XU32" ; break;
default: ret.append(Integer.toString(tok.id));
}
ret.append(String.format("%-11s", id));
}
// Symbol or literal
else switch (tok.type) {
case PROREG:
case SYSREG:
case SYMBOL:
ret.append(Integer.toString(tok.id));
break;
case FLOAT:
ret.append(String.format("%.6f",
Float.intBitsToFloat(tok.id)));
break;
case BOOL:
case SIGNED:
ret.append(Integer.toString(tok.id));
break;
case UNSIGNED:
ret.append(Long.toString(tok.id & 0xFFFFFFFFL));
break;
default: ret.append("Error");
}
// Advance to the next line
if (tok != last)
ret.append("\n");
}
return ret.toString();
}
// Produce a string representation of the internal address ranges
public String debugRanges() {
var ret = new StringBuilder();
// The address ranges were not successfully parsed
if (addressError.code != NONE)
return "Error";
// Process ranges
for (int x = 0; x < ranges.length; x++) {
var range = ranges[x];
if (x > 0)
ret.append("\n");
ret.append(String.format("%08X", range[0]));
if (range[0] != range[1])
ret.append(String.format("-%08X", range[1]));
}
return ret.toString();
}
}
+60 -63
View File
@@ -12,7 +12,7 @@ class CPU {
// Package fields
Access access; // Access state
int cycles; // Cycles until next stage
Ecxeption exception; // Exception state
int exception; // Exception code
int fetch; // Fetch unit index
Instruction inst; // Instruction state
int irq; // Interrupt lines
@@ -97,7 +97,6 @@ class CPU {
// Default constructor
CPU(JavaVue vue) {
access = new Access();
exception = new Ecxeption();
inst = new Instruction();
jumpFrom = new int[3];
jumpTo = new int[3];
@@ -114,28 +113,27 @@ class CPU {
// Process the simulation
void emulate(int cycles) {
// The CPU is halting
if (stage == HALT || stage == FATAL)
// The CPU is in fatal halt status
if (stage == FATAL)
return;
this.cycles = 0; // DEBUG: Stop processing after execute
// Process for the given number of cycles
for (;;) {
// The next event occurs after the given number of cycles
// The next stage occurs after the given number of cycles
if (this.cycles > cycles) {
this.cycles -= cycles;
return;
}
// Processing by stage
cycles -= this.cycles;
this.cycles = 0;
switch (stage) {
case EXCEPTION: if (exception ()) return; break;
case EXECUTE : if (execute ()) return; break;
case FETCH : if (fetch ()) return; break;
case HALT : if (testException()) return; break;
case EXCEPTION: if (exception()) return; break;
case EXECUTE : if (execute ()) return; break;
case FETCH : if (fetch ()) return; break;
case HALT : testException(); return;
}
}
@@ -177,11 +175,11 @@ this.cycles = 0; // DEBUG: Stop processing after execute
void reset() {
// Configure instance fields
cycles = 0;
exception.code = 0;
fetch = 0;
irq = 0;
stage = FETCH;
cycles = 0;
exception = 0;
fetch = -1;
irq = 0;
stage = FETCH;
// Clear all registers (hardware only sets ECR, PC and PSW)
for (int x = 0; x < 32; x++) {
@@ -257,11 +255,10 @@ this.cycles = 0; // DEBUG: Stop processing after execute
return 1; // Unreachable
}
// Determine the number of CPU cycles until something can happen
// Determine the number of CPU cycles until a breakpoint could trigger
int until(int cycles) {
if (stage == FATAL || stage == HALT)
return cycles;
return cycles < 0 ? this.cycles : Math.min(cycles, this.cycles);
return stage == FATAL || stage == HALT ?
cycles : Math.min(cycles, this.cycles);
}
@@ -273,19 +270,18 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Operations for exception stage
private boolean exception() {
// Application callback
vue.breakCode = vue.onException() ? 1 : 0;
if (vue.breakCode != 0)
// Check for breakpoints
if (vue.onBreakpoint(Breakpoint.EXCEPTION))
return true;
// Configure working variables
exception.code &= 0xFFFF;
boolean isIRQ = (exception.code & 0xFF00) == 0xFE00;
int psw = getSystemRegister(Vue.PSW);
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.code);
vue.write(0x00000000, Vue.S32, 0xFFFF0000 | exception);
vue.write(0x00000004, Vue.S32, psw);
vue.write(0x00000008, Vue.S32, pc);
stage = FATAL;
@@ -294,7 +290,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Duplexed exception
if (psw_ep != 0) {
ecr_fecc = exception.code;
ecr_fecc = exception;
fepc = pc;
fepsw = psw;
psw_np = 1;
@@ -303,37 +299,36 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Regular exception
else {
ecr_eicc = exception.code;
ecr_eicc = exception;
eipc = pc;
eipsw = psw;
psw_ep = 1;
pc = 0xFFFF0000 | exception.code & 0xFFF0;
pc = 0xFFFF0000 | exception & 0xFFF0;
if (pc == 0xFFFFFF70) // FIV
pc = 0xFFFFFF60;
}
// Interrupt
if (isIRQ) {
psw_i = Math.min(15, exception.code >> 4 & 15);
psw_i = Math.min(15, exception >> 4 & 15);
if (stage == HALT)
pc += 2;
}
// Common processing
cycles = 0; // TODO: Determine the actual number
exception.code = 0;
psw_ae = 0;
psw_id = 1;
stage = FETCH;
cycles = 0; // TODO: Determine the actual number
exception = 0;
psw_ae = 0;
psw_id = 1;
stage = FETCH;
return false;
}
// Operations for execute stage
private boolean execute() {
// Application callback
vue.breakCode = vue.onExecute() ? 1 : 0;
if (vue.breakCode != 0)
// Check for breakpoints
if (vue.onBreakpoint(Breakpoint.EXECUTE))
return true;
// Processing by instruction ID
@@ -415,7 +410,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
case Vue.XORI : XORI (); break;
//case Vue.XORNBSU: XORNBSU(); break;
default: // Invalid instruction
exception.code = 0xFF90;
exception = 0xFF90;
}
// An application break was requested
@@ -423,7 +418,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
return true;
// Common processing
if (exception.code == 0) {
if (exception == 0) {
cycles += CYCLES[inst.id];
pc += inst.size;
}
@@ -437,9 +432,14 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Operations for fetch stage
private boolean fetch() {
// Entering the fetch stage
if (fetch == -1)
fetch = 0;
// Read the bits from the bus
if (read(pc + (fetch << 1), Vue.U16, fetch))
return true;
// TODO: Determine how many cycles this takes
// First unit
if (fetch == 0) {
@@ -453,7 +453,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Second unit
else {
inst.bits |= access.value & 0xFFFF;
fetch = 0;
fetch = -1;
}
// Decode the instruction and advance to execute stage
@@ -471,9 +471,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
access.type = type;
access.value = vue.read(address, type);
// Application callback
vue.breakCode = vue.onRead() ? 1 : 0;
return vue.breakCode != 0;
// Check for breakpoints
return vue.onBreakpoint(Breakpoint.READ);
}
// Test a condition
@@ -495,16 +494,16 @@ this.cycles = 0; // DEBUG: Stop processing after execute
private boolean testException() {
// Check for an interrupt
if (irq != 0 && (exception.code | psw_id | psw_ep | psw_np) == 0) {
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.code = 0xFE00 | level << 4;
exception = 0xFE00 | level << 4;
}
// There is no exception
if (exception.code == 0)
if (exception == 0)
return stage == HALT && cycles == 0; // No further processing
// An exception has occurred
@@ -522,15 +521,13 @@ this.cycles = 0; // DEBUG: Stop processing after execute
access.type = type;
access.value = value;
// Application callback
vue.breakCode = vue.onWrite() ? 1 : 0;
if (vue.breakCode != 0)
// Check for breakpoints
if (vue.onBreakpoint(Breakpoint.WRITE))
return true;
if (access.type == Vue.CANCEL)
return false;
// Perform the operation
vue.write(access.address, access.type, access.value);
if (access.type != Vue.CANCEL)
vue.write(access.address, access.type, access.value);
return false;
}
@@ -572,7 +569,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Invalid operation
if (value > 0x7FFFFFFF || value < 0x80000000) {
exception.code = 0xFF70;
exception = 0xFF70;
return;
}
@@ -599,8 +596,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
)) continue;
// The value is a reserved operand
exception.code = 0xFF60;
psw_fro = 1;
exception = 0xFF60;
psw_fro = 1;
return true;
}
return false;
@@ -611,8 +608,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Overflow
if (full > Float.MAX_VALUE || full < -Float.MAX_VALUE) {
exception.code = 0xFF64;
psw_fov = 1;
exception = 0xFF64;
psw_fov = 1;
return;
}
@@ -803,7 +800,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Zero division
if (right == 0) {
exception.code = 0xFF80;
exception = 0xFF80;
return;
}
@@ -837,7 +834,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// An exception has occurred
if (right == 0) {
exception.code = left == 0 ? 0xFF70 : 0xFF68;
exception = left == 0 ? 0xFF70 : 0xFF68;
return;
}
@@ -854,7 +851,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Zero division
if (right == 0) {
exception.code = 0xFF80;
exception = 0xFF80;
return;
}
@@ -1105,7 +1102,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Trap
private void TRAP() {
exception.code = 0xFFA0 | inst.imm & 31;
exception = 0xFFA0 | inst.imm & 31;
pc += 2;
}
-26
View File
@@ -1,26 +0,0 @@
package vue;
// Intentionally misspelled to avoid ambiguity with java.lang.Exception
// Exception state
public class Ecxeption {
// Instance fields
public int code; // Exception code
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
Ecxeption() { }
// Cloning constructor
Ecxeption(Ecxeption o) {
this();
code = o.code;
}
}
+5 -1
View File
@@ -135,8 +135,12 @@ public class Instruction {
case 2:
reg2 = bits >> 21 & 31;
imm = extend < 0 ? bits << 11 >> 27 : bits >> 16 & 31;
if (id == BITSTRING)
if (id == BITSTRING) {
id = imm >= 16 ? Vue.ILLEGAL : LOOKUP_BITSTRING[imm];
subopcode = imm;
}
if (id == Vue.SETF)
cond = imm & 15;
break;
case 3:
opcode = 0x20;
+76 -59
View File
@@ -7,11 +7,11 @@ import java.util.*;
class JavaVue extends Vue {
// Package fields
int breakCode; // Application break code
int breakCode; // Application breakpoint code
int breakType; // Most recent breakpoint scenario
CPU cpu; // Processor
int hook; // Most recent breakpoint hook
GamePak pak; // Game pak
int[] stack; // Breakpoint evaluation stack
int[] stack; // Breakpoint condition evaluation stack
byte[] wram; // System WRAM
@@ -53,50 +53,47 @@ class JavaVue extends Vue {
// Process up to the given number of cycles
do {
// Determine the number of cycles during which nothing will happen
int cycles = -1;
// Determine the number of cycles where no breakpoint will occur
int cycles = maxCycles; // min(maxCycles, nextFrameCycles)
cycles = cpu .until(cycles);
//cycles = pad .until(cycles);
//cycles = link .until(cycles);
//cycles = timer.until(cycles);
//cycles = vip .until(cycles);
//cycles = vsu .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;
cpu .emulate(cycles);
//pad .emulate(cycles);
//link .emulate(cycles);
//timer.emulate(cycles);
//vip .emulate(cycles);
//vsu .emulate(cycles);
// An application break was requested
//if (...)
// break;
// Update the number of cycles remaining
if (maxCycles >= 0)
maxCycles -= cycles;
} while (maxCycles != 0);
maxCycles -= cycles;
} while (breakCode == 0 && maxCycles != 0);
// A break condition has occurred
return Math.max(0, maxCycles);
return maxCycles;
}
// Evaluate an expression
public Object evaluate(String expression) {
return null;
// Retrieve a snapshot of the current state's memory access
public Access getAccess() {
return cpu.access;
}
// Retrieve the most recent applicaiton break code
public int getBreakCode() {
return breakCode;
}
// Retrieve the most recent exception code
public int getException() {
return cpu.exception.code;
public int getExceptionCode() {
return cpu.exception;
}
// Retrieve a snapshot of the current state's instruction
public Instruction getInstruction() {
return cpu.inst;
}
// Retrieve a register value
@@ -139,6 +136,12 @@ class JavaVue extends Vue {
// Read a value from the CPU bus
public int read(int address, int type) {
// Error checking
if (type < 0 || type > 4)
return 0;
// Perform the operation
switch (address >> 24 & 7) {
case 5: return readBuffer(wram , address, type);
case 6: return readBuffer(pak.ram, address, type);
@@ -250,29 +253,48 @@ class JavaVue extends Vue {
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Evaluate the condition in a breakpoint
boolean evaluate(Breakpoint brk) {
return false;
// Retrieve the current state's breakpoint scenario
int getBreakType() {
return breakType;
}
// Exception break handler
boolean onException() {
return onHook(Breakpoint.EXCEPTION);
// Retrieve the current instruction fetch index
int getFetch() {
return cpu.fetch;
}
// Execute break handler
boolean onExecute() {
return onHook(Breakpoint.EXECUTE);
}
// Check for breakpoints
boolean onBreakpoint(int breakType) {
int end = 0;
boolean ranged = false;
int start = 0;
this.breakType = breakType;
// Read break handler
boolean onRead() {
return onHook(Breakpoint.READ);
}
// Processing for Execute
if (breakType == Breakpoint.EXECUTE) {
ranged = true;
start = cpu.pc;
end = start + cpu.inst.size - 1;
}
// Write break handler
boolean onWrite() {
return onHook(Breakpoint.WRITE);
// Processing for Read and Write
else if (breakType==Breakpoint.READ || breakType==Breakpoint.WRITE) {
ranged = true;
start = cpu.access.address;
end = start + TYPE_SIZES[cpu.access.type] - 1;
}
// Check all breakpoints
int count = breakpoints.size();
for (int x = 0; breakCode == 0 && x < count; x++) {
var brk = breakpoints.get(x);
if (
brk.appliesTo(breakType) &&
(!ranged || brk.inRange(start, end)) &&
brk.isTrue(stack, breakType, cpu.inst, cpu.access)
) breakCode = x + 1;
}
return breakCode != 0;
}
// Read a value from a byte buffer
@@ -331,6 +353,16 @@ class JavaVue extends Vue {
}
// A breakpoint's condition tokens have changed
void updateTokens(Breakpoint brk) {
super.updateTokens(brk);
int depth = 0;
for (var bre : breakpoints)
depth = Math.max(depth, bre.getDepth());
if (depth != stack.length)
stack = new int[depth];
}
// Write a value to a byte buffer
static void writeBuffer(byte[] data, int address, int type, int value) {
@@ -377,19 +409,4 @@ class JavaVue extends Vue {
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Check breakpoints during a hooked event
private boolean onHook(int hook) {
this.hook = hook;
for (var brk : breakpoints)
if (evaluate(brk))
return true;
return false;
}
}
+8 -1
View File
@@ -117,13 +117,20 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_emulate
return vueEmulate(&core->vue, maxCycles);
}
// Retrieve the application break code
// Retrieve the most recent exception code
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode
(JNIEnv *env, jobject vue, jlong handle) {
Core *core = *(Core **)&handle;
return vueGetBreakCode(&core->vue);
}
// Retrieve the most recent exception code
JNIEXPORT jint JNICALL Java_vue_NativeVue_getExceptionCode
(JNIEnv *env, jobject vue, jlong handle) {
Core *core = *(Core **)&handle;
return vueGetExceptionCode(&core->vue);
}
// Retrieve a register value
JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) {
+28 -19
View File
@@ -25,6 +25,7 @@ class NativeVue extends Vue {
///////////////////////////////////////////////////////////////////////////
// Produce a new breakpoint and add it to the collection
private native void breakpoint(long handle);
public Breakpoint breakpoint() {
var brk = super.breakpoint();
breakpoint(handle);
@@ -41,14 +42,26 @@ class NativeVue extends Vue {
public int emulate(int maxCycles)
{ return emulate(handle, maxCycles); }
// Retrieve the application break code
// Retrieve a snapshot of the current state's memory access
private native Access getAccess(long handle, Access access);
public Access getAccess() {
return getAccess(handle, new Access());
}
// Retrieve the most recent application break code
private native int getBreakCode(long handle);
public int getBreakCode() { return getBreakCode(handle); }
// Retrieve the most recent exception code
private native int getException(long handle);
public int getException() {
return getException(handle);
private native int getExceptionCode(long handle);
public int getExceptionCode() {
return getExceptionCode(handle);
}
// Retrieve a snapshot of the current state's instruction
private native Instruction getInstruction(long handle, Instruction inst);
public Instruction getInstruction() {
return getInstruction(handle, new Instruction());
}
// Retrieve a register value
@@ -75,6 +88,7 @@ class NativeVue extends Vue {
{ return readBytes(handle, address, dest, offset, length); }
// Remove a breakpoint from the collection
private native void remove(long handle, int index);
public boolean remove(Breakpoint brk) {
int index = breakpoints.indexOf(brk);
if (!super.remove(brk))
@@ -117,9 +131,16 @@ class NativeVue extends Vue {
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Evaluate the condition in a breakpoint
boolean evaluate(Breakpoint brk) {
return false;
// Retrieve the current state's breakpoint scenario
private native int getBreakType(long handle);
int getBreakType() {
return getBreakType(handle);
}
// Retrieve the current instruction fetch index
private native int getFetch(long handle);
int getFetch() {
return getFetch(handle);
}
// A breakpoint's address ranges have changed
@@ -137,16 +158,4 @@ class NativeVue extends Vue {
super.updateTokens(brk);
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Produce a new breakpoint and add it to the collection
private native void breakpoint(long handle);
// Remove a breakpoint from the collection
private native void remove(long handle, int index);
}
+39 -33
View File
@@ -138,7 +138,7 @@ public abstract class Vue {
// Produce an emulation core context
public static Vue create(boolean useNative) {
return !useNative ? new JavaVue() :
isNativeLoaded() ? new NativeVue() : null;
nativeID != null ? new NativeVue() : null;
}
// Retrieve the ID of the loaded native library, if any
@@ -146,11 +146,6 @@ public abstract class Vue {
return nativeID;
}
// Determine whether the native module is loaded
public static boolean isNativeLoaded() {
return nativeID != null;
}
// Specify the ID of the loaded native library
public static void setNativeID(String nativeID) {
Vue.nativeID = nativeID;
@@ -180,40 +175,32 @@ public abstract class Vue {
return brk;
}
// Evaluate an expression
public Object evaluate(String expression) {
var brk = new Breakpoint(this);
brk.setCondition(expression);
return brk.evaluate(null, null, null);
}
// Produce an array of the current breakpoint collection
public Breakpoint[] listBreakpoints() {
return breakpoints.toArray(new Breakpoint[breakpoints.size()]);
}
// Remove a breakpoint from the collection
public boolean remove(Breakpoint brk) {
if (!breakpoints.remove(brk))
return false;
brk.remove();
return true;
}
///////////////////////////////////////////////////////////////////////////
// Abstract Methods //
///////////////////////////////////////////////////////////////////////////
// Release any used resources
public abstract void dispose();
// Process the simulation
public abstract int emulate(int maxCycles);
// Evaluate an expression
public Object evaluate(String expression) {
var brk = new Breakpoint(this);
brk.setCondition(expression);
brk.setEnabled (true);
return brk.evaluateTyped(new int[brk.getDepth()], 0,
getInstruction(), getAccess());
}
// Retrieve a snapshot of the current state's memory access
public abstract Access getAccess();
// Retrieve the most recent applicaiton break code
public abstract int getBreakCode();
// Retrieve the most recent exception code
public abstract int getException();
public abstract int getExceptionCode();
// Retrieve a snapshot of the current state's instruction
public abstract Instruction getInstruction();
// Retrieve a register value
public abstract int getRegister(int index, boolean system);
@@ -224,6 +211,11 @@ public abstract class Vue {
// Determine whether the context is native-backed
public abstract boolean isNative();
// Produce an array of the current breakpoint collection
public Breakpoint[] listBreakpoints() {
return breakpoints.toArray(new Breakpoint[breakpoints.size()]);
}
// Read a value from the CPU bus
public abstract int read(int address, int type);
@@ -231,6 +223,14 @@ public abstract class Vue {
public abstract boolean readBytes(int address, byte[] dest, int offset,
int length);
// Remove a breakpoint from the collection
public boolean remove(Breakpoint brk) {
if (!breakpoints.remove(brk))
return false;
brk.remove();
return true;
}
// Initialize all system components
public abstract void reset();
@@ -253,6 +253,12 @@ public abstract class Vue {
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Retrieve the current state's breakpoint scenario
abstract int getBreakType();
// Retrieve the current instruction fetch index
abstract int getFetch();
// A breakpoint's address ranges have changed
void updateRanges(Breakpoint brk) { }