pvbemu/src/desktop/vue/Breakpoint.c

577 lines
20 KiB
C

// 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