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