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