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