|
|
1.1 ! root 1: /* ! 2: Hatari - breakcond.c ! 3: ! 4: Copyright (c) 2009-2010 by Eero Tamminen ! 5: ! 6: This file is distributed under the GNU Public License, version 2 or at ! 7: your option any later version. Read the file gpl.txt for details. ! 8: ! 9: breakcond.c - code for breakpoint conditions that can check variable ! 10: and memory values against each other, mask them etc. before deciding ! 11: whether the breakpoint should be triggered. See BreakCond_Help() ! 12: for the syntax. ! 13: */ ! 14: const char BreakCond_fileid[] = "Hatari breakcond.c : " __DATE__ " " __TIME__; ! 15: ! 16: #include <ctype.h> ! 17: #include <stdlib.h> ! 18: #include "config.h" ! 19: #include "main.h" ! 20: #include "m68000.h" ! 21: #include "memorySnapShot.h" ! 22: #include "nextMemory.h" ! 23: #include "str.h" ! 24: #include "video.h" /* for Hatari video variable addresses */ ! 25: ! 26: #include "debug_priv.h" ! 27: #include "breakcond.h" ! 28: #include "debugcpu.h" ! 29: #include "evaluate.h" ! 30: #include "symbols.h" ! 31: ! 32: ! 33: /* set to 1 to enable parsing function tracing / debug output */ ! 34: #define DEBUG 0 ! 35: ! 36: /* needs to go through long long to handle x=32 */ ! 37: #define BITMASK(x) ((Uint32)(((unsigned long long)1<<(x))-1)) ! 38: ! 39: #define BC_MAX_CONDITION_BREAKPOINTS 16 ! 40: #define BC_MAX_CONDITIONS_PER_BREAKPOINT 4 ! 41: ! 42: #define BC_DEFAULT_DSP_SPACE 'P' ! 43: ! 44: enum { ! 45: /* plain number */ ! 46: VALUE_TYPE_NUMBER = 0, ! 47: ! 48: /* functions to call to get value */ ! 49: VALUE_TYPE_FUNCTION32 = 2, ! 50: ! 51: /* internal Hatari value variables */ ! 52: VALUE_TYPE_VAR32 = 4, ! 53: ! 54: /* size must match register size used in BreakCond_ParseRegister() */ ! 55: VALUE_TYPE_REG16 = 16, ! 56: VALUE_TYPE_REG32 = 32 ! 57: } typedef value_t; ! 58: ! 59: static inline bool is_register_type(value_t vtype) { ! 60: /* type used for CPU/DSP registers */ ! 61: return (vtype == VALUE_TYPE_REG16 || vtype == VALUE_TYPE_REG32); ! 62: } ! 63: ! 64: typedef struct { ! 65: bool is_indirect; ! 66: char dsp_space; /* DSP has P, X, Y address spaces, zero if not DSP */ ! 67: value_t valuetype; /* Hatari value variable type */ ! 68: union { ! 69: Uint32 number; ! 70: Uint16 (*func16)(void); ! 71: Uint32 (*func32)(void); ! 72: Uint16 *reg16; ! 73: Uint32 *reg32; ! 74: } value; ! 75: Uint32 bits; /* CPU has 8/16/32 bit address widths */ ! 76: Uint32 mask; /* <width mask> && <value mask> */ ! 77: } bc_value_t; ! 78: ! 79: typedef struct { ! 80: bc_value_t lvalue; ! 81: bc_value_t rvalue; ! 82: char comparison; ! 83: bool track; /* track value changes */ ! 84: } bc_condition_t; ! 85: ! 86: typedef struct { ! 87: char *expression; ! 88: bc_condition_t conditions[BC_MAX_CONDITIONS_PER_BREAKPOINT]; ! 89: int ccount; /* condition count */ ! 90: int hits; /* how many times breakpoint hit */ ! 91: int skip; /* how many times to hit before breaking */ ! 92: bool once; /* remove after hit&break */ ! 93: bool trace; /* trace mode, don't break */ ! 94: } bc_breakpoint_t; ! 95: ! 96: static bc_breakpoint_t BreakPointsCpu[BC_MAX_CONDITION_BREAKPOINTS]; ! 97: static bc_breakpoint_t BreakPointsDsp[BC_MAX_CONDITION_BREAKPOINTS]; ! 98: static int BreakPointCpuCount; ! 99: static int BreakPointDspCount; ! 100: ! 101: ! 102: /* forward declarations */ ! 103: static bool BreakCond_Remove(int position, bool bForDsp); ! 104: static void BreakCond_Print(bc_breakpoint_t *bp); ! 105: ! 106: ! 107: /** ! 108: * Save breakpoints as debugger input file ! 109: * return true for success, false for failure ! 110: */ ! 111: bool BreakCond_Save(const char *filename) ! 112: { ! 113: FILE *fp; ! 114: int i; ! 115: ! 116: if (!(BreakPointCpuCount || BreakPointDspCount)) { ! 117: if (remove(filename)) { ! 118: perror("ERROR"); ! 119: return false; ! 120: } ! 121: return true; ! 122: } ! 123: ! 124: fprintf(stderr, "Saving breakpoints to '%s'...\n", filename); ! 125: fp = fopen(filename, "w"); ! 126: if (!fp) { ! 127: perror("ERROR"); ! 128: return false; ! 129: } ! 130: /* save conditional breakpoints as debugger input file */ ! 131: for (i = 0; i < BreakPointCpuCount; i++) { ! 132: fprintf(fp, "b %s\n", BreakPointsCpu[i].expression); ! 133: } ! 134: for (i = 0; i < BreakPointDspCount; i++) { ! 135: fprintf(fp, "db %s\n", BreakPointsDsp[i].expression); ! 136: } ! 137: fclose(fp); ! 138: return true; ! 139: } ! 140: ! 141: ! 142: /* --------------------- debugging code ------------------- */ ! 143: ! 144: #if DEBUG ! 145: /* see parsing code for usage examples */ ! 146: static int _traceIndent; ! 147: static void _spaces(void) ! 148: { ! 149: int spaces = _traceIndent; ! 150: while(spaces-- > 0) { ! 151: putchar(' '); /* fputc(' ',stdout); */ ! 152: } ! 153: } ! 154: #define ENTERFUNC(args) { _traceIndent += 2; _spaces(); printf args ; fflush(stdout); } ! 155: #define EXITFUNC(args) { _spaces(); printf args ; fflush(stdout); _traceIndent -= 2; } ! 156: #else ! 157: #define ENTERFUNC(args) ! 158: #define EXITFUNC(args) ! 159: #endif ! 160: ! 161: ! 162: /* ------------- breakpoint condition checking, internals ------------- */ ! 163: ! 164: /** ! 165: * Return value from given DSP memory space/address ! 166: */ ! 167: static Uint32 BreakCond_ReadDspMemory(Uint32 addr, const bc_value_t *bc_value) ! 168: { ! 169: return 0; ! 170: } ! 171: ! 172: /** ! 173: * Return value of given size read from given ST memory address ! 174: */ ! 175: static Uint32 BreakCond_ReadSTMemory(Uint32 addr, const bc_value_t *bc_value) ! 176: { ! 177: switch (bc_value->bits) { ! 178: case 32: ! 179: return NEXTMemory_ReadLong(addr); ! 180: case 16: ! 181: return NEXTMemory_ReadWord(addr); ! 182: case 8: ! 183: return NEXTMemory_ReadByte(addr); ! 184: default: ! 185: fprintf(stderr, "ERROR: unknown ST address size %d!\n", bc_value->bits); ! 186: abort(); ! 187: } ! 188: } ! 189: ! 190: ! 191: /** ! 192: * Return Uint32 value according to given bc_value_t specification ! 193: */ ! 194: static Uint32 BreakCond_GetValue(const bc_value_t *bc_value) ! 195: { ! 196: Uint32 value; ! 197: ! 198: switch (bc_value->valuetype) { ! 199: case VALUE_TYPE_NUMBER: ! 200: value = bc_value->value.number; ! 201: break; ! 202: case VALUE_TYPE_FUNCTION32: ! 203: value = bc_value->value.func32(); ! 204: break; ! 205: case VALUE_TYPE_REG16: ! 206: value = *(bc_value->value.reg16); ! 207: break; ! 208: case VALUE_TYPE_VAR32: ! 209: case VALUE_TYPE_REG32: ! 210: value = *(bc_value->value.reg32); ! 211: break; ! 212: default: ! 213: fprintf(stderr, "ERROR: unknown condition value size/type %d!\n", bc_value->valuetype); ! 214: abort(); ! 215: } ! 216: if (bc_value->is_indirect) { ! 217: value = BreakCond_ReadSTMemory(value, bc_value); ! 218: } ! 219: return (value & bc_value->mask); ! 220: } ! 221: ! 222: ! 223: /** ! 224: * Return true if all of the given breakpoint's conditions match ! 225: */ ! 226: static bool BreakCond_MatchConditions(const bc_condition_t *condition, int count) ! 227: { ! 228: Uint32 lvalue, rvalue; ! 229: bool hit = false; ! 230: int i; ! 231: ! 232: for (i = 0; i < count; condition++, i++) { ! 233: ! 234: lvalue = BreakCond_GetValue(&(condition->lvalue)); ! 235: rvalue = BreakCond_GetValue(&(condition->rvalue)); ! 236: ! 237: switch (condition->comparison) { ! 238: case '<': ! 239: hit = (lvalue < rvalue); ! 240: break; ! 241: case '>': ! 242: hit = (lvalue > rvalue); ! 243: break; ! 244: case '=': ! 245: hit = (lvalue == rvalue); ! 246: break; ! 247: case '!': ! 248: hit = (lvalue != rvalue); ! 249: break; ! 250: default: ! 251: fprintf(stderr, "ERROR: Unknown breakpoint value comparison operator '%c'!\n", ! 252: condition->comparison); ! 253: abort(); ! 254: } ! 255: if (!hit) { ! 256: return false; ! 257: } ! 258: } ! 259: /* all conditions matched */ ! 260: return true; ! 261: } ! 262: ! 263: ! 264: /** ! 265: * Show values for the tracked breakpoint conditions ! 266: */ ! 267: static void BreakCond_ShowTracked(bc_condition_t *condition, int count) ! 268: { ! 269: Uint32 addr, value; ! 270: char sep; ! 271: int i; ! 272: ! 273: sep = ' '; ! 274: for (i = 0; i < count; condition++, i++) { ! 275: if (!condition->track) { ! 276: continue; ! 277: } ! 278: ! 279: /* get the new value in address */ ! 280: value = BreakCond_GetValue(&(condition->lvalue)); ! 281: /* next monitor changes to this new value */ ! 282: condition->rvalue.value.number = value; ! 283: ! 284: if (condition->lvalue.is_indirect && ! 285: condition->lvalue.valuetype == VALUE_TYPE_NUMBER) { ! 286: /* simple memory address */ ! 287: addr = condition->lvalue.value.number; ! 288: fprintf(stderr, "%c $%x = $%x", sep, addr, value); ! 289: } else { ! 290: /* register tms. */ ! 291: fprintf(stderr, "%c $%x", sep, value); ! 292: } ! 293: sep = ','; ! 294: } ! 295: fprintf(stderr, "\n"); ! 296: } ! 297: ! 298: ! 299: /** ! 300: * Return which of the given condition breakpoints match ! 301: * or zero if none matched ! 302: */ ! 303: static int BreakCond_MatchBreakPoints(bc_breakpoint_t *bp, int count, const char *name) ! 304: { ! 305: int i; ! 306: ! 307: for (i = 0; i < count; bp++, i++) { ! 308: if (BreakCond_MatchConditions(bp->conditions, bp->ccount)) { ! 309: BreakCond_ShowTracked(bp->conditions, bp->ccount); ! 310: bp->hits++; ! 311: if (bp->skip && (bp->hits % bp->skip) == 0) { ! 312: return 0; ! 313: } ! 314: fprintf(stderr, "%d. %s breakpoint condition(s) matched %d times.\n", ! 315: i+1, name, bp->hits); ! 316: if (bp->trace) { ! 317: return 0; ! 318: } ! 319: BreakCond_Print(bp); ! 320: if (bp->once) { ! 321: BreakCond_Remove(i+1, (bp-i == BreakPointsDsp)); ! 322: } ! 323: /* indexes for BreakCond_Remove() start from 1 */ ! 324: return i + 1; ! 325: } ! 326: } ! 327: return 0; ! 328: } ! 329: ! 330: /* ------------- breakpoint condition checking, public API ------------- */ ! 331: ! 332: /** ! 333: * Return matched CPU breakpoint index or zero for an error. ! 334: */ ! 335: int BreakCond_MatchCpu(void) ! 336: { ! 337: return BreakCond_MatchBreakPoints(BreakPointsCpu, BreakPointCpuCount, "CPU"); ! 338: } ! 339: ! 340: /** ! 341: * Return matched DSP breakpoint index or zero for an error. ! 342: */ ! 343: int BreakCond_MatchDsp(void) ! 344: { ! 345: return BreakCond_MatchBreakPoints(BreakPointsDsp, BreakPointDspCount, "DSP"); ! 346: } ! 347: ! 348: /** ! 349: * Return number of condition breakpoints ! 350: */ ! 351: int BreakCond_BreakPointCount(bool bForDsp) ! 352: { ! 353: if (bForDsp) { ! 354: return BreakPointDspCount; ! 355: } else { ! 356: return BreakPointCpuCount; ! 357: } ! 358: } ! 359: ! 360: ! 361: /* -------------- breakpoint condition parsing, internals ------------- */ ! 362: ! 363: /* struct for passing around breakpoint conditions parsing state */ ! 364: typedef struct { ! 365: int arg; /* current arg */ ! 366: int argc; /* arg count */ ! 367: const char **argv; /* arg pointer array (+ strings) */ ! 368: const char *error; /* error from parsing args */ ! 369: } parser_state_t; ! 370: ! 371: ! 372: /* Hatari variable name & address array items */ ! 373: typedef struct { ! 374: const char *name; ! 375: Uint32 *addr; ! 376: value_t vtype; ! 377: size_t bits; ! 378: const char *constraints; ! 379: } var_addr_t; ! 380: ! 381: /* Accessor functions for calculated Hatari values */ ! 382: static Uint32 GetLineCycles(void) ! 383: { ! 384: int dummy1, dummy2, lcycles; ! 385: Video_GetPosition(&dummy1, &dummy2 , &lcycles); ! 386: return lcycles; ! 387: } ! 388: static Uint32 GetFrameCycles(void) ! 389: { ! 390: int dummy1, dummy2, fcycles; ! 391: Video_GetPosition(&fcycles, &dummy1, &dummy2); ! 392: return fcycles; ! 393: } ! 394: ! 395: ! 396: /* sorted by variable name so that this can be bisected */ ! 397: static const var_addr_t hatari_vars[] = { ! 398: { "FrameCycles", (Uint32*)GetFrameCycles, VALUE_TYPE_FUNCTION32, 0, NULL }, ! 399: { "LineCycles", (Uint32*)GetLineCycles, VALUE_TYPE_FUNCTION32, 0, "is always divisable by 4" } ! 400: }; ! 401: ! 402: ! 403: /** ! 404: * Readline match callback for CPU variable/symbol name completion. ! 405: * STATE = 0 -> different text from previous one. ! 406: * Return next match or NULL if no matches. ! 407: */ ! 408: char *BreakCond_MatchCpuVariable(const char *text, int state) ! 409: { ! 410: static int i, len; ! 411: const char *name; ! 412: ! 413: if (!state) { ! 414: /* first match */ ! 415: len = strlen(text); ! 416: i = 0; ! 417: } ! 418: /* next match */ ! 419: while (i < ARRAYSIZE(hatari_vars)) { ! 420: name = hatari_vars[i++].name; ! 421: if (strncasecmp(name, text, len) == 0) ! 422: return (strdup(name)); ! 423: } ! 424: /* no variable match, check all CPU symbols */ ! 425: return Symbols_MatchCpuAddress(text, state); ! 426: } ! 427: ! 428: /** ! 429: * Readline match callback for DSP variable/symbol name completion. ! 430: * STATE = 0 -> different text from previous one. ! 431: * Return next match or NULL if no matches. ! 432: */ ! 433: char *BreakCond_MatchDspVariable(const char *text, int state) ! 434: { ! 435: /* currently no DSP variables, check all DSP symbols */ ! 436: return Symbols_MatchDspAddress(text, state); ! 437: } ! 438: ! 439: ! 440: /** ! 441: * If given string is a Hatari variable name, set bc_value ! 442: * fields accordingly and return true, otherwise return false. ! 443: */ ! 444: static bool BreakCond_ParseVariable(const char *name, bc_value_t *bc_value) ! 445: { ! 446: /* left, right, middle, direction */ ! 447: int l, r, m, dir; ! 448: ! 449: ENTERFUNC(("BreakCond_ParseVariable('%s')\n", name)); ! 450: /* bisect */ ! 451: l = 0; ! 452: r = ARRAYSIZE(hatari_vars) - 1; ! 453: do { ! 454: m = (l+r) >> 1; ! 455: dir = strcasecmp(name, hatari_vars[m].name); ! 456: if (dir == 0) { ! 457: bc_value->value.reg32 = hatari_vars[m].addr; ! 458: bc_value->valuetype = hatari_vars[m].vtype; ! 459: bc_value->bits = hatari_vars[m].bits; ! 460: assert(bc_value->bits == 32 || bc_value->valuetype != VALUE_TYPE_VAR32); ! 461: EXITFUNC(("-> true\n")); ! 462: return true; ! 463: } ! 464: if (dir < 0) { ! 465: r = m-1; ! 466: } else { ! 467: l = m+1; ! 468: } ! 469: } while (l <= r); ! 470: EXITFUNC(("-> false\n")); ! 471: return false; ! 472: } ! 473: ! 474: ! 475: /** ! 476: * If given string matches a suitable symbol, set bc_value ! 477: * fields accordingly and return true, otherwise return false. ! 478: */ ! 479: static bool BreakCond_ParseSymbol(const char *name, bc_value_t *bc_value) ! 480: { ! 481: symtype_t symtype; ! 482: Uint32 addr; ! 483: ! 484: ENTERFUNC(("BreakCond_ParseSymbol('%s')\n", name)); ! 485: if (bc_value->is_indirect) { ! 486: /* indirect use of address makes sense only for data */ ! 487: symtype = SYMTYPE_DATA|SYMTYPE_BSS; ! 488: } else { ! 489: /* direct value can be compared for anything */ ! 490: symtype = SYMTYPE_ALL; ! 491: } ! 492: ! 493: if (bc_value->dsp_space) { ! 494: if (!Symbols_GetDspAddress(symtype, name, &addr)) { ! 495: EXITFUNC(("-> false (DSP)\n")); ! 496: return false; ! 497: } ! 498: /* all DSP memory values are 24-bits */ ! 499: bc_value->bits = 24; ! 500: bc_value->value.number = addr; ! 501: bc_value->valuetype = VALUE_TYPE_NUMBER; ! 502: EXITFUNC(("-> true (DSP)\n")); ! 503: return true; ! 504: } ! 505: ! 506: if (!Symbols_GetCpuAddress(symtype, name, &addr)) { ! 507: EXITFUNC(("-> false (CPU)\n")); ! 508: return false; ! 509: } ! 510: if (addr & 1) { ! 511: /* only bytes can be at odd addresses */ ! 512: bc_value->bits = 8; ! 513: } else { ! 514: bc_value->bits = 32; ! 515: } ! 516: bc_value->value.number = addr; ! 517: bc_value->valuetype = VALUE_TYPE_NUMBER; ! 518: EXITFUNC(("-> true (CPU)\n")); ! 519: return true; ! 520: } ! 521: ! 522: ! 523: /** ! 524: * Helper function to get CPU PC register value with static inline as Uint32 ! 525: */ ! 526: static Uint32 GetCpuPC(void) ! 527: { ! 528: return M68000_GetPC(); ! 529: } ! 530: /** ! 531: * Helper function to get CPU SR register value with static inline as Uint32 ! 532: */ ! 533: static Uint32 GetCpuSR(void) ! 534: { ! 535: return M68000_GetSR(); ! 536: } ! 537: ! 538: /** ! 539: * If given string is register name (for DSP or CPU), set bc_value ! 540: * fields accordingly and return true, otherwise return false. ! 541: */ ! 542: static bool BreakCond_ParseRegister(const char *regname, bc_value_t *bc_value) ! 543: { ! 544: int regsize; ! 545: ENTERFUNC(("BreakCond_ParseRegister('%s')\n", regname)); ! 546: regsize = DebugCpu_GetRegisterAddress(regname, &(bc_value->value.reg32)); ! 547: if (regsize) { ! 548: bc_value->bits = regsize; ! 549: /* valuetypes for registers are 16 & 32 */ ! 550: bc_value->valuetype = regsize; ! 551: EXITFUNC(("-> true (CPU)\n")); ! 552: return true; ! 553: } ! 554: /* Exact UAE core 32-bit PC & 16-bit SR register values ! 555: * can be gotten only through AUE accessors, not directly ! 556: */ ! 557: if (strcasecmp(regname, "PC") == 0) { ! 558: bc_value->bits = 32; ! 559: bc_value->value.func32 = GetCpuPC; ! 560: bc_value->valuetype = VALUE_TYPE_FUNCTION32; ! 561: EXITFUNC(("-> true (CPU)\n")); ! 562: return true; ! 563: } ! 564: if (strcasecmp(regname, "SR") == 0) { ! 565: bc_value->bits = 16; ! 566: bc_value->value.func32 = GetCpuSR; ! 567: bc_value->valuetype = VALUE_TYPE_FUNCTION32; ! 568: EXITFUNC(("-> true (CPU)\n")); ! 569: return true; ! 570: } ! 571: EXITFUNC(("-> false (CPU)\n")); ! 572: return false; ! 573: } ! 574: ! 575: /** ! 576: * If given address is valid (for DSP or CPU), return true. ! 577: */ ! 578: static bool BreakCond_CheckAddress(bc_value_t *bc_value) ! 579: { ! 580: Uint32 highbyte, bit23, addr = bc_value->value.number; ! 581: ! 582: ENTERFUNC(("BreakCond_CheckAddress(%x)\n", addr)); ! 583: ! 584: bit23 = (addr >> 23) & 1; ! 585: highbyte = (addr >> 24) & 0xff; ! 586: if ((bit23 == 0 && highbyte != 0) || ! 587: (bit23 == 1 && highbyte != 0xff)) { ! 588: fprintf(stderr, "WARNING: address 0x%x 23th bit isn't extended to bits 24-31.\n", addr); ! 589: } ! 590: EXITFUNC(("-> true (CPU)\n")); ! 591: return true; ! 592: } ! 593: ! 594: ! 595: /** ! 596: * Check for and parse a condition value address space/width modifier. ! 597: * Modify pstate according to parsing (arg index and error string). ! 598: * Return false for error and true for no or successfully parsed modifier. ! 599: */ ! 600: static bool BreakCond_ParseAddressModifier(parser_state_t *pstate, bc_value_t *bc_value) ! 601: { ! 602: char mode; ! 603: ! 604: ENTERFUNC(("BreakCond_ParseAddressModifier()\n")); ! 605: if (pstate->arg+2 > pstate->argc || ! 606: strcmp(pstate->argv[pstate->arg], ".") != 0) { ! 607: if (bc_value->dsp_space && bc_value->is_indirect) { ! 608: pstate->error = "DSP memory addresses need to specify address space"; ! 609: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 610: return false; ! 611: } ! 612: EXITFUNC(("arg:%d -> true (missing)\n", pstate->arg)); ! 613: return true; ! 614: } ! 615: if (!bc_value->is_indirect) { ! 616: pstate->error = "space/width modifier makes sense only for an address (register)"; ! 617: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 618: return false; ! 619: } ! 620: pstate->arg++; ! 621: if (bc_value->dsp_space) { ! 622: switch (pstate->argv[pstate->arg][0]) { ! 623: case 'p': ! 624: case 'x': ! 625: case 'y': ! 626: mode = toupper(pstate->argv[pstate->arg][0]); ! 627: break; ! 628: default: ! 629: pstate->error = "invalid address space modifier"; ! 630: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 631: return false; ! 632: } ! 633: } else { ! 634: switch (pstate->argv[pstate->arg][0]) { ! 635: case 'l': ! 636: mode = 32; ! 637: break; ! 638: case 'w': ! 639: mode = 16; ! 640: break; ! 641: case 'b': ! 642: mode = 8; ! 643: break; ! 644: default: ! 645: pstate->error = "invalid address width modifier"; ! 646: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 647: return false; ! 648: } ! 649: } ! 650: if (pstate->argv[pstate->arg][1]) { ! 651: pstate->error = "invalid address space/width modifier"; ! 652: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 653: return false; ! 654: } ! 655: if (bc_value->dsp_space) { ! 656: bc_value->dsp_space = mode; ! 657: EXITFUNC(("arg:%d -> space:%c, true\n", pstate->arg, mode)); ! 658: } else { ! 659: bc_value->bits = mode; ! 660: EXITFUNC(("arg:%d -> width:%d, true\n", pstate->arg, mode)); ! 661: } ! 662: pstate->arg++; ! 663: return true; ! 664: } ! 665: ! 666: ! 667: /** ! 668: * Check for and parse a condition value mask. ! 669: * Modify pstate according to parsing (arg index and error string). ! 670: * Return false for error and true for no or successfully parsed modifier. ! 671: */ ! 672: static bool BreakCond_ParseMaskModifier(parser_state_t *pstate, bc_value_t *bc_value) ! 673: { ! 674: ENTERFUNC(("BreakCond_ParseMaskModifier()\n")); ! 675: if (pstate->arg+2 > pstate->argc || ! 676: strcmp(pstate->argv[pstate->arg], "&") != 0) { ! 677: EXITFUNC(("arg:%d -> true (missing)\n", pstate->arg)); ! 678: return true; ! 679: } ! 680: if (bc_value->valuetype == VALUE_TYPE_NUMBER && ! 681: !bc_value->is_indirect) { ! 682: fprintf(stderr, "WARNING: plain numbers shouldn't need masks.\n"); ! 683: } ! 684: pstate->arg++; ! 685: if (!Eval_Number(pstate->argv[pstate->arg], &(bc_value->mask))) { ! 686: pstate->error = "invalid dec/hex/bin value"; ! 687: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 688: return false; ! 689: } ! 690: if (bc_value->mask == 0 || ! 691: (bc_value->valuetype == VALUE_TYPE_NUMBER && !bc_value->is_indirect && ! 692: bc_value->value.number && !(bc_value->value.number & bc_value->mask))) { ! 693: pstate->error = "mask zeroes value"; ! 694: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 695: return false; ! 696: } ! 697: EXITFUNC(("arg:%d -> true (%x)\n", pstate->arg, bc_value->mask)); ! 698: pstate->arg++; ! 699: return true; ! 700: } ! 701: ! 702: ! 703: /** ! 704: * Parse a breakpoint condition value. ! 705: * Modify pstate according to parsing (arg index and error string). ! 706: * Return true for success and false for error. ! 707: */ ! 708: static bool BreakCond_ParseValue(parser_state_t *pstate, bc_value_t *bc_value) ! 709: { ! 710: const char *str; ! 711: int skip = 1; ! 712: ! 713: ENTERFUNC(("BreakCond_Value()\n")); ! 714: if (pstate->arg >= pstate->argc) { ! 715: pstate->error = "value missing"; ! 716: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 717: return false; ! 718: } ! 719: /* parse indirection */ ! 720: if (pstate->arg+3 <= pstate->argc) { ! 721: if (strcmp(pstate->argv[pstate->arg+0], "(") == 0 && ! 722: strcmp(pstate->argv[pstate->arg+2], ")") == 0) { ! 723: bc_value->is_indirect = true; ! 724: pstate->arg++; ! 725: skip = 2; ! 726: } ! 727: } ! 728: ! 729: str = pstate->argv[pstate->arg]; ! 730: if (isalpha(*str) || *str == '_') { ! 731: /* parse direct or indirect variable/register/symbol name */ ! 732: if (bc_value->is_indirect) { ! 733: /* a valid register or data symbol name? */ ! 734: if (!BreakCond_ParseRegister(str, bc_value) && ! 735: !BreakCond_ParseSymbol(str, bc_value)) { ! 736: pstate->error = "invalid register/symbol name for indirection"; ! 737: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 738: return false; ! 739: } ! 740: } else { ! 741: /* a valid Hatari variable or register name? ! 742: * variables cannot be used for ST memory indirection. ! 743: */ ! 744: if (!BreakCond_ParseVariable(str, bc_value) && ! 745: !BreakCond_ParseRegister(str, bc_value) && ! 746: !BreakCond_ParseSymbol(str, bc_value)) { ! 747: pstate->error = "invalid variable/register/symbol name"; ! 748: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 749: return false; ! 750: } ! 751: } ! 752: } else { ! 753: /* a number */ ! 754: if (!Eval_Number(str, &(bc_value->value.number))) { ! 755: pstate->error = "invalid dec/hex/bin value"; ! 756: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 757: return false; ! 758: } ! 759: } ! 760: /* memory address (indirect value) -> OK as address? */ ! 761: if (bc_value->is_indirect && ! 762: bc_value->valuetype == VALUE_TYPE_NUMBER && ! 763: !BreakCond_CheckAddress(bc_value)) { ! 764: pstate->error = "invalid address"; ! 765: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 766: return false; ! 767: } ! 768: pstate->arg += skip; ! 769: ! 770: /* parse modifiers */ ! 771: if (!BreakCond_ParseAddressModifier(pstate, bc_value)) { ! 772: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 773: return false; ! 774: } ! 775: if (!BreakCond_ParseMaskModifier(pstate, bc_value)) { ! 776: EXITFUNC(("arg:%d -> false\n", pstate->arg)); ! 777: return false; ! 778: } ! 779: EXITFUNC(("arg:%d -> true (%s value)\n", pstate->arg, ! 780: (bc_value->is_indirect ? "indirect" : "direct"))); ! 781: return true; ! 782: } ! 783: ! 784: ! 785: /** ! 786: * Parse a breakpoint comparison character. ! 787: * Modify pstate according to parsing (arg index and error string). ! 788: * Return the character or nil for an error. ! 789: */ ! 790: static char BreakCond_ParseComparison(parser_state_t *pstate) ! 791: { ! 792: const char *comparison; ! 793: ! 794: ENTERFUNC(("BreakCond_ParseComparison(), arg:%d\n", pstate->arg)); ! 795: if (pstate->arg >= pstate->argc) { ! 796: pstate->error = "breakpoint comparison missing"; ! 797: EXITFUNC(("-> false\n")); ! 798: return false; ! 799: } ! 800: comparison = pstate->argv[pstate->arg]; ! 801: switch (comparison[0]) { ! 802: case '<': ! 803: case '>': ! 804: case '=': ! 805: case '!': ! 806: break; ! 807: default: ! 808: pstate->error = "invalid comparison character"; ! 809: EXITFUNC(("-> false\n")); ! 810: return false; ! 811: } ! 812: if (comparison[1]) { ! 813: pstate->error = "trailing comparison character(s)"; ! 814: EXITFUNC(("-> false\n")); ! 815: return false; ! 816: } ! 817: ! 818: pstate->arg++; ! 819: if (pstate->arg >= pstate->argc) { ! 820: pstate->error = "right side missing"; ! 821: EXITFUNC(("-> false\n")); ! 822: return false; ! 823: } ! 824: EXITFUNC(("-> '%c'\n", *comparison)); ! 825: return *comparison; ! 826: } ! 827: ! 828: ! 829: /** ! 830: * If no value, use the other value, if that also missing, use default ! 831: */ ! 832: static void BreakCond_InheritDefault(Uint32 *value1, Uint32 value2, Uint32 defvalue) ! 833: { ! 834: if (!*value1) { ! 835: if (value2) { ! 836: *value1 = value2; ! 837: } else { ! 838: *value1 = defvalue; ! 839: } ! 840: } ! 841: } ! 842: ! 843: /** ! 844: * Check & ensure that the masks and address sizes are sane ! 845: * and allow comparison with the other side. ! 846: * If yes, return true, otherwise false. ! 847: */ ! 848: static bool BreakCond_CrossCheckValues(parser_state_t *pstate, ! 849: bc_value_t *bc_value1, ! 850: bc_value_t *bc_value2) ! 851: { ! 852: Uint32 mask1, mask2, defbits; ! 853: ENTERFUNC(("BreakCond_CrossCheckValues()\n")); ! 854: ! 855: /* make sure there're valid bit widths and that masks have some value */ ! 856: if (bc_value1->dsp_space) { ! 857: defbits = 24; ! 858: } else { ! 859: defbits = 32; ! 860: } ! 861: BreakCond_InheritDefault(&(bc_value1->bits), bc_value2->bits, defbits); ! 862: BreakCond_InheritDefault(&(bc_value2->bits), bc_value1->bits, defbits); ! 863: BreakCond_InheritDefault(&(bc_value1->mask), bc_value2->mask, BITMASK(bc_value1->bits)); ! 864: BreakCond_InheritDefault(&(bc_value2->mask), bc_value1->mask, BITMASK(bc_value2->bits)); ! 865: ! 866: /* check first value mask & bit width */ ! 867: mask1 = BITMASK(bc_value1->bits) & bc_value1->mask; ! 868: ! 869: if (mask1 != bc_value1->mask) { ! 870: fprintf(stderr, "WARNING: mask 0x%x doesn't fit into %d address/register bits.\n", ! 871: bc_value1->mask, bc_value1->bits); ! 872: } ! 873: if (!bc_value1->dsp_space && ! 874: bc_value1->is_indirect && ! 875: (bc_value1->value.number & 1) && bc_value1->bits > 8) { ! 876: fprintf(stderr, "WARNING: odd CPU address 0x%x given without using byte (.b) width.\n", ! 877: bc_value1->value.number); ! 878: } ! 879: ! 880: /* cross-check both values masks */ ! 881: mask2 = BITMASK(bc_value2->bits) & bc_value2->mask; ! 882: ! 883: if ((mask1 & mask2) == 0) { ! 884: pstate->error = "values masks cancel each other"; ! 885: EXITFUNC(("-> false\n")); ! 886: return false; ! 887: } ! 888: if (bc_value2->is_indirect || ! 889: bc_value2->value.number == 0 || ! 890: bc_value2->valuetype != VALUE_TYPE_NUMBER) { ! 891: EXITFUNC(("-> true (no problematic direct types)\n")); ! 892: return true; ! 893: } ! 894: if ((bc_value2->value.number & mask1) != bc_value2->value.number) { ! 895: pstate->error = "number doesn't fit the other side address width&mask"; ! 896: EXITFUNC(("-> false\n")); ! 897: return false; ! 898: } ! 899: EXITFUNC(("-> true\n")); ! 900: return true; ! 901: } ! 902: ! 903: ! 904: /** ! 905: * Parse given breakpoint conditions and append them to breakpoints. ! 906: * Modify pstate according to parsing (arg index and error string). ! 907: * Return number of added conditions or zero for failure. ! 908: */ ! 909: static int BreakCond_ParseCondition(parser_state_t *pstate, bool bForDsp, ! 910: bc_condition_t *conditions, int ccount) ! 911: { ! 912: bc_condition_t condition; ! 913: ! 914: ENTERFUNC(("BreakCond_ParseCondition(...)\n")); ! 915: if (ccount >= BC_MAX_CONDITIONS_PER_BREAKPOINT) { ! 916: pstate->error = "max number of conditions exceeded"; ! 917: EXITFUNC(("-> 0 (no conditions free)\n")); ! 918: return 0; ! 919: } ! 920: ! 921: /* setup condition */ ! 922: memset(&condition, 0, sizeof(bc_condition_t)); ! 923: if (bForDsp) { ! 924: /* used also for checking whether value is for DSP */ ! 925: condition.lvalue.dsp_space = BC_DEFAULT_DSP_SPACE; ! 926: condition.rvalue.dsp_space = BC_DEFAULT_DSP_SPACE; ! 927: } ! 928: ! 929: /* parse condition */ ! 930: if (!BreakCond_ParseValue(pstate, &(condition.lvalue))) { ! 931: EXITFUNC(("-> 0\n")); ! 932: return 0; ! 933: } ! 934: condition.comparison = BreakCond_ParseComparison(pstate); ! 935: if (!condition.comparison) { ! 936: EXITFUNC(("-> 0\n")); ! 937: return 0; ! 938: } ! 939: if (!BreakCond_ParseValue(pstate, &(condition.rvalue))) { ! 940: EXITFUNC(("-> 0\n")); ! 941: return 0; ! 942: } ! 943: if (!(BreakCond_CrossCheckValues(pstate, &(condition.lvalue), &(condition.rvalue)) && ! 944: BreakCond_CrossCheckValues(pstate, &(condition.rvalue), &(condition.lvalue)))) { ! 945: EXITFUNC(("-> 0\n")); ! 946: return 0; ! 947: } ! 948: /* new condition */ ! 949: conditions[ccount++] = condition; ! 950: ! 951: /* continue with next condition? */ ! 952: if (pstate->arg == pstate->argc) { ! 953: EXITFUNC(("-> %d (conditions)\n", ccount-1)); ! 954: return ccount; ! 955: } ! 956: if (strcmp(pstate->argv[pstate->arg], "&&") != 0) { ! 957: pstate->error = "trailing content for breakpoint condition"; ! 958: EXITFUNC(("-> 0\n")); ! 959: return 0; ! 960: } ! 961: pstate->arg++; ! 962: ! 963: /* recurse conditions parsing */ ! 964: ccount = BreakCond_ParseCondition(pstate, bForDsp, conditions, ccount); ! 965: if (!ccount) { ! 966: EXITFUNC(("-> 0\n")); ! 967: return 0; ! 968: } ! 969: EXITFUNC(("-> %d (conditions)\n", ccount-1)); ! 970: return ccount; ! 971: } ! 972: ! 973: ! 974: /** ! 975: * Tokenize given breakpoint expression to given parser struct. ! 976: * Return normalized expression string that corresponds to tokenization ! 977: * or NULL on error. On error, pstate->error contains the error message ! 978: * and pstate->arg index to invalid character (instead of to token like ! 979: * after parsing). ! 980: */ ! 981: static char *BreakCond_TokenizeExpression(const char *expression, ! 982: parser_state_t *pstate) ! 983: { ! 984: char separator[] = { ! 985: '=', '!', '<', '>', /* comparison operators */ ! 986: '(', ')', '.', '&', /* other separators */ ! 987: '\0' /* terminator */ ! 988: }; ! 989: bool is_separated, has_comparison; ! 990: char sep, *dst, *normalized; ! 991: const char *src; ! 992: int i, tokens; ! 993: ! 994: memset(pstate, 0, sizeof(parser_state_t)); ! 995: ! 996: /* _minimum_ safe size for normalized expression is 2x+1 */ ! 997: normalized = malloc(2*strlen(expression)+1); ! 998: if (!normalized) { ! 999: pstate->error = "alloc failed"; ! 1000: return NULL; ! 1001: } ! 1002: ! 1003: /* check characters & normalize string */ ! 1004: dst = normalized; ! 1005: is_separated = false; ! 1006: has_comparison = false; ! 1007: for (src = expression; *src; src++) { ! 1008: /* discard white space in source */ ! 1009: if (isspace(*src)) { ! 1010: continue; ! 1011: } ! 1012: /* separate tokens with single space in destination */ ! 1013: for (i = 0; (sep = separator[i]); i++) { ! 1014: if (*src == sep) { ! 1015: if (dst > normalized) { ! 1016: /* don't separate boolean AND '&&' */ ! 1017: if (*src == '&' && *(src-1) == '&') { ! 1018: dst--; ! 1019: } else { ! 1020: if (!is_separated) { ! 1021: *dst++ = ' '; ! 1022: } ! 1023: } ! 1024: } ! 1025: *dst++ = *src; ! 1026: *dst++ = ' '; ! 1027: is_separated = true; ! 1028: if (i < 4) { ! 1029: has_comparison = true; ! 1030: } ! 1031: break; ! 1032: } ! 1033: } ! 1034: /* validate & copy other characters */ ! 1035: if (!sep) { ! 1036: /* variable/register/symbol or number prefix? */ ! 1037: if (!(isalnum(*src) || *src == '_' || ! 1038: *src == '$' || *src == '#' || *src == '%')) { ! 1039: pstate->error = "invalid character"; ! 1040: pstate->arg = src-expression; ! 1041: free(normalized); ! 1042: return NULL; ! 1043: } ! 1044: *dst++ = *src; ! 1045: is_separated = false; ! 1046: } ! 1047: } ! 1048: if (is_separated) { ! 1049: dst--; /* no trailing space */ ! 1050: } ! 1051: *dst = '\0'; ! 1052: ! 1053: if (!has_comparison) { ! 1054: pstate->error = "condition comparison missing"; ! 1055: pstate->arg = strlen(expression)/2; ! 1056: free(normalized); ! 1057: return NULL; ! 1058: } ! 1059: ! 1060: /* allocate exact space for tokenized string array + strings */ ! 1061: tokens = 1; ! 1062: for (dst = normalized; *dst; dst++) { ! 1063: if (*dst == ' ') { ! 1064: tokens++; ! 1065: } ! 1066: } ! 1067: pstate->argv = malloc(tokens*sizeof(char*)+strlen(normalized)+1); ! 1068: if (!pstate->argv) { ! 1069: pstate->error = "alloc failed"; ! 1070: free(normalized); ! 1071: return NULL; ! 1072: } ! 1073: /* and copy/tokenize... */ ! 1074: dst = (char*)(pstate->argv) + tokens*sizeof(char*); ! 1075: strcpy(dst, normalized); ! 1076: pstate->argv[0] = strtok(dst, " "); ! 1077: for (i = 1; (dst = strtok(NULL, " ")); i++) { ! 1078: pstate->argv[i] = dst; ! 1079: } ! 1080: assert(i == tokens); ! 1081: pstate->argc = tokens; ! 1082: #if DEBUG ! 1083: fprintf(stderr, "args->"); ! 1084: for (i = 0; i < tokens; i++) { ! 1085: fprintf(stderr, " %d: %s,", i, pstate->argv[i]); ! 1086: } ! 1087: fprintf(stderr, "\n"); ! 1088: #endif ! 1089: return normalized; ! 1090: } ! 1091: ! 1092: ! 1093: /** ! 1094: * Helper to set corrent breakpoint list and type name to given variables. ! 1095: * Return pointer to breakpoint list count ! 1096: */ ! 1097: static int* BreakCond_GetListInfo(bc_breakpoint_t **bp, ! 1098: const char **name, bool bForDsp) ! 1099: { ! 1100: int *bcount; ! 1101: if (bForDsp) { ! 1102: bcount = &BreakPointDspCount; ! 1103: *bp = BreakPointsDsp; ! 1104: *name = "DSP"; ! 1105: } else { ! 1106: bcount = &BreakPointCpuCount; ! 1107: *bp = BreakPointsCpu; ! 1108: *name = "CPU"; ! 1109: } ! 1110: return bcount; ! 1111: } ! 1112: ! 1113: ! 1114: /** ! 1115: * Check whether any of the breakpoint conditions is such that it's ! 1116: * intended for tracking given value changes (inequality comparison ! 1117: * on identical values) or for retrieving the current value to break ! 1118: * on next value change (other comparisons on identical values). ! 1119: * ! 1120: * On former case, mark it for tracking, on other cases, just ! 1121: * retrieve the value. ! 1122: */ ! 1123: static void BreakCond_CheckTracking(bc_breakpoint_t *bp) ! 1124: { ! 1125: bc_condition_t *condition; ! 1126: bool track = false; ! 1127: Uint32 value; ! 1128: int i; ! 1129: ! 1130: condition = bp->conditions; ! 1131: for (i = 0; i < bp->ccount; condition++, i++) { ! 1132: ! 1133: if (memcmp(&(condition->lvalue), &(condition->rvalue), sizeof(bc_value_t)) == 0) { ! 1134: /* set current value to right side */ ! 1135: value = BreakCond_GetValue(&(condition->rvalue)); ! 1136: condition->rvalue.value.number = value; ! 1137: condition->rvalue.valuetype = VALUE_TYPE_NUMBER; ! 1138: condition->rvalue.is_indirect = false; ! 1139: if (condition->comparison == '!') { ! 1140: /* which changes will be traced */ ! 1141: condition->track = true; ! 1142: track = true; ! 1143: } else { ! 1144: fprintf(stderr, "\t%d. condition: %c $%x\n", ! 1145: i+1, condition->comparison, value); ! 1146: } ! 1147: } ! 1148: } ! 1149: if (track) { ! 1150: fprintf(stderr, "-> Track value changes, show value(s) when matched.\n"); ! 1151: } ! 1152: } ! 1153: ! 1154: ! 1155: /** ! 1156: * Parse given breakpoint expression and store it. ! 1157: * Return true for success and false for failure. ! 1158: */ ! 1159: static bool BreakCond_Parse(const char *expression, bool bForDsp, bool trace, bool once, int skip) ! 1160: { ! 1161: parser_state_t pstate; ! 1162: bc_breakpoint_t *bp; ! 1163: const char *name; ! 1164: char *normalized; ! 1165: int *bcount; ! 1166: int ccount; ! 1167: ! 1168: bcount = BreakCond_GetListInfo(&bp, &name, bForDsp); ! 1169: if (*bcount >= BC_MAX_CONDITION_BREAKPOINTS) { ! 1170: fprintf(stderr, "ERROR: no free %s condition breakpoints left.\n", name); ! 1171: return false; ! 1172: } ! 1173: bp += *bcount; ! 1174: memset(bp, 0, sizeof(bc_breakpoint_t)); ! 1175: ! 1176: normalized = BreakCond_TokenizeExpression(expression, &pstate); ! 1177: if (normalized) { ! 1178: bp->expression = normalized; ! 1179: ccount = BreakCond_ParseCondition(&pstate, bForDsp, ! 1180: bp->conditions, 0); ! 1181: bp->ccount = ccount; ! 1182: } else { ! 1183: ccount = 0; ! 1184: } ! 1185: if (pstate.argv) { ! 1186: free(pstate.argv); ! 1187: } ! 1188: if (ccount > 0) { ! 1189: (*bcount)++; ! 1190: fprintf(stderr, "%s condition breakpoint %d with %d condition(s) added:\n\t%s\n", ! 1191: name, *bcount, ccount, bp->expression); ! 1192: BreakCond_CheckTracking(bp); ! 1193: if (skip) { ! 1194: fprintf(stderr, "-> Break only on every %d hit.\n", skip); ! 1195: bp->skip = skip; ! 1196: } ! 1197: if (once) { ! 1198: fprintf(stderr, "-> Once, delete after breaking.\n"); ! 1199: bp->once = once; ! 1200: } ! 1201: if (trace) { ! 1202: fprintf(stderr, "-> Trace instead of breaking, but show still hits.\n"); ! 1203: bp->trace = trace; ! 1204: } ! 1205: } else { ! 1206: if (normalized) { ! 1207: int offset, i = 0; ! 1208: char *s = normalized; ! 1209: while (*s && i < pstate.arg) { ! 1210: if (*s++ == ' ') { ! 1211: i++; ! 1212: } ! 1213: } ! 1214: offset = s - normalized; ! 1215: /* show tokenized string and point out ! 1216: * the token where the error was encountered ! 1217: */ ! 1218: fprintf(stderr, "ERROR in tokenized string:\n'%s'\n%*c-%s\n", ! 1219: normalized, offset+2, '^', pstate.error); ! 1220: free(normalized); ! 1221: bp->expression = NULL; ! 1222: } else { ! 1223: /* show original string and point out the character ! 1224: * where the error was encountered ! 1225: */ ! 1226: fprintf(stderr, "ERROR in parsed string:\n'%s'\n%*c-%s\n", ! 1227: expression, pstate.arg+2, '^', pstate.error); ! 1228: } ! 1229: } ! 1230: return (ccount > 0); ! 1231: } ! 1232: ! 1233: ! 1234: /** ! 1235: * print single breakpoint ! 1236: */ ! 1237: static void BreakCond_Print(bc_breakpoint_t *bp) ! 1238: { ! 1239: fprintf(stderr, "\t%s", bp->expression); ! 1240: if (bp->skip) { ! 1241: fprintf(stderr, " :%d", bp->skip); ! 1242: } ! 1243: if (bp->once) { ! 1244: fprintf(stderr, " :once"); ! 1245: } ! 1246: if (bp->trace) { ! 1247: fprintf(stderr, " :trace"); ! 1248: } ! 1249: fprintf(stderr, "\n"); ! 1250: } ! 1251: ! 1252: /** ! 1253: * List condition breakpoints ! 1254: */ ! 1255: static void BreakCond_List(bool bForDsp) ! 1256: { ! 1257: const char *name; ! 1258: bc_breakpoint_t *bp; ! 1259: int i, bcount; ! 1260: ! 1261: bcount = *BreakCond_GetListInfo(&bp, &name, bForDsp); ! 1262: if (!bcount) { ! 1263: fprintf(stderr, "No conditional %s breakpoints.\n", name); ! 1264: return; ! 1265: } ! 1266: ! 1267: fprintf(stderr, "%d conditional %s breakpoints:\n", bcount, name); ! 1268: for (i = 1; i <= bcount; bp++, i++) { ! 1269: fprintf(stderr, "%4d:", i); ! 1270: BreakCond_Print(bp); ! 1271: } ! 1272: } ! 1273: ! 1274: ! 1275: /** ! 1276: * Remove condition breakpoint at given position ! 1277: */ ! 1278: static bool BreakCond_Remove(int position, bool bForDsp) ! 1279: { ! 1280: const char *name; ! 1281: bc_breakpoint_t *bp; ! 1282: int *bcount, offset; ! 1283: ! 1284: bcount = BreakCond_GetListInfo(&bp, &name, bForDsp); ! 1285: if (!*bcount) { ! 1286: fprintf(stderr, "No (more) breakpoints to remove.\n"); ! 1287: return false; ! 1288: } ! 1289: if (position < 1 || position > *bcount) { ! 1290: fprintf(stderr, "ERROR: No such %s breakpoint.\n", name); ! 1291: return false; ! 1292: } ! 1293: offset = position - 1; ! 1294: fprintf(stderr, "Removed %s breakpoint %d:\n", name, position); ! 1295: BreakCond_Print(&(bp[offset])); ! 1296: free(bp[offset].expression); ! 1297: bp[offset].expression = NULL; ! 1298: ! 1299: if (position < *bcount) { ! 1300: memmove(bp+offset, bp+position, ! 1301: (*bcount-position)*sizeof(bc_breakpoint_t)); ! 1302: } ! 1303: (*bcount)--; ! 1304: return true; ! 1305: } ! 1306: ! 1307: ! 1308: /** ! 1309: * Remove all condition breakpoints ! 1310: */ ! 1311: static void BreakCond_RemoveAll(bool bForDsp) ! 1312: { ! 1313: while (BreakCond_Remove(1, bForDsp)) ! 1314: ; ! 1315: } ! 1316: ! 1317: ! 1318: /** ! 1319: * Return true if given CPU breakpoint has given CPU expression. ! 1320: * Used by the test code. ! 1321: */ ! 1322: int BreakCond_MatchCpuExpression(int position, const char *expression) ! 1323: { ! 1324: if (position < 1 || position > BreakPointCpuCount) { ! 1325: return false; ! 1326: } ! 1327: if (strcmp(expression, BreakPointsCpu[position-1].expression)) { ! 1328: return false; ! 1329: } ! 1330: return true; ! 1331: } ! 1332: ! 1333: ! 1334: /** ! 1335: * help ! 1336: */ ! 1337: static void BreakCond_Help(void) ! 1338: { ! 1339: Uint32 value; ! 1340: int i; ! 1341: fputs( ! 1342: " breakpoint = <condition> [ && <condition> ... ] [option]\n" ! 1343: " condition = <value>[.mode] [& <number>] <comparison> <value>[.mode]\n" ! 1344: "\n" ! 1345: " where:\n" ! 1346: " value = [(] <register/symbol/variable name | number> [)]\n" ! 1347: " number = [#|$|%]<digits>\n" ! 1348: " comparison = '<' | '>' | '=' | '!'\n" ! 1349: " addressing mode (width) = 'b' | 'w' | 'l'\n" ! 1350: " addressing mode (space) = 'p' | 'x' | 'y'\n" ! 1351: " option = : <count> | 'once' | 'trace'\n" ! 1352: "\n" ! 1353: " If the value is in parenthesis like in '($ff820)' or '(a0)', then\n" ! 1354: " the used value will be read from the memory address pointed by it.\n" ! 1355: "\n" ! 1356: " If the value expressions on both sides of the comparison are exactly\n" ! 1357: " the same, right side is replaced with its current value and for\n" ! 1358: " inequality ('!') comparison, the breakpoint tracks all further changes\n" ! 1359: " for the given address/register expression. 'trace' option for continuing\n" ! 1360: " without breaking can be useful with this. 'once' option removes breakpoint\n" ! 1361: " after hit and giving count as option will break only on every <count> hit.\n" ! 1362: "\n" ! 1363: " M68k addresses can have byte (b), word (w) or long (l, default) width.\n" ! 1364: " DSP addresses belong to different address spaces: P, X or Y. Note that\n" ! 1365: " on DSP only R0-R7 registers can be used for memory addressing.\n" ! 1366: "\n" ! 1367: " Valid Hatari variable names (and their current values) are:\n", stderr); ! 1368: for (i = 0; i < ARRAYSIZE(hatari_vars); i++) { ! 1369: switch (hatari_vars[i].vtype) { ! 1370: case VALUE_TYPE_FUNCTION32: ! 1371: value = ((Uint32(*)(void))(hatari_vars[i].addr))(); ! 1372: break; ! 1373: case VALUE_TYPE_VAR32: ! 1374: value = *(hatari_vars[i].addr); ! 1375: break; ! 1376: default: ! 1377: fprintf(stderr, "ERROR: variable '%s' has unsupported type '%d'\n", ! 1378: hatari_vars[i].name, hatari_vars[i].vtype); ! 1379: continue; ! 1380: } ! 1381: fprintf(stderr, " - %s (%d)", hatari_vars[i].name, value); ! 1382: if (hatari_vars[i].constraints) { ! 1383: fprintf(stderr, ", %s\n", hatari_vars[i].constraints); ! 1384: } else { ! 1385: fprintf(stderr, "\n"); ! 1386: } ! 1387: } ! 1388: fputs( ! 1389: "\n" ! 1390: " Examples:\n" ! 1391: " pc = $64543 && ($ff820).w & 3 = (a0) && d0 = %1100\n" ! 1392: " ($ffff9202).w ! ($ffff9202).w :trace\n" ! 1393: " (r0).x = 1 && (r0).y = 2\n", stderr); ! 1394: } ! 1395: ! 1396: ! 1397: /* ------------- breakpoint condition parsing, public API ------------ */ ! 1398: ! 1399: const char BreakCond_Description[] = ! 1400: "[ <condition> [:<count>|once|trace] | <index> | help | all ]\n" ! 1401: "\tSet breakpoint with given <condition>, remove breakpoint with\n" ! 1402: "\tgiven <index> or list all breakpoints when no args are given.\n" ! 1403: "\tAdding ':trace' to end of condition causes breakpoint match\n" ! 1404: "\tjust to be printed, not break. Adding ':once' will delete\n" ! 1405: "\tthe breakpoint after it's hit. Adding ':<count>' will break\n" ! 1406: "\tonly on every <count> hit. 'help' outputs breakpoint condition\n" ! 1407: "\tsyntax help, 'all' removes all breakpoints."; ! 1408: ! 1409: /** ! 1410: * Parse given command expression to set/remove/list ! 1411: * conditional breakpoints for CPU or DSP. ! 1412: * Return true for success and false for failure. ! 1413: */ ! 1414: bool BreakCond_Command(const char *args, bool bForDsp) ! 1415: { ! 1416: bool trace, once, ret = true; ! 1417: char *cut, *expression, *argscopy; ! 1418: unsigned int position; ! 1419: const char *end; ! 1420: int skip; ! 1421: ! 1422: if (!args) { ! 1423: BreakCond_List(bForDsp); ! 1424: return true; ! 1425: } ! 1426: argscopy = strdup(args); ! 1427: assert(argscopy); ! 1428: ! 1429: expression = Str_Trim(argscopy); ! 1430: ! 1431: /* subcommands */ ! 1432: if (strncmp(expression, "help", 4) == 0) { ! 1433: BreakCond_Help(); ! 1434: goto cleanup; ! 1435: } ! 1436: if (strcmp(expression, "all") == 0) { ! 1437: BreakCond_RemoveAll(bForDsp); ! 1438: goto cleanup; ! 1439: } ! 1440: ! 1441: ! 1442: /* postfix options */ ! 1443: skip = 0; ! 1444: once = false; ! 1445: trace = false; ! 1446: if ((cut = strchr(expression, ':'))) { ! 1447: *cut = '\0'; ! 1448: cut = Str_Trim(cut+1); ! 1449: if (strcmp(cut, "trace") == 0) { ! 1450: trace = true; ! 1451: } else if (strcmp(cut, "once") == 0) { ! 1452: once = true; ! 1453: } else { ! 1454: skip = atoi(cut); ! 1455: if (skip < 2) { ! 1456: ret = false; ! 1457: fprintf(stderr, "ERROR: invalid breakpoint skip count '%s'!\n", cut); ! 1458: goto cleanup; ! 1459: } ! 1460: } ! 1461: } ! 1462: ! 1463: /* index (for removal) */ ! 1464: end = expression; ! 1465: while (isdigit(*end)) { ! 1466: end++; ! 1467: } ! 1468: if (end > expression && *end == '\0' && ! 1469: sscanf(expression, "%u", &position) == 1) { ! 1470: ret = BreakCond_Remove(position, bForDsp); ! 1471: } else { ! 1472: /* add breakpoint? */ ! 1473: ret = BreakCond_Parse(expression, bForDsp, trace, once, skip); ! 1474: } ! 1475: cleanup: ! 1476: free(argscopy); ! 1477: return ret; ! 1478: } ! 1479: ! 1480: ! 1481: const char BreakAddr_Description[] = ! 1482: "<address> [:<count>|once|trace]\n" ! 1483: "\tCreate conditional breakpoint for given PC <address>.\n" ! 1484: "\tAdding ':trace' causes breakpoint match just to be printed,\n" ! 1485: "\tnot break. Adding ':once' will delete the breakpoint after\n" ! 1486: "\tit's hit. Adding ':<count>' will break only on every <count>\n" ! 1487: "\thit. Use conditional breakpoint commands to manage the created\n" ! 1488: "\tbreakpoints."; ! 1489: ! 1490: /** ! 1491: * Set CPU & DSP program counter address breakpoints by converting ! 1492: * them to conditional breakpoints. ! 1493: * Return true for success and false for failure. ! 1494: */ ! 1495: bool BreakAddr_Command(char *args, bool bForDsp) ! 1496: { ! 1497: const char *errstr, *expression = (const char *)args; ! 1498: char *cut, command[32]; ! 1499: Uint32 addr; ! 1500: int offset; ! 1501: ! 1502: /* split options */ ! 1503: if ((cut = strchr(args, ':'))) { ! 1504: *cut = '\0'; ! 1505: cut = Str_Trim(cut+1); ! 1506: if (strlen(cut) > 8) { ! 1507: cut[8] = '\0'; ! 1508: } ! 1509: } ! 1510: ! 1511: /* evaluate address expression */ ! 1512: errstr = Eval_Expression(expression, &addr, &offset, bForDsp); ! 1513: if (errstr) { ! 1514: fprintf(stderr, "ERROR in the address expression:\n'%s'\n%*c-%s\n", ! 1515: expression, offset+2, '^', errstr); ! 1516: return false; ! 1517: } ! 1518: ! 1519: /* add the address breakpoint with optional option */ ! 1520: sprintf(command, "pc=$%x %c%s", addr, cut?':':' ', cut?cut:""); ! 1521: if (!BreakCond_Command(command, bForDsp)) { ! 1522: return false; ! 1523: } ! 1524: ! 1525: /* on success, show on what instruction it was added */ ! 1526: uaecptr dummy; ! 1527: m68k_disasm(stderr, (uaecptr)addr, &dummy, 1); ! 1528: return true; ! 1529: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.