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