Preliminary breakpoint registration

This commit is contained in:
Guy Perfect 2020-10-13 20:15:27 -05:00
parent f893ecd2cf
commit 2d048a74c0
7 changed files with 334 additions and 107 deletions

View File

@ -7,10 +7,10 @@ include_windows = jni-windows-include
# Default goal # Default goal
.PHONY: default .PHONY: default
default: default:
@echo @echo $(include_linux)
@echo "Planet Virtual Boy Emulator" @echo "Planet Virtual Boy Emulator"
@echo " https://www.planetvb.com/" @echo " https://www.planetvb.com/"
@echo " August 14, 2020" @echo " October 13, 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"

View File

@ -51,15 +51,30 @@ public class Main {
// Begin application operations // Begin application operations
new App(useNative); new App(useNative);
/*
var brk = new Breakpoint(); var brk = new Breakpoint(null);
String exp = "([sp - 8] & 3) << 6 != 12.0 + r6 * ecr && [0x0500008C + r8] == 0";
System.out.println(exp); String exp =
"([sp - 8] & 3) << 6 != 12.0 + r6 * ecr && [0x0500008C + r8] == 0";
System.out.println("\n" + exp);
if (brk.setCondition(exp)) if (brk.setCondition(exp))
System.out.println(brk.debug()); System.out.println(brk.debug());
else System.out.println(brk.getErrorCode() + "\t" + else System.out.println(
brk.getErrorPosition() + ":" + brk.getErrorText()); brk.getErrorCode (Breakpoint.CONDITION) + "\t" +
*/ brk.getErrorPosition(Breakpoint.CONDITION) + ":" +
brk.getErrorText (Breakpoint.CONDITION)
);
exp = "123, 456 - 987 , f0-fffffff2";
System.out.println("\n" + exp);
if (brk.setAddresses(exp))
System.out.println(brk.test());
else System.out.println(
brk.getErrorCode (Breakpoint.ADDRESS) + "\t" +
brk.getErrorPosition(Breakpoint.ADDRESS) + ":" +
brk.getErrorText (Breakpoint.ADDRESS)
);
} }
} }

View File

