package vue; // Java imports import java.util.*; // Breakpoint definition public class Breakpoint { // Instance fields private String addresses; // Un-processed address ranges private String condition; // Un-processed condition private int[] errCode; // Error codes private int[] errPosition; // Character position of errors private String[] errText; // Offending error text 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 BADTYPE = -1; public static final int NONE = 0; public static final int BADLITERAL = 1; public static final int BADTOKEN = 2; public static final int EARLYEOF = 3; public static final int EMPTY = 4; public static final int INVALID = 5; public static final int NESTING = 6; public static final int UNEXPECTED = 7; // Error types public static final int ADDRESS = 0; public static final int CONDITION = 1; // Breakpoint hooks private static final int EXCEPTION = 0x00000001; private static final int EXECUTE = 0x00000002; private static final int READ = 0x00000004; private static final int WRITE = 0x00000008; // Token types private static final int BINARY = 4; private static final int CLOSE = 6; private static final int FLOAT = 1; private static final int UNARY = 3; private static final int OPEN = 5; private static final int SYMBOL = 2; private static final int WORD = 0; // 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 = 0b1000111; private static final int MODES_BEFORE = 0b1010000; // Operator IDs private static final int ADD = 2; private static final int BITWISE_AND = 3; private static final int BITWISE_NOT = 4; private static final int BITWISE_OR = 5; private static final int BITWISE_XOR = 6; private static final int CEIL = 7; private static final int DIVIDE = 8; private static final int EQUAL = 9; private static final int FLOOR = 10; //private static final int FLOAT = 1; // Same as type private static final int GREATER_EQUAL_SIGNED = 11; private static final int GREATER_EQUAL_UNSIGNED = 12; private static final int GREATER_SIGNED = 13; private static final int GREATER_UNSIGNED = 14; private static final int GROUP = 15; private static final int LESS_EQUAL_SIGNED = 16; private static final int LESS_EQUAL_UNSIGNED = 17; private static final int LESS_SIGNED = 18; private static final int LESS_UNSIGNED = 19; private static final int LOGICAL_AND = 20; private static final int LOGICAL_NOT = 21; private static final int LOGICAL_OR = 22; private static final int LOGICAL_XOR = 23; private static final int MULTIPLY = 24; private static final int NEGATE = 25; private static final int NOT_EQUAL = 26; private static final int READ_WORD = 27; private static final int REMAINDER = 28; private static final int ROUND = 29; private static final int SHIFT_LEFT = 30; private static final int SHIFT_RIGHT = 31; private static final int SHIFT_RIGHT_ARITHMETIC = 32; private static final int SUBTRACT = 33; private static final int TRUNC = 34; //private static final int WORD = 0; // Same as type private static final int XFLOAT = 35; private static final int XWORD = 36; // Token definitions private static final HashMap LITDEFS; private static final HashMap OPDEFS; private static final HashMap SYMDEFS; // Functional symbol IDs //private static final int ADDRESS = 0; // Same as error type private static final int CODE = 1; private static final int COND = 2; private static final int DISP = 3; private static final int FORMAT = 4; private static final int ID = 5; private static final int IMM = 6; private static final int OPCODE = 7; private static final int REG1 = 8; private static final int REG2 = 9; private static final int REGID = 10; private static final int SIZE = 11; private static final int SUBOPCODE = 12; private static final int VALUE = 13; private static final int VECTOR = 14; // Static initializer static { LITDEFS = new HashMap(); OPDEFS = new HashMap(); SYMDEFS = new HashMap(); // Operator definitions OPDEFS.put("(" , new OpDef( 0, OPEN , GROUP )); OPDEFS.put(")" , new OpDef( 0, CLOSE , GROUP )); OPDEFS.put("[" , new OpDef( 0, OPEN , READ_WORD )); OPDEFS.put("]" , new OpDef( 0, CLOSE , READ_WORD )); OPDEFS.put("~" , new OpDef( 1, UNARY , BITWISE_NOT )); OPDEFS.put("!" , new OpDef( 1, UNARY , LOGICAL_NOT )); OPDEFS.put("-" , new OpDef( 1, UNARY , NEGATE )); OPDEFS.put("ceil" , new OpDef( 1, UNARY , CEIL )); OPDEFS.put("float" , new OpDef( 1, UNARY , FLOAT )); OPDEFS.put("floor" , new OpDef( 1, UNARY , FLOOR )); OPDEFS.put("round" , new OpDef( 1, UNARY , ROUND )); OPDEFS.put("trunc" , new OpDef( 1, UNARY , TRUNC )); OPDEFS.put("word" , new OpDef( 1, UNARY , WORD )); OPDEFS.put("xfloat", new OpDef( 1, UNARY , XFLOAT )); OPDEFS.put("xword" , new OpDef( 1, UNARY , XWORD )); OPDEFS.put("/" , new OpDef( 2, BINARY, DIVIDE )); OPDEFS.put("*" , new OpDef( 2, BINARY, MULTIPLY )); OPDEFS.put("%" , new OpDef( 2, BINARY, REMAINDER )); OPDEFS.put("+" , new OpDef( 3, BINARY, ADD )); OPDEFS.put("<<" , new OpDef( 4, BINARY, SHIFT_LEFT )); OPDEFS.put(">>" , new OpDef( 4, BINARY, SHIFT_RIGHT )); OPDEFS.put(">>>" , new OpDef( 4, BINARY, SHIFT_RIGHT_ARITHMETIC)); OPDEFS.put(">" , new OpDef( 5, BINARY, GREATER_SIGNED )); OPDEFS.put(">_" , new OpDef( 5, BINARY, GREATER_UNSIGNED )); OPDEFS.put(">=" , new OpDef( 5, BINARY, GREATER_EQUAL_SIGNED )); OPDEFS.put(">=_" , new OpDef( 5, BINARY, GREATER_EQUAL_UNSIGNED)); OPDEFS.put("<" , new OpDef( 5, BINARY, LESS_SIGNED )); OPDEFS.put("<_" , new OpDef( 5, BINARY, LESS_UNSIGNED )); OPDEFS.put("<=" , new OpDef( 5, BINARY, LESS_EQUAL_SIGNED )); OPDEFS.put("<=_" , new OpDef( 5, BINARY, LESS_EQUAL_UNSIGNED )); OPDEFS.put("==" , new OpDef( 6, BINARY, EQUAL )); OPDEFS.put("!=" , new OpDef( 6, BINARY, NOT_EQUAL )); OPDEFS.put("&" , new OpDef( 7, BINARY, BITWISE_AND )); OPDEFS.put("^" , new OpDef( 8, BINARY, BITWISE_XOR )); OPDEFS.put("|" , new OpDef( 9, BINARY, BITWISE_OR )); OPDEFS.put("&&" , new OpDef(10, BINARY, LOGICAL_AND )); OPDEFS.put("^^" , new OpDef(11, BINARY, LOGICAL_XOR )); OPDEFS.put("||" , new OpDef(12, BINARY, LOGICAL_OR )); // Instruction ID literal definitions LITDEFS.put("add_imm", 0); LITDEFS.put("mulu" , 38); LITDEFS.put("add_reg", 1); LITDEFS.put("not" , 39); LITDEFS.put("addf.s" , 2); LITDEFS.put("notbsu" , 40); LITDEFS.put("addi" , 3); LITDEFS.put("or" , 41); LITDEFS.put("and" , 4); LITDEFS.put("orbsu" , 42); LITDEFS.put("andbsu" , 5); LITDEFS.put("ori" , 43); LITDEFS.put("andi" , 6); LITDEFS.put("ornbsu" , 44); LITDEFS.put("andnbsu", 7); LITDEFS.put("out.b" , 45); LITDEFS.put("bcond" , 8); LITDEFS.put("out.h" , 46); LITDEFS.put("caxi" , 9); LITDEFS.put("out.w" , 47); LITDEFS.put("cli" , 10); LITDEFS.put("reti" , 48); LITDEFS.put("cmp_imm", 11); LITDEFS.put("rev" , 49); LITDEFS.put("cmp_reg", 12); LITDEFS.put("sar_imm", 50); LITDEFS.put("cmpf.s" , 13); LITDEFS.put("sar_reg", 51); LITDEFS.put("cvt.sw" , 14); LITDEFS.put("sch0bsd", 52); LITDEFS.put("cvt.ws" , 15); LITDEFS.put("sch0bsu", 53); LITDEFS.put("div" , 16); LITDEFS.put("sch1bsd", 54); LITDEFS.put("divf.s" , 17); LITDEFS.put("sch1bsu", 55); LITDEFS.put("divu" , 18); LITDEFS.put("sei" , 56); LITDEFS.put("halt" , 19); LITDEFS.put("setf" , 57); LITDEFS.put("in.b" , 20); LITDEFS.put("shl_imm", 58); LITDEFS.put("in.h" , 21); LITDEFS.put("shl_reg", 59); LITDEFS.put("in.w" , 22); LITDEFS.put("shr_imm", 60); LITDEFS.put("jal" , 23); LITDEFS.put("shr_reg", 61); LITDEFS.put("jmp" , 24); LITDEFS.put("st.b" , 62); LITDEFS.put("jr" , 25); LITDEFS.put("st.h" , 63); LITDEFS.put("ld.b" , 26); LITDEFS.put("st.w" , 64); LITDEFS.put("ld.h" , 27); LITDEFS.put("stsr" , 65); LITDEFS.put("ld.w" , 28); LITDEFS.put("sub" , 66); LITDEFS.put("ldsr" , 29); LITDEFS.put("subf.s" , 67); LITDEFS.put("mov_imm", 30); LITDEFS.put("trap" , 68); LITDEFS.put("mov_reg", 31); LITDEFS.put("trnc.sw", 69); LITDEFS.put("movbsu" , 32); LITDEFS.put("xb" , 70); LITDEFS.put("movea" , 33); LITDEFS.put("xh" , 71); LITDEFS.put("movhi" , 34); LITDEFS.put("xor" , 72); LITDEFS.put("mpyhw" , 35); LITDEFS.put("xorbsu" , 73); LITDEFS.put("mul" , 36); LITDEFS.put("xori" , 74); LITDEFS.put("mulf.s" , 37); LITDEFS.put("xornbsu", 75); LITDEFS.put("illegal", -1); // Functional symbol definitions SYMDEFS.put("address" , ADDRESS ); SYMDEFS.put("code" , CODE ); SYMDEFS.put("cond" , COND ); SYMDEFS.put("disp" , DISP ); SYMDEFS.put("format" , FORMAT ); SYMDEFS.put("imm" , IMM ); SYMDEFS.put("id" , ID ); SYMDEFS.put("opcode" , OPCODE ); SYMDEFS.put("reg1" , REG1 ); SYMDEFS.put("reg2" , REG2 ); SYMDEFS.put("regid" , REGID ); SYMDEFS.put("size" , SIZE ); SYMDEFS.put("subopcode", SUBOPCODE); SYMDEFS.put("value" , VALUE ); SYMDEFS.put("vector" , VECTOR ); // Program register symbol definitions SYMDEFS.put("r0" , 100); SYMDEFS.put("r16", 116); SYMDEFS.put("r1" , 101); SYMDEFS.put("r17", 117); SYMDEFS.put("r2" , 102); SYMDEFS.put("r18", 118); SYMDEFS.put("r3" , 103); SYMDEFS.put("r19", 119); SYMDEFS.put("r4" , 104); SYMDEFS.put("r20", 120); SYMDEFS.put("r5" , 105); SYMDEFS.put("r21", 121); SYMDEFS.put("r6" , 106); SYMDEFS.put("r22", 122); SYMDEFS.put("r7" , 107); SYMDEFS.put("r23", 123); SYMDEFS.put("r8" , 108); SYMDEFS.put("r24", 124); SYMDEFS.put("r9" , 109); SYMDEFS.put("r25", 125); SYMDEFS.put("r10", 110); SYMDEFS.put("r26", 126); SYMDEFS.put("r11", 111); SYMDEFS.put("r27", 127); SYMDEFS.put("r12", 112); SYMDEFS.put("r28", 128); SYMDEFS.put("r13", 113); SYMDEFS.put("r29", 129); SYMDEFS.put("r14", 114); SYMDEFS.put("r30", 130); SYMDEFS.put("r15", 115); SYMDEFS.put("r31", 131); SYMDEFS.put("hp" , 102); SYMDEFS.put("gp" , 104); SYMDEFS.put("sp" , 103); SYMDEFS.put("tp" , 105); SYMDEFS.put("lp" , 131); // System register symbol definitions SYMDEFS.put("adtre", 200 + Vue.ADTRE); SYMDEFS.put("chcw" , 200 + Vue.CHCW ); SYMDEFS.put("ecr" , 200 + Vue.ECR ); SYMDEFS.put("eipc" , 200 + Vue.EIPC ); SYMDEFS.put("eipsw", 200 + Vue.EIPSW); SYMDEFS.put("fepc" , 200 + Vue.FEPC ); SYMDEFS.put("fepsw", 200 + Vue.FEPSW); SYMDEFS.put("pc" , Vue.PC ); SYMDEFS.put("psw" , 200 + Vue.PSW ); SYMDEFS.put("sr29" , 229 ); SYMDEFS.put("sr31" , 231 ); LITDEFS.put("pir" , 0x00005346); LITDEFS.put("tkcw" , 0x000000E0); LITDEFS.put("sr30" , 0x00000004); }; /////////////////////////////////////////////////////////////////////////// // Classes // /////////////////////////////////////////////////////////////////////////// // Operator definition private static class OpDef { int id; // Identifier int precedence; // Operator precedence int type; // Operator category // Constructor OpDef(int precedence, int type, int id) { this.id = id; this.precedence = precedence; this.type = type; } } // Expression token private static class Token { int id; // Operator or symbol identifier Token left; // Left operand Token parent; // Containing operator int precedence; // Operator precedence Token right; // Right operand int start; // Character position in expression String text; // Display text int type; // Token category int value; // Literal value // Constructor Token(int type, int start, String text) { this.start = start; this.text = text; this.type = type; } // Retrieve the applicable serialized "value" int flatten() { return type == FLOAT || type == WORD ? value : id; } } /////////////////////////////////////////////////////////////////////////// // Constructors // /////////////////////////////////////////////////////////////////////////// // Default constructor public Breakpoint(Vue vue) { addresses = ""; condition = ""; errCode = new int [] { NONE, NONE }; errPosition = new int [] { 0, 0 }; errText = new String[] { "", "" }; name = ""; ranges = new int[0][]; tokens = new Token[0]; this.vue = vue; } /////////////////////////////////////////////////////////////////////////// // Public Methods // /////////////////////////////////////////////////////////////////////////// // Produce a string representation of the internal token list public String debug() { var ret = new StringBuilder(); // 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 FLOAT : type = "Float" ; break; case SYMBOL: type = "Symbol"; break; case UNARY : type = "Unary" ; break; case WORD : type = "Word" ; break; } ret.append(String.format("%-6s ", type)); // Value or ID switch (tok.type) { case BINARY: // Fallthrough case SYMBOL: // Fallthrough case UNARY : ret.append(Integer.toString(tok.id)); break; case FLOAT: ret.append(String.format("%.6f", Float.intBitsToFloat(tok.value))); break; case WORD: ret.append(String.format( (Math.abs(tok.value) & 0xFFFF0000) != 0 ? "0x%08X" : "%d", tok.value)); } // 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 test() { var ret = new StringBuilder(); 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(); } // Evaluate the condition against its emulation context public boolean evaluate() { return vue == null ? false : vue.evaluate(this); } // Retrieve the most recent input addresses public String getAddresses() { return addresses; } // Retrieve the most recent input condition public String getCondition() { return condition; } // Retrieve the most recent error code public int getErrorCode(int type) { return type < 0 || type > 1 ? BADTYPE : errCode[type]; } // Retrieve the most recent error character position public int getErrorPosition(int type) { return type < 0 || type > 1 ? BADTYPE : errPosition[type]; } // Retrieve the most recent error text public String getErrorText(int type) { return type < 0 || type > 1 ? null : errText[type]; } // Retrieve whether the breakpoint hooks exceptions public boolean getException() { return (hooks & EXCEPTION) != 0; } // Retrieve whether the breakpoint hooks executions public boolean getExecute() { return (hooks & EXECUTE) != 0; } // Retrieve whether the breakpoint hooks reads public boolean getRead() { return (hooks & READ) != 0; } // Retrieve whether the breakpoint hooks writes public boolean getWrite() { return (hooks & WRITE) != 0; } // Retrieve the display name public String getName() { return name; } // 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) { errCode [ADDRESS] = BADLITERAL; errPosition[ADDRESS] = start + 1; errText [ADDRESS] = 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 errCode [ADDRESS] = UNEXPECTED; errPosition[ADDRESS] = x + 1; errText [ADDRESS] = Character.toString(c); if (vue != null) vue.updateRanges(this); return false; } // x // Unexpected end of input if (mode == 0 && ranges.size() > 0) { errCode [ADDRESS] = EARLYEOF; errPosition[ADDRESS] = x + 1; errText [ADDRESS] = ""; if (vue != null) vue.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()][]); errCode [ADDRESS] = NONE; errPosition[ADDRESS] = 0; errText [ADDRESS] = ""; 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; errCode [CONDITION] = NONE; errPosition[CONDITION] = 0; errText [CONDITION] = ""; tokens = new Token[0]; // Process the expression var tokens = parse(); if (tokens == null || !validate(tokens)) { if (vue != null) vue.updateTokens(this); return false; } tree(tokens); // The expression is empty if (tokens.size() == 0) { if (vue != null) vue.updateTokens(this); return true; } // Produce an RPN-ordered list of tokens var tok = tokens.remove(0); 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 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 the display name public void setName(String name) { this.name = name == null ? "" : name; } // Specify whether the breakpoint hooks reads public void setRead(boolean hook) { hooks = hook ? hooks | READ : hooks & ~READ; if (vue != null) 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 // /////////////////////////////////////////////////////////////////////////// // Evaluate the condition for a Java emulation context boolean evaluate(int[] stack) { // The condition is empty if (tokens.length == 0) return isEnabled && errCode[CONDITION] == NONE; // Process tokens int size = 0; for (var tok : tokens) { switch (tok.type) { case BINARY: size = evalBinary(tok.id, stack, size); continue; case SYMBOL: size = evalSymbol(tok.id, stack, size); continue; case UNARY : evalUnary (tok.id, stack, size); continue; } stack[size++] = tok.type; stack[size++] = tok.value; } return (stack[1] & (stack[0] == WORD ? 0xFFFFFFFF : 0x7FFFFFFF)) != 0; } // Determine the required stack size to evaluate the expression int depth() { // Error checking if (errCode[CONDITION] != 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 FLOAT : // Fallthrough case SYMBOL: // Fallthrough case WORD : max = Math.max(max, ++size); } return max; } // Produce a one-dimensional array from the address ranges int[] flattenRanges() { var ret = new int[ranges.length * 2]; for (int x = 0; x < ranges.length; x++) { var range = ranges[x]; ret[x / 2 ] = range[0]; ret[x / 2 + 1] = range[1]; } return ret; } // Produce a one-dimensional array from the condition tokens int[] flattenTokens() { var ret = new int[tokens.length * 2]; for (int x = 0; x < tokens.length; x++) { var token = tokens[x]; ret[x / 2 ] = token.type; ret[x / 2 + 1] = token.flatten(); } return ret; } // Retrieve the bit mask for enabled hooks int getHooks() { return hooks; } // Retrieve the list of address ranges int[][] getRanges() { return ranges; } // Retrieve the list of condition tokens Token[] getTokens() { return tokens; } // The breakpoint is being removed from its emulation context void remove() { vue = null; } /////////////////////////////////////////////////////////////////////////// // Private Methods // /////////////////////////////////////////////////////////////////////////// // Adjust a float value as needed private static float fixFloat(float value) { int bits = Float.floatToRawIntBits(value); int exp = bits & 0x7F800000; int digits = bits & 0x007FFFFF; return (bits & 0x7FFFFFFF) == 0 || // Zero exp == 0x7F800000 || // Indefinite exp == 0 && digits != 0 // Denormal ? 0 : value; } // Parse a condition into tokens private ArrayList parse() { 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; } // 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') { errCode [CONDITION] = UNEXPECTED; errPosition[CONDITION] = x + 1; errText [CONDITION] = Character.toString(c); return null; } // Configure as a hexadecimal integer isHex = true; continue; } // The literal contains "." if (c == '.') { // "." cannot appear here if (isHex || isFloat) { errCode [CONDITION] = UNEXPECTED; errPosition[CONDITION] = x + 1; errText [CONDITION] = 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(WORD, start, new String(chars, start, x - start)); // Parse the literal value try { if (isHex) ret.value = (int) Long.parseLong(ret.text.substring(2), 16); else if (isFloat) { ret.type = FLOAT; ret.value = Float.floatToRawIntBits( fixFloat(Float.parseFloat(ret.text))); } else ret.value = Integer.parseInt(ret.text); return ret; } // Could not parse the value catch (Exception e) { errCode [CONDITION] = BADLITERAL; errPosition[CONDITION] = x + 1; errText [CONDITION] = ret.text; return null; } } // x return null; // Unreachable } // Parse an operator private Token parseOperator(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 could be part of the token if (!( c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == ' ' || c == '\t' )) continue; // Produce a new token var ret = new Token(0, start, null); // Find the longest operator match for (int length = x - start; length >= 1; length--) { String text = new String(chars, start, length); var def = OPDEFS.get(text); // There is no matching operator if (def == null) continue; // A matching operator was found ret.id = def.id; ret.precedence = def.precedence; ret.text = text; ret.type = def.type; return ret; } // The operator was not identified errCode [CONDITION] = BADTOKEN; errPosition[CONDITION] = start + 1; errText [CONDITION] = Character.toString(chars[start]); return null; } // x return null; // Unreachable } // 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 var def = OPDEFS.get(text); if (def != null) { ret.id = def.id; ret.precedence = def.precedence; ret.type = def.type; return ret; } // The token is a literal Integer value = LITDEFS.get(text); if (value != null) { ret.type = WORD; ret.value = value; return ret; } // The token is a symbol value = SYMDEFS.get(text); if (value != null) { ret.id = value; ret.type = SYMBOL; return ret; } // The token is not recognized errCode [CONDITION] = BADTOKEN; errPosition[CONDITION] = x + 1; errText [CONDITION] = ret.text; return null; } // x return null; // Unreachable } // Build an expression tree from a list of tokens private 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 == READ_WORD) { var tok = new Token(UNARY, 0, "{Read Word}"); tok.id = READ_WORD; 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) { errCode [CONDITION] = INVALID; errPosition[CONDITION] = tok.start; errText [CONDITION] = 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)) { errCode [CONDITION] = NESTING; errPosition[CONDITION] = tok.start; errText [CONDITION] = 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 if (!stack.empty()) { errCode [CONDITION] = EARLYEOF; errPosition[CONDITION] = condition.length(); errText [CONDITION] = stack.pop().text; } // Successfully parsed the condition return true; } /////////////////////////////////////////////////////////////////////////// // Evaluation Methods // /////////////////////////////////////////////////////////////////////////// // Resolve a float from word bits private static float asFloat(int value) { return Float.intBitsToFloat(value); } // Resolve the word bits of a float private static int asWord(float value) { return Float.floatToRawIntBits(fixFloat(value)); } // Convert an unsigned word to a float private static float ufloat(int value) { return (float) (value & 0xFFFFFFFFL); } // Convert a float to a word private static int toWord(float value) { value = (float) Math.round(value); return value >= 0x7FFFFFFF || value < 0x80000000 ? 0 : (int) value; } // Evaluate a binary operator private static int evalBinary(int id, int[] stack, int size) { int rv = stack[size - 1]; int rt = stack[size - 2]; boolean rw = rt == WORD; int lv = stack[size - 3]; int lt = stack[size - 4]; boolean lw = lt == WORD; int type = rw && lw ? WORD : FLOAT; int value = 0; switch (id) { // Arithmetic case ADD : value = evalAdd (lw, lv, rw, rv); break; case DIVIDE : value = evalDivide (lw, lv, rw, rv); break; case MULTIPLY : value = evalMultiply (lw, lv, rw, rv); break; case REMAINDER : value = evalRemainder (lw, lv, rw, rv); break; case SUBTRACT : value = evalSubtract (lw, lv, rw, rv); break; default: type = WORD; switch (id) { // Bitwise case BITWISE_AND : value = evalBitwiseAnd (lw, lv, rw, rv); break; case BITWISE_OR : value = evalBitwiseOr (lw, lv, rw, rv); break; case BITWISE_XOR : value = evalBitwiseXOr (lw, lv, rw, rv); break; case SHIFT_LEFT : value = evalShiftLeft (lw, lv, rw, rv); break; case SHIFT_RIGHT : value = evalShiftRight (lw, lv, rw, rv); break; case SHIFT_RIGHT_ARITHMETIC: value = evalShiftRightArithmetic(lw, lv, rw, rv); break; // Relational case EQUAL : value = evalEqual (lw, lv, rw, rv); break; case GREATER_EQUAL_SIGNED : value = evalGreaterEqualSigned (lw, lv, rw, rv); break; case GREATER_EQUAL_UNSIGNED: value = evalGreaterEqualUnsigned(lw, lv, rw, rv); break; case GREATER_SIGNED : value = evalGreaterSigned (lw, lv, rw, rv); break; case GREATER_UNSIGNED : value = evalGreaterUnsigned (lw, lv, rw, rv); break; case LESS_EQUAL_SIGNED : value = evalLessEqualSigned (lw, lv, rw, rv); break; case LESS_EQUAL_UNSIGNED : value = evalLessEqualUnsigned (lw, lv, rw, rv); break; case LESS_SIGNED : value = evalLessSigned (lw, lv, rw, rv); break; case LESS_UNSIGNED : value = evalLessUnsigned (lw, lv, rw, rv); break; case NOT_EQUAL : value = evalNotEqual (lw, lv, rw, rv); break; // Logical default: lw = (lv & (lw ? 0xFFFFFFFF : 0x7FFFFFFF)) != 0; rw = (rv & (rw ? 0xFFFFFFFF : 0x7FFFFFFF)) != 0; // Evaluate && case LOGICAL_AND: type = lt; value = lv; if (lw) { type = rt; value = rv; } break; // Evaluate || case LOGICAL_OR: type = lt; value = lv; if (!lw) { type = rt; value = rv; } break; // Evaluate ^^ case LOGICAL_XOR: type = rt; value = rv; if (lw == rw) { type = WORD; value = 0; } else if (lw) { type = lt; value = lv; } break; }} stack[size - 4] = type; stack[size - 3] = value; return size - 2; } // Evaluate a functional symbol private int evalSymbol(int id, int[] stack, int size) { int ret = 0; var vue = (JavaVue) this.vue; if (id == Vue.PC) ret = vue.cpu.pc; else if (id >= 200) ret = vue.cpu.getSystemRegister(id - 200); else if (id >= 100) ret = vue.cpu.program[id - 100]; else if (vue.cpu.stage == CPU.EXCEPTION && id == CODE) ret = vue.cpu.exception.code; else if (vue.cpu.stage == CPU.EXECUTE) switch (id) { case ADDRESS : ret = evalAddress (); break; case COND : ret = evalCond (); break; case DISP : ret = evalDisp (); break; case FORMAT : ret = vue.cpu.inst.format; break; case ID : ret = vue.cpu.inst.id ; break; case IMM : ret = evalImm (); break; case OPCODE : ret = vue.cpu.inst.opcode; break; case REG1 : ret = evalReg1 (); break; case REG2 : ret = evalReg2 (); break; case REGID : ret = evalRegId (); break; case SIZE : ret = vue.cpu.inst.size ; break; case SUBOPCODE: ret = evalSubopcode (); break; case VALUE : ret = evalValue (); break; case VECTOR : ret = evalVector (); break; } stack[size++] = WORD; stack[size++] = ret; return size; } // Evaluate a unary operator private void evalUnary(int id, int[] stack, int size) { int type = stack[size - 2]; int value = stack[size - 1]; boolean isWord = type == WORD; switch (id) { case BITWISE_NOT: value = evalBitwiseNot(isWord, value); type = WORD ; break; case LOGICAL_NOT: value = evalLogicalNot(isWord, value); type = WORD ; break; case NEGATE : value = evalNegate (isWord, value); break; case CEIL : value = evalCeil (isWord, value); break; case FLOAT : value = evalFloat (isWord, value); type = FLOAT; break; case FLOOR : value = evalFloor (isWord, value); break; case READ_WORD : value = evalReadWord (isWord, value); type = WORD ; break; case ROUND : value = evalRound (isWord, value); break; case TRUNC : value = evalTrunc (isWord, value); break; case WORD : value = evalWord (isWord, value); type = WORD ; break; case XFLOAT : value = evalXFloat (isWord, value); type = FLOAT; break; case XWORD : type = WORD; } stack[size - 2] = type; stack[size - 1] = value; } /////////////////////////////////////////////////////////////////////////// // Binary Methods // /////////////////////////////////////////////////////////////////////////// // Evaluate + private static int evalAdd( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return leftWord && rightWord ? leftValue + rightValue : asWord( (leftWord ? (float) leftValue : asFloat(leftValue )) + (rightWord ? (float) rightValue : asFloat(rightValue)) ); } // Evaluate & private static int evalBitwiseAnd( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord ? leftValue : toWord(asFloat(leftValue ))) & (rightWord ? rightValue : toWord(asFloat(rightValue))); } // Evaluate | private static int evalBitwiseOr( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord ? leftValue : toWord(asFloat(leftValue ))) | (rightWord ? rightValue : toWord(asFloat(rightValue))); } // Evaluate ^ private static int evalBitwiseXOr( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord ? leftValue : toWord(asFloat(leftValue ))) ^ (rightWord ? rightValue : toWord(asFloat(rightValue))); } // Evaluate / private static int evalDivide( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (rightWord ? rightValue : rightValue & 0x7FFFFFFF) == 0 ? 0 : leftWord && rightWord ? leftValue / rightValue : asWord( (leftWord ? (float) leftValue : asFloat(leftValue )) / (rightWord ? (float) rightValue : asFloat(rightValue)) ); } // Evaluate == private static int evalEqual( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord && rightWord ? leftValue == rightValue : ( (leftWord ? (float) leftValue : asFloat(leftValue )) == (rightWord ? (float) rightValue : asFloat(rightValue)) )) ? 1 : 0; } // Evaluate >= private static int evalGreaterEqualSigned( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord && rightWord ? leftValue >= rightValue : ( (leftWord ? (float) leftValue : asFloat(leftValue )) >= (rightWord ? (float) rightValue : asFloat(rightValue)) )) ? 1 : 0; } // Evaluate >=_ private static int evalGreaterEqualUnsigned( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord && rightWord ? Integer.compareUnsigned(leftValue, rightValue) >= 0 : ( (leftWord ? ufloat(leftValue ) : asFloat(leftValue )) >= (rightWord ? ufloat(rightValue) : asFloat(rightValue)) )) ? 1 : 0; } // Evaluate > private static int evalGreaterSigned( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord && rightWord ? leftValue > rightValue : ( (leftWord ? (float) leftValue : asFloat(leftValue )) > (rightWord ? (float) rightValue : asFloat(rightValue)) )) ? 1 : 0; } // Evaluate >_ private static int evalGreaterUnsigned( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord && rightWord ? Integer.compareUnsigned(leftValue, rightValue) > 0 : ( (leftWord ? ufloat(leftValue ) : asFloat(leftValue )) > (rightWord ? ufloat(rightValue) : asFloat(rightValue)) )) ? 1 : 0; } // Evaluate <= private static int evalLessEqualSigned( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord && rightWord ? leftValue <= rightValue : ( (leftWord ? (float) leftValue : asFloat(leftValue )) <= (rightWord ? (float) rightValue : asFloat(rightValue)) )) ? 1 : 0; } // Evaluate <=_ private static int evalLessEqualUnsigned( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord && rightWord ? Integer.compareUnsigned(leftValue, rightValue) <= 0 : ( (leftWord ? ufloat(leftValue ) : asFloat(leftValue )) <= (rightWord ? ufloat(rightValue) : asFloat(rightValue)) )) ? 1 : 0; } // Evaluate < private static int evalLessSigned( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord && rightWord ? leftValue < rightValue : ( (leftWord ? (float) leftValue : asFloat(leftValue )) < (rightWord ? (float) rightValue : asFloat(rightValue)) )) ? 1 : 0; } // Evaluate <_ private static int evalLessUnsigned( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord && rightWord ? Integer.compareUnsigned(leftValue, rightValue) < 0 : ( (leftWord ? ufloat(leftValue ) : asFloat(leftValue )) < (rightWord ? ufloat(rightValue) : asFloat(rightValue)) )) ? 1 : 0; } // Evaluate * private static int evalMultiply( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return leftWord && rightWord ? leftValue * rightValue : asWord( (leftWord ? (float) leftValue : asFloat(leftValue )) * (rightWord ? (float) rightValue : asFloat(rightValue)) ); } // Evaluate != private static int evalNotEqual( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (leftWord && rightWord ? leftValue != rightValue : ( (leftWord ? (float) leftValue : asFloat(leftValue )) != (rightWord ? (float) rightValue : asFloat(rightValue)) )) ? 1 : 0; } // Evaluate % private static int evalRemainder( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return (rightWord ? rightValue : rightValue & 0x7FFFFFFF) == 0 ? 0 : leftWord && rightWord ? leftValue % rightValue : asWord( (leftWord ? (float) leftValue : asFloat(leftValue )) % (rightWord ? (float) rightValue : asFloat(rightValue)) ); } // Evaluate << private static int evalShiftLeft( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { leftValue = leftWord ? leftValue : toWord(asFloat(leftValue )); leftValue = (rightWord ? rightValue : toWord(asFloat(rightValue)))&31; return leftValue << rightValue; } // Evaluate >>> private static int evalShiftRight( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { leftValue = leftWord ? leftValue : toWord(asFloat(leftValue )); leftValue = (rightWord ? rightValue : toWord(asFloat(rightValue)))&31; return leftValue >>> rightValue; } // Evaluate >> private static int evalShiftRightArithmetic( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { leftValue = leftWord ? leftValue : toWord(asFloat(leftValue )); leftValue = (rightWord ? rightValue : toWord(asFloat(rightValue)))&31; return leftValue >> rightValue; } // Evaluate - private static int evalSubtract( boolean leftWord, int leftValue, boolean rightWord, int rightValue) { return leftWord && rightWord ? leftValue - rightValue : asWord( (leftWord ? (float) leftValue : asFloat(leftValue )) - (rightWord ? (float) rightValue : asFloat(rightValue)) ); } /////////////////////////////////////////////////////////////////////////// // Symbol Methods // /////////////////////////////////////////////////////////////////////////// // Evaluate address private int evalAddress() { var vue = (JavaVue) this.vue; if (vue.cpu.inst.format == 6) return vue.cpu.program[vue.cpu.inst.reg1] + vue.cpu.inst.disp; switch (vue.cpu.inst.id) { case Vue.BCOND: case Vue.JAL: case Vue.JMP: case Vue.JR: return vue.cpu.pc + vue.cpu.inst.disp; case Vue.RETI: return vue.cpu.psw_np != 0 ? vue.cpu.fepc : vue.cpu.eipc; case Vue.TRAP: return 0xFFFFFFA0 + vue.cpu.inst.imm & 0xFFFFFFF0; } return 0; } // Evaluate cond private int evalCond() { var vue = (JavaVue) this.vue; switch (vue.cpu.inst.id) { case Vue.BCOND: return vue.cpu.inst.cond; case Vue.SETF : return vue.cpu.inst.imm; } return 0; } // Evaluate disp private int evalDisp() { var vue = (JavaVue) this.vue; switch (vue.cpu.inst.format) { case 3: case 4: case 6: return vue.cpu.inst.disp; } return 0; } // Evaluate imm private int evalImm() { var vue = (JavaVue) this.vue; switch (vue.cpu.inst.format) { case 2: case 5: return vue.cpu.inst.imm; } return 0; } // Evaluate reg1 private int evalReg1() { var vue = (JavaVue) this.vue; switch (vue.cpu.inst.format) { case 1: case 5: case 6: case 7: return vue.cpu.inst.reg1; } return 0; } // Evaluate reg2 private int evalReg2() { var vue = (JavaVue) this.vue; switch (vue.cpu.inst.format) { case 1: case 2: case 5: case 6: case 7: return vue.cpu.inst.reg2; } return 0; } // Evaluate regid private int evalRegId() { var vue = (JavaVue) this.vue; switch (vue.cpu.inst.id) { case Vue.LDSR: case Vue.STSR: return vue.cpu.inst.imm; } return 0; } // Evaluate subopcode private int evalSubopcode() { var vue = (JavaVue) this.vue; switch (vue.cpu.inst.opcode) { case 0x1F: return vue.cpu.inst.imm; case 0x3E: return vue.cpu.inst.subopcode; } return 0; } // Evaluate value private int evalValue() { var vue = (JavaVue) this.vue; return vue.cpu.inst.format == 6 ? vue.cpu.access.value : 0; } // Evaluate vector private int evalVector() { var vue = (JavaVue) this.vue; return vue.cpu.inst.id == Vue.TRAP ? vue.cpu.inst.imm : 0; } /////////////////////////////////////////////////////////////////////////// // Unary Methods // /////////////////////////////////////////////////////////////////////////// // Evaluate ~ private static int evalBitwiseNot(boolean isWord, int value) { return isWord ? ~value : ~toWord(asFloat(value)); } // Evaluate ! private static int evalLogicalNot(boolean isWord, int value) { return (isWord ? value != 0 : (value & 0x7FFFFFFF) != 0) ? 1 : 0; } // Evaluate - private static int evalNegate(boolean isWord, int value) { return isWord ? -value : asWord(-asFloat(value)); } // Evaluate ceil private static int evalCeil(boolean isWord, int value) { return isWord ? value : asWord((float) Math.ceil(asFloat(value))); } // Evaluate float private static int evalFloat(boolean isWord, int value) { return isWord ? asWord((float) value) : value; } // Evaluate floor private static int evalFloor(boolean isWord, int value) { return isWord ? value : asWord((float) Math.floor(asFloat(value))); } // Evaluate [] private int evalReadWord(boolean isWord, int value) { var vue = (JavaVue) this.vue; return vue.read(isWord ? value : toWord(asFloat(value)), Vue.S32); } // Evaluate round private static int evalRound(boolean isWord, int value) { return isWord ? value : asWord((float) Math.round(asFloat(value))); } // Evaluate trunc private static int evalTrunc(boolean isWord, int value) { return isWord ? value : asWord((float) (value < 0 ? Math.ceil(asFloat(value)) : Math.ceil(asFloat(value)))); } // Evaluate word private static int evalWord(boolean isWord, int value) { return isWord ? value : toWord(asFloat(value)); } // Evaluate xfloat private static int evalXFloat(boolean isWord, int value) { return isWord ? asWord(asFloat(value)) : value; } }