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