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