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 OPDEFS; private static final HashMap SYMDEFS; // Static initializer static { OPDEFS = new HashMap(); SYMDEFS = new HashMap(); // 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(); // 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 parseTokens() { var tokens = new ArrayList(); // 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 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 tokens) { int mode = 0; var stack = new Stack(); // 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(); } }