Integrating breakpoints into the native module
This commit is contained in:
parent
167bc4f8bf
commit
edab431a1f
|
@ -643,7 +643,7 @@ static void cpuDIVU(Vue *vue) {
|
||||||
|
|
||||||
/* Halt */
|
/* Halt */
|
||||||
#define cpuHALT(vue) \
|
#define cpuHALT(vue) \
|
||||||
vue->cpu.stage = CPU_HALT, vue->cpu.inst.size = 0
|
vue->cpu.stage = CPU_HALT; vue->cpu.inst.size = 0
|
||||||
|
|
||||||
/* Input Byte from Port */
|
/* Input Byte from Port */
|
||||||
#define cpuIN_B(vue) cpuIN_LD(vue, VUE_U8)
|
#define cpuIN_B(vue) cpuIN_LD(vue, VUE_U8)
|
||||||
|
@ -656,7 +656,7 @@ static void cpuDIVU(Vue *vue) {
|
||||||
|
|
||||||
/* Jump and Link */
|
/* Jump and Link */
|
||||||
#define cpuJAL(vue) \
|
#define cpuJAL(vue) \
|
||||||
vue->cpu.program[31] = vue->cpu.pc + 4, \
|
vue->cpu.program[31] = vue->cpu.pc + 4; \
|
||||||
cpuJump(vue, vue->cpu.pc + vue->cpu.inst.disp)
|
cpuJump(vue, vue->cpu.pc + vue->cpu.inst.disp)
|
||||||
|
|
||||||
/* Jump Register */
|
/* Jump Register */
|
||||||
|
@ -848,7 +848,7 @@ static void cpuREV(Vue *vue) {
|
||||||
|
|
||||||
/* Trap */
|
/* Trap */
|
||||||
#define cpuTRAP(vue) \
|
#define cpuTRAP(vue) \
|
||||||
vue->cpu.exception = 0xFFA0 | (vue->cpu.inst.imm & 15), \
|
vue->cpu.exception = 0xFFA0 | (vue->cpu.inst.imm & 15); \
|
||||||
vue->cpu.pc += 2
|
vue->cpu.pc += 2
|
||||||
|
|
||||||
/* Truncate Short Floating to Word */
|
/* Truncate Short Floating to Word */
|
||||||
|
|
|
@ -177,6 +177,7 @@ typedef int32_t (*VueOnFrame )(Vue * );
|
||||||
|
|
||||||
/* Emulation state */
|
/* Emulation state */
|
||||||
struct Vue {
|
struct Vue {
|
||||||
|
void *tag; /* Application reference */
|
||||||
int32_t breakCode; /* Application break code */
|
int32_t breakCode; /* Application break code */
|
||||||
uint8_t wram[0x10000]; /* System memory */
|
uint8_t wram[0x10000]; /* System memory */
|
||||||
|
|
||||||
|
@ -265,6 +266,7 @@ VUEAPI uint32_t vueEmulate (Vue *vue, uint32_t maxCycles);
|
||||||
VUEAPI int32_t vueGetBreakCode (Vue *vue);
|
VUEAPI int32_t vueGetBreakCode (Vue *vue);
|
||||||
VUEAPI uint16_t vueGetExceptionCode (Vue *vue);
|
VUEAPI uint16_t vueGetExceptionCode (Vue *vue);
|
||||||
VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system);
|
VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system);
|
||||||
|
VUEAPI void* vueGetTag (Vue *vue);
|
||||||
VUEAPI void vueInitialize (Vue *vue);
|
VUEAPI void vueInitialize (Vue *vue);
|
||||||
VUEAPI VueOnException vueOnException (Vue *vue, VueOnException callback);
|
VUEAPI VueOnException vueOnException (Vue *vue, VueOnException callback);
|
||||||
VUEAPI VueOnExecute vueOnExecute (Vue *vue, VueOnExecute callback);
|
VUEAPI VueOnExecute vueOnExecute (Vue *vue, VueOnExecute callback);
|
||||||
|
@ -276,6 +278,7 @@ VUEAPI vbool vueReadBytes (Vue *vue, uint32_t address, uint8_t *
|
||||||
VUEAPI void vueReset (Vue *vue);
|
VUEAPI void vueReset (Vue *vue);
|
||||||
VUEAPI int32_t vueSetRegister (Vue *vue, int32_t index, vbool system, int32_t value);
|
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 vbool vueSetROM (Vue *vue, uint8_t *rom, uint32_t size);
|
||||||
|
VUEAPI void* vueSetTag (Vue *vue, void *tag);
|
||||||
VUEAPI void vueWrite (Vue *vue, uint32_t address, int32_t type, int32_t value);
|
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 vbool vueWriteBytes (Vue *vue, uint32_t address, uint8_t *src, uint32_t length);
|
||||||
|
|
||||||
|
|
|
@ -219,6 +219,11 @@ int32_t vueGetRegister(Vue *vue, int32_t index, vbool system) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Retrieve the application reference */
|
||||||
|
void* vueGetTag(Vue *vue) {
|
||||||
|
return vue == NULL ? NULL : vue->tag;
|
||||||
|
}
|
||||||
|
|
||||||
/* Prepare an emulation state context for use */
|
/* Prepare an emulation state context for use */
|
||||||
void vueInitialize(Vue *vue) {
|
void vueInitialize(Vue *vue) {
|
||||||
if (vue == NULL)
|
if (vue == NULL)
|
||||||
|
@ -264,7 +269,7 @@ VueOnFrame vueOnFrame(Vue *vue, VueOnFrame callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Specify a read breakpoint callback */
|
/* Specify a read breakpoint callback */
|
||||||
VueOnAccess vueSetRead(Vue *vue, VueOnAccess callback) {
|
VueOnAccess vueOnRead(Vue *vue, VueOnAccess callback) {
|
||||||
VueOnAccess ret;
|
VueOnAccess ret;
|
||||||
if (vue == NULL)
|
if (vue == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -283,6 +288,16 @@ VueOnAccess vueOnWrite(Vue *vue, VueOnAccess callback) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Specify a new application reference */
|
||||||
|
void* vueSetTag(Vue *vue, void *tag) {
|
||||||
|
void *ret;
|
||||||
|
if (vue == NULL)
|
||||||
|
return NULL;
|
||||||
|
ret = vue->tag;
|
||||||
|
vue->tag = tag;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Read a value from the CPU bus */
|
/* Read a value from the CPU bus */
|
||||||
int32_t vueRead(Vue *vue, uint32_t address, int32_t type) {
|
int32_t vueRead(Vue *vue, uint32_t address, int32_t type) {
|
||||||
|
|
||||||
|
@ -382,7 +397,7 @@ void vueWrite(Vue *vue, uint32_t address, int32_t type, int32_t value) {
|
||||||
|
|
||||||
/* Perform the operation */
|
/* Perform the operation */
|
||||||
switch (address >> 24 & 7) {
|
switch (address >> 24 & 7) {
|
||||||
case 5: writeBuffer(vue->wram , 0x1000,
|
case 5: writeBuffer(vue->wram , 0x10000,
|
||||||
address, type, value); break;
|
address, type, value); break;
|
||||||
case 6: writeBuffer(vue->pak.ram, vue->pak.ramSize,
|
case 6: writeBuffer(vue->pak.ram, vue->pak.ramSize,
|
||||||
address, type, value); break;
|
address, type, value); break;
|
||||||
|
|
|
@ -146,8 +146,8 @@ class CPUWindow extends ChildWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the display
|
// Update the display
|
||||||
void refresh() {
|
void refresh(boolean seekToPC) {
|
||||||
panDasm .refresh();
|
panDasm .refresh(seekToPC);
|
||||||
lstSystem .refresh();
|
lstSystem .refresh();
|
||||||
lstProgram.refresh();
|
lstProgram.refresh();
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,8 +84,11 @@ class DisassemblerPane extends JScrollPane {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the display
|
// Update the display
|
||||||
void refresh() {
|
void refresh(boolean seekToPC) {
|
||||||
client.repaint();
|
client.repaint();
|
||||||
|
int pc = parent.parent.vue.getRegister(Vue.PC, true);
|
||||||
|
if (!isVisible(pc))
|
||||||
|
seek(pc, tall(false) / 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,12 +111,17 @@ class DisassemblerPane extends JScrollPane {
|
||||||
|
|
||||||
// Goto
|
// Goto
|
||||||
if (ctrl && code == KeyEvent.VK_G) {
|
if (ctrl && code == KeyEvent.VK_G) {
|
||||||
String addr = JOptionPane.showInputDialog(
|
String text = JOptionPane.showInputDialog(
|
||||||
this, "Goto:", "Goto", JOptionPane.PLAIN_MESSAGE);
|
this, "Goto:", "Goto", JOptionPane.PLAIN_MESSAGE);
|
||||||
if (addr != null && addr.trim().length() != 0) {
|
Object eval = vue.evaluate(text);
|
||||||
try { seek((int) Long.parseLong(addr, 16), count / 3); }
|
int addr = 0;
|
||||||
catch (Exception x) { }
|
if (eval instanceof Integer)
|
||||||
}
|
addr = (Integer) eval;
|
||||||
|
else if (eval instanceof Long)
|
||||||
|
addr = (int) (long) (Long) eval;
|
||||||
|
else return;
|
||||||
|
if (!isVisible(addr))
|
||||||
|
seek(addr, count / 3);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,29 +136,23 @@ class DisassemblerPane extends JScrollPane {
|
||||||
|
|
||||||
// Single Step
|
// Single Step
|
||||||
case KeyEvent.VK_F11:
|
case KeyEvent.VK_F11:
|
||||||
step.setCondition("pc!=" + pc);
|
step.setCondition("!fetch&&pc!=" + pc);
|
||||||
step.setEnabled(true);
|
step.setEnabled(true);
|
||||||
vue.emulate(20000000);
|
vue.emulate(20000000);
|
||||||
if (step.evaluate())
|
if (step.evaluate())
|
||||||
step.setEnabled(false);
|
step.setEnabled(false);
|
||||||
parent.parent.refreshDebug();
|
parent.parent.refreshDebug(true);
|
||||||
pc = vue.getRegister(Vue.PC, true);
|
|
||||||
if (!isVisible(pc))
|
|
||||||
seek(pc, count / 3);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Run to Next
|
// Run to Next
|
||||||
case KeyEvent.VK_F12:
|
case KeyEvent.VK_F12:
|
||||||
step.setCondition("pc==" +
|
step.setCondition("!fetch&&pc==" +
|
||||||
(pc + Instruction.size(vue.read(pc, Vue.U16) >> 10)));
|
(pc + Instruction.size(vue.read(pc, Vue.U16) >> 10)));
|
||||||
step.setEnabled(true);
|
step.setEnabled(true);
|
||||||
vue.emulate(20000000);
|
vue.emulate(20000000);
|
||||||
if (step.evaluate())
|
if (step.evaluate())
|
||||||
step.setEnabled(false);
|
step.setEnabled(false);
|
||||||
parent.parent.refreshDebug();
|
parent.parent.refreshDebug(true);
|
||||||
pc = vue.getRegister(Vue.PC, true);
|
|
||||||
if (!isVisible(pc))
|
|
||||||
seek(pc, count / 3);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,10 +100,10 @@ class MainWindow extends JFrame {
|
||||||
|
|
||||||
// Configure internal breakpoints
|
// Configure internal breakpoints
|
||||||
brkStep = vue.breakpoint();
|
brkStep = vue.breakpoint();
|
||||||
brkStep.setExecute(true);
|
brkStep.setRead(true);
|
||||||
|
|
||||||
// Display window
|
// Display window
|
||||||
refreshDebug();
|
refreshDebug(true);
|
||||||
pack();
|
pack();
|
||||||
setLocationRelativeTo(null);
|
setLocationRelativeTo(null);
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
|
@ -191,8 +191,8 @@ class MainWindow extends JFrame {
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Refresh all debug views
|
// Refresh all debug views
|
||||||
void refreshDebug() {
|
void refreshDebug(boolean seekToPC) {
|
||||||
cpu .refresh();
|
cpu .refresh(seekToPC);
|
||||||
memory.refresh();
|
memory.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,7 +300,7 @@ class MainWindow extends JFrame {
|
||||||
// Pause emulation
|
// Pause emulation
|
||||||
vue.setROM(bytes, 0, bytes.length);
|
vue.setROM(bytes, 0, bytes.length);
|
||||||
vue.reset();
|
vue.reset();
|
||||||
refreshDebug();
|
refreshDebug(true);
|
||||||
// Resume emulation
|
// Resume emulation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,12 +151,16 @@ class MemoryWindow extends ChildWindow {
|
||||||
|
|
||||||
// Goto
|
// Goto
|
||||||
if (ctrl && code == KeyEvent.VK_G) {
|
if (ctrl && code == KeyEvent.VK_G) {
|
||||||
String addr = JOptionPane.showInputDialog(
|
String text = JOptionPane.showInputDialog(
|
||||||
this, "Goto:", "Goto", JOptionPane.PLAIN_MESSAGE);
|
this, "Goto:", "Goto", JOptionPane.PLAIN_MESSAGE);
|
||||||
if (addr != null && addr.trim().length() != 0) {
|
Object eval = parent.vue.evaluate(text);
|
||||||
try { setAddress((int) Long.parseLong(addr, 16)); }
|
int addr = 0;
|
||||||
catch (Exception x) { }
|
if (eval instanceof Integer)
|
||||||
}
|
addr = (Integer) eval;
|
||||||
|
else if (eval instanceof Long)
|
||||||
|
addr = (int) (long) (Long) eval;
|
||||||
|
else return;
|
||||||
|
setAddress(addr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,575 @@
|
||||||
// This file is included through NativeVUE.c and cannot be built directly. */
|
// This file is included through NativeVue.c and cannot be built directly. */
|
||||||
#ifdef NATIVEVUE
|
#ifdef NATIVEVUE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constants //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Hook flags
|
||||||
|
#define BRK_EXCEPTION 0x00000001
|
||||||
|
#define BRK_EXECUTE 0x00000002
|
||||||
|
#define BRK_FRAME 0x00000004
|
||||||
|
#define BRK_READ 0x00000008
|
||||||
|
#define BRK_WRITE 0x00000010
|
||||||
|
|
||||||
|
// Token types
|
||||||
|
#define BRK_BOOL 0 /* Value types */
|
||||||
|
#define BRK_SIGNED 1
|
||||||
|
#define BRK_UNSIGNED 2
|
||||||
|
#define BRK_FLOAT 3
|
||||||
|
#define BRK_SYMBOL 4 /* Symbol types */
|
||||||
|
#define BRK_PROREG 5
|
||||||
|
#define BRK_SYSREG 6
|
||||||
|
#define BRK_UNARY 7 /* Operator types */
|
||||||
|
#define BRK_BINARY 8
|
||||||
|
|
||||||
|
|
||||||
|
// Functional symbol IDs
|
||||||
|
#define BRK_ADDRESS 0
|
||||||
|
#define BRK_BREAK 1
|
||||||
|
#define BRK_CODE 2
|
||||||
|
#define BRK_COND 3
|
||||||
|
#define BRK_DISP 4
|
||||||
|
#define BRK_FETCH 5
|
||||||
|
#define BRK_FORMAT 6
|
||||||
|
#define BRK_ID 7
|
||||||
|
#define BRK_IMM 8
|
||||||
|
#define BRK_OPCODE 9
|
||||||
|
#define BRK_REG1 10
|
||||||
|
#define BRK_REG2 11
|
||||||
|
#define BRK_REGID 12
|
||||||
|
#define BRK_SIZE 13
|
||||||
|
#define BRK_SUBOPCODE 14
|
||||||
|
#define BRK_TYPE 15
|
||||||
|
#define BRK_VALUE 16
|
||||||
|
#define BRK_VECTOR 17
|
||||||
|
|
||||||
|
// Evaluation operator IDs
|
||||||
|
#define BRK_XS32 1 /* xs32, xword */
|
||||||
|
#define BRK_XU32 2 /* xu32, xuword */
|
||||||
|
#define BRK_XFLOAT 3 /* xfloat */
|
||||||
|
#define BRK_READ8 4 /* [] */
|
||||||
|
#define BRK_READ16 5 /* [] */
|
||||||
|
#define BRK_READ32 6 /* [] */
|
||||||
|
#define BRK_BOOL_ - 2 /* bool */
|
||||||
|
#define BRK_S32 - 3 /* s32, word */
|
||||||
|
#define BRK_U32 - 4 /* u32, uword */
|
||||||
|
#define BRK_FLOAT_ - 5 /* float */
|
||||||
|
#define BRK_ADD - 6 /* + */
|
||||||
|
#define BRK_AND_B - 7 /* & */
|
||||||
|
#define BRK_AND_L - 8 /* && */
|
||||||
|
#define BRK_CEIL - 9 /* ceil */
|
||||||
|
#define BRK_DIVIDE -10 /* / */
|
||||||
|
#define BRK_EQUAL -11 /* == */
|
||||||
|
#define BRK_FLOOR -12 /* floor */
|
||||||
|
#define BRK_GREATER -13 /* > */
|
||||||
|
#define BRK_GREQUAL -14 /* >= */
|
||||||
|
#define BRK_LEFT_L -15 /* << */
|
||||||
|
#define BRK_LEQUAL -16 /* <= */
|
||||||
|
#define BRK_LESS -17 /* < */
|
||||||
|
#define BRK_MULTIPLY -18 /* * */
|
||||||
|
#define BRK_NEQUAL -19 /* != */
|
||||||
|
#define BRK_NOT_B -20 /* ~ */
|
||||||
|
#define BRK_NOT_L -21 /* ! */
|
||||||
|
#define BRK_NEGATE -22 /* - */
|
||||||
|
#define BRK_OR_B -23 /* | */
|
||||||
|
#define BRK_OR_L -24 /* || */
|
||||||
|
#define BRK_REMAINDER -25 /* % */
|
||||||
|
#define BRK_RIGHT_A -26 /* >> */
|
||||||
|
#define BRK_RIGHT_L -27 /* >>> */
|
||||||
|
#define BRK_ROUND -28 /* round */
|
||||||
|
#define BRK_S8 -29 /* s8, byte */
|
||||||
|
#define BRK_S16 -30 /* s16, halfword */
|
||||||
|
#define BRK_SUBTRACT -31 /* - */
|
||||||
|
#define BRK_TRUNC -32 /* trunc */
|
||||||
|
#define BRK_U8 -33 /* u8, ubyte */
|
||||||
|
#define BRK_U16 -34 /* u16, uhalfword */
|
||||||
|
#define BRK_XOR_B -36 /* ^ */
|
||||||
|
#define BRK_XOR_L -37 /* ^^ */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Internal Functions //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Adjust a float value as needed
|
||||||
|
static int brkAdjust(float value) {
|
||||||
|
int32_t bits = *(int32_t *)&value;
|
||||||
|
int exp = bits & 0x7F800000;
|
||||||
|
return
|
||||||
|
(bits & 0x7FFFFFFF) == 0 || // Zero
|
||||||
|
exp == 0x7F800000 || // Indefinite
|
||||||
|
exp == 0 // Denormal
|
||||||
|
? 0 : bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate a binary operator
|
||||||
|
static int32_t brkEvalBinary(int32_t id, int32_t left, int32_t right) {
|
||||||
|
|
||||||
|
// Processing by ID
|
||||||
|
switch (id) {
|
||||||
|
|
||||||
|
// Add
|
||||||
|
case -BRK_ADD * 4 + BRK_BOOL:
|
||||||
|
case -BRK_ADD * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_ADD * 4 + BRK_UNSIGNED:
|
||||||
|
return left + right;
|
||||||
|
case -BRK_ADD * 4 + BRK_FLOAT:
|
||||||
|
return brkAdjust(*(float *)&left + *(float *)&right);
|
||||||
|
|
||||||
|
// And Bitwise
|
||||||
|
case -BRK_AND_B * 4 + BRK_BOOL:
|
||||||
|
case -BRK_AND_B * 4 + BRK_FLOAT: // Prevented by parser
|
||||||
|
case -BRK_AND_B * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_AND_B * 4 + BRK_UNSIGNED:
|
||||||
|
return left & right;
|
||||||
|
|
||||||
|
// And Logical
|
||||||
|
case -BRK_AND_L * 4 + BRK_BOOL:
|
||||||
|
case -BRK_AND_L * 4 + BRK_FLOAT:
|
||||||
|
case -BRK_AND_L * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_AND_L * 4 + BRK_UNSIGNED:
|
||||||
|
return left == 0 ? left : right;
|
||||||
|
|
||||||
|
// Divide
|
||||||
|
case -BRK_DIVIDE * 4 + BRK_BOOL:
|
||||||
|
case -BRK_DIVIDE * 4 + BRK_SIGNED:
|
||||||
|
return right == 0 ? 0 : left / right;
|
||||||
|
case -BRK_DIVIDE * 4 + BRK_UNSIGNED:
|
||||||
|
return right == 0 ? 0 : (uint32_t) left / right;
|
||||||
|
case -BRK_DIVIDE * 4 + BRK_FLOAT:
|
||||||
|
return right == 0 ? 0 :
|
||||||
|
brkAdjust(*(float *)&left / *(float *)&right);
|
||||||
|
|
||||||
|
// Equal
|
||||||
|
case -BRK_EQUAL * 4 + BRK_BOOL:
|
||||||
|
case -BRK_EQUAL * 4 + BRK_FLOAT:
|
||||||
|
case -BRK_EQUAL * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_EQUAL * 4 + BRK_UNSIGNED:
|
||||||
|
return left == right ? 1 : 0;
|
||||||
|
|
||||||
|
// Greater
|
||||||
|
case -BRK_GREATER * 4 + BRK_BOOL:
|
||||||
|
case -BRK_GREATER * 4 + BRK_SIGNED:
|
||||||
|
return left > right ? 1 : 0;
|
||||||
|
case -BRK_GREATER * 4 + BRK_UNSIGNED:
|
||||||
|
return (uint32_t) left > right ? 1 : 0;
|
||||||
|
case -BRK_GREATER * 4 + BRK_FLOAT:
|
||||||
|
return *(float *)&left > *(float *)&right ? 1 : 0;
|
||||||
|
|
||||||
|
// Greater or Equal
|
||||||
|
case -BRK_GREQUAL * 4 + BRK_BOOL:
|
||||||
|
case -BRK_GREQUAL * 4 + BRK_SIGNED:
|
||||||
|
return left >= right ? 1 : 0;
|
||||||
|
case -BRK_GREQUAL * 4 + BRK_UNSIGNED:
|
||||||
|
return (uint32_t) left >= right ? 1 : 0;
|
||||||
|
case -BRK_GREQUAL * 4 + BRK_FLOAT:
|
||||||
|
return *(float *)&left >= *(float *)&right ? 1 : 0;
|
||||||
|
|
||||||
|
// Shift Left
|
||||||
|
case -BRK_LEFT_L * 4 + BRK_BOOL:
|
||||||
|
case -BRK_LEFT_L * 4 + BRK_FLOAT: // Prevented by parser
|
||||||
|
case -BRK_LEFT_L * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_LEFT_L * 4 + BRK_UNSIGNED:
|
||||||
|
return left << (right & 31);
|
||||||
|
|
||||||
|
// Less or Equal
|
||||||
|
case -BRK_LEQUAL * 4 + BRK_BOOL:
|
||||||
|
case -BRK_LEQUAL * 4 + BRK_SIGNED:
|
||||||
|
return left <= right ? 1 : 0;
|
||||||
|
case -BRK_LEQUAL * 4 + BRK_UNSIGNED:
|
||||||
|
return (uint32_t) left <= right ? 1 : 0;
|
||||||
|
case -BRK_LEQUAL * 4 + BRK_FLOAT:
|
||||||
|
return *(float *)&left <= *(float *)&right ? 1 : 0;
|
||||||
|
|
||||||
|
// Less
|
||||||
|
case -BRK_LESS * 4 + BRK_BOOL:
|
||||||
|
case -BRK_LESS * 4 + BRK_SIGNED:
|
||||||
|
return left < right ? 1 : 0;
|
||||||
|
case -BRK_LESS * 4 + BRK_UNSIGNED:
|
||||||
|
return (uint32_t) left < right ? 1 : 0;
|
||||||
|
case -BRK_LESS * 4 + BRK_FLOAT:
|
||||||
|
return *(float *)&left < *(float *)&right ? 1 : 0;
|
||||||
|
|
||||||
|
// Multiply
|
||||||
|
case -BRK_MULTIPLY * 4 + BRK_BOOL:
|
||||||
|
case -BRK_MULTIPLY * 4 + BRK_SIGNED:
|
||||||
|
return left * right;
|
||||||
|
case -BRK_MULTIPLY * 4 + BRK_UNSIGNED:
|
||||||
|
return (uint32_t) left * right;
|
||||||
|
case -BRK_MULTIPLY * 4 + BRK_FLOAT:
|
||||||
|
return brkAdjust(*(float *)&left * *(float *)&right);
|
||||||
|
|
||||||
|
// Not Equal
|
||||||
|
case -BRK_NEQUAL * 4 + BRK_BOOL:
|
||||||
|
case -BRK_NEQUAL * 4 + BRK_FLOAT:
|
||||||
|
case -BRK_NEQUAL * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_NEQUAL * 4 + BRK_UNSIGNED:
|
||||||
|
return left != right ? 1 : 0;
|
||||||
|
|
||||||
|
// Or Bitwise
|
||||||
|
case -BRK_OR_B * 4 + BRK_BOOL:
|
||||||
|
case -BRK_OR_B * 4 + BRK_FLOAT: // Prevented by parser
|
||||||
|
case -BRK_OR_B * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_OR_B * 4 + BRK_UNSIGNED:
|
||||||
|
return left | right;
|
||||||
|
|
||||||
|
// Or Logical
|
||||||
|
case -BRK_OR_L * 4 + BRK_BOOL:
|
||||||
|
case -BRK_OR_L * 4 + BRK_FLOAT:
|
||||||
|
case -BRK_OR_L * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_OR_L * 4 + BRK_UNSIGNED:
|
||||||
|
return left != 0 ? left : right;
|
||||||
|
|
||||||
|
// Remainder
|
||||||
|
case -BRK_REMAINDER * 4 + BRK_BOOL:
|
||||||
|
case -BRK_REMAINDER * 4 + BRK_SIGNED:
|
||||||
|
return right == 0 ? 0 : left % right;
|
||||||
|
case -BRK_REMAINDER * 4 + BRK_UNSIGNED:
|
||||||
|
return right == 0 ? 0 : (uint32_t) left % right;
|
||||||
|
case -BRK_REMAINDER * 4 + BRK_FLOAT:
|
||||||
|
return right == 0 ? 0 :
|
||||||
|
brkAdjust(fmodf(*(float *)&left, *(float *)&right));
|
||||||
|
|
||||||
|
// Shift Right Arithmetic
|
||||||
|
case -BRK_RIGHT_A * 4 + BRK_BOOL:
|
||||||
|
case -BRK_RIGHT_A * 4 + BRK_FLOAT: // Prevented by parser
|
||||||
|
case -BRK_RIGHT_A * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_RIGHT_A * 4 + BRK_UNSIGNED:
|
||||||
|
return SIGN_EXTEND(left >> (right & 31), 32 - (right & 31));
|
||||||
|
|
||||||
|
// Shift Right Logical
|
||||||
|
case -BRK_RIGHT_L * 4 + BRK_BOOL:
|
||||||
|
case -BRK_RIGHT_L * 4 + BRK_FLOAT: // Prevented by parser
|
||||||
|
case -BRK_RIGHT_L * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_RIGHT_L * 4 + BRK_UNSIGNED:
|
||||||
|
return left >> (right & 31) & ((int32_t) -1 << (right & 31));
|
||||||
|
|
||||||
|
// Subtract
|
||||||
|
case -BRK_SUBTRACT * 4 + BRK_BOOL:
|
||||||
|
case -BRK_SUBTRACT * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_SUBTRACT * 4 + BRK_UNSIGNED:
|
||||||
|
return left - right;
|
||||||
|
case -BRK_SUBTRACT * 4 + BRK_FLOAT:
|
||||||
|
return brkAdjust(*(float *)&left - *(float *)&right);
|
||||||
|
|
||||||
|
// Exclusive Or Bitwise
|
||||||
|
case -BRK_XOR_B * 4 + BRK_BOOL:
|
||||||
|
case -BRK_XOR_B * 4 + BRK_FLOAT: // Prevented by parser
|
||||||
|
case -BRK_XOR_B * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_XOR_B * 4 + BRK_UNSIGNED:
|
||||||
|
return left ^ right;
|
||||||
|
|
||||||
|
// Exclusive Or Logical
|
||||||
|
case -BRK_XOR_L * 4 + BRK_BOOL:
|
||||||
|
case -BRK_XOR_L * 4 + BRK_FLOAT:
|
||||||
|
case -BRK_XOR_L * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_XOR_L * 4 + BRK_UNSIGNED:
|
||||||
|
return left != 0 && right != 0 ? 0 : left != 0 ? left : right;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unreachable
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate a unary operator
|
||||||
|
static int32_t brkEvalUnary(Vue *vue, int32_t id, int32_t operand) {
|
||||||
|
float val; // Working variable
|
||||||
|
|
||||||
|
// Processing by ID
|
||||||
|
switch (id) {
|
||||||
|
|
||||||
|
// Cast to Boolean
|
||||||
|
case -BRK_BOOL_ * 4 + BRK_BOOL: // Removed by parser
|
||||||
|
case -BRK_BOOL_ * 4 + BRK_FLOAT:
|
||||||
|
case -BRK_BOOL_ * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_BOOL_ * 4 + BRK_UNSIGNED:
|
||||||
|
return operand == 0 ? 0 : 1;
|
||||||
|
|
||||||
|
// Round up
|
||||||
|
case -BRK_CEIL * 4 + BRK_BOOL: // Removed by parser
|
||||||
|
case -BRK_CEIL * 4 + BRK_FLOAT:
|
||||||
|
case -BRK_CEIL * 4 + BRK_SIGNED: // Removed by parser
|
||||||
|
case -BRK_CEIL * 4 + BRK_UNSIGNED: // Removed by parser
|
||||||
|
return brkAdjust(ceilf(*(float *)&operand));
|
||||||
|
|
||||||
|
// Cast to Float
|
||||||
|
case -BRK_FLOAT_ * 4 + BRK_BOOL:
|
||||||
|
case -BRK_FLOAT_ * 4 + BRK_FLOAT: // Removed by parser
|
||||||
|
case -BRK_FLOAT_ * 4 + BRK_SIGNED:
|
||||||
|
return brkAdjust(operand);
|
||||||
|
case -BRK_FLOAT_ * 4 + BRK_UNSIGNED:
|
||||||
|
return brkAdjust((uint32_t) operand);
|
||||||
|
|
||||||
|
// Round down
|
||||||
|
case -BRK_FLOOR * 4 + BRK_BOOL: // Removed by parser
|
||||||
|
case -BRK_FLOOR * 4 + BRK_FLOAT:
|
||||||
|
case -BRK_FLOOR * 4 + BRK_SIGNED: // Removed by parser
|
||||||
|
case -BRK_FLOOR * 4 + BRK_UNSIGNED: // Removed by parser
|
||||||
|
return brkAdjust(floorf(*(float *)&operand));
|
||||||
|
|
||||||
|
// Bitwise Not
|
||||||
|
case -BRK_NOT_B * 4 + BRK_BOOL:
|
||||||
|
case -BRK_NOT_B * 4 + BRK_FLOAT: // Prevented by parser
|
||||||
|
case -BRK_NOT_B * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_NOT_B * 4 + BRK_UNSIGNED:
|
||||||
|
return ~operand;
|
||||||
|
|
||||||
|
// Logical Not
|
||||||
|
case -BRK_NOT_L * 4 + BRK_BOOL:
|
||||||
|
case -BRK_NOT_L * 4 + BRK_FLOAT:
|
||||||
|
case -BRK_NOT_L * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_NOT_L * 4 + BRK_UNSIGNED:
|
||||||
|
return operand == 0 ? 1 : 0;
|
||||||
|
|
||||||
|
// Negate
|
||||||
|
case -BRK_NEGATE * 4 + BRK_BOOL:
|
||||||
|
case -BRK_NEGATE * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_NEGATE * 4 + BRK_UNSIGNED: // Removed by parser
|
||||||
|
return -operand;
|
||||||
|
case -BRK_NEGATE * 4 + BRK_FLOAT:
|
||||||
|
return brkAdjust(-*(float *)&operand);
|
||||||
|
|
||||||
|
// Read Byte
|
||||||
|
case BRK_READ8:
|
||||||
|
return vueRead(vue, operand, VUE_S8);
|
||||||
|
|
||||||
|
// Read Halfword
|
||||||
|
case BRK_READ16:
|
||||||
|
return vueRead(vue, operand, VUE_S16);
|
||||||
|
|
||||||
|
// Read Word
|
||||||
|
case BRK_READ32:
|
||||||
|
return vueRead(vue, operand, VUE_S32);
|
||||||
|
|
||||||
|
// Round to Nearest
|
||||||
|
case -BRK_ROUND * 4 + BRK_BOOL: // Removed by parser
|
||||||
|
case -BRK_ROUND * 4 + BRK_FLOAT:
|
||||||
|
case -BRK_ROUND * 4 + BRK_SIGNED: // Removed by parser
|
||||||
|
case -BRK_ROUND * 4 + BRK_UNSIGNED: // Removed by parser
|
||||||
|
return brkAdjust(roundf(*(float *)&operand));
|
||||||
|
|
||||||
|
// Cast to Signed Byte
|
||||||
|
case -BRK_S8 * 4 + BRK_FLOAT:
|
||||||
|
operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand);
|
||||||
|
// Fallthrough
|
||||||
|
case -BRK_S8 * 4 + BRK_BOOL:
|
||||||
|
case -BRK_S8 * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_S8 * 4 + BRK_UNSIGNED:
|
||||||
|
return SIGN_EXTEND(operand, 8);
|
||||||
|
|
||||||
|
// Cast to Signed Halfword
|
||||||
|
case -BRK_S16 * 4 + BRK_FLOAT:
|
||||||
|
operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand);
|
||||||
|
// Fallthrough
|
||||||
|
case -BRK_S16 * 4 + BRK_BOOL:
|
||||||
|
case -BRK_S16 * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_S16 * 4 + BRK_UNSIGNED:
|
||||||
|
return SIGN_EXTEND(operand, 16);
|
||||||
|
|
||||||
|
// Cast to Signed Word
|
||||||
|
case -BRK_S32 * 4 + BRK_BOOL:
|
||||||
|
case -BRK_S32 * 4 + BRK_SIGNED: // Removed by parser
|
||||||
|
case -BRK_S32 * 4 + BRK_UNSIGNED:
|
||||||
|
return operand;
|
||||||
|
case -BRK_S32 * 4 + BRK_FLOAT:
|
||||||
|
val = *(float *)&operand;
|
||||||
|
return val >= -MAX_WORD && val < MAX_WORD ? (int32_t) val : 0;
|
||||||
|
|
||||||
|
// Truncate
|
||||||
|
case -BRK_TRUNC * 4 + BRK_BOOL: // Removed by parser
|
||||||
|
case -BRK_TRUNC * 4 + BRK_FLOAT:
|
||||||
|
case -BRK_TRUNC * 4 + BRK_SIGNED: // Removed by parser
|
||||||
|
case -BRK_TRUNC * 4 + BRK_UNSIGNED: // Removed by parser
|
||||||
|
return brkAdjust(truncf(*(float *)&operand));
|
||||||
|
|
||||||
|
// Cast to Unsigned Byte
|
||||||
|
case -BRK_U8 * 4 + BRK_FLOAT:
|
||||||
|
operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand);
|
||||||
|
case -BRK_U8 * 4 + BRK_BOOL:
|
||||||
|
case -BRK_U8 * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_U8 * 4 + BRK_UNSIGNED:
|
||||||
|
return operand & 0xFF;
|
||||||
|
|
||||||
|
// Cast to Unsigned Halfword
|
||||||
|
case -BRK_U16 * 4 + BRK_FLOAT:
|
||||||
|
operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand);
|
||||||
|
case -BRK_U16 * 4 + BRK_BOOL:
|
||||||
|
case -BRK_U16 * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_U16 * 4 + BRK_UNSIGNED:
|
||||||
|
return operand & 0xFFFF;
|
||||||
|
|
||||||
|
// Cast to Unsigned Word
|
||||||
|
case -BRK_U32 * 4 + BRK_BOOL:
|
||||||
|
case -BRK_U32 * 4 + BRK_SIGNED:
|
||||||
|
case -BRK_U32 * 4 + BRK_UNSIGNED: // Removed by parser
|
||||||
|
return operand;
|
||||||
|
case -BRK_U32 * 4 + BRK_FLOAT:
|
||||||
|
val = *(float *)&operand;
|
||||||
|
return val >= 0 && val < MAX_UWORD ? (uint32_t) val : 0;
|
||||||
|
|
||||||
|
// Reinterpret as Float
|
||||||
|
case BRK_XFLOAT:
|
||||||
|
return brkAdjust(*(float *)&operand);
|
||||||
|
|
||||||
|
// Reinterpret as Signed Word, Reinterpret as Unsigned Word
|
||||||
|
//case BRK_XS32: // Removed by parser
|
||||||
|
//case BRK_XU32: // Removed by parser
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unreachable
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate the condition, returning only the computed value
|
||||||
|
static int32_t brkEvaluate(Core *core, Breakpoint *brk) {
|
||||||
|
int32_t *stack = core->stack;
|
||||||
|
Vue *vue = &core->vue;
|
||||||
|
|
||||||
|
// Process all tokens
|
||||||
|
int size = 0;
|
||||||
|
Token *tok;
|
||||||
|
int32_t x, sym; // Working variables
|
||||||
|
for (tok = &brk->tokens[x=0]; x < brk->numTokens; tok = &brk->tokens[++x])
|
||||||
|
switch (tok->type) {
|
||||||
|
|
||||||
|
// Binary operator
|
||||||
|
case BRK_BINARY:
|
||||||
|
stack[size - 2] =
|
||||||
|
brkEvalBinary(tok->id, stack[size - 2], stack[size - 1]);
|
||||||
|
size--;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Literal
|
||||||
|
case BRK_BOOL:
|
||||||
|
case BRK_FLOAT:
|
||||||
|
case BRK_SIGNED:
|
||||||
|
case BRK_UNSIGNED:
|
||||||
|
stack[size++] = tok->id;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// CPU register
|
||||||
|
case BRK_PROREG:
|
||||||
|
case BRK_SYSREG:
|
||||||
|
stack[size++] = vueGetRegister(vue,tok->id,tok->type==BRK_SYSREG);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Unary operator
|
||||||
|
case BRK_UNARY:
|
||||||
|
stack[size - 1] = brkEvalUnary(vue, tok->id, stack[size - 1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Symbol
|
||||||
|
case BRK_SYMBOL:
|
||||||
|
sym = vue->cpu.inst.imm; // IMM, REGID, VECTOR
|
||||||
|
switch (tok->id) {
|
||||||
|
case BRK_ADDRESS : sym = vue->cpu.access.address; break;
|
||||||
|
case BRK_BREAK : sym = core->breakType ; break;
|
||||||
|
case BRK_CODE : sym = vue->cpu.exception ; break;
|
||||||
|
case BRK_COND : sym = vue->cpu.inst.cond ; break;
|
||||||
|
case BRK_DISP : sym = vue->cpu.inst.disp ; break;
|
||||||
|
case BRK_FETCH : sym = vue->cpu.fetch ; break;
|
||||||
|
case BRK_FORMAT : sym = vue->cpu.inst.format ; break;
|
||||||
|
case BRK_ID : sym = vue->cpu.inst.id ; break;
|
||||||
|
case BRK_OPCODE : sym = vue->cpu.inst.opcode ; break;
|
||||||
|
case BRK_REG1 : sym = vue->cpu.inst.reg1 ; break;
|
||||||
|
case BRK_REG2 : sym = vue->cpu.inst.reg2 ; break;
|
||||||
|
case BRK_SIZE : sym = vue->cpu.inst.size ; break;
|
||||||
|
case BRK_SUBOPCODE: sym = vue->cpu.inst.subopcode; break;
|
||||||
|
case BRK_TYPE : sym = vue->cpu.access.type ; break;
|
||||||
|
case BRK_VALUE : sym = vue->cpu.access.value ; break;
|
||||||
|
}
|
||||||
|
stack[size++] = sym;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the remaining stack value
|
||||||
|
return stack[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine whether the breakpoint applies to a given address range
|
||||||
|
static vbool brkInRange(Breakpoint *brk, uint32_t start, uint32_t end) {
|
||||||
|
|
||||||
|
// Implicit inclusion
|
||||||
|
if (brk->numRanges == 0)
|
||||||
|
return VUE_TRUE;
|
||||||
|
|
||||||
|
// Check all ranges
|
||||||
|
for (int x = 0; x < brk->numRanges; x++) {
|
||||||
|
uint32_t *range = &brk->ranges[x * 2];
|
||||||
|
if (
|
||||||
|
start - range[0] <= range[1] - range[0] ||
|
||||||
|
range[0] - start <= end - start
|
||||||
|
) return VUE_TRUE;
|
||||||
|
}
|
||||||
|
return VUE_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for breakpoints
|
||||||
|
static int32_t brkOnBreakpoint(Vue *vue, int32_t breakType) {
|
||||||
|
Core *core = (Core *) vue->tag;
|
||||||
|
uint32_t end = 0;
|
||||||
|
vbool ranged = VUE_FALSE;
|
||||||
|
uint32_t start = 0;
|
||||||
|
core->breakType = breakType;
|
||||||
|
|
||||||
|
// Processing for Execute
|
||||||
|
if (breakType == BRK_EXECUTE) {
|
||||||
|
ranged = VUE_TRUE;
|
||||||
|
start = vue->cpu.pc;
|
||||||
|
end = start + vue->cpu.inst.size - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processing for Read and Write
|
||||||
|
else if (breakType == BRK_READ || breakType == BRK_WRITE) {
|
||||||
|
ranged = VUE_TRUE;
|
||||||
|
start = vue->cpu.access.address;
|
||||||
|
end = start + TYPE_SIZES[vue->cpu.access.type] - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check all breakpoints
|
||||||
|
for (int x = 0; x < core->numBreakpoints; x++) {
|
||||||
|
Breakpoint *brk = &core->breakpoints[x];
|
||||||
|
if (
|
||||||
|
brk->enabled &&
|
||||||
|
(breakType == 0 || (breakType & brk->hooks) != 0) &&
|
||||||
|
(!ranged || brkInRange(brk, start, end)) &&
|
||||||
|
(brk->numTokens == 0 || brkEvaluate(core, brk) != 0)
|
||||||
|
) return x + 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for exception breakpoints
|
||||||
|
static int32_t brkOnException(Vue *vue, uint16_t code) {
|
||||||
|
return brkOnBreakpoint(vue, BRK_EXCEPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for execute breakpoints
|
||||||
|
static int32_t brkOnExecute(Vue *vue, VueInstruction *inst) {
|
||||||
|
return brkOnBreakpoint(vue, BRK_EXECUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for frame breakpoints
|
||||||
|
static int32_t brkOnFrame(Vue *vue) {
|
||||||
|
return brkOnBreakpoint(vue, BRK_FRAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for read breakpoints
|
||||||
|
static int32_t brkOnRead(Vue *vue, VueAccess *acc) {
|
||||||
|
return brkOnBreakpoint(vue, BRK_READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for write breakpoints
|
||||||
|
static int32_t brkOnWrite(Vue *vue, VueAccess *acc) {
|
||||||
|
return brkOnBreakpoint(vue, BRK_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // NATIVEVUE
|
#endif // NATIVEVUE
|
||||||
|
|
|
@ -853,6 +853,11 @@ public class Breakpoint {
|
||||||
return depth;
|
return depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve the breakpoint types this breakpoint applies to
|
||||||
|
int getHooks() {
|
||||||
|
return hooks;
|
||||||
|
}
|
||||||
|
|
||||||
// The breakpoint is being removed from its emulation context
|
// The breakpoint is being removed from its emulation context
|
||||||
void remove() {
|
void remove() {
|
||||||
vue = null;
|
vue = null;
|
||||||
|
@ -1469,6 +1474,7 @@ public class Breakpoint {
|
||||||
case BINARY:
|
case BINARY:
|
||||||
stack[size - 2] =
|
stack[size - 2] =
|
||||||
evalBinary(tok.id, stack[size - 2], stack[size - 1]);
|
evalBinary(tok.id, stack[size - 2], stack[size - 1]);
|
||||||
|
size--;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Literal
|
// Literal
|
||||||
|
|
|
@ -16,38 +16,36 @@
|
||||||
// Memory access type sizes
|
// Memory access type sizes
|
||||||
static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
|
static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
|
||||||
|
|
||||||
|
// Word data type limits
|
||||||
|
#define MAX_WORD 2147483648.0f
|
||||||
|
#define MAX_UWORD 4294967296.0f
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Types //
|
// Types //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Breakpoint address range
|
|
||||||
typedef struct {
|
|
||||||
int32_t start; // First address, inclusive
|
|
||||||
int32_t end; // Last address, inclusive
|
|
||||||
} Range;
|
|
||||||
|
|
||||||
// Breakpoint condition token
|
// Breakpoint condition token
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t type; // Token category
|
int32_t type; // Token category
|
||||||
int32_t value; // Operator or symbol ID, or literal value
|
int32_t id; // Operator or symbol ID, or literal value
|
||||||
} Token;
|
} Token;
|
||||||
|
|
||||||
// Precompiled breakpoint
|
// Precompiled breakpoint
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t depth; // Maximum number of stack entries
|
int32_t enabled; // The breakpoint is enabled
|
||||||
int32_t enabled; // The breakpoint is enabled
|
int32_t hooks; // Events hooked by the breakpoint
|
||||||
int32_t hooks; // Events hooked by the breakpoint
|
int32_t numRanges; // Number of address ranges
|
||||||
int32_t numRanges; // Number of address ranges
|
int32_t numTokens; // Number of condition tokens
|
||||||
int32_t numTokens; // Number of condition tokens
|
uint32_t *ranges; // Address ranges
|
||||||
Range *ranges; // Address ranges
|
Token *tokens; // Condition tokens in RPN order
|
||||||
Token *tokens; // Condition tokens in RPN order
|
|
||||||
} Breakpoint;
|
} Breakpoint;
|
||||||
|
|
||||||
// Core context state type
|
// Core context state type
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Vue vue; // Context into the generic C library
|
Vue vue; // Context into the generic C library
|
||||||
|
int32_t breakType; // Most recent breakpoint scenario
|
||||||
int32_t numBreakpoints; // Number of breakpoints
|
int32_t numBreakpoints; // Number of breakpoints
|
||||||
Breakpoint *breakpoints; // Application breakpoints
|
Breakpoint *breakpoints; // Application breakpoints
|
||||||
int32_t *stack; // Expression stack for breakpoints
|
int32_t *stack; // Expression stack for breakpoints
|
||||||
|
@ -75,19 +73,42 @@ typedef struct {
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Public Methods //
|
// Constructors //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Native constructor
|
// Native constructor
|
||||||
JNIEXPORT jlong JNICALL Java_vue_NativeVue_construct
|
JNIEXPORT jlong JNICALL Java_vue_NativeVue_construct
|
||||||
(JNIEnv *env, jobject vue) {
|
(JNIEnv *env, jobject uve) {
|
||||||
Core *mem[] = { NULL, NULL };
|
Core *mem[] = { NULL, NULL };
|
||||||
Core *core = mem[0] = calloc(sizeof (Core), 1);
|
Core *core = mem[0] = calloc(sizeof (Core), 1);
|
||||||
vueInitialize(&core->vue);
|
Vue *vue = &core->vue;
|
||||||
vueReset(&core->vue);
|
vueInitialize (vue);
|
||||||
|
vueOnException(vue, &brkOnException);
|
||||||
|
vueOnExecute (vue, &brkOnExecute );
|
||||||
|
vueOnFrame (vue, &brkOnFrame );
|
||||||
|
vueOnRead (vue, &brkOnRead );
|
||||||
|
vueOnWrite (vue, &brkOnWrite );
|
||||||
|
vueReset (vue);
|
||||||
|
vueSetTag (vue, core);
|
||||||
return *(jlong *)&core;
|
return *(jlong *)&core;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Public Methods //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Produce a new breakpoint and add it to the collection
|
||||||
|
JNIEXPORT void JNICALL Java_vue_NativeVue_breakpoint
|
||||||
|
(JNIEnv *env, jobject vue, jlong handle) {
|
||||||
|
Core *core = *(Core **)&handle;
|
||||||
|
core->numBreakpoints++;
|
||||||
|
core->breakpoints =
|
||||||
|
realloc(core->breakpoints,core->numBreakpoints * sizeof (Breakpoint));
|
||||||
|
memset(&core->breakpoints[core->numBreakpoints-1], 0,sizeof (Breakpoint));
|
||||||
|
}
|
||||||
|
|
||||||
// Release any used resources
|
// Release any used resources
|
||||||
JNIEXPORT void JNICALL Java_vue_NativeVue_dispose
|
JNIEXPORT void JNICALL Java_vue_NativeVue_dispose
|
||||||
(JNIEnv *env, jobject vue, jlong handle) {
|
(JNIEnv *env, jobject vue, jlong handle) {
|
||||||
|
@ -98,15 +119,15 @@ JNIEXPORT void JNICALL Java_vue_NativeVue_dispose
|
||||||
// Delete breakpoints
|
// Delete breakpoints
|
||||||
for (x = 0; x < core->numBreakpoints; x++) {
|
for (x = 0; x < core->numBreakpoints; x++) {
|
||||||
brk = &core->breakpoints[x];
|
brk = &core->breakpoints[x];
|
||||||
if (brk->ranges != NULL) free(brk->ranges);
|
free(brk->ranges);
|
||||||
if (brk->tokens != NULL) free(brk->tokens);
|
free(brk->tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete core
|
// Delete core
|
||||||
if (core->breakpoints != NULL) free(core->breakpoints);
|
free(core->breakpoints);
|
||||||
if (core->stack != NULL) free(core->stack );
|
free(core->stack );
|
||||||
if (core->vue.pak.rom != NULL) free(core->vue.pak.rom);
|
free(core->vue.pak.rom);
|
||||||
if (core->vue.pak.ram != NULL) free(core->vue.pak.ram);
|
free(core->vue.pak.ram);
|
||||||
free(core);
|
free(core);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +138,22 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_emulate
|
||||||
return vueEmulate(&core->vue, maxCycles);
|
return vueEmulate(&core->vue, maxCycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve a snapshot of the current state's memory access
|
||||||
|
JNIEXPORT jobject JNICALL Java_vue_NativeVue_getAccess
|
||||||
|
(JNIEnv *env, jobject vue, jlong handle, jobject acc) {
|
||||||
|
Core *core = *(Core **)&handle;
|
||||||
|
jclass cls = (*env)->GetObjectClass(env, acc);
|
||||||
|
(*env)->SetIntField(env, acc, (*env)->GetFieldID(env, cls, "address", "I"),
|
||||||
|
core->vue.cpu.access.address);
|
||||||
|
(*env)->SetIntField(env, acc, (*env)->GetFieldID(env, cls, "fetch", "I"),
|
||||||
|
core->vue.cpu.fetch);
|
||||||
|
(*env)->SetIntField(env, acc, (*env)->GetFieldID(env, cls, "type", "I"),
|
||||||
|
core->vue.cpu.access.type);
|
||||||
|
(*env)->SetIntField(env, acc, (*env)->GetFieldID(env, cls, "value", "I"),
|
||||||
|
core->vue.cpu.access.value);
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve the most recent exception code
|
// Retrieve the most recent exception code
|
||||||
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode
|
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode
|
||||||
(JNIEnv *env, jobject vue, jlong handle) {
|
(JNIEnv *env, jobject vue, jlong handle) {
|
||||||
|
@ -131,11 +168,41 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_getExceptionCode
|
||||||
return vueGetExceptionCode(&core->vue);
|
return vueGetExceptionCode(&core->vue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve a snapshot of the current state's instruction
|
||||||
|
JNIEXPORT jobject JNICALL Java_vue_NativeVue_getInstruction
|
||||||
|
(JNIEnv *env, jobject vue, jlong handle, jobject inst) {
|
||||||
|
Core *core = *(Core **)&handle;
|
||||||
|
jclass cls = (*env)->GetObjectClass(env, inst);
|
||||||
|
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "bits", "I"),
|
||||||
|
core->vue.cpu.inst.bits);
|
||||||
|
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "cond", "I"),
|
||||||
|
core->vue.cpu.inst.cond);
|
||||||
|
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "disp", "I"),
|
||||||
|
core->vue.cpu.inst.disp);
|
||||||
|
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "format", "I"),
|
||||||
|
core->vue.cpu.inst.format);
|
||||||
|
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "id", "I"),
|
||||||
|
core->vue.cpu.inst.id);
|
||||||
|
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "imm", "I"),
|
||||||
|
core->vue.cpu.inst.imm);
|
||||||
|
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "opcode", "I"),
|
||||||
|
core->vue.cpu.inst.opcode);
|
||||||
|
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "reg1", "I"),
|
||||||
|
core->vue.cpu.inst.reg1);
|
||||||
|
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "reg2", "I"),
|
||||||
|
core->vue.cpu.inst.reg2);
|
||||||
|
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env, cls, "size", "I"),
|
||||||
|
core->vue.cpu.inst.size);
|
||||||
|
(*env)->SetIntField(env, inst, (*env)->GetFieldID(env,cls,"subopcode","I"),
|
||||||
|
core->vue.cpu.inst.subopcode);
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve a register value
|
// Retrieve a register value
|
||||||
JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister
|
JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister
|
||||||
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) {
|
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) {
|
||||||
Core *core = *(Core **)&handle;
|
Core *core = *(Core **)&handle;
|
||||||
return vueGetRegister(&core->vue, index, system);;
|
return vueGetRegister(&core->vue, index, system);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve a copy of the ROM data
|
// Retrieve a copy of the ROM data
|
||||||
|
@ -155,12 +222,6 @@ JNIEXPORT jbyteArray JNICALL Java_vue_NativeVue_getROM
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine whether the context is native-backed
|
|
||||||
JNIEXPORT jboolean JNICALL Java_vue_NativeVue_isNative
|
|
||||||
(JNIEnv *env, jobject vue) {
|
|
||||||
return JNI_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a value from the CPU bus
|
// Read a value from the CPU bus
|
||||||
JNIEXPORT jint JNICALL Java_vue_NativeVue_read
|
JNIEXPORT jint JNICALL Java_vue_NativeVue_read
|
||||||
(JNIEnv *env, jobject vue, jlong handle, jint address, jint type) {
|
(JNIEnv *env, jobject vue, jlong handle, jint address, jint type) {
|
||||||
|
@ -189,6 +250,18 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVue_readBytes
|
||||||
return JNI_TRUE;
|
return JNI_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove a breakpoint from the collection
|
||||||
|
JNIEXPORT void JNICALL Java_vue_NativeVue_remove
|
||||||
|
(JNIEnv *env, jobject vue, jlong handle, jint index) {
|
||||||
|
Core *core = *(Core **)&handle;
|
||||||
|
Breakpoint *brk = &core->breakpoints[index];
|
||||||
|
free(brk->ranges);
|
||||||
|
free(brk->tokens);
|
||||||
|
memmove(brk, &core->breakpoints[index + 1],
|
||||||
|
(core->numBreakpoints - index - 1) * sizeof (Breakpoint));
|
||||||
|
core->numBreakpoints--;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize all system components
|
// Initialize all system components
|
||||||
JNIEXPORT void JNICALL Java_vue_NativeVue_reset
|
JNIEXPORT void JNICALL Java_vue_NativeVue_reset
|
||||||
(JNIEnv *env, jobject vue, jlong handle) {
|
(JNIEnv *env, jobject vue, jlong handle) {
|
||||||
|
@ -226,8 +299,7 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVue_setROM
|
||||||
|
|
||||||
// Transfer the ROM data to the emulation state
|
// Transfer the ROM data to the emulation state
|
||||||
Core *core = *(Core **)&handle;
|
Core *core = *(Core **)&handle;
|
||||||
if (core->vue.pak.rom != NULL)
|
free(core->vue.pak.rom);
|
||||||
free(core->vue.pak.rom);
|
|
||||||
vueSetROM(&core->vue, rom, length);
|
vueSetROM(&core->vue, rom, length);
|
||||||
return JNI_TRUE;
|
return JNI_TRUE;
|
||||||
}
|
}
|
||||||
|
@ -264,5 +336,55 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVue_writeBytes
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Private Methods //
|
// Package Methods //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Retrieve the current state's breakpoint scenario
|
||||||
|
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakType
|
||||||
|
(JNIEnv *env, jobject vue, jlong handle) {
|
||||||
|
Core *core = *(Core **)&handle;
|
||||||
|
return core->breakType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the current instruction fetch index
|
||||||
|
JNIEXPORT jint JNICALL Java_vue_NativeVue_getFetch
|
||||||
|
(JNIEnv *env, jobject vue, jlong handle) {
|
||||||
|
Core *core = *(Core **)&handle;
|
||||||
|
return core->vue.cpu.fetch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A breakpoint's address ranges have changed
|
||||||
|
JNIEXPORT void JNICALL Java_vue_NativeVue_updateRanges
|
||||||
|
(JNIEnv *env, jobject vue, jlong handle, jint index, jintArray ranges) {
|
||||||
|
Core *core = *(Core **)&handle;
|
||||||
|
Breakpoint *brk = &core->breakpoints[index];
|
||||||
|
brk->numRanges = (*env)->GetArrayLength(env, ranges);
|
||||||
|
brk->ranges = realloc(brk->ranges, brk->numRanges * 8);
|
||||||
|
jint *elems = (*env)->GetIntArrayElements(env, ranges, NULL);
|
||||||
|
memcpy(brk->ranges, elems, brk->numRanges * 8);
|
||||||
|
(*env)->ReleaseIntArrayElements(env, ranges, elems, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A breakpoint's enabled/hook state has changed
|
||||||
|
JNIEXPORT void JNICALL Java_vue_NativeVue_updateState
|
||||||
|
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean enabled,
|
||||||
|
jint hooks) {
|
||||||
|
Core *core = *(Core **)&handle;
|
||||||
|
Breakpoint *brk = &core->breakpoints[index];
|
||||||
|
brk->enabled = enabled;
|
||||||
|
brk->hooks = hooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A breakpoint's condition tokens have changed
|
||||||
|
JNIEXPORT void JNICALL Java_vue_NativeVue_updateTokens
|
||||||
|
(JNIEnv *env, jobject vue, jlong handle, jint index, jintArray tokens,
|
||||||
|
jint depth, jint maxDepth) {
|
||||||
|
Core *core = *(Core **)&handle;
|
||||||
|
core->stack = realloc(core->stack, maxDepth * 4);
|
||||||
|
Breakpoint *brk = &core->breakpoints[index];
|
||||||
|
brk->numTokens = (*env)->GetArrayLength(env, tokens) / 2;
|
||||||
|
brk->tokens = realloc(brk->tokens, brk->numTokens * sizeof (Token));
|
||||||
|
jint *elems = (*env)->GetIntArrayElements(env, tokens, NULL);
|
||||||
|
memcpy(brk->tokens, elems, brk->numTokens * sizeof (Token));
|
||||||
|
(*env)->ReleaseIntArrayElements(env, tokens, elems, 0);
|
||||||
|
}
|
||||||
|
|
|
@ -74,7 +74,9 @@ class NativeVue extends Vue {
|
||||||
public byte[] getROM() { return getROM(handle); }
|
public byte[] getROM() { return getROM(handle); }
|
||||||
|
|
||||||
// Determine whether the context is native-backed
|
// Determine whether the context is native-backed
|
||||||
public native boolean isNative();
|
public boolean isNative() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Read a value from the CPU bus
|
// Read a value from the CPU bus
|
||||||
private native int read(long handle, int address, int type);
|
private native int read(long handle, int address, int type);
|
||||||
|
@ -144,18 +146,37 @@ class NativeVue extends Vue {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A breakpoint's address ranges have changed
|
// A breakpoint's address ranges have changed
|
||||||
|
private native void updateRanges(long handle, int index, int[] ranges);
|
||||||
void updateRanges(Breakpoint brk) {
|
void updateRanges(Breakpoint brk) {
|
||||||
super.updateRanges(brk);
|
super.updateRanges(brk);
|
||||||
|
int index = breakpoints.indexOf(brk);
|
||||||
|
if (index >= 0)
|
||||||
|
updateRanges(handle, index, brk.flattenRanges());
|
||||||
}
|
}
|
||||||
|
|
||||||
// A breakpoint's enabled/hook state has changed
|
// A breakpoint's enabled/hook state has changed
|
||||||
|
private native void updateState(long handle, int index, boolean isEnabled,
|
||||||
|
int hooks);
|
||||||
void updateState(Breakpoint brk) {
|
void updateState(Breakpoint brk) {
|
||||||
super.updateState(brk);
|
super.updateState(brk);
|
||||||
|
int index = breakpoints.indexOf(brk);
|
||||||
|
updateState(handle, index, brk.isEnabled(),
|
||||||
|
brk.getHooks());
|
||||||
}
|
}
|
||||||
|
|
||||||
// A breakpoint's condition tokens have changed
|
// A breakpoint's condition tokens have changed
|
||||||
|
private native void updateTokens(long handle, int index, int[] tokens,
|
||||||
|
int depth, int maxDepth);
|
||||||
void updateTokens(Breakpoint brk) {
|
void updateTokens(Breakpoint brk) {
|
||||||
super.updateTokens(brk);
|
super.updateTokens(brk);
|
||||||
|
int index = breakpoints.indexOf(brk);
|
||||||
|
if (index < 0)
|
||||||
|
return;
|
||||||
|
int maxDepth = 0;
|
||||||
|
for (var bre : breakpoints)
|
||||||
|
maxDepth = Math.max(maxDepth, bre.getDepth());
|
||||||
|
updateTokens(handle, index, brk.flattenTokens(),
|
||||||
|
brk.getDepth(), maxDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue