diff --git a/src/core/cpu.c b/src/core/cpu.c index f862569..2d50e6e 100644 --- a/src/core/cpu.c +++ b/src/core/cpu.c @@ -643,7 +643,7 @@ static void cpuDIVU(Vue *vue) { /* Halt */ #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 */ #define cpuIN_B(vue) cpuIN_LD(vue, VUE_U8) @@ -656,7 +656,7 @@ static void cpuDIVU(Vue *vue) { /* Jump and Link */ #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) /* Jump Register */ @@ -848,7 +848,7 @@ static void cpuREV(Vue *vue) { /* Trap */ #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 /* Truncate Short Floating to Word */ diff --git a/src/core/include/vue.h b/src/core/include/vue.h index 2a87b74..8cf40c2 100644 --- a/src/core/include/vue.h +++ b/src/core/include/vue.h @@ -177,6 +177,7 @@ typedef int32_t (*VueOnFrame )(Vue * ); /* Emulation state */ struct Vue { + void *tag; /* Application reference */ int32_t breakCode; /* Application break code */ 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 uint16_t vueGetExceptionCode (Vue *vue); VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system); +VUEAPI void* vueGetTag (Vue *vue); VUEAPI void vueInitialize (Vue *vue); VUEAPI VueOnException vueOnException (Vue *vue, VueOnException 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 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* vueSetTag (Vue *vue, void *tag); 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); diff --git a/src/core/vue.c b/src/core/vue.c index 225829b..8f2b4de 100644 --- a/src/core/vue.c +++ b/src/core/vue.c @@ -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 */ void vueInitialize(Vue *vue) { if (vue == NULL) @@ -264,7 +269,7 @@ VueOnFrame vueOnFrame(Vue *vue, VueOnFrame callback) { } /* Specify a read breakpoint callback */ -VueOnAccess vueSetRead(Vue *vue, VueOnAccess callback) { +VueOnAccess vueOnRead(Vue *vue, VueOnAccess callback) { VueOnAccess ret; if (vue == NULL) return NULL; @@ -283,6 +288,16 @@ VueOnAccess vueOnWrite(Vue *vue, VueOnAccess callback) { 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 */ 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 */ switch (address >> 24 & 7) { - case 5: writeBuffer(vue->wram , 0x1000, + case 5: writeBuffer(vue->wram , 0x10000, address, type, value); break; case 6: writeBuffer(vue->pak.ram, vue->pak.ramSize, address, type, value); break; diff --git a/src/desktop/app/CPUWindow.java b/src/desktop/app/CPUWindow.java index 7fec2ce..698ad5a 100644 --- a/src/desktop/app/CPUWindow.java +++ b/src/desktop/app/CPUWindow.java @@ -146,8 +146,8 @@ class CPUWindow extends ChildWindow { } // Update the display - void refresh() { - panDasm .refresh(); + void refresh(boolean seekToPC) { + panDasm .refresh(seekToPC); lstSystem .refresh(); lstProgram.refresh(); } diff --git a/src/desktop/app/DisassemblerPane.java b/src/desktop/app/DisassemblerPane.java index dd6e832..7757606 100644 --- a/src/desktop/app/DisassemblerPane.java +++ b/src/desktop/app/DisassemblerPane.java @@ -84,8 +84,11 @@ class DisassemblerPane extends JScrollPane { } // Update the display - void refresh() { + void refresh(boolean seekToPC) { 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 if (ctrl && code == KeyEvent.VK_G) { - String addr = JOptionPane.showInputDialog( + String text = JOptionPane.showInputDialog( this, "Goto:", "Goto", JOptionPane.PLAIN_MESSAGE); - if (addr != null && addr.trim().length() != 0) { - try { seek((int) Long.parseLong(addr, 16), count / 3); } - catch (Exception x) { } - } + Object eval = vue.evaluate(text); + int addr = 0; + 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; } @@ -128,29 +136,23 @@ class DisassemblerPane extends JScrollPane { // Single Step case KeyEvent.VK_F11: - step.setCondition("pc!=" + pc); + step.setCondition("!fetch&&pc!=" + pc); 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); + parent.parent.refreshDebug(true); break; // Run to Next case KeyEvent.VK_F12: - step.setCondition("pc==" + + step.setCondition("!fetch&&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); + parent.parent.refreshDebug(true); break; } diff --git a/src/desktop/app/MainWindow.java b/src/desktop/app/MainWindow.java index 265823e..5f693eb 100644 --- a/src/desktop/app/MainWindow.java +++ b/src/desktop/app/MainWindow.java @@ -100,10 +100,10 @@ class MainWindow extends JFrame { // Configure internal breakpoints brkStep = vue.breakpoint(); - brkStep.setExecute(true); + brkStep.setRead(true); // Display window - refreshDebug(); + refreshDebug(true); pack(); setLocationRelativeTo(null); setVisible(true); @@ -191,8 +191,8 @@ class MainWindow extends JFrame { /////////////////////////////////////////////////////////////////////////// // Refresh all debug views - void refreshDebug() { - cpu .refresh(); + void refreshDebug(boolean seekToPC) { + cpu .refresh(seekToPC); memory.refresh(); } @@ -300,7 +300,7 @@ class MainWindow extends JFrame { // Pause emulation vue.setROM(bytes, 0, bytes.length); vue.reset(); - refreshDebug(); + refreshDebug(true); // Resume emulation } diff --git a/src/desktop/app/MemoryWindow.java b/src/desktop/app/MemoryWindow.java index 298fad6..8f52a63 100644 --- a/src/desktop/app/MemoryWindow.java +++ b/src/desktop/app/MemoryWindow.java @@ -151,12 +151,16 @@ class MemoryWindow extends ChildWindow { // Goto if (ctrl && code == KeyEvent.VK_G) { - String addr = JOptionPane.showInputDialog( + String text = JOptionPane.showInputDialog( this, "Goto:", "Goto", JOptionPane.PLAIN_MESSAGE); - if (addr != null && addr.trim().length() != 0) { - try { setAddress((int) Long.parseLong(addr, 16)); } - catch (Exception x) { } - } + Object eval = parent.vue.evaluate(text); + int addr = 0; + if (eval instanceof Integer) + addr = (Integer) eval; + else if (eval instanceof Long) + addr = (int) (long) (Long) eval; + else return; + setAddress(addr); return; } diff --git a/src/desktop/vue/Breakpoint.c b/src/desktop/vue/Breakpoint.c index b2ce744..2187e5d 100644 --- a/src/desktop/vue/Breakpoint.c +++ b/src/desktop/vue/Breakpoint.c @@ -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 + + +/////////////////////////////////////////////////////////////////////////////// +// 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 diff --git a/src/desktop/vue/Breakpoint.java b/src/desktop/vue/Breakpoint.java index 16e3df9..1c77f29 100644 --- a/src/desktop/vue/Breakpoint.java +++ b/src/desktop/vue/Breakpoint.java @@ -853,6 +853,11 @@ public class Breakpoint { return depth; } + // Retrieve the breakpoint types this breakpoint applies to + int getHooks() { + return hooks; + } + // The breakpoint is being removed from its emulation context void remove() { vue = null; @@ -1469,6 +1474,7 @@ public class Breakpoint { case BINARY: stack[size - 2] = evalBinary(tok.id, stack[size - 2], stack[size - 1]); + size--; break; // Literal diff --git a/src/desktop/vue/NativeVue.c b/src/desktop/vue/NativeVue.c index f9a01a2..2710e89 100644 --- a/src/desktop/vue/NativeVue.c +++ b/src/desktop/vue/NativeVue.c @@ -16,38 +16,36 @@ // Memory access type sizes 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 // /////////////////////////////////////////////////////////////////////////////// -// Breakpoint address range -typedef struct { - int32_t start; // First address, inclusive - int32_t end; // Last address, inclusive -} Range; - // Breakpoint condition token typedef struct { - int32_t type; // Token category - int32_t value; // Operator or symbol ID, or literal value + int32_t type; // Token category + int32_t id; // Operator or symbol ID, or literal value } Token; // Precompiled breakpoint typedef struct { - int32_t depth; // Maximum number of stack entries - int32_t enabled; // The breakpoint is enabled - int32_t hooks; // Events hooked by the breakpoint - int32_t numRanges; // Number of address ranges - int32_t numTokens; // Number of condition tokens - Range *ranges; // Address ranges - Token *tokens; // Condition tokens in RPN order + int32_t enabled; // The breakpoint is enabled + int32_t hooks; // Events hooked by the breakpoint + int32_t numRanges; // Number of address ranges + int32_t numTokens; // Number of condition tokens + uint32_t *ranges; // Address ranges + Token *tokens; // Condition tokens in RPN order } Breakpoint; // Core context state type typedef struct { Vue vue; // Context into the generic C library + int32_t breakType; // Most recent breakpoint scenario int32_t numBreakpoints; // Number of breakpoints Breakpoint *breakpoints; // Application breakpoints int32_t *stack; // Expression stack for breakpoints @@ -75,19 +73,42 @@ typedef struct { /////////////////////////////////////////////////////////////////////////////// -// Public Methods // +// Constructors // /////////////////////////////////////////////////////////////////////////////// // Native constructor JNIEXPORT jlong JNICALL Java_vue_NativeVue_construct - (JNIEnv *env, jobject vue) { + (JNIEnv *env, jobject uve) { Core *mem[] = { NULL, NULL }; Core *core = mem[0] = calloc(sizeof (Core), 1); - vueInitialize(&core->vue); - vueReset(&core->vue); + Vue *vue = &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; } + + +/////////////////////////////////////////////////////////////////////////////// +// 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 JNIEXPORT void JNICALL Java_vue_NativeVue_dispose (JNIEnv *env, jobject vue, jlong handle) { @@ -98,15 +119,15 @@ JNIEXPORT void JNICALL Java_vue_NativeVue_dispose // Delete breakpoints for (x = 0; x < core->numBreakpoints; x++) { brk = &core->breakpoints[x]; - if (brk->ranges != NULL) free(brk->ranges); - if (brk->tokens != NULL) free(brk->tokens); + free(brk->ranges); + free(brk->tokens); } // Delete core - if (core->breakpoints != NULL) free(core->breakpoints); - if (core->stack != NULL) free(core->stack ); - if (core->vue.pak.rom != NULL) free(core->vue.pak.rom); - if (core->vue.pak.ram != NULL) free(core->vue.pak.ram); + free(core->breakpoints); + free(core->stack ); + free(core->vue.pak.rom); + free(core->vue.pak.ram); free(core); } @@ -117,6 +138,22 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_emulate 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 JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode (JNIEnv *env, jobject vue, jlong handle) { @@ -131,11 +168,41 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_getExceptionCode 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 JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister (JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) { Core *core = *(Core **)&handle; - return vueGetRegister(&core->vue, index, system);; + return vueGetRegister(&core->vue, index, system); } // Retrieve a copy of the ROM data @@ -155,12 +222,6 @@ JNIEXPORT jbyteArray JNICALL Java_vue_NativeVue_getROM 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 JNIEXPORT jint JNICALL Java_vue_NativeVue_read (JNIEnv *env, jobject vue, jlong handle, jint address, jint type) { @@ -189,6 +250,18 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVue_readBytes 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 JNIEXPORT void JNICALL Java_vue_NativeVue_reset (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 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); 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); +} diff --git a/src/desktop/vue/NativeVue.java b/src/desktop/vue/NativeVue.java index 16e87e1..4000f1d 100644 --- a/src/desktop/vue/NativeVue.java +++ b/src/desktop/vue/NativeVue.java @@ -74,7 +74,9 @@ class NativeVue extends Vue { public byte[] getROM() { return getROM(handle); } // Determine whether the context is native-backed - public native boolean isNative(); + public boolean isNative() { + return true; + } // Read a value from the CPU bus 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 + private native void updateRanges(long handle, int index, int[] ranges); void updateRanges(Breakpoint 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 + private native void updateState(long handle, int index, boolean isEnabled, + int hooks); void updateState(Breakpoint brk) { super.updateState(brk); + int index = breakpoints.indexOf(brk); + updateState(handle, index, brk.isEnabled(), + brk.getHooks()); } // 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) { 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); } }