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