Integrating breakpoints into the native module

This commit is contained in:
Guy Perfect 2020-12-20 18:14:31 -06:00
parent 167bc4f8bf
commit edab431a1f
11 changed files with 814 additions and 70 deletions

View File

@ -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 */

View File

@ -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);

View File

@ -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;

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}
}