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