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