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