2104 lines
77 KiB
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();
|
|
}
|
|
|
|
} |