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