Implementing native breakpoint interface
This commit is contained in:
parent
2d048a74c0
commit
826e921dac
2
makefile
2
makefile
|
@ -10,7 +10,7 @@ default:
|
||||||
@echo $(include_linux)
|
@echo $(include_linux)
|
||||||
@echo "Planet Virtual Boy Emulator"
|
@echo "Planet Virtual Boy Emulator"
|
||||||
@echo " https://www.planetvb.com/"
|
@echo " https://www.planetvb.com/"
|
||||||
@echo " October 13, 2020"
|
@echo " October 16, 2020"
|
||||||
@echo
|
@echo
|
||||||
@echo "Intended build environment: Debian i386 or amd64"
|
@echo "Intended build environment: Debian i386 or amd64"
|
||||||
@echo " gcc-multilib"
|
@echo " gcc-multilib"
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class Main {
|
||||||
// Begin application operations
|
// Begin application operations
|
||||||
new App(useNative);
|
new App(useNative);
|
||||||
|
|
||||||
|
/*
|
||||||
var brk = new Breakpoint(null);
|
var brk = new Breakpoint(null);
|
||||||
|
|
||||||
String exp =
|
String exp =
|
||||||
|
@ -74,6 +74,7 @@ public class Main {
|
||||||
brk.getErrorPosition(Breakpoint.ADDRESS) + ":" +
|
brk.getErrorPosition(Breakpoint.ADDRESS) + ":" +
|
||||||
brk.getErrorText (Breakpoint.ADDRESS)
|
brk.getErrorText (Breakpoint.ADDRESS)
|
||||||
);
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -612,13 +612,13 @@ static void evalUnary(Vue *vue, int32_t id, int32_t *stack, int32_t size) {
|
||||||
int32_t evaluate(Vue *vue, Breakpoint *brk, int32_t *stack) {
|
int32_t evaluate(Vue *vue, Breakpoint *brk, int32_t *stack) {
|
||||||
|
|
||||||
// The condition is empty
|
// The condition is empty
|
||||||
if (brk->numCondition == 0)
|
if (brk->numTokens == 0)
|
||||||
return brk->enabled;
|
return brk->enabled;
|
||||||
|
|
||||||
// Process tokens
|
// Process tokens
|
||||||
int size = 0;
|
int size = 0;
|
||||||
for (int x = 0; x < brk->numCondition; x++) {
|
for (int x = 0; x < brk->numTokens; x++) {
|
||||||
Token *tok = &brk->condition[x];
|
Token *tok = &brk->tokens[x];
|
||||||
switch (tok->type) {
|
switch (tok->type) {
|
||||||
case BINARY: size =
|
case BINARY: size =
|
||||||
evalBinary( tok->value, stack, size); continue;
|
evalBinary( tok->value, stack, size); continue;
|
||||||
|
|
|
@ -7,15 +7,16 @@ import java.util.*;
|
||||||
public class Breakpoint {
|
public class Breakpoint {
|
||||||
|
|
||||||
// Instance fields
|
// Instance fields
|
||||||
private int[][] addresses; // Applicable address ranges
|
private String addresses; // Un-processed address ranges
|
||||||
private String addressText; // Un-processed address ranges
|
|
||||||
private String condition; // Un-processed condition
|
private String condition; // Un-processed condition
|
||||||
private int[] errCode; // Error codes
|
private int[] errCode; // Error codes
|
||||||
private int[] errPosition; // Character position of errors
|
private int[] errPosition; // Character position of errors
|
||||||
private String[] errText; // Offending error text
|
private String[] errText; // Offending error text
|
||||||
|
private int hooks; // Applied breakpoint types
|
||||||
private boolean isEnabled; // Breakpoint is active
|
private boolean isEnabled; // Breakpoint is active
|
||||||
private String name; // Display name
|
private String name; // Display name
|
||||||
private Token[] tokens; // Evaluation tokens
|
private int[][] ranges; // Applicable address ranges
|
||||||
|
private Token[] tokens; // Condition tokens
|
||||||
private Vue vue; // Containing emulation context
|
private Vue vue; // Containing emulation context
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,6 +40,12 @@ public class Breakpoint {
|
||||||
public static final int ADDRESS = 0;
|
public static final int ADDRESS = 0;
|
||||||
public static final int CONDITION = 1;
|
public static final int CONDITION = 1;
|
||||||
|
|
||||||
|
// Breakpoint hooks
|
||||||
|
private static final int EXCEPTION = 0x00000001;
|
||||||
|
private static final int EXECUTE = 0x00000002;
|
||||||
|
private static final int READ = 0x00000004;
|
||||||
|
private static final int WRITE = 0x00000008;
|
||||||
|
|
||||||
// Token types
|
// Token types
|
||||||
private static final int BINARY = 4;
|
private static final int BINARY = 4;
|
||||||
private static final int CLOSE = 6;
|
private static final int CLOSE = 6;
|
||||||
|
@ -296,6 +303,11 @@ public class Breakpoint {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve the applicable serialized "value"
|
||||||
|
int flatten() {
|
||||||
|
return type == FLOAT || type == WORD ? value : id;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -306,13 +318,13 @@ public class Breakpoint {
|
||||||
|
|
||||||
// Default constructor
|
// Default constructor
|
||||||
public Breakpoint(Vue vue) {
|
public Breakpoint(Vue vue) {
|
||||||
addresses = new int[0][];
|
addresses = "";
|
||||||
addressText = "";
|
|
||||||
condition = "";
|
condition = "";
|
||||||
errCode = new int [] { NONE, NONE };
|
errCode = new int [] { NONE, NONE };
|
||||||
errPosition = new int [] { 0, 0 };
|
errPosition = new int [] { 0, 0 };
|
||||||
errText = new String[] { "", "" };
|
errText = new String[] { "", "" };
|
||||||
name = "";
|
name = "";
|
||||||
|
ranges = new int[0][];
|
||||||
tokens = new Token[0];
|
tokens = new Token[0];
|
||||||
this.vue = vue;
|
this.vue = vue;
|
||||||
}
|
}
|
||||||
|
@ -378,8 +390,8 @@ public class Breakpoint {
|
||||||
// Produce a string representation of the internal address ranges
|
// Produce a string representation of the internal address ranges
|
||||||
public String test() {
|
public String test() {
|
||||||
var ret = new StringBuilder();
|
var ret = new StringBuilder();
|
||||||
for (int x = 0; x < addresses.length; x++) {
|
for (int x = 0; x < ranges.length; x++) {
|
||||||
var range = addresses[x];
|
var range = ranges[x];
|
||||||
if (x > 0)
|
if (x > 0)
|
||||||
ret.append("\n");
|
ret.append("\n");
|
||||||
ret.append(String.format("%08X", range[0]));
|
ret.append(String.format("%08X", range[0]));
|
||||||
|
@ -396,7 +408,7 @@ public class Breakpoint {
|
||||||
|
|
||||||
// Retrieve the most recent input addresses
|
// Retrieve the most recent input addresses
|
||||||
public String getAddresses() {
|
public String getAddresses() {
|
||||||
return addressText;
|
return addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the most recent input condition
|
// Retrieve the most recent input condition
|
||||||
|
@ -419,6 +431,26 @@ public class Breakpoint {
|
||||||
return type < 0 || type > 1 ? null : errText[type];
|
return type < 0 || type > 1 ? null : errText[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve whether the breakpoint hooks exceptions
|
||||||
|
public boolean getException() {
|
||||||
|
return (hooks & EXCEPTION) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve whether the breakpoint hooks executions
|
||||||
|
public boolean getExecute() {
|
||||||
|
return (hooks & EXECUTE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve whether the breakpoint hooks reads
|
||||||
|
public boolean getRead() {
|
||||||
|
return (hooks & READ) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve whether the breakpoint hooks writes
|
||||||
|
public boolean getWrite() {
|
||||||
|
return (hooks & WRITE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve the display name
|
// Retrieve the display name
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
|
@ -438,7 +470,7 @@ public class Breakpoint {
|
||||||
int x = 0; // Input position
|
int x = 0; // Input position
|
||||||
|
|
||||||
// Configure instance fields
|
// Configure instance fields
|
||||||
addressText = addresses == null ? addresses = "" : addresses;
|
this.addresses = addresses == null ? addresses = "" : addresses;
|
||||||
|
|
||||||
// Parse the input
|
// Parse the input
|
||||||
var chars = (addresses + " ").toCharArray();
|
var chars = (addresses + " ").toCharArray();
|
||||||
|
@ -485,7 +517,7 @@ public class Breakpoint {
|
||||||
errPosition[ADDRESS] = start + 1;
|
errPosition[ADDRESS] = start + 1;
|
||||||
errText [ADDRESS] = text;
|
errText [ADDRESS] = text;
|
||||||
if (vue != null)
|
if (vue != null)
|
||||||
vue.update(this);
|
vue.updateRanges(this);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -535,7 +567,7 @@ public class Breakpoint {
|
||||||
errPosition[ADDRESS] = x + 1;
|
errPosition[ADDRESS] = x + 1;
|
||||||
errText [ADDRESS] = Character.toString(c);
|
errText [ADDRESS] = Character.toString(c);
|
||||||
if (vue != null)
|
if (vue != null)
|
||||||
vue.update(this);
|
vue.updateRanges(this);
|
||||||
return false;
|
return false;
|
||||||
} // x
|
} // x
|
||||||
|
|
||||||
|
@ -545,7 +577,7 @@ public class Breakpoint {
|
||||||
errPosition[ADDRESS] = x + 1;
|
errPosition[ADDRESS] = x + 1;
|
||||||
errText [ADDRESS] = "";
|
errText [ADDRESS] = "";
|
||||||
if (vue != null)
|
if (vue != null)
|
||||||
vue.update(this);
|
vue.updateRanges(this);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,13 +586,12 @@ public class Breakpoint {
|
||||||
ranges.add(new int[] { first, first });
|
ranges.add(new int[] { first, first });
|
||||||
|
|
||||||
// Parsing was successful
|
// Parsing was successful
|
||||||
this.addresses = ranges.toArray(new int[ranges.size()][]);
|
this.ranges = ranges.toArray(new int[ranges.size()][]);
|
||||||
this.addressText = addresses;
|
|
||||||
errCode [ADDRESS] = NONE;
|
errCode [ADDRESS] = NONE;
|
||||||
errPosition[ADDRESS] = 0;
|
errPosition[ADDRESS] = 0;
|
||||||
errText [ADDRESS] = "";
|
errText [ADDRESS] = "";
|
||||||
if (vue != null)
|
if (vue != null)
|
||||||
vue.update(this);
|
vue.updateRanges(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,7 +609,7 @@ public class Breakpoint {
|
||||||
var tokens = parse();
|
var tokens = parse();
|
||||||
if (tokens == null || !validate(tokens)) {
|
if (tokens == null || !validate(tokens)) {
|
||||||
if (vue != null)
|
if (vue != null)
|
||||||
vue.update(this);
|
vue.updateTokens(this);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
tree(tokens);
|
tree(tokens);
|
||||||
|
@ -586,7 +617,7 @@ public class Breakpoint {
|
||||||
// The expression is empty
|
// The expression is empty
|
||||||
if (tokens.size() == 0) {
|
if (tokens.size() == 0) {
|
||||||
if (vue != null)
|
if (vue != null)
|
||||||
vue.update(this);
|
vue.updateTokens(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,15 +647,48 @@ public class Breakpoint {
|
||||||
|
|
||||||
// The expression was successfully parsed
|
// The expression was successfully parsed
|
||||||
if (vue != null)
|
if (vue != null)
|
||||||
vue.update(this);
|
vue.updateTokens(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Specify whether the breakpoint is enabled
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
isEnabled = enabled;
|
||||||
|
if (vue != null)
|
||||||
|
vue.updateState(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify whether the breakpoint hooks exceptions
|
||||||
|
public void setException(boolean hook) {
|
||||||
|
hooks = hook ? hooks | EXCEPTION : hooks & ~EXCEPTION;
|
||||||
|
if (vue != null)
|
||||||
|
vue.updateState(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify whether the breakpoint hooks executions
|
||||||
|
public void setExecute(boolean hook) {
|
||||||
|
hooks = hook ? hooks | EXECUTE : hooks & ~EXECUTE;
|
||||||
|
if (vue != null)
|
||||||
|
vue.updateState(this);
|
||||||
|
}
|
||||||
|
|
||||||
// Specify the display name
|
// Specify the display name
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name == null ? "" : name;
|
this.name = name == null ? "" : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify whether the breakpoint hooks reads
|
||||||
|
public void setRead(boolean hook) {
|
||||||
|
hooks = hook ? hooks | READ : hooks & ~READ;
|
||||||
if (vue != null)
|
if (vue != null)
|
||||||
vue.update(this);
|
vue.updateState(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify whether the breakpoint hooks reads
|
||||||
|
public void setWrite(boolean hook) {
|
||||||
|
hooks = hook ? hooks | WRITE : hooks & ~WRITE;
|
||||||
|
if (vue != null)
|
||||||
|
vue.updateState(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -634,7 +698,7 @@ public class Breakpoint {
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Evaluate the condition for a Java emulation context
|
// Evaluate the condition for a Java emulation context
|
||||||
boolean evaluate(JavaVue vue, int[] stack) {
|
boolean evaluate(int[] stack) {
|
||||||
|
|
||||||
// The condition is empty
|
// The condition is empty
|
||||||
if (tokens.length == 0)
|
if (tokens.length == 0)
|
||||||
|
@ -647,9 +711,9 @@ public class Breakpoint {
|
||||||
case BINARY: size =
|
case BINARY: size =
|
||||||
evalBinary(tok.id, stack, size); continue;
|
evalBinary(tok.id, stack, size); continue;
|
||||||
case SYMBOL: size =
|
case SYMBOL: size =
|
||||||
evalSymbol(vue, tok.id, stack, size); continue;
|
evalSymbol(tok.id, stack, size); continue;
|
||||||
case UNARY :
|
case UNARY :
|
||||||
evalUnary (vue, tok.id, stack, size); continue;
|
evalUnary (tok.id, stack, size); continue;
|
||||||
}
|
}
|
||||||
stack[size++] = tok.type;
|
stack[size++] = tok.type;
|
||||||
stack[size++] = tok.value;
|
stack[size++] = tok.value;
|
||||||
|
@ -676,6 +740,43 @@ public class Breakpoint {
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Produce a one-dimensional array from the address ranges
|
||||||
|
int[] flattenRanges() {
|
||||||
|
var ret = new int[ranges.length * 2];
|
||||||
|
for (int x = 0; x < ranges.length; x++) {
|
||||||
|
var range = ranges[x];
|
||||||
|
ret[x / 2 ] = range[0];
|
||||||
|
ret[x / 2 + 1] = range[1];
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce a one-dimensional array from the condition tokens
|
||||||
|
int[] flattenTokens() {
|
||||||
|
var ret = new int[tokens.length * 2];
|
||||||
|
for (int x = 0; x < tokens.length; x++) {
|
||||||
|
var token = tokens[x];
|
||||||
|
ret[x / 2 ] = token.type;
|
||||||
|
ret[x / 2 + 1] = token.flatten();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the bit mask for enabled hooks
|
||||||
|
int getHooks() {
|
||||||
|
return hooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the list of address ranges
|
||||||
|
int[][] getRanges() {
|
||||||
|
return ranges;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the list of condition tokens
|
||||||
|
Token[] getTokens() {
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
// The breakpoint is being removed from its emulation context
|
// The breakpoint is being removed from its emulation context
|
||||||
void remove() {
|
void remove() {
|
||||||
vue = null;
|
vue = null;
|
||||||
|
@ -1155,8 +1256,9 @@ public class Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate a functional symbol
|
// Evaluate a functional symbol
|
||||||
private static int evalSymbol(JavaVue vue, int id, int[] stack, int size) {
|
private int evalSymbol(int id, int[] stack, int size) {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
var vue = (JavaVue) this.vue;
|
||||||
if (id == Vue.PC)
|
if (id == Vue.PC)
|
||||||
ret = vue.cpu.pc;
|
ret = vue.cpu.pc;
|
||||||
else if (id >= 200)
|
else if (id >= 200)
|
||||||
|
@ -1166,20 +1268,20 @@ public class Breakpoint {
|
||||||
else if (vue.cpu.stage == CPU.EXCEPTION && id == CODE)
|
else if (vue.cpu.stage == CPU.EXCEPTION && id == CODE)
|
||||||
ret = vue.cpu.exception.code;
|
ret = vue.cpu.exception.code;
|
||||||
else if (vue.cpu.stage == CPU.EXECUTE) switch (id) {
|
else if (vue.cpu.stage == CPU.EXECUTE) switch (id) {
|
||||||
case ADDRESS : ret = evalAddress (vue); break;
|
case ADDRESS : ret = evalAddress (); break;
|
||||||
case COND : ret = evalCond (vue); break;
|
case COND : ret = evalCond (); break;
|
||||||
case DISP : ret = evalDisp (vue); break;
|
case DISP : ret = evalDisp (); break;
|
||||||
case FORMAT : ret = vue.cpu.inst.format; break;
|
case FORMAT : ret = vue.cpu.inst.format; break;
|
||||||
case ID : ret = vue.cpu.inst.id ; break;
|
case ID : ret = vue.cpu.inst.id ; break;
|
||||||
case IMM : ret = evalImm (vue); break;
|
case IMM : ret = evalImm (); break;
|
||||||
case OPCODE : ret = vue.cpu.inst.opcode; break;
|
case OPCODE : ret = vue.cpu.inst.opcode; break;
|
||||||
case REG1 : ret = evalReg1 (vue); break;
|
case REG1 : ret = evalReg1 (); break;
|
||||||
case REG2 : ret = evalReg2 (vue); break;
|
case REG2 : ret = evalReg2 (); break;
|
||||||
case REGID : ret = evalRegId (vue); break;
|
case REGID : ret = evalRegId (); break;
|
||||||
case SIZE : ret = vue.cpu.inst.size ; break;
|
case SIZE : ret = vue.cpu.inst.size ; break;
|
||||||
case SUBOPCODE: ret = evalSubopcode (vue); break;
|
case SUBOPCODE: ret = evalSubopcode (); break;
|
||||||
case VALUE : ret = evalValue (vue); break;
|
case VALUE : ret = evalValue (); break;
|
||||||
case VECTOR : ret = evalVector (vue); break;
|
case VECTOR : ret = evalVector (); break;
|
||||||
}
|
}
|
||||||
stack[size++] = WORD;
|
stack[size++] = WORD;
|
||||||
stack[size++] = ret;
|
stack[size++] = ret;
|
||||||
|
@ -1187,7 +1289,7 @@ public class Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate a unary operator
|
// Evaluate a unary operator
|
||||||
private static void evalUnary(JavaVue vue, int id, int[] stack, int size) {
|
private void evalUnary(int id, int[] stack, int size) {
|
||||||
int type = stack[size - 2];
|
int type = stack[size - 2];
|
||||||
int value = stack[size - 1];
|
int value = stack[size - 1];
|
||||||
boolean isWord = type == WORD;
|
boolean isWord = type == WORD;
|
||||||
|
@ -1201,7 +1303,7 @@ public class Breakpoint {
|
||||||
case FLOAT : value = evalFloat (isWord, value);
|
case FLOAT : value = evalFloat (isWord, value);
|
||||||
type = FLOAT; break;
|
type = FLOAT; break;
|
||||||
case FLOOR : value = evalFloor (isWord, value); break;
|
case FLOOR : value = evalFloor (isWord, value); break;
|
||||||
case READ_WORD : value = evalReadWord (isWord, value, vue);
|
case READ_WORD : value = evalReadWord (isWord, value);
|
||||||
type = WORD ; break;
|
type = WORD ; break;
|
||||||
case ROUND : value = evalRound (isWord, value); break;
|
case ROUND : value = evalRound (isWord, value); break;
|
||||||
case TRUNC : value = evalTrunc (isWord, value); break;
|
case TRUNC : value = evalTrunc (isWord, value); break;
|
||||||
|
@ -1414,7 +1516,8 @@ public class Breakpoint {
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Evaluate address
|
// Evaluate address
|
||||||
private static int evalAddress(JavaVue vue) {
|
private int evalAddress() {
|
||||||
|
var vue = (JavaVue) this.vue;
|
||||||
if (vue.cpu.inst.format == 6)
|
if (vue.cpu.inst.format == 6)
|
||||||
return vue.cpu.program[vue.cpu.inst.reg1] + vue.cpu.inst.disp;
|
return vue.cpu.program[vue.cpu.inst.reg1] + vue.cpu.inst.disp;
|
||||||
switch (vue.cpu.inst.id) {
|
switch (vue.cpu.inst.id) {
|
||||||
|
@ -1429,7 +1532,8 @@ public class Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate cond
|
// Evaluate cond
|
||||||
private static int evalCond(JavaVue vue) {
|
private int evalCond() {
|
||||||
|
var vue = (JavaVue) this.vue;
|
||||||
switch (vue.cpu.inst.id) {
|
switch (vue.cpu.inst.id) {
|
||||||
case Vue.BCOND: return vue.cpu.inst.cond;
|
case Vue.BCOND: return vue.cpu.inst.cond;
|
||||||
case Vue.SETF : return vue.cpu.inst.imm;
|
case Vue.SETF : return vue.cpu.inst.imm;
|
||||||
|
@ -1438,7 +1542,8 @@ public class Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate disp
|
// Evaluate disp
|
||||||
private static int evalDisp(JavaVue vue) {
|
private int evalDisp() {
|
||||||
|
var vue = (JavaVue) this.vue;
|
||||||
switch (vue.cpu.inst.format) {
|
switch (vue.cpu.inst.format) {
|
||||||
case 3: case 4: case 6: return vue.cpu.inst.disp;
|
case 3: case 4: case 6: return vue.cpu.inst.disp;
|
||||||
}
|
}
|
||||||
|
@ -1446,7 +1551,8 @@ public class Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate imm
|
// Evaluate imm
|
||||||
private static int evalImm(JavaVue vue) {
|
private int evalImm() {
|
||||||
|
var vue = (JavaVue) this.vue;
|
||||||
switch (vue.cpu.inst.format) {
|
switch (vue.cpu.inst.format) {
|
||||||
case 2: case 5: return vue.cpu.inst.imm;
|
case 2: case 5: return vue.cpu.inst.imm;
|
||||||
}
|
}
|
||||||
|
@ -1454,7 +1560,8 @@ public class Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate reg1
|
// Evaluate reg1
|
||||||
private static int evalReg1(JavaVue vue) {
|
private int evalReg1() {
|
||||||
|
var vue = (JavaVue) this.vue;
|
||||||
switch (vue.cpu.inst.format) {
|
switch (vue.cpu.inst.format) {
|
||||||
case 1: case 5: case 6: case 7: return vue.cpu.inst.reg1;
|
case 1: case 5: case 6: case 7: return vue.cpu.inst.reg1;
|
||||||
}
|
}
|
||||||
|
@ -1462,7 +1569,8 @@ public class Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate reg2
|
// Evaluate reg2
|
||||||
private static int evalReg2(JavaVue vue) {
|
private int evalReg2() {
|
||||||
|
var vue = (JavaVue) this.vue;
|
||||||
switch (vue.cpu.inst.format) {
|
switch (vue.cpu.inst.format) {
|
||||||
case 1: case 2: case 5: case 6: case 7: return vue.cpu.inst.reg2;
|
case 1: case 2: case 5: case 6: case 7: return vue.cpu.inst.reg2;
|
||||||
}
|
}
|
||||||
|
@ -1470,7 +1578,8 @@ public class Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate regid
|
// Evaluate regid
|
||||||
private static int evalRegId(JavaVue vue) {
|
private int evalRegId() {
|
||||||
|
var vue = (JavaVue) this.vue;
|
||||||
switch (vue.cpu.inst.id) {
|
switch (vue.cpu.inst.id) {
|
||||||
case Vue.LDSR: case Vue.STSR: return vue.cpu.inst.imm;
|
case Vue.LDSR: case Vue.STSR: return vue.cpu.inst.imm;
|
||||||
}
|
}
|
||||||
|
@ -1478,7 +1587,8 @@ public class Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate subopcode
|
// Evaluate subopcode
|
||||||
private static int evalSubopcode(JavaVue vue) {
|
private int evalSubopcode() {
|
||||||
|
var vue = (JavaVue) this.vue;
|
||||||
switch (vue.cpu.inst.opcode) {
|
switch (vue.cpu.inst.opcode) {
|
||||||
case 0x1F: return vue.cpu.inst.imm;
|
case 0x1F: return vue.cpu.inst.imm;
|
||||||
case 0x3E: return vue.cpu.inst.subopcode;
|
case 0x3E: return vue.cpu.inst.subopcode;
|
||||||
|
@ -1487,12 +1597,14 @@ public class Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate value
|
// Evaluate value
|
||||||
private static int evalValue(JavaVue vue) {
|
private int evalValue() {
|
||||||
|
var vue = (JavaVue) this.vue;
|
||||||
return vue.cpu.inst.format == 6 ? vue.cpu.access.value : 0;
|
return vue.cpu.inst.format == 6 ? vue.cpu.access.value : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate vector
|
// Evaluate vector
|
||||||
private static int evalVector(JavaVue vue) {
|
private int evalVector() {
|
||||||
|
var vue = (JavaVue) this.vue;
|
||||||
return vue.cpu.inst.id == Vue.TRAP ? vue.cpu.inst.imm : 0;
|
return vue.cpu.inst.id == Vue.TRAP ? vue.cpu.inst.imm : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1533,7 +1645,8 @@ public class Breakpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate []
|
// Evaluate []
|
||||||
private static int evalReadWord(boolean isWord, int value, JavaVue vue) {
|
private int evalReadWord(boolean isWord, int value) {
|
||||||
|
var vue = (JavaVue) this.vue;
|
||||||
return vue.read(isWord ? value : toWord(asFloat(value)), Vue.S32);
|
return vue.read(isWord ? value : toWord(asFloat(value)), Vue.S32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,19 +22,27 @@ static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
|
||||||
// Types //
|
// Types //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Breakpoint token
|
// Breakpoint address range
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t type;
|
int32_t start; // First address, inclusive
|
||||||
int32_t value;
|
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
|
||||||
} 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 numAddresses; // Number of address ranges
|
int32_t hooks; // Events hooked by the breakpoint
|
||||||
int32_t numCondition; // Number of tokens in condition
|
int32_t numRanges; // Number of address ranges
|
||||||
int32_t *addresses; // Address ranges
|
int32_t numTokens; // Number of condition tokens
|
||||||
Token *condition; // Condition tokens in RPN order
|
Range *ranges; // Address ranges
|
||||||
|
Token *tokens; // Condition tokens in RPN order
|
||||||
} Breakpoint;
|
} Breakpoint;
|
||||||
|
|
||||||
// Core context state type
|
// Core context state type
|
||||||
|
@ -67,7 +75,7 @@ typedef struct {
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Method Functions //
|
// Public Methods //
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Native constructor
|
// Native constructor
|
||||||
|
@ -84,6 +92,17 @@ JNIEXPORT jlong JNICALL Java_vue_NativeVue_construct
|
||||||
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) {
|
||||||
Core *core = *(Core **)&handle;
|
Core *core = *(Core **)&handle;
|
||||||
|
Breakpoint *brk; // Handle to breakpoint
|
||||||
|
int x; // Iterator
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete core
|
||||||
if (core->breakpoints != NULL) free(core->breakpoints);
|
if (core->breakpoints != NULL) free(core->breakpoints);
|
||||||
if (core->stack != NULL) free(core->stack );
|
if (core->stack != NULL) free(core->stack );
|
||||||
if (core->vue.pak.rom != NULL) free(core->vue.pak.rom);
|
if (core->vue.pak.rom != NULL) free(core->vue.pak.rom);
|
||||||
|
@ -234,3 +253,108 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVue_writeBytes
|
||||||
(*env)->ReleaseByteArrayElements(env, data, elems, 0);
|
(*env)->ReleaseByteArrayElements(env, data, elems, 0);
|
||||||
return JNI_TRUE;
|
return JNI_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Private 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;
|
||||||
|
int32_t size; // Number of bytes to allocate
|
||||||
|
|
||||||
|
// Allocate memory for the breakpoint
|
||||||
|
core->numBreakpoints++;
|
||||||
|
size = core->numBreakpoints * sizeof (Breakpoint);
|
||||||
|
core->breakpoints = core->breakpoints == NULL ?
|
||||||
|
malloc(size) : realloc(core->breakpoints, size);
|
||||||
|
|
||||||
|
// Initialize the breakpoint
|
||||||
|
memset(&core->breakpoints[core->numBreakpoints-1], 0, sizeof (Breakpoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
|
||||||
|
// Delete the breakpoint
|
||||||
|
if (brk->ranges != NULL) free(brk->ranges);
|
||||||
|
if (brk->tokens != NULL) free(brk->tokens);
|
||||||
|
|
||||||
|
// Move all subsequent breakpoints forward
|
||||||
|
if (index != core->numBreakpoints - 1)
|
||||||
|
memmove(brk, &brk[1],
|
||||||
|
(core->numBreakpoints - index) * sizeof (Breakpoint));
|
||||||
|
core->numBreakpoints--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
jint *elems; // Array elements
|
||||||
|
int32_t size; // Number of bytes to allocate
|
||||||
|
|
||||||
|
// Determine the number of address ranges
|
||||||
|
brk->numRanges = (*env)->GetArrayLength(env, ranges) / 2;
|
||||||
|
if (brk->numRanges == 0) {
|
||||||
|
if (brk->ranges != NULL)
|
||||||
|
free(brk->ranges);
|
||||||
|
brk->ranges = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate memory for the address ranges
|
||||||
|
size = brk->numRanges * sizeof (Range);
|
||||||
|
brk->ranges = brk->ranges == NULL ?
|
||||||
|
malloc(size) : realloc(brk->ranges, size);
|
||||||
|
|
||||||
|
// Initialize address ranges
|
||||||
|
elems = (*env)->GetIntArrayElements(env, ranges, NULL);
|
||||||
|
memcpy(brk->ranges, elems, size);
|
||||||
|
(*env)->ReleaseIntArrayElements(env, ranges, elems, JNI_ABORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
Core *core = *(Core **)&handle;
|
||||||
|
Breakpoint *brk = &core->breakpoints[index];
|
||||||
|
jint *elems; // Array elements
|
||||||
|
int32_t size; // Number of bytes to allocate
|
||||||
|
|
||||||
|
// Determine the number of condition tokens
|
||||||
|
brk->numTokens = (*env)->GetArrayLength(env, tokens) / 2;
|
||||||
|
if (brk->numTokens == 0) {
|
||||||
|
if (brk->tokens != NULL)
|
||||||
|
free(brk->tokens);
|
||||||
|
brk->tokens = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate memory for the condition tokens
|
||||||
|
size = brk->numTokens * sizeof (Token);
|
||||||
|
brk->tokens = brk->tokens == NULL ?
|
||||||
|
malloc(size) : realloc(brk->tokens, size);
|
||||||
|
|
||||||
|
// Initialize condition tokens
|
||||||
|
elems = (*env)->GetIntArrayElements(env, tokens, NULL);
|
||||||
|
memcpy(brk->tokens, elems, size);
|
||||||
|
(*env)->ReleaseIntArrayElements(env, tokens, elems, JNI_ABORT);
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,13 @@ class NativeVue extends Vue {
|
||||||
// Public Methods //
|
// Public Methods //
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Produce a new breakpoint and add it to the collection
|
||||||
|
public Breakpoint breakpoint() {
|
||||||
|
var brk = super.breakpoint();
|
||||||
|
breakpoint(handle);
|
||||||
|
return brk;
|
||||||
|
}
|
||||||
|
|
||||||
// Release any used resources
|
// Release any used resources
|
||||||
private native void dispose(long handle);
|
private native void dispose(long handle);
|
||||||
public void dispose()
|
public void dispose()
|
||||||
|
@ -61,6 +68,15 @@ class NativeVue extends Vue {
|
||||||
public boolean readBytes(int address, byte[] dest, int offset, int length)
|
public boolean readBytes(int address, byte[] dest, int offset, int length)
|
||||||
{ return readBytes(handle, address, dest, offset, length); }
|
{ return readBytes(handle, address, dest, offset, length); }
|
||||||
|
|
||||||
|
// Remove a breakpoint from the collection
|
||||||
|
public boolean remove(Breakpoint brk) {
|
||||||
|
int index = breakpoints.indexOf(brk);
|
||||||
|
if (!super.remove(brk))
|
||||||
|
return false;
|
||||||
|
remove(handle, index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize all system components
|
// Initialize all system components
|
||||||
private native void reset(long handle);
|
private native void reset(long handle);
|
||||||
public void reset()
|
public void reset()
|
||||||
|
@ -100,4 +116,45 @@ class NativeVue extends Vue {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A breakpoint's address ranges have changed
|
||||||
|
void updateRanges(Breakpoint brk) {
|
||||||
|
super.updateRanges(brk);
|
||||||
|
updateRanges(handle, breakpoints.indexOf(brk), brk.flattenRanges());
|
||||||
|
}
|
||||||
|
|
||||||
|
// A breakpoint's enabled/hook state has changed
|
||||||
|
void updateState(Breakpoint brk) {
|
||||||
|
super.updateState(brk);
|
||||||
|
updateState(handle, breakpoints.indexOf(brk),
|
||||||
|
brk.isEnabled(), brk.getHooks());
|
||||||
|
}
|
||||||
|
|
||||||
|
// A breakpoint's condition tokens have changed
|
||||||
|
void updateTokens(Breakpoint brk) {
|
||||||
|
super.updateTokens(brk);
|
||||||
|
updateTokens(handle, breakpoints.indexOf(brk), brk.flattenTokens());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Private Methods //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Produce a new breakpoint and add it to the collection
|
||||||
|
private native void breakpoint(long handle);
|
||||||
|
|
||||||
|
// Remove a breakpoint from the collection
|
||||||
|
private native void remove(long handle, int index);
|
||||||
|
|
||||||
|
// A breakpoint's address ranges have changed
|
||||||
|
private native void updateRanges(long handle, int index, int[] ranges);
|
||||||
|
|
||||||
|
// A breakpoint's enabled/hook state has changed
|
||||||
|
private native void updateState(long handle, int index, boolean enabled,
|
||||||
|
int hooks);
|
||||||
|
|
||||||
|
// A breakpoint's condition tokens have changed
|
||||||
|
private native void updateTokens(long handle, int index, int[] tokens);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,8 +249,13 @@ public abstract class Vue {
|
||||||
// Evaluate the condition in a breakpoint
|
// Evaluate the condition in a breakpoint
|
||||||
abstract boolean evaluate(Breakpoint brk);
|
abstract boolean evaluate(Breakpoint brk);
|
||||||
|
|
||||||
// A breakpoint has been updated
|
// A breakpoint's address ranges have changed
|
||||||
void update(Breakpoint brk) {
|
void updateRanges(Breakpoint brk) { }
|
||||||
}
|
|
||||||
|
// A breakpoint's enabled/hook state has changed
|
||||||
|
void updateState(Breakpoint brk) { }
|
||||||
|
|
||||||
|
// A breakpoint's condition tokens have changed
|
||||||
|
void updateTokens(Breakpoint brk) { }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue