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