// This file is included through NativeVue.c and cannot be built directly. */ #ifdef NATIVEVUE /////////////////////////////////////////////////////////////////////////////// // Constants // /////////////////////////////////////////////////////////////////////////////// // Hook flags #define BRK_EXCEPTION 0x00000001 #define BRK_EXECUTE 0x00000002 #define BRK_FRAME 0x00000004 #define BRK_READ 0x00000008 #define BRK_WRITE 0x00000010 // Token types #define BRK_BOOL 0 /* Value types */ #define BRK_SIGNED 1 #define BRK_UNSIGNED 2 #define BRK_FLOAT 3 #define BRK_SYMBOL 4 /* Symbol types */ #define BRK_PROREG 5 #define BRK_SYSREG 6 #define BRK_UNARY 7 /* Operator types */ #define BRK_BINARY 8 // Functional symbol IDs #define BRK_ADDRESS 0 #define BRK_BREAK 1 #define BRK_CODE 2 #define BRK_COND 3 #define BRK_DISP 4 #define BRK_FETCH 5 #define BRK_FORMAT 6 #define BRK_ID 7 #define BRK_IMM 8 #define BRK_OPCODE 9 #define BRK_REG1 10 #define BRK_REG2 11 #define BRK_REGID 12 #define BRK_SIZE 13 #define BRK_SUBOPCODE 14 #define BRK_TYPE 15 #define BRK_VALUE 16 #define BRK_VECTOR 17 // Evaluation operator IDs #define BRK_XS32 1 /* xs32, xword */ #define BRK_XU32 2 /* xu32, xuword */ #define BRK_XFLOAT 3 /* xfloat */ #define BRK_READ8 4 /* [] */ #define BRK_READ16 5 /* [] */ #define BRK_READ32 6 /* [] */ #define BRK_BOOL_ - 2 /* bool */ #define BRK_S32 - 3 /* s32, word */ #define BRK_U32 - 4 /* u32, uword */ #define BRK_FLOAT_ - 5 /* float */ #define BRK_ADD - 6 /* + */ #define BRK_AND_B - 7 /* & */ #define BRK_AND_L - 8 /* && */ #define BRK_CEIL - 9 /* ceil */ #define BRK_DIVIDE -10 /* / */ #define BRK_EQUAL -11 /* == */ #define BRK_FLOOR -12 /* floor */ #define BRK_GREATER -13 /* > */ #define BRK_GREQUAL -14 /* >= */ #define BRK_LEFT_L -15 /* << */ #define BRK_LEQUAL -16 /* <= */ #define BRK_LESS -17 /* < */ #define BRK_MULTIPLY -18 /* * */ #define BRK_NEQUAL -19 /* != */ #define BRK_NOT_B -20 /* ~ */ #define BRK_NOT_L -21 /* ! */ #define BRK_NEGATE -22 /* - */ #define BRK_OR_B -23 /* | */ #define BRK_OR_L -24 /* || */ #define BRK_REMAINDER -25 /* % */ #define BRK_RIGHT_A -26 /* >> */ #define BRK_RIGHT_L -27 /* >>> */ #define BRK_ROUND -28 /* round */ #define BRK_S8 -29 /* s8, byte */ #define BRK_S16 -30 /* s16, halfword */ #define BRK_SUBTRACT -31 /* - */ #define BRK_TRUNC -32 /* trunc */ #define BRK_U8 -33 /* u8, ubyte */ #define BRK_U16 -34 /* u16, uhalfword */ #define BRK_XOR_B -36 /* ^ */ #define BRK_XOR_L -37 /* ^^ */ /////////////////////////////////////////////////////////////////////////////// // Internal Functions // /////////////////////////////////////////////////////////////////////////////// // Adjust a float value as needed static int brkAdjust(float value) { int32_t bits = *(int32_t *)&value; int exp = bits & 0x7F800000; return (bits & 0x7FFFFFFF) == 0 || // Zero exp == 0x7F800000 || // Indefinite exp == 0 // Denormal ? 0 : bits; } // Evaluate a binary operator static int32_t brkEvalBinary(int32_t id, int32_t left, int32_t right) { // Processing by ID switch (id) { // Add case -BRK_ADD * 4 + BRK_BOOL: case -BRK_ADD * 4 + BRK_SIGNED: case -BRK_ADD * 4 + BRK_UNSIGNED: return left + right; case -BRK_ADD * 4 + BRK_FLOAT: return brkAdjust(*(float *)&left + *(float *)&right); // And Bitwise case -BRK_AND_B * 4 + BRK_BOOL: case -BRK_AND_B * 4 + BRK_FLOAT: // Prevented by parser case -BRK_AND_B * 4 + BRK_SIGNED: case -BRK_AND_B * 4 + BRK_UNSIGNED: return left & right; // And Logical case -BRK_AND_L * 4 + BRK_BOOL: case -BRK_AND_L * 4 + BRK_FLOAT: case -BRK_AND_L * 4 + BRK_SIGNED: case -BRK_AND_L * 4 + BRK_UNSIGNED: return left == 0 ? left : right; // Divide case -BRK_DIVIDE * 4 + BRK_BOOL: case -BRK_DIVIDE * 4 + BRK_SIGNED: return right == 0 ? 0 : left / right; case -BRK_DIVIDE * 4 + BRK_UNSIGNED: return right == 0 ? 0 : (uint32_t) left / right; case -BRK_DIVIDE * 4 + BRK_FLOAT: return right == 0 ? 0 : brkAdjust(*(float *)&left / *(float *)&right); // Equal case -BRK_EQUAL * 4 + BRK_BOOL: case -BRK_EQUAL * 4 + BRK_FLOAT: case -BRK_EQUAL * 4 + BRK_SIGNED: case -BRK_EQUAL * 4 + BRK_UNSIGNED: return left == right ? 1 : 0; // Greater case -BRK_GREATER * 4 + BRK_BOOL: case -BRK_GREATER * 4 + BRK_SIGNED: return left > right ? 1 : 0; case -BRK_GREATER * 4 + BRK_UNSIGNED: return (uint32_t) left > right ? 1 : 0; case -BRK_GREATER * 4 + BRK_FLOAT: return *(float *)&left > *(float *)&right ? 1 : 0; // Greater or Equal case -BRK_GREQUAL * 4 + BRK_BOOL: case -BRK_GREQUAL * 4 + BRK_SIGNED: return left >= right ? 1 : 0; case -BRK_GREQUAL * 4 + BRK_UNSIGNED: return (uint32_t) left >= right ? 1 : 0; case -BRK_GREQUAL * 4 + BRK_FLOAT: return *(float *)&left >= *(float *)&right ? 1 : 0; // Shift Left case -BRK_LEFT_L * 4 + BRK_BOOL: case -BRK_LEFT_L * 4 + BRK_FLOAT: // Prevented by parser case -BRK_LEFT_L * 4 + BRK_SIGNED: case -BRK_LEFT_L * 4 + BRK_UNSIGNED: return left << (right & 31); // Less or Equal case -BRK_LEQUAL * 4 + BRK_BOOL: case -BRK_LEQUAL * 4 + BRK_SIGNED: return left <= right ? 1 : 0; case -BRK_LEQUAL * 4 + BRK_UNSIGNED: return (uint32_t) left <= right ? 1 : 0; case -BRK_LEQUAL * 4 + BRK_FLOAT: return *(float *)&left <= *(float *)&right ? 1 : 0; // Less case -BRK_LESS * 4 + BRK_BOOL: case -BRK_LESS * 4 + BRK_SIGNED: return left < right ? 1 : 0; case -BRK_LESS * 4 + BRK_UNSIGNED: return (uint32_t) left < right ? 1 : 0; case -BRK_LESS * 4 + BRK_FLOAT: return *(float *)&left < *(float *)&right ? 1 : 0; // Multiply case -BRK_MULTIPLY * 4 + BRK_BOOL: case -BRK_MULTIPLY * 4 + BRK_SIGNED: return left * right; case -BRK_MULTIPLY * 4 + BRK_UNSIGNED: return (uint32_t) left * right; case -BRK_MULTIPLY * 4 + BRK_FLOAT: return brkAdjust(*(float *)&left * *(float *)&right); // Not Equal case -BRK_NEQUAL * 4 + BRK_BOOL: case -BRK_NEQUAL * 4 + BRK_FLOAT: case -BRK_NEQUAL * 4 + BRK_SIGNED: case -BRK_NEQUAL * 4 + BRK_UNSIGNED: return left != right ? 1 : 0; // Or Bitwise case -BRK_OR_B * 4 + BRK_BOOL: case -BRK_OR_B * 4 + BRK_FLOAT: // Prevented by parser case -BRK_OR_B * 4 + BRK_SIGNED: case -BRK_OR_B * 4 + BRK_UNSIGNED: return left | right; // Or Logical case -BRK_OR_L * 4 + BRK_BOOL: case -BRK_OR_L * 4 + BRK_FLOAT: case -BRK_OR_L * 4 + BRK_SIGNED: case -BRK_OR_L * 4 + BRK_UNSIGNED: return left != 0 ? left : right; // Remainder case -BRK_REMAINDER * 4 + BRK_BOOL: case -BRK_REMAINDER * 4 + BRK_SIGNED: return right == 0 ? 0 : left % right; case -BRK_REMAINDER * 4 + BRK_UNSIGNED: return right == 0 ? 0 : (uint32_t) left % right; case -BRK_REMAINDER * 4 + BRK_FLOAT: return right == 0 ? 0 : brkAdjust(fmodf(*(float *)&left, *(float *)&right)); // Shift Right Arithmetic case -BRK_RIGHT_A * 4 + BRK_BOOL: case -BRK_RIGHT_A * 4 + BRK_FLOAT: // Prevented by parser case -BRK_RIGHT_A * 4 + BRK_SIGNED: case -BRK_RIGHT_A * 4 + BRK_UNSIGNED: return SIGN_EXTEND(left >> (right & 31), 32 - (right & 31)); // Shift Right Logical case -BRK_RIGHT_L * 4 + BRK_BOOL: case -BRK_RIGHT_L * 4 + BRK_FLOAT: // Prevented by parser case -BRK_RIGHT_L * 4 + BRK_SIGNED: case -BRK_RIGHT_L * 4 + BRK_UNSIGNED: return left >> (right & 31) & ((int32_t) -1 << (right & 31)); // Subtract case -BRK_SUBTRACT * 4 + BRK_BOOL: case -BRK_SUBTRACT * 4 + BRK_SIGNED: case -BRK_SUBTRACT * 4 + BRK_UNSIGNED: return left - right; case -BRK_SUBTRACT * 4 + BRK_FLOAT: return brkAdjust(*(float *)&left - *(float *)&right); // Exclusive Or Bitwise case -BRK_XOR_B * 4 + BRK_BOOL: case -BRK_XOR_B * 4 + BRK_FLOAT: // Prevented by parser case -BRK_XOR_B * 4 + BRK_SIGNED: case -BRK_XOR_B * 4 + BRK_UNSIGNED: return left ^ right; // Exclusive Or Logical case -BRK_XOR_L * 4 + BRK_BOOL: case -BRK_XOR_L * 4 + BRK_FLOAT: case -BRK_XOR_L * 4 + BRK_SIGNED: case -BRK_XOR_L * 4 + BRK_UNSIGNED: return left != 0 && right != 0 ? 0 : left != 0 ? left : right; } // Unreachable return 0; } // Evaluate a unary operator static int32_t brkEvalUnary(Vue *vue, int32_t id, int32_t operand) { float val; // Working variable // Processing by ID switch (id) { // Cast to Boolean case -BRK_BOOL_ * 4 + BRK_BOOL: // Removed by parser case -BRK_BOOL_ * 4 + BRK_FLOAT: case -BRK_BOOL_ * 4 + BRK_SIGNED: case -BRK_BOOL_ * 4 + BRK_UNSIGNED: return operand == 0 ? 0 : 1; // Round up case -BRK_CEIL * 4 + BRK_BOOL: // Removed by parser case -BRK_CEIL * 4 + BRK_FLOAT: case -BRK_CEIL * 4 + BRK_SIGNED: // Removed by parser case -BRK_CEIL * 4 + BRK_UNSIGNED: // Removed by parser return brkAdjust(ceilf(*(float *)&operand)); // Cast to Float case -BRK_FLOAT_ * 4 + BRK_BOOL: case -BRK_FLOAT_ * 4 + BRK_FLOAT: // Removed by parser case -BRK_FLOAT_ * 4 + BRK_SIGNED: return brkAdjust(operand); case -BRK_FLOAT_ * 4 + BRK_UNSIGNED: return brkAdjust((uint32_t) operand); // Round down case -BRK_FLOOR * 4 + BRK_BOOL: // Removed by parser case -BRK_FLOOR * 4 + BRK_FLOAT: case -BRK_FLOOR * 4 + BRK_SIGNED: // Removed by parser case -BRK_FLOOR * 4 + BRK_UNSIGNED: // Removed by parser return brkAdjust(floorf(*(float *)&operand)); // Bitwise Not case -BRK_NOT_B * 4 + BRK_BOOL: case -BRK_NOT_B * 4 + BRK_FLOAT: // Prevented by parser case -BRK_NOT_B * 4 + BRK_SIGNED: case -BRK_NOT_B * 4 + BRK_UNSIGNED: return ~operand; // Logical Not case -BRK_NOT_L * 4 + BRK_BOOL: case -BRK_NOT_L * 4 + BRK_FLOAT: case -BRK_NOT_L * 4 + BRK_SIGNED: case -BRK_NOT_L * 4 + BRK_UNSIGNED: return operand == 0 ? 1 : 0; // Negate case -BRK_NEGATE * 4 + BRK_BOOL: case -BRK_NEGATE * 4 + BRK_SIGNED: case -BRK_NEGATE * 4 + BRK_UNSIGNED: // Removed by parser return -operand; case -BRK_NEGATE * 4 + BRK_FLOAT: return brkAdjust(-*(float *)&operand); // Read Byte case BRK_READ8: return vueRead(vue, operand, VUE_S8); // Read Halfword case BRK_READ16: return vueRead(vue, operand, VUE_S16); // Read Word case BRK_READ32: return vueRead(vue, operand, VUE_S32); // Round to Nearest case -BRK_ROUND * 4 + BRK_BOOL: // Removed by parser case -BRK_ROUND * 4 + BRK_FLOAT: case -BRK_ROUND * 4 + BRK_SIGNED: // Removed by parser case -BRK_ROUND * 4 + BRK_UNSIGNED: // Removed by parser return brkAdjust(roundf(*(float *)&operand)); // Cast to Signed Byte case -BRK_S8 * 4 + BRK_FLOAT: operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand); // Fallthrough case -BRK_S8 * 4 + BRK_BOOL: case -BRK_S8 * 4 + BRK_SIGNED: case -BRK_S8 * 4 + BRK_UNSIGNED: return SIGN_EXTEND(operand, 8); // Cast to Signed Halfword case -BRK_S16 * 4 + BRK_FLOAT: operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand); // Fallthrough case -BRK_S16 * 4 + BRK_BOOL: case -BRK_S16 * 4 + BRK_SIGNED: case -BRK_S16 * 4 + BRK_UNSIGNED: return SIGN_EXTEND(operand, 16); // Cast to Signed Word case -BRK_S32 * 4 + BRK_BOOL: case -BRK_S32 * 4 + BRK_SIGNED: // Removed by parser case -BRK_S32 * 4 + BRK_UNSIGNED: return operand; case -BRK_S32 * 4 + BRK_FLOAT: val = *(float *)&operand; return val >= -MAX_WORD && val < MAX_WORD ? (int32_t) val : 0; // Truncate case -BRK_TRUNC * 4 + BRK_BOOL: // Removed by parser case -BRK_TRUNC * 4 + BRK_FLOAT: case -BRK_TRUNC * 4 + BRK_SIGNED: // Removed by parser case -BRK_TRUNC * 4 + BRK_UNSIGNED: // Removed by parser return brkAdjust(truncf(*(float *)&operand)); // Cast to Unsigned Byte case -BRK_U8 * 4 + BRK_FLOAT: operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand); case -BRK_U8 * 4 + BRK_BOOL: case -BRK_U8 * 4 + BRK_SIGNED: case -BRK_U8 * 4 + BRK_UNSIGNED: return operand & 0xFF; // Cast to Unsigned Halfword case -BRK_U16 * 4 + BRK_FLOAT: operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand); case -BRK_U16 * 4 + BRK_BOOL: case -BRK_U16 * 4 + BRK_SIGNED: case -BRK_U16 * 4 + BRK_UNSIGNED: return operand & 0xFFFF; // Cast to Unsigned Word case -BRK_U32 * 4 + BRK_BOOL: case -BRK_U32 * 4 + BRK_SIGNED: case -BRK_U32 * 4 + BRK_UNSIGNED: // Removed by parser return operand; case -BRK_U32 * 4 + BRK_FLOAT: val = *(float *)&operand; return val >= 0 && val < MAX_UWORD ? (uint32_t) val : 0; // Reinterpret as Float case BRK_XFLOAT: return brkAdjust(*(float *)&operand); // Reinterpret as Signed Word, Reinterpret as Unsigned Word //case BRK_XS32: // Removed by parser //case BRK_XU32: // Removed by parser } // Unreachable return 0; } // Evaluate the condition, returning only the computed value static int32_t brkEvaluate(Core *core, Breakpoint *brk) { int32_t *stack = core->stack; Vue *vue = &core->vue; // Process all tokens int size = 0; Token *tok; int32_t x, sym; // Working variables for (tok = &brk->tokens[x=0]; x < brk->numTokens; tok = &brk->tokens[++x]) switch (tok->type) { // Binary operator case BRK_BINARY: stack[size - 2] = brkEvalBinary(tok->id, stack[size - 2], stack[size - 1]); size--; break; // Literal case BRK_BOOL: case BRK_FLOAT: case BRK_SIGNED: case BRK_UNSIGNED: stack[size++] = tok->id; break; // CPU register case BRK_PROREG: case BRK_SYSREG: stack[size++] = vueGetRegister(vue,tok->id,tok->type==BRK_SYSREG); break; // Unary operator case BRK_UNARY: stack[size - 1] = brkEvalUnary(vue, tok->id, stack[size - 1]); break; // Symbol case BRK_SYMBOL: sym = vue->cpu.inst.imm; // IMM, REGID, VECTOR switch (tok->id) { case BRK_ADDRESS : sym = vue->cpu.access.address; break; case BRK_BREAK : sym = core->breakType ; break; case BRK_CODE : sym = vue->cpu.exception ; break; case BRK_COND : sym = vue->cpu.inst.cond ; break; case BRK_DISP : sym = vue->cpu.inst.disp ; break; case BRK_FETCH : sym = vue->cpu.fetch ; break; case BRK_FORMAT : sym = vue->cpu.inst.format ; break; case BRK_ID : sym = vue->cpu.inst.id ; break; case BRK_OPCODE : sym = vue->cpu.inst.opcode ; break; case BRK_REG1 : sym = vue->cpu.inst.reg1 ; break; case BRK_REG2 : sym = vue->cpu.inst.reg2 ; break; case BRK_SIZE : sym = vue->cpu.inst.size ; break; case BRK_SUBOPCODE: sym = vue->cpu.inst.subopcode; break; case BRK_TYPE : sym = vue->cpu.access.type ; break; case BRK_VALUE : sym = vue->cpu.access.value ; break; } stack[size++] = sym; break; } // Return the remaining stack value return stack[0]; } // Determine whether the breakpoint applies to a given address range static vbool brkInRange(Breakpoint *brk, uint32_t start, uint32_t end) { // Implicit inclusion if (brk->numRanges == 0) return VUE_TRUE; // Check all ranges for (int x = 0; x < brk->numRanges; x++) { uint32_t *range = &brk->ranges[x * 2]; if ( start - range[0] <= range[1] - range[0] || range[0] - start <= end - start ) return VUE_TRUE; } return VUE_FALSE; } // Check for breakpoints static int32_t brkOnBreakpoint(Vue *vue, int32_t breakType) { Core *core = (Core *) vue->tag; uint32_t end = 0; vbool ranged = VUE_FALSE; uint32_t start = 0; core->breakType = breakType; // Processing for Execute if (breakType == BRK_EXECUTE) { ranged = VUE_TRUE; start = vue->cpu.pc; end = start + vue->cpu.inst.size - 1; } // Processing for Read and Write else if (breakType == BRK_READ || breakType == BRK_WRITE) { ranged = VUE_TRUE; start = vue->cpu.access.address; end = start + TYPE_SIZES[vue->cpu.access.type] - 1; } // Check all breakpoints int32_t fetch = breakType == BRK_READ && vue->cpu.fetch != -1 ? 1 : 0; for (int x = 0; x < core->numBreakpoints; x++) { Breakpoint *brk = &core->breakpoints[x]; if ( brk->enabled && (brk->fetch || !fetch) && (breakType == 0 || (breakType & brk->hooks) != 0) && (!ranged || brkInRange(brk, start, end)) && (brk->numTokens == 0 || brkEvaluate(core, brk) != 0) ) return x + 1; } return 0; } // Check for exception breakpoints static int32_t brkOnException(Vue *vue, uint16_t code) { return brkOnBreakpoint(vue, BRK_EXCEPTION); } // Check for execute breakpoints static int32_t brkOnExecute(Vue *vue, VueInstruction *inst) { return brkOnBreakpoint(vue, BRK_EXECUTE); } // Check for frame breakpoints static int32_t brkOnFrame(Vue *vue) { return brkOnBreakpoint(vue, BRK_FRAME); } // Check for read breakpoints static int32_t brkOnRead(Vue *vue, VueAccess *acc) { return brkOnBreakpoint(vue, BRK_READ); } // Check for write breakpoints static int32_t brkOnWrite(Vue *vue, VueAccess *acc) { return brkOnBreakpoint(vue, BRK_WRITE); } #endif // NATIVEVUE