1675 lines
60 KiB
Java
1675 lines
60 KiB
Java
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<String, Integer> LITDEFS;
|
|
private static final HashMap<String, OpDef > OPDEFS;
|
|
private static final HashMap<String, Integer> 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<String, Integer>();
|
|
OPDEFS = new HashMap<String, OpDef >();
|
|
SYMDEFS = new HashMap<String, Integer>();
|
|
|
|
// 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<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) {
|
|
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<Token> parse() {
|
|
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;
|
|
}
|
|
|
|
// 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<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 == 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<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) {
|
|
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;
|
|
}
|
|
|
|
}
|