pvbemu/src/desktop/vue/Breakpoint.java

2104 lines
77 KiB
Java

package vue;
// Java imports
import java.util.*;
// Breakpoint descriptor
public class Breakpoint {
// Instance fields
private Error addressError; // Address parsing error
private String addresses; // Un-processed address ranges
private String condition; // Un-processed condition
private Error conditionError; // Condition parsing error
private int dataType; // Condition evaluation data type
private int depth; // Stack size for condition evaluation
private boolean fetch; // Breakpoint traps fetch reads
private int hooks; // Applied breakpoint types
private boolean isEnabled; // Breakpoint is active
private String name; // Display name
private int[][] ranges; // Applicable address ranges
private Token[] tokens; // Condition tokens
private Vue vue; // Containing emulation context
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Error codes
public static final int NONE = 0; // Parsing was successful
public static final int BADLITERAL = 1; // Primitive parse failed
public static final int BADOPERAND = 2; // Operator/operand type mismatch
public static final int BADTOKEN = 3; // No token matched
public static final int EARLYEOF = 4; // Last token not value or close
public static final int INVALID = 5; // Operator not allowed here
public static final int NESTING = 6; // ()[] mismatch, underflow
public static final int UNEXPECTED = 7; // Character not allowed here
// Hook flags
static final int EXCEPTION = 0x00000001;
static final int EXECUTE = 0x00000002;
static final int FRAME = 0x00000004;
static final int READ = 0x00000008;
static final int WRITE = 0x00000010;
// Token types
private static final int BOOL = 0; // Value types
private static final int SIGNED = 1;
private static final int UNSIGNED = 2;
private static final int FLOAT = 3;
private static final int SYMBOL = 4; // Symbol types
private static final int PROREG = 5;
private static final int SYSREG = 6;
private static final int UNARY = 7; // Operator types
private static final int BINARY = 8;
private static final int OPEN = 9;
private static final int CLOSE = 10;
// Expected token modes adjacent to any given token
// 0 = Value, unary or open expected, 1 = Binary or close expected
private static final int MODES_AFTER = 0b10001111111;
private static final int MODES_BEFORE = 0b10100000000;
// Functional symbol IDs
private static final int ADDRESS = 0;
private static final int BREAK = 1;
private static final int CODE = 2;
private static final int COND = 3;
private static final int DISP = 4;
private static final int FETCH = 5;
private static final int FORMAT = 6;
private static final int ID = 7;
private static final int IMM = 8;
private static final int OPCODE = 9;
private static final int REG1 = 10;
private static final int REG2 = 11;
private static final int REGID = 12;
private static final int SIZE = 13;
private static final int SUBOPCODE = 14;
private static final int TYPE = 15;
private static final int VALUE = 16;
private static final int VECTOR = 17;
// Evaluation operator IDs
private static final int GROUP = 0; // ()
private static final int XS32 = 1; // xs32, xword
private static final int XU32 = 2; // xu32, xuword
private static final int XFLOAT = 3; // xfloat
private static final int READ8 = 4; // []
private static final int READ16 = 5; // []
private static final int READ32 = 6; // []
private static final int BOOL_ = - 2; // bool
private static final int S32 = - 3; // s32, word
private static final int U32 = - 4; // u32, uword
private static final int FLOAT_ = - 5; // float
private static final int ADD = - 6; // +
private static final int AND_B = - 7; // &
private static final int AND_L = - 8; // &&
private static final int CEIL = - 9; // ceil
private static final int DIVIDE = -10; // /
private static final int EQUAL = -11; // ==
private static final int FLOOR = -12; // floor
private static final int GREATER = -13; // >
private static final int GREQUAL = -14; // >=
private static final int LEFT_L = -15; // <<
private static final int LEQUAL = -16; // <=
private static final int LESS = -17; // <
private static final int MULTIPLY = -18; // *
private static final int NEQUAL = -19; // !=
private static final int NOT_B = -20; // ~
private static final int NOT_L = -21; // !
private static final int NEGATE = -22; // -
private static final int OR_B = -23; // |
private static final int OR_L = -24; // ||
private static final int REMAINDER = -25; // %
private static final int RIGHT_A = -26; // >>
private static final int RIGHT_L = -27; // >>>
private static final int ROUND = -28; // round
private static final int S8 = -29; // s8, byte
private static final int S16 = -30; // s16, halfword
private static final int SUBTRACT = -31; // -
private static final int TRUNC = -32; // trunc
private static final int U8 = -33; // u8, ubyte
private static final int U16 = -34; // u16, uhalfword
private static final int XOR_B = -36; // ^
private static final int XOR_L = -37; // ^^
// Integer-float conversion
private static final float MAX_WORD = (float) Math.pow(2, 31);
private static final float MAX_UWORD = (float) Math.pow(2, 32);
// Token definitions
private static final HashMap<String, Def> OPDEFS;
private static final HashMap<String, Def> SYMDEFS;
// Static initializer
static {
OPDEFS = new HashMap<String, Def>();
SYMDEFS = new HashMap<String, Def>();
// Operator definitions
OPDEFS.put("(" , new Def( 0, OPEN , GROUP ));
OPDEFS.put(")" , new Def( 0, CLOSE , GROUP ));
OPDEFS.put("[" , new Def( 0, OPEN , READ32 ));
OPDEFS.put("]" , new Def( 0, CLOSE , READ32 ));
OPDEFS.put("~" , new Def( 1, UNARY , NOT_B ));
OPDEFS.put("!" , new Def( 1, UNARY , NOT_L ));
OPDEFS.put("-" , new Def( 1, UNARY , NEGATE ));
OPDEFS.put("bool" , new Def( 1, UNARY , BOOL_ ));
OPDEFS.put("byte" , new Def( 1, UNARY , S8 ));
OPDEFS.put("ceil" , new Def( 1, UNARY , CEIL ));
OPDEFS.put("float" , new Def( 1, UNARY , FLOAT_ ));
OPDEFS.put("floor" , new Def( 1, UNARY , FLOOR ));
OPDEFS.put("halfword" , new Def( 1, UNARY , S16 ));
OPDEFS.put("round" , new Def( 1, UNARY , ROUND ));
OPDEFS.put("s8" , new Def( 1, UNARY , S8 ));
OPDEFS.put("s16" , new Def( 1, UNARY , S16 ));
OPDEFS.put("s32" , new Def( 1, UNARY , S32 ));
OPDEFS.put("trunc" , new Def( 1, UNARY , TRUNC ));
OPDEFS.put("u8" , new Def( 1, UNARY , U8 ));
OPDEFS.put("u16" , new Def( 1, UNARY , U16 ));
OPDEFS.put("u32" , new Def( 1, UNARY , U32 ));
OPDEFS.put("ubyte" , new Def( 1, UNARY , U8 ));
OPDEFS.put("uhalfword", new Def( 1, UNARY , U16 ));
OPDEFS.put("uword" , new Def( 1, UNARY , U32 ));
OPDEFS.put("word" , new Def( 1, UNARY , S32 ));
OPDEFS.put("xfloat" , new Def( 1, UNARY , XFLOAT ));
OPDEFS.put("xs32" , new Def( 1, UNARY , XS32 ));
OPDEFS.put("xu32" , new Def( 1, UNARY , XU32 ));
OPDEFS.put("xuword" , new Def( 1, UNARY , XU32 ));
OPDEFS.put("xword" , new Def( 1, UNARY , XS32 ));
OPDEFS.put("/" , new Def( 2, BINARY, DIVIDE ));
OPDEFS.put("*" , new Def( 2, BINARY, MULTIPLY ));
OPDEFS.put("%" , new Def( 2, BINARY, REMAINDER));
OPDEFS.put("+" , new Def( 3, BINARY, ADD ));
OPDEFS.put("<<" , new Def( 4, BINARY, LEFT_L ));
OPDEFS.put(">>" , new Def( 4, BINARY, RIGHT_A ));
OPDEFS.put(">>>" , new Def( 4, BINARY, RIGHT_L ));
OPDEFS.put(">" , new Def( 5, BINARY, GREATER ));
OPDEFS.put(">=" , new Def( 5, BINARY, GREQUAL ));
OPDEFS.put("<" , new Def( 5, BINARY, LESS ));
OPDEFS.put("<=" , new Def( 5, BINARY, LEQUAL ));
OPDEFS.put("==" , new Def( 6, BINARY, EQUAL ));
OPDEFS.put("!=" , new Def( 6, BINARY, NEQUAL ));
OPDEFS.put("&" , new Def( 7, BINARY, AND_B ));
OPDEFS.put("^" , new Def( 8, BINARY, XOR_B ));
OPDEFS.put("|" , new Def( 9, BINARY, OR_B ));
OPDEFS.put("&&" , new Def(10, BINARY, AND_L ));
OPDEFS.put("^^" , new Def(11, BINARY, XOR_L ));
OPDEFS.put("||" , new Def(12, BINARY, OR_L ));
// Boolean symbol definitions
SYMDEFS.put("false", new Def(BOOL, 0));
SYMDEFS.put("true" , new Def(BOOL, 1));
// Break type symbl definitions
SYMDEFS.put("exception", new Def(SIGNED, EXCEPTION));
SYMDEFS.put("execute" , new Def(SIGNED, EXECUTE ));
SYMDEFS.put("read" , new Def(SIGNED, READ ));
SYMDEFS.put("write" , new Def(SIGNED, WRITE ));
// Condition code symbol definitions
SYMDEFS.put("c" , new Def(SIGNED, 1));
SYMDEFS.put("e" , new Def(SIGNED, 2));
SYMDEFS.put("f" , new Def(SIGNED, 13));
SYMDEFS.put("ge", new Def(SIGNED, 14));
SYMDEFS.put("gt", new Def(SIGNED, 15));
SYMDEFS.put("h" , new Def(SIGNED, 11));
SYMDEFS.put("l" , new Def(SIGNED, 1));
SYMDEFS.put("le", new Def(SIGNED, 7));
SYMDEFS.put("lt", new Def(SIGNED, 6));
SYMDEFS.put("n" , new Def(SIGNED, 4));
SYMDEFS.put("nc", new Def(SIGNED, 9));
SYMDEFS.put("ne", new Def(SIGNED, 10));
SYMDEFS.put("nh", new Def(SIGNED, 3));
SYMDEFS.put("nl", new Def(SIGNED, 9));
SYMDEFS.put("nv", new Def(SIGNED, 8));
SYMDEFS.put("nz", new Def(SIGNED, 10));
SYMDEFS.put("p" , new Def(SIGNED, 12));
SYMDEFS.put("t" , new Def(SIGNED, 5));
SYMDEFS.put("v" , new Def(SIGNED, 0));
SYMDEFS.put("z" , new Def(SIGNED, 2));
// Instruction ID symbol definitions
SYMDEFS.put("illegal", new Def(SIGNED, Vue.ILLEGAL));
SYMDEFS.put("add_imm", new Def(SIGNED, Vue.ADD_IMM));
SYMDEFS.put("add_reg", new Def(SIGNED, Vue.ADD_REG));
SYMDEFS.put("addf.s" , new Def(SIGNED, Vue.ADDF_S ));
SYMDEFS.put("addi" , new Def(SIGNED, Vue.ADDI ));
SYMDEFS.put("and" , new Def(SIGNED, Vue.AND ));
SYMDEFS.put("andbsu" , new Def(SIGNED, Vue.ANDBSU ));
SYMDEFS.put("andi" , new Def(SIGNED, Vue.ANDI ));
SYMDEFS.put("andnbsu", new Def(SIGNED, Vue.ANDNBSU));
SYMDEFS.put("bcond" , new Def(SIGNED, Vue.BCOND ));
SYMDEFS.put("caxi" , new Def(SIGNED, Vue.CAXI ));
SYMDEFS.put("cli" , new Def(SIGNED, Vue.CLI ));
SYMDEFS.put("cmp_imm", new Def(SIGNED, Vue.CMP_IMM));
SYMDEFS.put("cmp_reg", new Def(SIGNED, Vue.CMP_REG));
SYMDEFS.put("cmpf.s" , new Def(SIGNED, Vue.CMPF_S ));
SYMDEFS.put("cvt.sw" , new Def(SIGNED, Vue.CVT_SW ));
SYMDEFS.put("cvt.ws" , new Def(SIGNED, Vue.CVT_WS ));
SYMDEFS.put("div" , new Def(SIGNED, Vue.DIV ));
SYMDEFS.put("divf.s" , new Def(SIGNED, Vue.DIVF_S ));
SYMDEFS.put("divu" , new Def(SIGNED, Vue.DIVU ));
SYMDEFS.put("halt" , new Def(SIGNED, Vue.HALT ));
SYMDEFS.put("in.b" , new Def(SIGNED, Vue.IN_B ));
SYMDEFS.put("in.h" , new Def(SIGNED, Vue.IN_H ));
SYMDEFS.put("in.w" , new Def(SIGNED, Vue.IN_W ));
SYMDEFS.put("jal" , new Def(SIGNED, Vue.JAL ));
SYMDEFS.put("jmp" , new Def(SIGNED, Vue.JMP ));
SYMDEFS.put("jr" , new Def(SIGNED, Vue.JR ));
SYMDEFS.put("ld.b" , new Def(SIGNED, Vue.LD_B ));
SYMDEFS.put("ld.h" , new Def(SIGNED, Vue.LD_H ));
SYMDEFS.put("ld.w" , new Def(SIGNED, Vue.LD_W ));
SYMDEFS.put("ldsr" , new Def(SIGNED, Vue.LDSR ));
SYMDEFS.put("mov_imm", new Def(SIGNED, Vue.MOV_IMM));
SYMDEFS.put("mov_reg", new Def(SIGNED, Vue.MOV_REG));
SYMDEFS.put("movbsu" , new Def(SIGNED, Vue.MOVBSU ));
SYMDEFS.put("movea" , new Def(SIGNED, Vue.MOVEA ));
SYMDEFS.put("movhi" , new Def(SIGNED, Vue.MOVHI ));
SYMDEFS.put("mpyhw" , new Def(SIGNED, Vue.MPYHW ));
SYMDEFS.put("mul" , new Def(SIGNED, Vue.MUL ));
SYMDEFS.put("mulf.s" , new Def(SIGNED, Vue.MULF_S ));
SYMDEFS.put("mulu" , new Def(SIGNED, Vue.MULU ));
SYMDEFS.put("not" , new Def(SIGNED, Vue.NOT ));
SYMDEFS.put("notbsu" , new Def(SIGNED, Vue.NOTBSU ));
SYMDEFS.put("or" , new Def(SIGNED, Vue.OR ));
SYMDEFS.put("orbsu" , new Def(SIGNED, Vue.ORBSU ));
SYMDEFS.put("ori" , new Def(SIGNED, Vue.ORI ));
SYMDEFS.put("ornbsu" , new Def(SIGNED, Vue.ORNBSU ));
SYMDEFS.put("out.b" , new Def(SIGNED, Vue.OUT_B ));
SYMDEFS.put("out.h" , new Def(SIGNED, Vue.OUT_H ));
SYMDEFS.put("out.w" , new Def(SIGNED, Vue.OUT_W ));
SYMDEFS.put("reti" , new Def(SIGNED, Vue.RETI ));
SYMDEFS.put("rev" , new Def(SIGNED, Vue.REV ));
SYMDEFS.put("sar_imm", new Def(SIGNED, Vue.SAR_IMM));
SYMDEFS.put("sar_reg", new Def(SIGNED, Vue.SAR_REG));
SYMDEFS.put("sch0bsd", new Def(SIGNED, Vue.SCH0BSD));
SYMDEFS.put("sch0bsu", new Def(SIGNED, Vue.SCH0BSU));
SYMDEFS.put("sch1bsd", new Def(SIGNED, Vue.SCH1BSD));
SYMDEFS.put("sch1bsu", new Def(SIGNED, Vue.SCH1BSU));
SYMDEFS.put("sei" , new Def(SIGNED, Vue.SEI ));
SYMDEFS.put("setf" , new Def(SIGNED, Vue.SETF ));
SYMDEFS.put("shl_imm", new Def(SIGNED, Vue.SHL_IMM));
SYMDEFS.put("shl_reg", new Def(SIGNED, Vue.SHL_REG));
SYMDEFS.put("shr_imm", new Def(SIGNED, Vue.SHR_IMM));
SYMDEFS.put("shr_reg", new Def(SIGNED, Vue.SHR_REG));
SYMDEFS.put("st.b" , new Def(SIGNED, Vue.ST_B ));
SYMDEFS.put("st.h" , new Def(SIGNED, Vue.ST_H ));
SYMDEFS.put("st.w" , new Def(SIGNED, Vue.ST_W ));
SYMDEFS.put("stsr" , new Def(SIGNED, Vue.STSR ));
SYMDEFS.put("sub" , new Def(SIGNED, Vue.SUB ));
SYMDEFS.put("subf.s" , new Def(SIGNED, Vue.SUBF_S ));
SYMDEFS.put("trap" , new Def(SIGNED, Vue.TRAP ));
SYMDEFS.put("trnc.sw", new Def(SIGNED, Vue.TRNC_SW));
SYMDEFS.put("xb" , new Def(SIGNED, Vue.XB ));
SYMDEFS.put("xh" , new Def(SIGNED, Vue.XH ));
SYMDEFS.put("xor" , new Def(SIGNED, Vue.XOR ));
SYMDEFS.put("xorbsu" , new Def(SIGNED, Vue.XORBSU ));
SYMDEFS.put("xori" , new Def(SIGNED, Vue.XORI ));
SYMDEFS.put("xornbsu", new Def(SIGNED, Vue.XORNBSU));
// Functional symbol definitions
SYMDEFS.put("address" , new Def(SYMBOL, ADDRESS ));
SYMDEFS.put("break" , new Def(SYMBOL, BREAK ));
SYMDEFS.put("code" , new Def(SYMBOL, CODE ));
SYMDEFS.put("cond" , new Def(SYMBOL, COND ));
SYMDEFS.put("disp" , new Def(SYMBOL, DISP ));
SYMDEFS.put("fetch" , new Def(SYMBOL, FETCH ));
SYMDEFS.put("format" , new Def(SYMBOL, FORMAT ));
SYMDEFS.put("id" , new Def(SYMBOL, ID ));
SYMDEFS.put("imm" , new Def(SYMBOL, IMM ));
SYMDEFS.put("opcode" , new Def(SYMBOL, OPCODE ));
SYMDEFS.put("reg1" , new Def(SYMBOL, REG1 ));
SYMDEFS.put("reg2" , new Def(SYMBOL, REG2 ));
SYMDEFS.put("regid" , new Def(SYMBOL, REGID ));
SYMDEFS.put("size" , new Def(SYMBOL, SIZE ));
SYMDEFS.put("subopcode", new Def(SYMBOL, SUBOPCODE));
SYMDEFS.put("type" , new Def(SYMBOL, TYPE ));
SYMDEFS.put("value" , new Def(SYMBOL, VALUE ));
SYMDEFS.put("vector" , new Def(SYMBOL, VECTOR ));
// Program register symbol definitions
SYMDEFS.put("r0" , new Def(PROREG, 0));
SYMDEFS.put("r1" , new Def(PROREG, 1));
SYMDEFS.put("r2" , new Def(PROREG, 2));
SYMDEFS.put("r3" , new Def(PROREG, 3));
SYMDEFS.put("r4" , new Def(PROREG, 4));
SYMDEFS.put("r5" , new Def(PROREG, 5));
SYMDEFS.put("r6" , new Def(PROREG, 6));
SYMDEFS.put("r7" , new Def(PROREG, 7));
SYMDEFS.put("r8" , new Def(PROREG, 8));
SYMDEFS.put("r9" , new Def(PROREG, 9));
SYMDEFS.put("r10", new Def(PROREG, 10));
SYMDEFS.put("r11", new Def(PROREG, 11));
SYMDEFS.put("r12", new Def(PROREG, 12));
SYMDEFS.put("r13", new Def(PROREG, 13));
SYMDEFS.put("r14", new Def(PROREG, 14));
SYMDEFS.put("r15", new Def(PROREG, 15));
SYMDEFS.put("r16", new Def(PROREG, 16));
SYMDEFS.put("r17", new Def(PROREG, 17));
SYMDEFS.put("r18", new Def(PROREG, 18));
SYMDEFS.put("r19", new Def(PROREG, 19));
SYMDEFS.put("r20", new Def(PROREG, 20));
SYMDEFS.put("r21", new Def(PROREG, 21));
SYMDEFS.put("r22", new Def(PROREG, 22));
SYMDEFS.put("r23", new Def(PROREG, 23));
SYMDEFS.put("r24", new Def(PROREG, 24));
SYMDEFS.put("r25", new Def(PROREG, 25));
SYMDEFS.put("r26", new Def(PROREG, 26));
SYMDEFS.put("r27", new Def(PROREG, 27));
SYMDEFS.put("r28", new Def(PROREG, 28));
SYMDEFS.put("r29", new Def(PROREG, 29));
SYMDEFS.put("r30", new Def(PROREG, 30));
SYMDEFS.put("r31", new Def(PROREG, 31));
SYMDEFS.put("gp" , new Def(PROREG, Vue.GP));
SYMDEFS.put("hp" , new Def(PROREG, Vue.HP));
SYMDEFS.put("lp" , new Def(PROREG, Vue.LP));
SYMDEFS.put("sp" , new Def(PROREG, Vue.SP));
SYMDEFS.put("tp" , new Def(PROREG, Vue.TP));
// System register symbol definitions
SYMDEFS.put("adtre", new Def(SYSREG, Vue.ADTRE ));
SYMDEFS.put("chcw" , new Def(SYSREG, Vue.CHCW ));
SYMDEFS.put("ecr" , new Def(SYSREG, Vue.ECR ));
SYMDEFS.put("eipc" , new Def(SYSREG, Vue.EIPC ));
SYMDEFS.put("eipsw", new Def(SYSREG, Vue.EIPSW ));
SYMDEFS.put("fepc" , new Def(SYSREG, Vue.FEPC ));
SYMDEFS.put("fepsw", new Def(SYSREG, Vue.FEPSW ));
SYMDEFS.put("pc" , new Def(SYSREG, Vue.PC ));
SYMDEFS.put("pir" , new Def(SIGNED, 0x00005346));
SYMDEFS.put("psw" , new Def(SYSREG, Vue.PSW ));
SYMDEFS.put("tkcw" , new Def(SIGNED, 0x000000E0));
SYMDEFS.put("sr29" , new Def(SYSREG, 29 ));
SYMDEFS.put("sr30" , new Def(SIGNED, 0x00000004));
SYMDEFS.put("sr31" , new Def(SYSREG, 31 ));
}
///////////////////////////////////////////////////////////////////////////
// Classes //
///////////////////////////////////////////////////////////////////////////
// Operator or value definition
private static class Def {
int id; // Operator/symbol ID or literal value
int precedence; // Operator precedence
int type; // Token category
// Default constructor
Def() { }
// Precedence constructor
Def(int precedence, int type, int id) {
this.id = id;
this.precedence = precedence;
this.type = type;
}
// Value constructor
Def(int type, int id) {
this(0, type, id);
}
}
// Parsing error descriptor
public static class Error {
public final int code; // Error code
public final int position; // Character position of error
public final String text; // Offending token
// Constructor
private Error(int code, int position, String text) {
this.code = code;
this.position = position;
this.text = text;
}
}
// Expression token
private static class Token extends Def {
int dataType; // Effective type at runtime
Token left; // Left node
Token parent; // Parent node
Token right; // Right node
int start; // Character position in expression
String text; // Display text
// Constructor
Token(int type, int start, String text) {
dataType = type == PROREG || type == SYMBOL ||
type == SYSREG ? SIGNED : type;
this.start = start;
this.text = text;
this.type = type;
}
}
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Package constructor
public Breakpoint(Vue vue) {
addressError = new Error(NONE, 0, "");
addresses = "";
condition = "";
conditionError = new Error(NONE, 0, "");
fetch = true;
name = "";
ranges = new int[0][];
tokens = new Token[0];
this.vue = vue;
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Determine whether the breakpoint applies to any break scenarios
public boolean any() {
return hooks != 0;
}
// Evaluate the condition against the emulation context
public boolean evaluate() {
return evaluate(null, null);
}
// Evaluate the condition against the emulation context
public boolean evaluate(Instruction inst, Access acc) {
// Error checking
if (vue == null || !isEnabled || hooks == 0)
return false;
// Working variables
boolean applies = false;
int pc = vue.getRegister(Vue.PC, true);
// Resolve state objects
if (inst == null)
inst = new Instruction(vue.read(pc, Vue.S32));
if (acc == null)
acc = inst.access(vue);
// Check whether the breakpoint applies to the current state
if ((hooks & EXCEPTION) != 0)
applies = applies || vue.getExceptionCode() != 0;
if ((hooks & EXECUTE) != 0)
applies = applies || inRange(pc, pc + inst.size - 1);
if ((hooks & (READ | WRITE)) != 0 && acc != null) switch (inst.id) {
case Vue.CAXI :
case Vue.IN_B : case Vue.IN_H : case Vue.IN_W :
case Vue.LD_B : case Vue.LD_H : case Vue.LD_W :
case Vue.OUT_B: case Vue.OUT_H: case Vue.OUT_W:
case Vue.ST_B : case Vue.ST_H : case Vue.ST_W :
applies = applies || inRange(acc.address,
acc.address + JavaVue.TYPE_SIZES[acc.type] - 1);
}
if (!applies)
return false;
// Evaluate the condition
if (acc == null)
acc = new Access();
return isTrue(new int[depth], 0, inst, acc);
}
// Retrieve the most recent address text
public String getAddresses() {
return addresses;
}
// Retrieve the most recent condition text
public String getCondition() {
return condition;
}
// Retrieve the most recent address parsing error
public Error getAddressError() {
return addressError;
}
// Retrieve the most recent condition parsing error
public Error getConditionError() {
return conditionError;
}
// Determine whether the breakpoint hooks exceptions
public boolean getException() {
return (hooks & EXCEPTION) != 0;
}
// Determine whether the breakpoint hooks executions
public boolean getExecute() {
return (hooks & EXECUTE) != 0;
}
// Determine whether the breakpoint hooks fetch reads
public boolean getFetch() {
return fetch;
}
// Determine whether the breakpoint hooks reads
public boolean getRead() {
return (hooks & READ) != 0;
}
// Determine whether the breakpoint hooks writes
public boolean getWrite() {
return (hooks & WRITE) != 0;
}
// Retrieve the display name
public String getName() {
return name;
}
// Determine whether the breakpoint can be used
boolean isActive() {
return
isEnabled &&
addressError .code == NONE &&
conditionError.code == NONE
;
}
// Determine whether the breakpoint is enabled
public boolean isEnabled() {
return isEnabled;
}
// Specify and parse a list of address ranges
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
// Configure instance fields
this.addresses = addresses == null ? addresses = "" : addresses;
// Parse the input
var chars = (addresses + " ").toCharArray();
for (x = 0; x < chars.length; x++) {
char c = chars[x];
boolean white = c == ' ' || c == '\t';
boolean digit =
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) {
addressError = new Error(BADLITERAL, start + 1, text);
if (vue != null)
vue.updateRanges(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
addressError = new Error(UNEXPECTED, x + 1, Character.toString(c));
if (vue != null)
vue.updateRanges(this);
return false;
} // x
// Unexpected end of input
if (mode == 0 && (ranges.size() > 0 || first != null)) {
addressError = new Error(EARLYEOF, x + 1, "");
if (vue != null)
vue.updateRanges(this);
return false;
}
// A one-address range was processed
if (first != null)
ranges.add(new int[] { first, first });
// Parsing was successful
this.ranges = ranges.toArray(new int[ranges.size()][]);
addressError = new Error(NONE, 0, "");
if (vue != null)
vue.updateRanges(this);
return true;
}
// Specify and parse a condition
public boolean setCondition(String condition) {
// Configure instance fields
this.condition = condition == null ? condition = "" : condition;
conditionError = new Error(NONE, 0, "");
depth = 0;
tokens = new Token[0];
// Process the expression
var tokens = parseTokens();
if (tokens == null || !validate(tokens)) {
if (vue != null)
vue.updateTokens(this);
return false;
}
tree(tokens);
// Perform operator-specific pre-processing on the expression
Token tok = tokens.size() == 0 ? null : transform(tokens.remove(0));
if (conditionError.code != NONE) {
if (vue != null)
vue.updateTokens(this);
return false;
}
// Produce an RPN-ordered list of tokens
dataType = tok == null ? BOOL : tok.dataType;
while (tok != null) {
// Traverse to left child node
if (tok.left != null) {
tok = tok.left;
tok.parent.left = null;
continue;
}
// Traverse to right child node
if (tok.right != null) {
tok = tok.right;
tok.parent.right = null;
continue;
}
// No children: add node to output
tokens.add(tok);
tok = tok.parent;
}
this.tokens = tokens.toArray(new Token[tokens.size()]);
// The expression was successfully parsed
depth = depth();
if (vue != null)
vue.updateTokens(this);
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 whether the breakpoint hooks fetch reads
public void setFetch(boolean fetch) {
this.fetch = fetch;
if (vue != null)
vue.updateState(this);
}
// Specify the display name
public String setName(String name) {
return 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)
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);
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Determine whether the breakpoint applies to a break scenario
boolean appliesTo(int breakType, boolean fetch) {
return isActive() && (this.fetch || !fetch) &&
(breakType == 0 || (breakType & hooks) != 0);
}
// Perform a typed evaluation of the condition expression
Object evaluateTyped(int[]stack,int breakType,Instruction inst,Access acc){
if (tokens.length == 0)
return null;
int value = evaluate(stack, breakType, inst, acc);
switch (dataType) {
case BOOL : return (Boolean) (value != 0);
case FLOAT : return (Float ) Float.intBitsToFloat(value);
case UNSIGNED: return (Long ) (value & 0xFFFFFFFFL);
}
return (Integer) value;
}
// Determine whether the breakpoint applies to a given address range
boolean inRange(int start, int end) {
// Implicit inclusion
if (ranges.length == 0)
return true;
// Check all ranges
for (var range : ranges)
if (
Integer.compareUnsigned(
start - range[0], range[1] - range[0]) <= 0 ||
Integer.compareUnsigned(
range[0] - start , end - start ) <= 0
) return true;
return false;
}
// Determine whether the breakpoint's condition is truthy
boolean isTrue(int[] stack, int breakType, Instruction inst, Access acc) {
return tokens.length == 0 ? true :
evaluate(stack, breakType, inst, acc) != 0;
}
// Produce a one-dimensional array from the address ranges
int[] flattenRanges() {
var ret = new int[ranges.length * 2];
for (int x = 0, y = 0; x < ranges.length; x++) {
var range = ranges[x];
ret[y++] = range[0];
ret[y++] = 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, y = 0; x < tokens.length; x++) {
var token = tokens[x];
ret[y++] = token.type;
ret[y++] = token.id;
}
return ret;
}
// Retrieve the required condition evaluation stack size
int getDepth() {
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;
}
///////////////////////////////////////////////////////////////////////////
// Condition Methods //
///////////////////////////////////////////////////////////////////////////
// Determine whether a token is a literal
private static boolean isLiteral(Token tok) {
switch (tok.type) {
case BOOL:
case FLOAT:
case SIGNED:
case UNSIGNED: return true;
}
return false;
}
// Remove a token from a tree
private static Token remove(Token tok) {
if (tok.parent != null) {
if (tok.parent.left == tok)
tok.parent.left = tok.right;
else tok.parent.right = tok.right;
}
tok.right.parent = tok.parent;
return tok.right;
}
// Parse a literal
private Token parseLiteral(char[] chars, int start) {
boolean isFloat = chars[start] == '.'; // The figure is a float
boolean isHex = false; // The figure is in hexadecimal
// Process through the end of the expression
for (int x = start + 1; x < chars.length; x++) {
char c = chars[x];
// The literal begins with "0x"
if (c == 'x' || c == 'X') {
// "x" cannot appear here
if (isFloat || x != start + 1 || chars[start] != '0') {
conditionError =
new Error(UNEXPECTED, x + 1, Character.toString(c));
return null;
}
// Configure as a hexadecimal integer
isHex = true;
continue;
}
// The literal contains "."
if (c == '.') {
// "." cannot appear here
if (isHex || isFloat) {
conditionError =
new Error(UNEXPECTED, x + 1, Character.toString(c));
return null;
}
// Configure as a float
isFloat = true;
continue;
}
// The character is part of the token
if (
c >= '0' && c <= '9' ||
isHex && (c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F')
) continue;
// Produce a new token
var ret = new Token(SIGNED, start,
new String(chars, start, x - start));
// Parse the literal value
try {
if (isFloat) {
ret.dataType = ret.type = FLOAT;
ret.id = Float.floatToRawIntBits(
adjust(Float.parseFloat(ret.text)));
} else {
long val = !isHex ? Long.parseLong(ret.text) :
Long.parseLong(ret.text.substring(2), 16);
if (val >= 0x80000000L && val <= 0xFFFFFFFFL)
ret.dataType = ret.type = UNSIGNED;
else if (val < 0x80000000 || val > 0x7FFFFFFF)
throw new Exception();
ret.id = (int) val;
}
return ret;
}
// Could not parse the value
catch (Exception e) {
conditionError = new Error(BADLITERAL, ret.start+1, ret.text);
return null;
}
} // x
return null; // Unreachable
}
// Parse an operator
private Token parseOperator(char[] chars, int start) {
int x; // Iterator
// Locate the entire potential token
for (x = start + 1; x < chars.length; x++) {
char c = chars[x];
if (
c >= 'a' && c <= 'z' ||
c >= 'A' && c <= 'Z' ||
c >= '0' && c <= '9' ||
c == ' ' || c == '\t'
) break;
} // x
// Find the longest operator within the token
for (int y = x; y > start; y--) {
String text = new String(chars, start, y - start);
var def = OPDEFS.get(text);
// An operator was found
if (def != null) {
var ret = new Token(def.type, start, text);
ret.id = def.id;
ret.precedence = def.precedence;
return ret;
}
} // y
// No operator was identified
conditionError = new Error(BADTOKEN, start + 1,
new String(chars, start, x - start));
return null;
}
// Parse a symbol (which may be an operator)
private Token parseSymbol(char[] chars, int start) {
// Process through the end of the expression
for (int x = start + 1; x < chars.length; x++) {
char c = chars[x];
// The character is part of the token
if (
c >= 'a' && c <= 'z' ||
c >= 'A' && c <= 'Z' ||
c >= '0' && c <= '9' ||
c == '_' || c == '.'
) continue;
// Produce a new token
var ret = new Token(SYMBOL, start,
new String(chars, start, x - start));
String text = ret.text.toLowerCase();
// The token is an operator or symbol
var def = OPDEFS .get(text);
if (def == null)
def = SYMDEFS.get(text);
if (def != null) {
ret.id = def.id;
ret.precedence = def.precedence;
ret.type = def.type;
if (def.type <= FLOAT)
ret.dataType = def.type;
return ret;
}
// The token is not recognized
conditionError = new Error(BADTOKEN, ret.start + 1, ret.text);
return null;
} // x
return null; // Unreachable
}
// Parse condition text into tokens
private ArrayList<Token> parseTokens() {
var tokens = new ArrayList<Token>();
// Parse the expression
var chars = (condition + " ").toCharArray();
for (int x = 0; x < chars.length; x++) {
char c = chars[x];
// Ignore whitespace
if (c == ' ' || c == '\t')
continue;
// Produce a token based on the first character
Token tok =
c >= '0' && c <= '9' || c == '.' ?
parseLiteral(chars, x) :
c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' ?
parseSymbol (chars, x) :
parseOperator (chars, x)
;
// There was an error processing the token
if (tok == null)
return null;
// Advance to the next token
tokens.add(tok);
x += tok.text.length() - 1;
} // x
return tokens;
}
// Perform implicit pre-processing operations
private Token transform(Token tok) {
var err = new Error(BADOPERAND, tok.start + 1, tok.text);
// Transform child tokens
if (
tok.left != null && (tok.left = transform(tok.left )) == null ||
tok.right != null && (tok.right = transform(tok.right)) == null
) return null;
// Unary operators
if (tok.type == UNARY) {
int id = tok.id;
boolean literal = isLiteral(tok.right);
int newType = -1;
tok.dataType = tok.right.dataType;
tok.id = -tok.id * 4 + tok.dataType;
// Process by ID
switch (id) {
// Cast to Boolean
case BOOL_:
if (tok.right.dataType == BOOL)
return remove(tok);
newType = BOOL;
break;
// Round Up
case CEIL:
if (tok.right.dataType != FLOAT)
return remove(tok);
break;
// Cast to Float
case FLOAT_:
if (tok.right.dataType == FLOAT)
return remove(tok);
if (tok.right.type == UNARY && tok.right.id == READ32)
tok.id = id = XFLOAT;
newType = FLOAT;
break;
// Round Down
case FLOOR:
if (tok.right.dataType != FLOAT)
return remove(tok);
break;
// Negate
case NEGATE:
if (tok.right.dataType != UNSIGNED)
break;
conditionError = err;
return null;
// Not Bitwise
case NOT_B:
if (tok.right.dataType != FLOAT)
break;
conditionError = err;
return null;
// Not Logical
case NOT_L:
break;
// Round to Nearest
case ROUND:
if (tok.right.dataType != FLOAT)
return remove(tok);
break;
// Cast to Signed Byte
case S8:
if (tok.right.type == UNARY && tok.right.id == READ32)
tok.right.id = READ8;
newType = SIGNED;
break;
// Cast to Signed Halfword
case S16:
if (tok.right.type == UNARY && tok.right.id == READ32)
tok.right.id = READ16;
newType = SIGNED;
break;
// Cast to Signed Word
case S32:
newType = SIGNED;
break;
// Truncate
case TRUNC:
if (tok.right.dataType != FLOAT)
return remove(tok);
break;
// Cast to Unsigned Byte
case U8:
if (tok.right.type == UNARY && tok.right.id == READ32)
tok.right.id = READ8;
newType = UNSIGNED;
break;
// Cast to Unsigned Halfword
case U16:
if (tok.right.type == UNARY && tok.right.id == READ32)
tok.right.id = READ16;
newType = UNSIGNED;
break;
// Cast to Unsigned Word
case U32:
newType = UNSIGNED;
break;
// Reinterpret as Signed Word
case XS32:
tok.right.dataType = SIGNED;
return remove(tok);
// Reinterpret as Unsigned Word
case XU32:
tok.right.dataType = UNSIGNED;
return remove(tok);
// Reinterpret as Float
case XFLOAT:
newType = FLOAT;
break;
}
// The operand is not a literal
if (!literal) {
if (newType != -1)
tok.dataType = newType;
return tok;
}
// Pre-process the literal operand
tok.right.id = evalUnary(id, tok.right);
if (newType != -1)
tok.right.type = tok.right.dataType = newType;
return remove(tok);
}
// Binary operators
if (tok.type == BINARY) {
int dataType = Math.max(tok.left.dataType, tok.right.dataType);
int id = tok.id;
boolean literal = isLiteral(tok.left) && isLiteral(tok.right);
tok.dataType = dataType;
tok.id = -id * 4 + tok.dataType;
// Process by ID
switch (id) {
// Boolean logical operations
case EQUAL : // Equal
case GREATER: // Greater
case GREQUAL: // Greater or Equal
case LEQUAL : // Less or Equal
case LESS : // Less
case NEQUAL : // Not Equal
tok.dataType = BOOL;
break;
// Non-boolean logical operations
case AND_L: // And Logical
case OR_L : // Or Logical
case XOR_L: // Exclusive Or Logical
break;
// Bitwise operations
case AND_B : // And Bitwise
case LEFT_L : // Shift Left
case OR_B : // Or Bitwise
case RIGHT_A: // Shift Right Arithmetic
case RIGHT_L: // Shift Right Logical
case XOR_B : // Exclusive Or Bitwise
if (tok.dataType == FLOAT) {
conditionError = err;
return null;
}
// Fallthrough
// Arithmetic operations
case ADD : // Add
case DIVIDE : // Divide
case MULTIPLY : // Multiply
case REMAINDER: // Remainder
case SUBTRACT : // Subtract
if (tok.dataType == BOOL)
tok.dataType = SIGNED;
break;
}
// Convert operands
for (int op = 0; op < 2; op++) {
var top = op == 0 ? tok.left : tok.right;
// The operand is already the result type
if (top.dataType == dataType)
continue;
// Select the ID of the conversion operation
int cvt = 0;
switch (dataType) {
case BOOL : cvt = BOOL_ ; break;
case FLOAT : cvt = FLOAT_; break;
case SIGNED : cvt = S32 ; break;
case UNSIGNED: cvt = U32 ; break;
}
// Convert the literal operand directly
if (isLiteral(top)) {
top.id = evalUnary(cvt, top);
top.type = top.dataType = dataType;
}
// Insert a conversion token
else {
var imp = new Token(UNARY, -1, "");
switch (dataType) {
case BOOL : imp.text = "bool" ; break;
case FLOAT : imp.text = "float"; break;
case SIGNED : imp.text = "s32" ; break;
case UNSIGNED: imp.text = "u32" ; break;
}
imp.dataType = dataType;
imp.id = -cvt * 4 + top.dataType;
imp.parent = tok;
imp.right = top;
top.parent = imp;
top = imp;
}
// Common processing
if (op == 0)
tok.left = top;
else tok.right = top;
}
// Common processing
if (literal) {
tok.type = tok.dataType;
tok.id = evalBinary(tok.id, tok.left.id, tok.right.id);
tok.left = tok.right = null;
}
return tok;
}
// Value token
return tok;
}
// Build an expression tree from a list of tokens
private static void tree(ArrayList<Token> tokens) {
// Process all operators
while (tokens.size() > 1) {
int end = tokens.size() - 1;
int start = 0;
// Locate the bounds of the innermost nested group
for (int x = 0; x <= end; x++) {
var tok = tokens.get(x);
if (tok.type == OPEN)
start = x + 1;
if (tok.type != CLOSE)
continue;
end = x - 1;
break;
}
// Apply unary operators
for (int x = end; x >= start; x--) {
var tok = tokens.get(x);
if (tok.right != null || tok.type != UNARY)
continue;
tok.right = tokens.remove(x + 1);
tok.right.parent = tok;
end--;
}
// Apply binary operators
while (start != end) {
int index = -1;
Token tok = null;
// Locate the left-most operator with the highest precedence
for (int x = start; x < end; x++) {
var tik = tokens.get(x);
if (tik.right != null || tik.type != BINARY ||
tok != null && tik.precedence >= tok.precedence)
continue;
index = x;
tok = tik;
}
// Apply the operator
tok.right = tokens.remove(index + 1);
tok.left = tokens.remove(index - 1);
tok.left.parent = tok.right.parent = tok;
end -= 2;
}
// There are no group operators
if (tokens.size() == 1)
break;
// Apply the group operators
tokens.remove(end + 1);
if (tokens.remove(start - 1).id == READ32) {
var tok = new Token(UNARY, -1, "[]");
tok.id = READ32;
tok.right = tokens.remove(start - 1);
tok.right.parent = tok;
tokens.add(start - 1, tok);
}
} // size
}
// Ensure a sequence of tokens is valid
private boolean validate(ArrayList<Token> tokens) {
int mode = 0;
var stack = new Stack<Token>();
// Validate all tokens
for (var tok : tokens) {
// Expected token mode mismatch
if ((MODES_BEFORE >> tok.type & 1) != mode) {
// The token is invalid
if (tok.id != NEGATE) {
conditionError = new Error(INVALID, tok.start+1, tok.text);
return false;
}
// Convert negate to subtract
tok.id = SUBTRACT;
tok.precedence = 3;
tok.type = BINARY;
}
// Nesting error
if (tok.type == CLOSE &&
(stack.empty() || stack.pop().id != tok.id)) {
conditionError = new Error(NESTING, tok.start + 1, tok.text);
return false;
}
// The token opens a group
if (tok.type == OPEN)
stack.push(tok);
// The token is valid
mode = MODES_AFTER >> tok.type & 1;
continue;
}
// A group was not closed, or a binary operation was incomplete
if (!stack.empty() || mode == 0 && tokens.size() != 0) {
conditionError =
new Error(EARLYEOF, condition.length(), "");
return false;
}
// Successfully parsed the condition
return true;
}
///////////////////////////////////////////////////////////////////////////
// Evaluation Methods //
///////////////////////////////////////////////////////////////////////////
// Evaluate the condition, returning only the computed value
private int evaluate(int[]stack,int breakType,Instruction inst,Access acc){
// Process all tokens
int size = 0;
for (var tok : tokens) switch (tok.type) {
// Binary operator
case BINARY:
stack[size - 2] =
evalBinary(tok.id, stack[size - 2], stack[size - 1]);
size--;
break;
// Literal
case BOOL:
case FLOAT:
case SIGNED:
case UNSIGNED:
stack[size++] = tok.id;
break;
// CPU register
case PROREG:
case SYSREG:
stack[size++] = vue.getRegister(tok.id, tok.type == SYSREG);
break;
// Unary operator
case UNARY:
stack[size - 1] = evalUnary(tok.id, stack[size - 1]);
break;
// Symbol
case SYMBOL:
int sym = inst.imm; // IMM, REGID, VECTOR
switch (tok.id) {
case ADDRESS : sym = acc.address ; break;
case BREAK : sym = breakType ; break;
case CODE : sym = vue.getExceptionCode(); break;
case COND : sym = inst.cond ; break;
case DISP : sym = inst.disp ; break;
case FETCH : sym = acc.fetch ; break;
case FORMAT : sym = inst.format ; break;
case ID : sym = inst.id ; break;
case OPCODE : sym = inst.opcode ; break;
case REG1 : sym = inst.reg1 ; break;
case REG2 : sym = inst.reg2 ; break;
case SIZE : sym = inst.size ; break;
case SUBOPCODE: sym = inst.subopcode ; break;
case TYPE : sym = acc.type ; break;
case VALUE : sym = acc.value ; break;
}
stack[size++] = sym;
break;
}
// Return the remaining stack value
return stack[0];
}
// Evaluate a unary operator given a token
private int evalUnary(int id, Token tok) {
return evalUnary(-id * 4 + tok.dataType, tok.id);
}
// Evaluate a unary operator
private int evalUnary(int id, int operand) {
// Processing by ID
switch (id) {
// Cast to Boolean
case -BOOL_ * 4 + BOOL: // Removed by parser
case -BOOL_ * 4 + FLOAT:
case -BOOL_ * 4 + SIGNED:
case -BOOL_ * 4 + UNSIGNED:
return operand == 0 ? 0 : 1;
// Round up
case -CEIL * 4 + BOOL: // Removed by parser
case -CEIL * 4 + FLOAT:
case -CEIL * 4 + SIGNED: // Removed by parser
case -CEIL * 4 + UNSIGNED: // Removed by parser
return Float.floatToIntBits(adjust((float)
Math.ceil(Float.intBitsToFloat(operand))));
// Cast to Float
case -FLOAT_ * 4 + BOOL:
case -FLOAT_ * 4 + FLOAT: // Removed by parser
case -FLOAT_ * 4 + SIGNED:
return Float.floatToIntBits(adjust(operand));
case -FLOAT_ * 4 + UNSIGNED:
return Float.floatToIntBits(adjust(operand & 0xFFFFFFFFL));
// Round down
case -FLOOR * 4 + BOOL: // Removed by parser
case -FLOOR * 4 + FLOAT:
case -FLOOR * 4 + SIGNED: // Removed by parser
case -FLOOR * 4 + UNSIGNED: // Removed by parser
return Float.floatToIntBits(adjust((float)
Math.floor(Float.intBitsToFloat(operand))));
// Bitwise Not
case -NOT_B * 4 + BOOL:
case -NOT_B * 4 + FLOAT: // Prevented by parser
case -NOT_B * 4 + SIGNED:
case -NOT_B * 4 + UNSIGNED:
return ~operand;
// Logical Not
case -NOT_L * 4 + BOOL:
case -NOT_L * 4 + FLOAT:
case -NOT_L * 4 + SIGNED:
case -NOT_L * 4 + UNSIGNED:
return operand == 0 ? 1 : 0;
// Negate
case -NEGATE * 4 + BOOL:
case -NEGATE * 4 + SIGNED:
case -NEGATE * 4 + UNSIGNED: // Removed by parser
return -operand;
case -NEGATE * 4 + FLOAT:
return Float.floatToIntBits(adjust(
-Float.intBitsToFloat(operand)));
// Read Byte
case READ8:
return vue.read(operand, Vue.S8);
// Read Halfword
case READ16:
return vue.read(operand, Vue.S16);
// Read Word
case READ32:
return vue.read(operand, Vue.S32);
// Round to Nearest
case -ROUND * 4 + BOOL: // Removed by parser
case -ROUND * 4 + FLOAT:
case -ROUND * 4 + SIGNED: // Removed by parser
case -ROUND * 4 + UNSIGNED: // Removed by parser
return Float.floatToIntBits(adjust((float)
Math.round(Float.intBitsToFloat(operand))));
// Cast to Signed Byte
case -S8 * 4 + FLOAT:
operand = evalUnary(-S32 * 4 + FLOAT, operand);
// Fallthrough
case -S8 * 4 + BOOL:
case -S8 * 4 + SIGNED:
case -S8 * 4 + UNSIGNED:
return operand << 24 >> 24;
// Cast to Signed Halfword
case -S16 * 4 + FLOAT:
operand = evalUnary(-S32 * 4 + FLOAT, operand);
// Fallthrough
case -S16 * 4 + BOOL:
case -S16 * 4 + SIGNED:
case -S16 * 4 + UNSIGNED:
return operand << 24 >> 24;
// Cast to Signed Word
case -S32 * 4 + BOOL:
case -S32 * 4 + SIGNED: // Removed by parser
case -S32 * 4 + UNSIGNED:
return operand;
case -S32 * 4 + FLOAT:
float val = Float.intBitsToFloat(operand);
return val >= -MAX_WORD && val < MAX_WORD ? (int) val : 0;
// Truncate
case -TRUNC * 4 + BOOL: // Removed by parser
case -TRUNC * 4 + FLOAT:
case -TRUNC * 4 + SIGNED: // Removed by parser
case -TRUNC * 4 + UNSIGNED: // Removed by parser
return Float.floatToIntBits(adjust(
trunc(Float.intBitsToFloat(operand))));
// Cast to Unsigned Byte
case -U8 * 4 + FLOAT:
operand = evalUnary(-S32 * 4 + FLOAT, operand);
case -U8 * 4 + BOOL:
case -U8 * 4 + SIGNED:
case -U8 * 4 + UNSIGNED:
return operand << 24 >>> 24;
// Cast to Unsigned Halfword
case -U16 * 4 + FLOAT:
operand = evalUnary(-S32 * 4 + FLOAT, operand);
case -U16 * 4 + BOOL:
case -U16 * 4 + SIGNED:
case -U16 * 4 + UNSIGNED:
return operand << 16 >>> 16;
// Cast to Unsigned Word
case -U32 * 4 + BOOL:
case -U32 * 4 + SIGNED:
case -U32 * 4 + UNSIGNED: // Removed by parser
return operand;
case -U32 * 4 + FLOAT:
float va1 = Float.intBitsToFloat(operand);
return va1 >= 0 && va1 < MAX_UWORD ? (int) (long) va1 : 0;
// Reinterpret as Float
case XFLOAT:
return Float.floatToIntBits(adjust(
Float.intBitsToFloat(operand)));
// Reinterpret as Signed Word, Reinterpret as Unsigned Word
case XS32: // Removed by parser
case XU32: // Removed by parser
}
// Unreachable
return 0;
}
// Evaluate a binary operator
private static int evalBinary(int id, int left, int right) {
// Processing by ID
switch (id) {
// Add
case -ADD * 4 + BOOL:
case -ADD * 4 + SIGNED:
case -ADD * 4 + UNSIGNED:
return left + right;
case -ADD * 4 + FLOAT:
return Float.floatToIntBits(adjust(
Float.intBitsToFloat(left ) +
Float.intBitsToFloat(right)
));
// And Bitwise
case -AND_B * 4 + BOOL:
case -AND_B * 4 + FLOAT: // Prevented by parser
case -AND_B * 4 + SIGNED:
case -AND_B * 4 + UNSIGNED:
return left & right;
// And Logical
case -AND_L * 4 + BOOL:
case -AND_L * 4 + FLOAT:
case -AND_L * 4 + SIGNED:
case -AND_L * 4 + UNSIGNED:
return left == 0 ? left : right;
// Divide
case -DIVIDE * 4 + BOOL:
case -DIVIDE * 4 + SIGNED:
return right == 0 ? 0 : left / right;
case -DIVIDE * 4 + UNSIGNED:
return right == 0 ? 0 :
(int) ((left & 0xFFFFFFFFL) / (right & 0xFFFFFFFFL));
case -DIVIDE * 4 + FLOAT:
return right == 0 ? 0 : Float.floatToIntBits(adjust(
Float.intBitsToFloat(left ) /
Float.intBitsToFloat(right)
));
// Equal
case -EQUAL * 4 + BOOL:
case -EQUAL * 4 + FLOAT:
case -EQUAL * 4 + SIGNED:
case -EQUAL * 4 + UNSIGNED:
return left == right ? 1 : 0;
// Greater
case -GREATER * 4 + BOOL:
case -GREATER * 4 + SIGNED:
return left > right ? 1 : 0;
case -GREATER * 4 + UNSIGNED:
return Integer.compareUnsigned(left, right) > 0 ? 1 : 0;
case -GREATER * 4 + FLOAT:
return Float.intBitsToFloat(left) >
Float.intBitsToFloat(right) ? 1 : 0;
// Greater or Equal
case -GREQUAL * 4 + BOOL:
case -GREQUAL * 4 + SIGNED:
return left >= right ? 1 : 0;
case -GREQUAL * 4 + UNSIGNED:
return Integer.compareUnsigned(left, right) >= 0 ? 1 : 0;
case -GREQUAL * 4 + FLOAT:
return Float.intBitsToFloat(left) >=
Float.intBitsToFloat(right) ? 1 : 0;
// Shift Left
case -LEFT_L * 4 + BOOL:
case -LEFT_L * 4 + FLOAT: // Prevented by parser
case -LEFT_L * 4 + SIGNED:
case -LEFT_L * 4 + UNSIGNED:
return left << (right & 31);
// Less or Equal
case -LEQUAL * 4 + BOOL:
case -LEQUAL * 4 + SIGNED:
return left <= right ? 1 : 0;
case -LEQUAL * 4 + UNSIGNED:
return Integer.compareUnsigned(left, right) <= 0 ? 1 : 0;
case -LEQUAL * 4 + FLOAT:
return Float.intBitsToFloat(left) <=
Float.intBitsToFloat(right) ? 1 : 0;
// Less
case -LESS * 4 + BOOL:
case -LESS * 4 + SIGNED:
return left < right ? 1 : 0;
case -LESS * 4 + UNSIGNED:
return Integer.compareUnsigned(left, right) < 0 ? 1 : 0;
case -LESS * 4 + FLOAT:
return Float.intBitsToFloat(left) <
Float.intBitsToFloat(right) ? 1 : 0;
// Multiply
case -MULTIPLY * 4 + BOOL:
case -MULTIPLY * 4 + SIGNED:
return left * right;
case -MULTIPLY * 4 + UNSIGNED:
return (int) ((left & 0xFFFFFFFFL) * (right & 0xFFFFFFFFL));
case -MULTIPLY * 4 + FLOAT:
return Float.floatToIntBits(adjust(
Float.intBitsToFloat(left ) *
Float.intBitsToFloat(right)
));
// Not Equal
case -NEQUAL * 4 + BOOL:
case -NEQUAL * 4 + FLOAT:
case -NEQUAL * 4 + SIGNED:
case -NEQUAL * 4 + UNSIGNED:
return left != right ? 1 : 0;
// Or Bitwise
case -OR_B * 4 + BOOL:
case -OR_B * 4 + FLOAT: // Prevented by parser
case -OR_B * 4 + SIGNED:
case -OR_B * 4 + UNSIGNED:
return left | right;
// Or Logical
case -OR_L * 4 + BOOL:
case -OR_L * 4 + FLOAT:
case -OR_L * 4 + SIGNED:
case -OR_L * 4 + UNSIGNED:
return left != 0 ? left : right;
// Remainder
case -REMAINDER * 4 + BOOL:
case -REMAINDER * 4 + SIGNED:
return right == 0 ? 0 : left % right;
case -REMAINDER * 4 + UNSIGNED:
return right == 0 ? 0 :
(int) ((left & 0xFFFFFFFFL) % (right & 0xFFFFFFFFL));
case -REMAINDER * 4 + FLOAT:
return right == 0 ? 0 : Float.floatToIntBits(adjust(
Float.intBitsToFloat(left ) %
Float.intBitsToFloat(right)
));
// Shift Right Arithmetic
case -RIGHT_A * 4 + BOOL:
case -RIGHT_A * 4 + FLOAT: // Prevented by parser
case -RIGHT_A * 4 + SIGNED:
case -RIGHT_A * 4 + UNSIGNED:
return left >> (right & 31);
// Shift Right Logical
case -RIGHT_L * 4 + BOOL:
case -RIGHT_L * 4 + FLOAT: // Prevented by parser
case -RIGHT_L * 4 + SIGNED:
case -RIGHT_L * 4 + UNSIGNED:
return left >>> (right & 31);
// Subtract
case -SUBTRACT * 4 + BOOL:
case -SUBTRACT * 4 + SIGNED:
case -SUBTRACT * 4 + UNSIGNED:
return left - right;
case -SUBTRACT * 4 + FLOAT:
return Float.floatToIntBits(adjust(
Float.intBitsToFloat(left) -
Float.intBitsToFloat(right)
));
// Exclusive Or Bitwise
case -XOR_B * 4 + BOOL:
case -XOR_B * 4 + FLOAT: // Prevented by parser
case -XOR_B * 4 + SIGNED:
case -XOR_B * 4 + UNSIGNED:
return left ^ right;
// Exclusive Or Logical
case -XOR_L * 4 + BOOL:
case -XOR_L * 4 + FLOAT:
case -XOR_L * 4 + SIGNED:
case -XOR_L * 4 + UNSIGNED:
return left != 0 && right != 0 ? 0 : left != 0 ? left : right;
}
// Unreachable
return 0;
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Adjust a float value as needed
private static float adjust(float value) {
int bits = Float.floatToRawIntBits(value);
int exp = bits & 0x7F800000;
return
(bits & 0x7FFFFFFF) == 0 || // Zero
exp == 0x7F800000 || // Indefinite
exp == 0 // Denormal
? 0 : value;
}
// Determine the required stack size to evaluate the expression
private int depth() {
// Error checking
if (conditionError.code != NONE)
return 0;
// Count the maximum size of the stack
int max = 0;
int size = 0;
for (var tok : tokens) switch (tok.type) {
case BINARY: size--; break;
case UNARY : break;
default : max = Math.max(max, ++size);
}
return max;
}
// Remove the fraction part of a float
private static float trunc(float x) {
return (float) (x < 0 ? Math.ceil(x) : Math.floor(x));
}
///////////////////////////////////////////////////////////////////////////
// Debugging Methods //
///////////////////////////////////////////////////////////////////////////
// Produce a string representation of the condition token list
public String debugTokens() {
var ret = new StringBuilder();
// The condition was not successfully parsed
if (conditionError.code != NONE)
return "Error";
// There is no condition
if (tokens.length == 0)
return "";
// Determine the maximum width of the text fields
int max = 0;
for (var tok : tokens)
max = Math.max(max, tok.text.length());
// Output all tokens
var last = tokens[tokens.length - 1];
for (var tok : tokens) {
// Text
ret.append(String.format("%-" + max + "s ", tok.text));
// Type
String type = null;
switch (tok.type) {
case BINARY : type = "Binary" ; break;
case BOOL : type = "Bool" ; break;
case FLOAT : type = "Float" ; break;
case PROREG : type = "ProReg" ; break;
case SYSREG : type = "SysReg" ; break;
case SIGNED : type = "Signed" ; break;
case SYMBOL : type = "Symbol" ; break;
case UNARY : type = "Unary" ; break;
case UNSIGNED: type = "Unsigned"; break;
default: type = Integer.toString(tok.type);
}
ret.append(String.format("%-8s ", type));
// Data type
switch (tok.dataType) {
case BOOL : type = "Bool" ; break;
case FLOAT : type = "Float" ; break;
case SIGNED : type = "Signed" ; break;
case UNSIGNED: type = "Unsigned"; break;
default: type = Integer.toString(tok.dataType);
}
ret.append(String.format("%-8s ", type));
// Operator
if (tok.type == BINARY || tok.type == UNARY) {
String id = Integer.toString(tok.id);
switch (-tok.id / 4) {
case 0 : id = "REINTERPRET"; break;
case READ8 : id = "READ8" ; break;
case READ16 : id = "READ16" ; break;
case READ32 : id = "READ32" ; break;
case ADD : id = "ADD" ; break;
case AND_B : id = "AND_B" ; break;
case AND_L : id = "AND_L" ; break;
case BOOL_ : id = "BOOL_" ; break;
case CEIL : id = "CEIL" ; break;
case DIVIDE : id = "DIVIDE" ; break;
case EQUAL : id = "EQUAL" ; break;
case FLOAT_ : id = "FLOAT_" ; break;
case FLOOR : id = "FLOOR" ; break;
case GREATER : id = "GREATER" ; break;
case GREQUAL : id = "GREQUAL" ; break;
case LEFT_L : id = "LEFT_L" ; break;
case LEQUAL : id = "LEQUAL" ; break;
case LESS : id = "LESS" ; break;
case MULTIPLY : id = "MULTIPLY" ; break;
case NEQUAL : id = "NEQUAL" ; break;
case NOT_B : id = "NOT_B" ; break;
case NOT_L : id = "NOT_L" ; break;
case NEGATE : id = "NEGATE" ; break;
case OR_B : id = "OR_B" ; break;
case OR_L : id = "OR_L" ; break;
case REMAINDER: id = "REMAINDER" ; break;
case RIGHT_A : id = "RIGHT_A" ; break;
case RIGHT_L : id = "RIGHT_L" ; break;
case ROUND : id = "ROUND" ; break;
case S8 : id = "S8" ; break;
case S16 : id = "S16" ; break;
case S32 : id = "S32" ; break;
case SUBTRACT : id = "SUBTRACT" ; break;
case TRUNC : id = "TRUNC" ; break;
case U8 : id = "U8" ; break;
case U16 : id = "U16" ; break;
case U32 : id = "U32" ; break;
case XFLOAT : id = "XFLOAT" ; break;
case XOR_B : id = "XOR_B" ; break;
case XOR_L : id = "XOR_L" ; break;
case XS32 : id = "XS32" ; break;
case XU32 : id = "XU32" ; break;
default: ret.append(Integer.toString(tok.id));
}
ret.append(String.format("%-11s", id));
}
// Symbol or literal
else switch (tok.type) {
case PROREG:
case SYSREG:
case SYMBOL:
ret.append(Integer.toString(tok.id));
break;
case FLOAT:
ret.append(String.format("%.6f",
Float.intBitsToFloat(tok.id)));
break;
case BOOL:
case SIGNED:
ret.append(Integer.toString(tok.id));
break;
case UNSIGNED:
ret.append(Long.toString(tok.id & 0xFFFFFFFFL));
break;
default: ret.append("Error");
}
// Advance to the next line
if (tok != last)
ret.append("\n");
}
return ret.toString();
}
// Produce a string representation of the internal address ranges
public String debugRanges() {
var ret = new StringBuilder();
// The address ranges were not successfully parsed
if (addressError.code != NONE)
return "Error";
// Process ranges
for (int x = 0; x < ranges.length; x++) {
var range = ranges[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();
}
}