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