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