@ -8,13 +8,15 @@ public class Breakpoint {
// Instance fields // Instance fields
private int[][] addresses; // Applicable address ranges private int[][] addresses; // Applicable address ranges
private String addressText; // Un-processed address ranges
private String condition; // Un-processed condition private String condition; // Un-processed condition
private int errCode; // Condition error code private int[] errCode; // Error codes
private int errPosition; // Character of condition error private int[] errPosition; // Character position of errors
private String errText; // Offending condition text private String[] errText; // Offending error text
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 Token[] tokens; // Evaluation tokens
private Vue vue; // Containing emulation context
@ -23,6 +25,7 @@ public class Breakpoint {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Error codes // Error codes
public static final int BADTYPE = -1;
public static final int NONE = 0; public static final int NONE = 0;
public static final int BADLITERAL = 1; public static final int BADLITERAL = 1;
public static final int BADTOKEN = 2; public static final int BADTOKEN = 2;
@ -32,6 +35,10 @@ public class Breakpoint {
public static final int NESTING = 6; public static final int NESTING = 6;
public static final int UNEXPECTED = 7; public static final int UNEXPECTED = 7;
// Error types
public static final int ADDRESS = 0;
public static final int CONDITION = 1;
// 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;
@ -91,7 +98,7 @@ public class Breakpoint {
private static final HashMap<String, Integer> SYMDEFS; private static final HashMap<String, Integer> SYMDEFS;
// Functional symbol IDs // Functional symbol IDs
private static final int ADDRESS = 0; //private static final int ADDRESS = 0; // Same as error type
private static final int CODE = 1; private static final int CODE = 1;
private static final int COND = 2; private static final int COND = 2;
private static final int DISP = 3; private static final int DISP = 3;
@ -298,14 +305,16 @@ public class Breakpoint {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Default constructor // Default constructor
public Breakpoint() { public Breakpoint(Vue vue) {
addresses = new int[0][]; addresses = new int[0][];
addressText = "";
condition = ""; condition = "";
errCode = NONE; errCode = new int [] { NONE, NONE };
errPosition = 0; errPosition = new int [] { 0, 0 };
errText = ""; errText = new String[] { "", "" };
name = ""; name = "";
tokens = new Token[0]; tokens = new Token[0];
this.vue = vue;
} }
@ -366,24 +375,48 @@ public class Breakpoint {
return ret.toString(); return ret.toString();
} }
// Produce a string representation of the internal address ranges
public String test() {
var ret = new StringBuilder();
for (int x = 0; x < addresses.length; x++) {
var range = addresses[x];
if (x > 0)
ret.append("\n");
ret.append(String.format("%08X", range[0]));
if (range[0] != range[1])
ret.append(String.format("-%08X", range[1]));
}
return ret.toString();
}
// Evaluate the condition against its emulation context
public boolean evaluate() {
return vue == null ? false : vue.evaluate(this);
}
// Retrieve the most recent input addresses
public String getAddresses() {
return addressText;
}
// Retrieve the most recent input condition // Retrieve the most recent input condition
public String getCondition() { public String getCondition() {
return condition; return condition;
} }
// Retrieve the most recent error code // Retrieve the most recent error code
public int getErrorCode() { public int getErrorCode(int type) {
return errCode; return type < 0 || type > 1 ? BADTYPE : errCode[type];
} }
// Retrieve the most recent error character position // Retrieve the most recent error character position
public int getErrorPosition() { public int getErrorPosition(int type) {
return errPosition; return type < 0 || type > 1 ? BADTYPE : errPosition[type];
} }
// Retrieve the most recent error text // Retrieve the most recent error text
public String getErrorText() { public String getErrorText(int type) {
return errText; return type < 0 || type > 1 ? null : errText[type];
} }
// Retrieve the display name // Retrieve the display name
@ -397,24 +430,137 @@ public class Breakpoint {
} }
// Specify and parse a list of address ranges // Specify and parse a list of address ranges
public boolean setAddresses(int[][] addresses) { public boolean setAddresses(String addresses) {
Integer first = null; // Value of first address
int mode = 0; // Processing state
var ranges = new ArrayList<int[]>(); // Output
int start = 0; // Position of address literal
int x = 0; // Input position
// Error checking // Configure instance fields
if (addresses == null) addressText = addresses == null ? addresses = "" : addresses;
return false;
for (var range : addresses)
if (range == null || range.length > 2)
return false;
// Accept the new address ranges // Parse the input
this.addresses = new int[addresses.length][2]; var chars = (addresses + " ").toCharArray();
for (int x = 0; x < addresses.length; x++) { for (x = 0; x < chars.length; x++) {
var range = addresses[x]; char c = chars[x];
this.addresses[x] = new int[] { boolean white = c == ' ' || c == '\t';
range[0], boolean digit =
range[range.length - 1] c >= '0' && c <= '9' ||
}; c >= 'A' && c <= 'F' ||
c >= 'a' && c <= 'f'
;
// Before
if (mode == 0) {
// Ignore whitespace
if (white)
continue;
// Begin an address
if (digit) {
start = x;
mode = 1; // During
continue;
} }
}
// During
else if (mode == 1) {
// Ignore digits
if (digit)
continue;
// The end of the address has been found
String text = new String(chars, start, x - start);
int addr = 0;
try { addr = (int) Long.parseLong(text, 16); }
// Could not parse the address
catch (Exception e) {
errCode [ADDRESS] = BADLITERAL;
errPosition[ADDRESS] = start + 1;
errText [ADDRESS] = text;
if (vue != null)
vue.update(this);
return false;
}
// Processed the first address
if (first == null)
first = addr;
// Processed the second address
else {
ranges.add(new int[] { first, addr });
first = null;
}
// Begin searching for a delimiter
mode = 2; // After
x--;
continue;
}
// After
else if (mode == 2) {
// Ignore whitespace
if (white)
continue;
// Begin searching for the second address
if (c == '-' && first != null) {
mode = 0; // Before
continue;
}
// All addresses in the range have been processed
if (c == ',') {
if (first != null) {
ranges.add(new int[] { first, first });
first = null;
}
mode = 0; // Before
continue;
}
}
// Invalid character
errCode [ADDRESS] = UNEXPECTED;
errPosition[ADDRESS] = x + 1;
errText [ADDRESS] = Character.toString(c);
if (vue != null)
vue.update(this);
return false;
} // x
// Unexpected end of input
if (mode == 0 && ranges.size() > 0) {
errCode [ADDRESS] = EARLYEOF;
errPosition[ADDRESS] = x + 1;
errText [ADDRESS] = "";
if (vue != null)
vue.update(this);
return false;
}
// A one-address range was processed
if (first != null)
ranges.add(new int[] { first, first });
// Parsing was successful
this.addresses = ranges.toArray(new int[ranges.size()][]);
this.addressText = addresses;
errCode [ADDRESS] = NONE;
errPosition[ADDRESS] = 0;
errText [ADDRESS] = "";
if (vue != null)
vue.update(this);
return true; return true;
} }
@ -423,20 +569,26 @@ public class Breakpoint {
// Configure instance fields // Configure instance fields
this.condition = condition == null ? condition = "" : condition; this.condition = condition == null ? condition = "" : condition;
errCode = NONE; errCode [CONDITION] = NONE;
errPosition = 0; errPosition[CONDITION] = 0;
errText = ""; errText [CONDITION] = "";
tokens = new Token[0]; tokens = new Token[0];
// Process the expression // Process the expression
var tokens = parse(); var tokens = parse();
if (tokens == null || !validate(tokens)) if (tokens == null || !validate(tokens)) {
if (vue != null)
vue.update(this);
return false; return false;
}
tree(tokens); tree(tokens);
// The expression is empty // The expression is empty
if (tokens.size() == 0) if (tokens.size() == 0) {
if (vue != null)
vue.update(this);
return true; return true;
}
// Produce an RPN-ordered list of tokens // Produce an RPN-ordered list of tokens
var tok = tokens.remove(0); var tok = tokens.remove(0);
@ -463,12 +615,16 @@ public class Breakpoint {
this.tokens = tokens.toArray(new Token[tokens.size()]); this.tokens = tokens.toArray(new Token[tokens.size()]);
// The expression was successfully parsed // The expression was successfully parsed
if (vue != null)
vue.update(this);
return true; return true;
} }
// 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;
if (vue != null)
vue.update(this);
} }
@ -477,12 +633,12 @@ public class Breakpoint {
// Package Methods // // Package Methods //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Evaluate the condition for an emulation context // Evaluate the condition for a Java emulation context
boolean evaluate(JavaVue vue, int[] stack) { boolean evaluate(JavaVue vue, int[] stack) {
// The condition is empty // The condition is empty
if (tokens.length == 0) if (tokens.length == 0)
return isEnabled && errCode == NONE; return isEnabled && errCode[CONDITION] == NONE;
// Process tokens // Process tokens
int size = 0; int size = 0;
@ -505,7 +661,7 @@ public class Breakpoint {
int depth() { int depth() {
// Error checking // Error checking
if (errCode != NONE) if (errCode[CONDITION] != NONE)
return 0; return 0;
// Count the maximum size of the stack // Count the maximum size of the stack
@ -520,6 +676,11 @@ public class Breakpoint {
return max; return max;
} }
// The breakpoint is being removed from its emulation context
void remove() {
vue = null;
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -586,9 +747,9 @@ public class Breakpoint {
// "x" cannot appear here // "x" cannot appear here
if (isFloat || x != start + 1 || chars[start] != '0') { if (isFloat || x != start + 1 || chars[start] != '0') {
errCode = UNEXPECTED; errCode [CONDITION] = UNEXPECTED;
errPosition = x + 1; errPosition[CONDITION] = x + 1;
errText = Character.toString(c); errText [CONDITION] = Character.toString(c);
return null; return null;
} }
@ -602,9 +763,9 @@ public class Breakpoint {
// "." cannot appear here // "." cannot appear here
if (isHex || isFloat) { if (isHex || isFloat) {
errCode = UNEXPECTED; errCode [CONDITION] = UNEXPECTED;
errPosition = x + 1; errPosition[CONDITION] = x + 1;
errText = Character.toString(c); errText [CONDITION] = Character.toString(c);
return null; return null;
} }
@ -637,9 +798,9 @@ public class Breakpoint {
// Could not parse the value // Could not parse the value
catch (Exception e) { catch (Exception e) {
errCode = BADLITERAL; errCode [CONDITION] = BADLITERAL;
errPosition = x + 1; errPosition[CONDITION] = x + 1;
errText = ret.text; errText [CONDITION] = ret.text;
return null; return null;
} }
@ -684,9 +845,9 @@ public class Breakpoint {
} }
// The operator was not identified // The operator was not identified
errCode = BADTOKEN; errCode [CONDITION] = BADTOKEN;
errPosition = start + 1; errPosition[CONDITION] = start + 1;
errText = Character.toString(chars[start]); errText [CONDITION] = Character.toString(chars[start]);
return null; return null;
} // x } // x
@ -739,9 +900,9 @@ public class Breakpoint {
} }
// The token is not recognized // The token is not recognized
errCode = BADTOKEN; errCode [CONDITION] = BADTOKEN;
errPosition = x + 1; errPosition[CONDITION] = x + 1;
errText = ret.text; errText [CONDITION] = ret.text;
return null; return null;
} // x } // x
@ -830,9 +991,9 @@ public class Breakpoint {
// The token is invalid // The token is invalid
if (tok.id != NEGATE) { if (tok.id != NEGATE) {
errCode = INVALID; errCode [CONDITION] = INVALID;
errPosition = tok.start; errPosition[CONDITION] = tok.start;
errText = tok.text; errText [CONDITION] = tok.text;
return false; return false;
} }
@ -845,9 +1006,9 @@ public class Breakpoint {
// Nesting error // Nesting error
if (tok.type == CLOSE && if (tok.type == CLOSE &&
(stack.empty() || stack.pop().id != tok.id)) { (stack.empty() || stack.pop().id != tok.id)) {
errCode = NESTING; errCode [CONDITION] = NESTING;
errPosition = tok.start; errPosition[CONDITION] = tok.start;
errText = tok.text; errText [CONDITION] = tok.text;
return false; return false;
} }
@ -862,9 +1023,9 @@ public class Breakpoint {
// A group was not closed // A group was not closed
if (!stack.empty()) { if (!stack.empty()) {
errCode = EARLYEOF; errCode [CONDITION] = EARLYEOF;
errPosition = condition.length(); errPosition[CONDITION] = condition.length();
errText = stack.pop().text; errText [CONDITION] = stack.pop().text;
} }
// Successfully parsed the condition // Successfully parsed the condition

View File

@ -87,12 +87,6 @@ class JavaVue extends Vue {
return Math.max(0, maxCycles); return Math.max(0, maxCycles);
} }
// Evaluate the condition in a breakpoint
public boolean evaluate(Breakpoint brk) {
return brk != null ? false :
brk.evaluate(this, new int[brk.depth() * 2]);
}
// Retrieve the application break code // Retrieve the application break code
public int getBreakCode() { public int getBreakCode() {
return breakCode; return breakCode;
@ -249,6 +243,11 @@ class JavaVue extends Vue {
// Package Methods // // Package Methods //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Evaluate the condition in a breakpoint
boolean evaluate(Breakpoint brk) {
return false;
}
// Exception break handler // Exception break handler
int onException(Ecxeption exp) { int onException(Ecxeption exp) {
return 0; return 0;

View File

@ -84,10 +84,10 @@ 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;
if (core->vue.pak.rom != NULL) if (core->breakpoints != NULL) free(core->breakpoints);
free(core->vue.pak.rom); if (core->stack != NULL) free(core->stack );
if (core->vue.pak.ram != NULL) if (core->vue.pak.rom != NULL) free(core->vue.pak.rom);
free(core->vue.pak.ram); if (core->vue.pak.ram != NULL) free(core->vue.pak.ram);
free(core); free(core);
} }
@ -98,13 +98,6 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_emulate
return vueEmulate(&core->vue, maxCycles); return vueEmulate(&core->vue, maxCycles);
} }
// Evaluate the condition in a breakpoint
JNIEXPORT jboolean JNICALL Java_vue_NativeVue_evaluate
(JNIEnv *env, jobject vue, jlong handle, jobject brk) {
Core *core = *(Core **)&handle;
return JNI_FALSE;
}
// Retrieve the application break code // Retrieve the application break code
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode
(JNIEnv *env, jobject vue, jlong handle) { (JNIEnv *env, jobject vue, jlong handle) {

View File

@ -34,11 +34,6 @@ class NativeVue extends Vue {
public int emulate(int maxCycles) public int emulate(int maxCycles)
{ return emulate(handle, maxCycles); } { return emulate(handle, maxCycles); }
// Evaluate the condition in a breakpoint
private native boolean evaluate(long handle, Breakpoint brk);
public boolean evaluate(Breakpoint brk)
{ return evaluate(handle, brk); }
// Retrieve the application break code // Retrieve the application break code
private native int getBreakCode(long handle); private native int getBreakCode(long handle);
public int getBreakCode() { return getBreakCode(handle); } public int getBreakCode() { return getBreakCode(handle); }
@ -94,4 +89,15 @@ class NativeVue extends Vue {
public boolean writeBytes(int address, byte[] src, int offset, int length) public boolean writeBytes(int address, byte[] src, int offset, int length)
{ return writeBytes(handle, address, src, offset, length); } { return writeBytes(handle, address, src, offset, length); }
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Evaluate the condition in a breakpoint
boolean evaluate(Breakpoint brk) {
return false;
}
} }

View File

@ -1,8 +1,14 @@
package vue; package vue;
// Java imports
import java.util.*;
// Template for emulation core implementations // Template for emulation core implementations
public abstract class Vue { public abstract class Vue {
// Instance fields
ArrayList<Breakpoint> breakpoints; // Current breakpoints
// Static fields // Static fields
private static String nativeID = null; // ID of loaded native library private static String nativeID = null; // ID of loaded native library
@ -152,19 +158,53 @@ public abstract class Vue {
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
Vue() {
breakpoints = new ArrayList<Breakpoint>();
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Public Methods // // Public Methods //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Produce a new breakpoint and add it to the collection
public Breakpoint breakpoint() {
var brk = new Breakpoint(this);
breakpoints.add(brk);
return brk;
}
// Produce an array of the current breakpoint collection
public Breakpoint[] listBreakpoints() {
return breakpoints.toArray(new Breakpoint[breakpoints.size()]);
}
// Remove a breakpoint from the collection
public boolean remove(Breakpoint brk) {
if (!breakpoints.remove(brk))
return false;
brk.remove();
return true;
}
///////////////////////////////////////////////////////////////////////////
// Abstract Methods //
///////////////////////////////////////////////////////////////////////////
// Release any used resources // Release any used resources
public abstract void dispose(); public abstract void dispose();
// Process the simulation // Process the simulation
public abstract int emulate(int maxCycles); public abstract int emulate(int maxCycles);
// Evaluate the condition in a breakpoint
public abstract boolean evaluate(Breakpoint brk);
// Retrieve the application break code // Retrieve the application break code
public abstract int getBreakCode(); public abstract int getBreakCode();
@ -200,4 +240,17 @@ public abstract class Vue {
public abstract boolean writeBytes(int address, byte[] src, int offset, public abstract boolean writeBytes(int address, byte[] src, int offset,
int length); int length);
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Evaluate the condition in a breakpoint
abstract boolean evaluate(Breakpoint brk);
// A breakpoint has been updated
void update(Breakpoint brk) {
}
} }