|
|
1.1 root 1: /*
2: Hatari - breakcond.c
3:
4: Copyright (c) 2009 by Eero Tamminen
5:
6: This file is distributed under the GNU Public License, version 2 or at
7: 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 "m68000.h"
21: #include "memorySnapShot.h"
22: #include "dsp.h"
23: #include "debugui.h"
24: #include "stMemory.h"
25: #include "str.h"
26: #include "video.h" /* for Hatari video variable addresses */
27: #include "breakcond.h"
28:
29:
30: /* set to 1 to enable parsing function tracing / debug output */
31: #define DEBUG 0
32:
33: /* needs to go through long long to handle x=32 */
34: #define BITMASK(x) ((Uint32)(((unsigned long long)1<<(x))-1))
35:
36: #define BC_MAX_CONDITION_BREAKPOINTS 16
37: #define BC_MAX_CONDITIONS_PER_BREAKPOINT 4
38:
39: #define BC_DEFAULT_DSP_SPACE 'P'
40:
41: enum {
42: /* plain number */
43: VALUE_TYPE_NUMBER = 0,
44:
45: /* functions to call to get value */
46: VALUE_TYPE_FUNCTION32 = 2,
47:
48: /* internal Hatari value variables */
49: VALUE_TYPE_VAR32 = 4,
50:
51: /* size must match register size used in BreakCond_ParseRegister() */
52: VALUE_TYPE_REG16 = 16,
53: VALUE_TYPE_REG32 = 32
54: } typedef value_t;
55:
56: static inline bool is_register_type(value_t vtype) {
57: /* type used for CPU/DSP registers */
58: return (vtype == VALUE_TYPE_REG16 || vtype == VALUE_TYPE_REG32);
59: }
60:
61: typedef struct {
62: bool is_indirect;
63: char dsp_space; /* DSP has P, X, Y address spaces, zero if not DSP */
64: value_t valuetype; /* Hatari value variable type */
65: union {
66: Uint32 number;
67: Uint16 (*func16)(void);
68: Uint32 (*func32)(void);
69: Uint16 *reg16;
70: Uint32 *reg32;
71: } value;
72: Uint32 bits; /* CPU has 8/16/32 bit address widths */
73: Uint32 mask; /* <width mask> && <value mask> */
74: } bc_value_t;
75:
76: typedef struct {
77: bc_value_t lvalue;
78: bc_value_t rvalue;
79: char comparison;
80: } bc_condition_t;
81:
82: typedef struct {
83: char *expression;
84: bc_condition_t conditions[BC_MAX_CONDITIONS_PER_BREAKPOINT];
85: int ccount;
86: } bc_breakpoint_t;
87:
88: static bc_breakpoint_t BreakPointsCpu[BC_MAX_CONDITION_BREAKPOINTS];
89: static bc_breakpoint_t BreakPointsDsp[BC_MAX_CONDITION_BREAKPOINTS];
90: static int BreakPointCpuCount;
91: static int BreakPointDspCount;
92:
93: /* --------------------- memory snapshot ------------------- */
94:
95: /**
96: * Save/Restore snapshot of local breakpoint variables
97: */
98: void BreakCond_MemorySnapShot_Capture(bool bSave)
99: {
100: char tmp[256], **str;
101: int i, idx, count;
102:
103: if (!bSave) {
104: /* free current data before restore */
105: for (i = 0; i < BreakPointCpuCount; i++) {
106: free(BreakPointsCpu[i].expression);
107: }
108: for (i = 0; i < BreakPointDspCount; i++) {
109: free(BreakPointsDsp[i].expression);
110: }
111: }
112:
113: /* save/restore arrays & counts */
114: MemorySnapShot_Store(&BreakPointCpuCount, sizeof(BreakPointCpuCount));
115: MemorySnapShot_Store(&BreakPointDspCount, sizeof(BreakPointDspCount));
116: MemorySnapShot_Store(&BreakPointsCpu, sizeof(BreakPointsCpu));
117: MemorySnapShot_Store(&BreakPointsDsp, sizeof(BreakPointsDsp));
118:
119: /* save/restore dynamically allocated strings */
120: for (i = 0; i < 2*BC_MAX_CONDITION_BREAKPOINTS; i++) {
121: if (i >= BC_MAX_CONDITION_BREAKPOINTS) {
122: idx = i-BC_MAX_CONDITION_BREAKPOINTS;
123: str = &(BreakPointsDsp[idx].expression);
124: count = BreakPointDspCount;
125: } else {
126: idx = i;
127: str = &(BreakPointsCpu[idx].expression);
128: count = BreakPointCpuCount;
129: }
130: if (bSave) {
131: /* clean + zero-terminate, copy & save */
132: memset(tmp, 0, sizeof(tmp));
133: if (idx < count) {
134: strncpy(tmp, *str, sizeof(tmp)-1);
135: }
136: MemorySnapShot_Store(&tmp, sizeof(tmp));
137: } else {
138: MemorySnapShot_Store(&tmp, sizeof(tmp));
139: if (idx < count) {
140: *str = strdup(tmp);
141: assert(*str);
142: } else {
143: *str = NULL;
144: }
145: }
146: }
147: }
148:
149: /* --------------------- debugging code ------------------- */
150:
151: #if DEBUG
152: /* see parsing code for usage examples */
153: static int _traceIndent;
154: static void _spaces(void)
155: {
156: int spaces = _traceIndent;
157: while(spaces-- > 0) {
158: putchar(' '); /* fputc(' ',stdout); */
159: }
160: }
161: #define ENTERFUNC(args) { _traceIndent += 2; _spaces(); printf args ; fflush(stdout); }
162: #define EXITFUNC(args) { _spaces(); printf args ; fflush(stdout); _traceIndent -= 2; }
163: #else
164: #define ENTERFUNC(args)
165: #define EXITFUNC(args)
166: #endif
167:
168:
169: /* ------------- breakpoint condition checking, internals ------------- */
170:
171: /**
172: * Return value from given DSP memory space/address
173: */
174: static Uint32 BreakCond_ReadDspMemory(Uint32 addr, const bc_value_t *bc_value)
175: {
176: const char *dummy;
177: return DSP_ReadMemory(addr, bc_value->dsp_space, &dummy) & BITMASK(24);
178: }
179:
180: /**
181: * Return value of given size read from given ST memory address
182: */
183: static Uint32 BreakCond_ReadSTMemory(Uint32 addr, const bc_value_t *bc_value)
184: {
185: /* Mask to a 24 bit address. With this e.g. $ffff820a is also
186: * recognized as IO mem $ff820a (which is the same in the 68000).
187: */
188: addr &= 0x00ffffff;
189:
190: switch (bc_value->bits) {
191: case 32:
192: return STMemory_ReadLong(addr);
193: case 16:
194: return STMemory_ReadWord(addr);
195: case 8:
196: return STMemory_ReadByte(addr);
197: default:
198: fprintf(stderr, "ERROR: unknown ST address size %d!\n", bc_value->bits);
199: abort();
200: }
201: }
202:
203:
204: /**
205: * Return Uint32 value according to given bc_value_t specification
206: */
207: static Uint32 BreakCond_GetValue(const bc_value_t *bc_value)
208: {
209: Uint32 value;
210:
211: switch (bc_value->valuetype) {
212: case VALUE_TYPE_NUMBER:
213: value = bc_value->value.number;
214: break;
215: case VALUE_TYPE_FUNCTION32:
216: value = bc_value->value.func32();
217: break;
218: case VALUE_TYPE_REG16:
219: value = *(bc_value->value.reg16);
220: break;
221: case VALUE_TYPE_VAR32:
222: case VALUE_TYPE_REG32:
223: value = *(bc_value->value.reg32);
224: break;
225: default:
226: fprintf(stderr, "ERROR: unknown condition value size/type %d!\n", bc_value->valuetype);
227: abort();
228: }
229: if (bc_value->is_indirect) {
230: if (bc_value->dsp_space) {
231: value = BreakCond_ReadDspMemory(value, bc_value);
232: } else {
233: value = BreakCond_ReadSTMemory(value, bc_value);
234: }
235: }
236: return (value & bc_value->mask);
237: }
238:
239:
240: /**
241: * Return true if all of the given breakpoint's conditions match
242: */
243: static bool BreakCond_MatchConditions(const bc_condition_t *condition, int count)
244: {
245: Uint32 lvalue, rvalue;
246: bool hit = false;
247: int i;
248:
249: for (i = 0; i < count; condition++, i++) {
250:
251: lvalue = BreakCond_GetValue(&(condition->lvalue));
252: rvalue = BreakCond_GetValue(&(condition->rvalue));
253:
254: switch (condition->comparison) {
255: case '<':
256: hit = (lvalue < rvalue);
257: break;
258: case '>':
259: hit = (lvalue > rvalue);
260: break;
261: case '=':
262: hit = (lvalue == rvalue);
263: break;
264: case '!':
265: hit = (lvalue != rvalue);
266: break;
267: default:
268: fprintf(stderr, "ERROR: Unknown breakpoint value comparison operator '%c'!\n",
269: condition->comparison);
270: abort();
271: }
272: if (!hit) {
273: return false;
274: }
275: }
276: /* all conditions matched */
277: return true;
278: }
279:
280:
281: /**
282: * Return which of the given condition breakpoints match
283: * or zero if none matched
284: */
285: static int BreakCond_MatchBreakPoints(const bc_breakpoint_t *bp, int count)
286: {
287: int i;
288:
289: for (i = 0; i < count; bp++, i++) {
290: if (BreakCond_MatchConditions(bp->conditions, bp->ccount)) {
291: fprintf(stderr, "%d. breakpoint '%s' conditions matched.\n",
292: i+1, bp->expression);
293: /* indexes for BreakCond_Remove() start from 1 */
294: return i + 1;
295: }
296: }
297: return 0;
298: }
299:
300: /* ------------- breakpoint condition checking, public API ------------- */
301:
302: /**
303: * Return true if any of the CPU breakpoint/conditions match
304: */
305: int BreakCond_MatchCpu(void)
306: {
307: return BreakCond_MatchBreakPoints(BreakPointsCpu, BreakPointCpuCount);
308: }
309:
310: /**
311: * Return true if any of the DSP breakpoint/conditions match
312: */
313: int BreakCond_MatchDsp(void)
314: {
315: return BreakCond_MatchBreakPoints(BreakPointsDsp, BreakPointDspCount);
316: }
317:
318: /**
319: * Return number of condition breakpoints
320: */
321: int BreakCond_BreakPointCount(bool bForDsp)
322: {
323: if (bForDsp) {
324: return BreakPointDspCount;
325: } else {
326: return BreakPointCpuCount;
327: }
328: }
329:
330:
331: /* -------------- breakpoint condition parsing, internals ------------- */
332:
333: /* struct for passing around breakpoint conditions parsing state */
334: typedef struct {
335: int arg; /* current arg */
336: int argc; /* arg count */
337: const char **argv; /* arg pointer array (+ strings) */
338: const char *error; /* error from parsing args */
339: } parser_state_t;
340:
341:
342: /* Hatari variable name & address array items */
343: typedef struct {
344: const char *name;
345: Uint32 *addr;
346: value_t vtype;
347: size_t bits;
348: const char *constraints;
349: } var_addr_t;
350:
351: /* Accessor functions for calculated Hatari values */
352: static Uint32 GetLineCycles(void)
353: {
354: int dummy1, dummy2, lcycles;
355: Video_GetPosition(&dummy1, &dummy2 , &lcycles);
356: return lcycles;
357: }
358: static Uint32 GetFrameCycles(void)
359: {
360: int dummy1, dummy2, fcycles;
361: Video_GetPosition(&fcycles, &dummy1, &dummy2);
362: return fcycles;
363: }
364:
365: /* sorted by variable name so that this can be bisected */
366: static const var_addr_t hatari_vars[] = {
367: { "FrameCycles", (Uint32*)GetFrameCycles, VALUE_TYPE_FUNCTION32, 0, NULL },
368: { "HBL", (Uint32*)&nHBL, VALUE_TYPE_VAR32, sizeof(nHBL)*8, NULL },
369: { "LineCycles", (Uint32*)GetLineCycles, VALUE_TYPE_FUNCTION32, 0, "is always divisable by 4" },
370: { "VBL", (Uint32*)&nVBLs, VALUE_TYPE_VAR32, sizeof(nVBLs)*8, NULL }
371: };
372:
373:
374: /**
375: * If given string is a Hatari variable name, set bc_value
376: * fields accordingly and return true, otherwise return false.
377: */
378: static bool BreakCond_ParseVariable(const char *name, bc_value_t *bc_value)
379: {
380: /* left, right, middle, direction */
381: int l, r, m, dir;
382:
383: ENTERFUNC(("BreakCond_ParseVariable('%s')\n", name));
384: /* bisect */
385: l = 0;
386: r = sizeof (hatari_vars) / sizeof (*hatari_vars) - 1;
387: do {
388: m = (l+r) >> 1;
389: dir = strcasecmp(name, hatari_vars[m].name);
390: if (dir == 0) {
391: bc_value->value.reg32 = hatari_vars[m].addr;
392: bc_value->valuetype = hatari_vars[m].vtype;
393: bc_value->bits = hatari_vars[m].bits;
394: assert(bc_value->bits == 32 || bc_value->valuetype != VALUE_TYPE_VAR32);
395: EXITFUNC(("-> true\n"));
396: return true;
397: }
398: if (dir < 0) {
399: r = m-1;
400: } else {
401: l = m+1;
402: }
403: } while (l <= r);
404: EXITFUNC(("-> false\n"));
405: return false;
406: }
407:
408:
409: /**
410: * Helper function to get CPU PC register value with static inline as Uint32
411: */
412: static Uint32 GetCpuPC(void)
413: {
414: return M68000_GetPC();
415: }
416: /**
417: * Helper function to get CPU SR register value with static inline as Uint32
418: */
419: static Uint32 GetCpuSR(void)
420: {
421: return M68000_GetSR();
422: }
423:
424: /**
425: * If given string is register name (for DSP or CPU), set bc_value
426: * fields accordingly and return true, otherwise return false.
427: */
428: static bool BreakCond_ParseRegister(const char *regname, bc_value_t *bc_value, parser_state_t *pstate)
429: {
430: int regsize;
431: ENTERFUNC(("BreakCond_ParseRegister('%s')\n", regname));
432: if (bc_value->dsp_space) {
433: regsize = DSP_GetRegisterAddress(regname,
434: &(bc_value->value.reg32),
435: &(bc_value->mask));
436: if (regsize) {
437: if (bc_value->is_indirect && toupper(regname[0]) != 'R') {
438: pstate->error = "only R0-R7 registers can be used for indirect addressing";
439: EXITFUNC(("-> false (DSP)\n"));
440: return false;
441: }
442: /* all DSP memory values are 24-bits */
443: bc_value->bits = 24;
444: bc_value->valuetype = regsize;
445: EXITFUNC(("-> true (DSP)\n"));
446: return true;
447: }
448: pstate->error = "invalid DSP register name";
449: EXITFUNC(("-> false (DSP)\n"));
450: return false;
451: }
452: regsize = DebugUI_GetCpuRegisterAddress(regname, &(bc_value->value.reg32));
453: if (regsize) {
454: bc_value->bits = regsize;
455: bc_value->valuetype = regsize;
456: EXITFUNC(("-> true (CPU)\n"));
457: return true;
458: }
459: /* Exact UAE core 32-bit PC & 16-bit SR register values
460: * can be gotten only through AUE accessors, not directly
461: */
462: if (strcasecmp(regname, "PC") == 0) {
463: bc_value->bits = 32;
464: bc_value->value.func32 = GetCpuPC;
465: bc_value->valuetype = VALUE_TYPE_FUNCTION32;
466: EXITFUNC(("-> true (CPU)\n"));
467: return true;
468: }
469: if (strcasecmp(regname, "SR") == 0) {
470: bc_value->bits = 16;
471: bc_value->value.func32 = GetCpuSR;
472: bc_value->valuetype = VALUE_TYPE_FUNCTION32;
473: EXITFUNC(("-> true (CPU)\n"));
474: return true;
475: }
476: pstate->error = "invalid CPU register name";
477: EXITFUNC(("-> false (CPU)\n"));
478: return false;
479: }
480:
481: /**
482: * If given address is valid (for DSP or CPU), return true.
483: */
484: static bool BreakCond_CheckAddress(bc_value_t *bc_value)
485: {
486: Uint32 highbyte, bit23, addr = bc_value->value.number;
487:
488: ENTERFUNC(("BreakCond_CheckAddress(%x)\n", addr));
489: if (bc_value->dsp_space) {
490: if (addr > 0xFFFF) {
491: EXITFUNC(("-> false (DSP)\n"));
492: return false;
493: }
494: EXITFUNC(("-> true (DSP)\n"));
495: return true;
496: }
497:
498: bit23 = (addr >> 23) & 1;
499: highbyte = (addr >> 24) & 0xff;
500: if ((bit23 == 0 && highbyte != 0) ||
501: (bit23 == 1 && highbyte != 0xff)) {
502: fprintf(stderr, "WARNING: address 0x%x 23th bit isn't extended to bits 24-31.\n", addr);
503: }
504: /* use a 24-bit address */
505: addr &= 0x00ffffff;
506: if ((addr > STRamEnd && addr < 0xe00000) ||
507: (addr >= 0xff0000 && addr < 0xff8000)) {
508: EXITFUNC(("-> false (CPU)\n"));
509: return false;
510: }
511: EXITFUNC(("-> true (CPU)\n"));
512: return true;
513: }
514:
515:
516: /**
517: * Check for and parse a condition value address space/width modifier.
518: * Modify pstate according to parsing (arg index and error string).
519: * Return false for error and true for no or successfully parsed modifier.
520: */
521: static bool BreakCond_ParseAddressModifier(parser_state_t *pstate, bc_value_t *bc_value)
522: {
523: char mode;
524:
525: ENTERFUNC(("BreakCond_ParseAddressModifier()\n"));
526: if (pstate->arg+2 > pstate->argc ||
527: strcmp(pstate->argv[pstate->arg], ".") != 0) {
528: if (bc_value->dsp_space && bc_value->is_indirect) {
529: pstate->error = "DSP memory addresses need to specify address space";
530: EXITFUNC(("arg:%d -> false\n", pstate->arg));
531: return false;
532: }
533: EXITFUNC(("arg:%d -> true (missing)\n", pstate->arg));
534: return true;
535: }
536: if (!bc_value->is_indirect) {
537: pstate->error = "space/width modifier makes sense only for an address (register)";
538: EXITFUNC(("arg:%d -> false\n", pstate->arg));
539: return false;
540: }
541: pstate->arg++;
542: if (bc_value->dsp_space) {
543: switch (pstate->argv[pstate->arg][0]) {
544: case 'p':
545: case 'x':
546: case 'y':
547: mode = toupper(pstate->argv[pstate->arg][0]);
548: break;
549: default:
550: pstate->error = "invalid address space modifier";
551: EXITFUNC(("arg:%d -> false\n", pstate->arg));
552: return false;
553: }
554: } else {
555: switch (pstate->argv[pstate->arg][0]) {
556: case 'l':
557: mode = 32;
558: break;
559: case 'w':
560: mode = 16;
561: break;
562: case 'b':
563: mode = 8;
564: break;
565: default:
566: pstate->error = "invalid address width modifier";
567: EXITFUNC(("arg:%d -> false\n", pstate->arg));
568: return false;
569: }
570: }
571: if (pstate->argv[pstate->arg][1]) {
572: pstate->error = "invalid address space/width modifier";
573: EXITFUNC(("arg:%d -> false\n", pstate->arg));
574: return false;
575: }
576: if (bc_value->dsp_space) {
577: bc_value->dsp_space = mode;
578: EXITFUNC(("arg:%d -> space:%c, true\n", pstate->arg, mode));
579: } else {
580: bc_value->bits = mode;
581: EXITFUNC(("arg:%d -> width:%d, true\n", pstate->arg, mode));
582: }
583: pstate->arg++;
584: return true;
585: }
586:
587:
588: /**
589: * Check for and parse a condition value mask.
590: * Modify pstate according to parsing (arg index and error string).
591: * Return false for error and true for no or successfully parsed modifier.
592: */
593: static bool BreakCond_ParseMaskModifier(parser_state_t *pstate, bc_value_t *bc_value)
594: {
595: ENTERFUNC(("BreakCond_ParseMaskModifier()\n"));
596: if (pstate->arg+2 > pstate->argc ||
597: strcmp(pstate->argv[pstate->arg], "&") != 0) {
598: EXITFUNC(("arg:%d -> true (missing)\n", pstate->arg));
599: return true;
600: }
601: if (bc_value->valuetype == VALUE_TYPE_NUMBER &&
602: !bc_value->is_indirect) {
603: fprintf(stderr, "WARNING: plain numbers shouldn't need masks.\n");
604: }
605: pstate->arg++;
606: if (!Str_GetNumber(pstate->argv[pstate->arg], &(bc_value->mask))) {
607: pstate->error = "invalid dec/hex/bin value";
608: EXITFUNC(("arg:%d -> false\n", pstate->arg));
609: return false;
610: }
611: if (bc_value->mask == 0 ||
612: (bc_value->valuetype == VALUE_TYPE_NUMBER && !bc_value->is_indirect &&
613: bc_value->value.number && !(bc_value->value.number & bc_value->mask))) {
614: pstate->error = "mask zeroes value";
615: EXITFUNC(("arg:%d -> false\n", pstate->arg));
616: return false;
617: }
618: EXITFUNC(("arg:%d -> true (%x)\n", pstate->arg, bc_value->mask));
619: pstate->arg++;
620: return true;
621: }
622:
623:
624: /**
625: * Parse a breakpoint condition value.
626: * Modify pstate according to parsing (arg index and error string).
627: * Return true for success and false for error.
628: */
629: static bool BreakCond_ParseValue(parser_state_t *pstate, bc_value_t *bc_value)
630: {
631: const char *str;
632: int skip = 1;
633:
634: ENTERFUNC(("BreakCond_Value()\n"));
635: if (pstate->arg >= pstate->argc) {
636: pstate->error = "value missing";
637: EXITFUNC(("arg:%d -> false\n", pstate->arg));
638: return false;
639: }
640: /* parse indirection */
641: if (pstate->arg+3 <= pstate->argc) {
642: if (strcmp(pstate->argv[pstate->arg+0], "(") == 0 &&
643: strcmp(pstate->argv[pstate->arg+2], ")") == 0) {
644: bc_value->is_indirect = true;
645: pstate->arg++;
646: skip = 2;
647: }
648: }
649:
650: str = pstate->argv[pstate->arg];
651: /* parse direct or indirect value */
652: if (isalpha(str[0])) {
653: if (bc_value->is_indirect) {
654: /* a valid register name? */
655: if (!BreakCond_ParseRegister(str, bc_value, pstate)) {
656: EXITFUNC(("arg:%d -> false\n", pstate->arg));
657: return false;
658: }
659: } else {
660: /* a valid Hatari variable or register name?
661: * variables cannot be used for ST memory indirection.
662: */
663: if (!BreakCond_ParseVariable(str, bc_value) &&
664: !BreakCond_ParseRegister(str, bc_value, pstate)) {
665: pstate->error = "invalid variable/register name";
666: EXITFUNC(("arg:%d -> false\n", pstate->arg));
667: return false;
668: }
669: }
670: } else {
671: /* a number */
672: if (!Str_GetNumber(str, &(bc_value->value.number))) {
673: pstate->error = "invalid dec/hex/bin value";
674: EXITFUNC(("arg:%d -> false\n", pstate->arg));
675: return false;
676: }
677: /* suitable as emulated memory address (indirect)? */
678: if (bc_value->is_indirect &&
679: bc_value->valuetype == VALUE_TYPE_NUMBER &&
680: !BreakCond_CheckAddress(bc_value)) {
681: pstate->error = "invalid address";
682: EXITFUNC(("arg:%d -> false\n", pstate->arg));
683: return false;
684: }
685: }
686: pstate->arg += skip;
687:
688: /* parse modifiers */
689: if (!BreakCond_ParseAddressModifier(pstate, bc_value)) {
690: EXITFUNC(("arg:%d -> false\n", pstate->arg));
691: return false;
692: }
693: if (!BreakCond_ParseMaskModifier(pstate, bc_value)) {
694: EXITFUNC(("arg:%d -> false\n", pstate->arg));
695: return false;
696: }
697: EXITFUNC(("arg:%d -> true (%s value)\n", pstate->arg,
698: (bc_value->is_indirect ? "indirect" : "direct")));
699: return true;
700: }
701:
702:
703: /**
704: * Parse a breakpoint comparison character.
705: * Modify pstate according to parsing (arg index and error string).
706: * Return the character or nil for an error.
707: */
708: static char BreakCond_ParseComparison(parser_state_t *pstate)
709: {
710: const char *comparison;
711:
712: ENTERFUNC(("BreakCond_ParseComparison(), arg:%d\n", pstate->arg));
713: if (pstate->arg >= pstate->argc) {
714: pstate->error = "breakpoint comparison missing";
715: EXITFUNC(("-> false\n"));
716: return false;
717: }
718: comparison = pstate->argv[pstate->arg];
719: switch (comparison[0]) {
720: case '<':
721: case '>':
722: case '=':
723: case '!':
724: break;
725: default:
726: pstate->error = "invalid comparison character";
727: EXITFUNC(("-> false\n"));
728: return false;
729: }
730: if (comparison[1]) {
731: pstate->error = "trailing comparison character(s)";
732: EXITFUNC(("-> false\n"));
733: return false;
734: }
735:
736: pstate->arg++;
737: if (pstate->arg >= pstate->argc) {
738: pstate->error = "right side missing";
739: EXITFUNC(("-> false\n"));
740: return false;
741: }
742: EXITFUNC(("-> '%c'\n", *comparison));
743: return *comparison;
744: }
745:
746:
747: /**
748: * If no value, use the other value, if that also missing, use default
749: */
750: static void BreakCond_InheritDefault(Uint32 *value1, Uint32 value2, Uint32 defvalue)
751: {
752: if (!*value1) {
753: if (value2) {
754: *value1 = value2;
755: } else {
756: *value1 = defvalue;
757: }
758: }
759: }
760:
761: /**
762: * Check & ensure that the masks and address sizes are sane
763: * and allow comparison with the other side.
764: * If yes, return true, otherwise false.
765: */
766: static bool BreakCond_CrossCheckValues(parser_state_t *pstate,
767: bc_value_t *bc_value1,
768: bc_value_t *bc_value2)
769: {
770: Uint32 mask1, mask2, defbits;
771: ENTERFUNC(("BreakCond_CrossCheckValues()\n"));
772:
773: /* make sure there're valid bit widths and that masks have some value */
774: if (bc_value1->dsp_space) {
775: defbits = 24;
776: } else {
777: defbits = 32;
778: }
779: BreakCond_InheritDefault(&(bc_value1->bits), bc_value2->bits, defbits);
780: BreakCond_InheritDefault(&(bc_value2->bits), bc_value1->bits, defbits);
781: BreakCond_InheritDefault(&(bc_value1->mask), bc_value2->mask, BITMASK(bc_value1->bits));
782: BreakCond_InheritDefault(&(bc_value2->mask), bc_value1->mask, BITMASK(bc_value2->bits));
783:
784: /* check first value mask & bit width */
785: mask1 = BITMASK(bc_value1->bits) & bc_value1->mask;
786:
787: if (mask1 != bc_value1->mask) {
788: fprintf(stderr, "WARNING: mask 0x%x doesn't fit into %d address/register bits.\n",
789: bc_value1->mask, bc_value1->bits);
790: }
791: if (!bc_value1->dsp_space &&
792: bc_value1->is_indirect &&
793: (bc_value1->value.number & 1) && bc_value1->bits > 8) {
794: fprintf(stderr, "WARNING: odd CPU address 0x%x given without using byte (.b) width.\n",
795: bc_value1->value.number);
796: }
797:
798: /* cross-check both values masks */
799: mask2 = BITMASK(bc_value2->bits) & bc_value2->mask;
800:
801: if ((mask1 & mask2) == 0) {
802: pstate->error = "values masks cancel each other";
803: EXITFUNC(("-> false\n"));
804: return false;
805: }
806: if (bc_value2->is_indirect ||
807: bc_value2->value.number == 0 ||
808: bc_value2->valuetype != VALUE_TYPE_NUMBER) {
809: EXITFUNC(("-> true (no problematic direct types)\n"));
810: return true;
811: }
812: if ((bc_value2->value.number & mask1) != bc_value2->value.number) {
813: pstate->error = "number doesn't fit the other side address width&mask";
814: EXITFUNC(("-> false\n"));
815: return false;
816: }
817: EXITFUNC(("-> true\n"));
818: return true;
819: }
820:
821:
822: /**
823: * Parse given breakpoint conditions and append them to breakpoints.
824: * Modify pstate according to parsing (arg index and error string).
825: * Return number of added conditions or zero for failure.
826: */
827: static int BreakCond_ParseCondition(parser_state_t *pstate, bool bForDsp,
828: bc_condition_t *conditions, int ccount)
829: {
830: bc_condition_t condition;
831:
832: ENTERFUNC(("BreakCond_ParseCondition(...)\n"));
833: if (ccount >= BC_MAX_CONDITIONS_PER_BREAKPOINT) {
834: pstate->error = "max number of conditions exceeded";
835: EXITFUNC(("-> 0 (no conditions free)\n"));
836: return 0;
837: }
838:
839: /* setup condition */
840: memset(&condition, 0, sizeof(bc_condition_t));
841: if (bForDsp) {
842: /* used also for checking whether value is for DSP */
843: condition.lvalue.dsp_space = BC_DEFAULT_DSP_SPACE;
844: condition.rvalue.dsp_space = BC_DEFAULT_DSP_SPACE;
845: }
846:
847: /* parse condition */
848: if (!BreakCond_ParseValue(pstate, &(condition.lvalue))) {
849: EXITFUNC(("-> 0\n"));
850: return 0;
851: }
852: condition.comparison = BreakCond_ParseComparison(pstate);
853: if (!condition.comparison) {
854: EXITFUNC(("-> 0\n"));
855: return 0;
856: }
857: if (!BreakCond_ParseValue(pstate, &(condition.rvalue))) {
858: EXITFUNC(("-> 0\n"));
859: return 0;
860: }
861: if (!(BreakCond_CrossCheckValues(pstate, &(condition.lvalue), &(condition.rvalue)) &&
862: BreakCond_CrossCheckValues(pstate, &(condition.rvalue), &(condition.lvalue)))) {
863: EXITFUNC(("-> 0\n"));
864: return 0;
865: }
866: /* new condition */
867: conditions[ccount++] = condition;
868:
869: /* continue with next condition? */
870: if (pstate->arg == pstate->argc) {
871: EXITFUNC(("-> %d (conditions)\n", ccount-1));
872: return ccount;
873: }
874: if (strcmp(pstate->argv[pstate->arg], "&&") != 0) {
875: pstate->error = "trailing content for breakpoint condition";
876: EXITFUNC(("-> 0\n"));
877: return 0;
878: }
879: pstate->arg++;
880:
881: /* recurse conditions parsing */
882: ccount = BreakCond_ParseCondition(pstate, bForDsp, conditions, ccount);
883: if (!ccount) {
884: EXITFUNC(("-> 0\n"));
885: return 0;
886: }
887: EXITFUNC(("-> %d (conditions)\n", ccount-1));
888: return ccount;
889: }
890:
891:
892: /**
893: * Tokenize given breakpoint expression to given parser struct.
894: * Return normalized expression string that corresponds to tokenization
895: * or NULL on error. On error, pstate->error contains the error message
896: * and pstate->arg index to invalid character (instead of to token like
897: * after parsing).
898: */
899: static char *BreakCond_TokenizeExpression(const char *expression,
900: parser_state_t *pstate)
901: {
902: char separator[] = {
903: '=', '!', '<', '>', /* comparison operators */
904: '(', ')', '.', '&', /* other separators */
905: '\0' /* terminator */
906: };
907: bool is_separated, has_comparison;
908: char sep, *dst, *normalized;
909: const char *src;
910: int i, tokens;
911:
912: memset(pstate, 0, sizeof(parser_state_t));
913:
914: /* _minimum_ safe size for normalized expression is 2x+1 */
915: normalized = malloc(2*strlen(expression)+1);
916: if (!normalized) {
917: pstate->error = "alloc failed";
918: return NULL;
919: }
920:
921: /* check characters & normalize string */
922: dst = normalized;
923: is_separated = false;
924: has_comparison = false;
925: for (src = expression; *src; src++) {
926: /* discard white space in source */
927: if (isspace(*src)) {
928: continue;
929: }
930: /* separate tokens with single space in destination */
931: for (i = 0; (sep = separator[i]); i++) {
932: if (*src == sep) {
933: if (dst > normalized) {
934: /* don't separate boolean AND '&&' */
935: if (*src == '&' && *(src-1) == '&') {
936: dst--;
937: } else {
938: if (!is_separated) {
939: *dst++ = ' ';
940: }
941: }
942: }
943: *dst++ = *src;
944: *dst++ = ' ';
945: is_separated = true;
946: if (i < 4) {
947: has_comparison = true;
948: }
949: break;
950: }
951: }
952: /* validate & copy other characters */
953: if (!sep) {
954: if (!(isalnum(*src) || isblank(*src) ||
955: *src == '$' || *src == '%')) {
956: pstate->error = "invalid character";
957: pstate->arg = src-expression;
958: free(normalized);
959: return NULL;
960: }
961: *dst++ = tolower(*src);
962: is_separated = false;
963: }
964: }
965: if (is_separated) {
966: dst--; /* no trailing space */
967: }
968: *dst = '\0';
969:
970: if (!has_comparison) {
971: pstate->error = "condition comparison missing";
972: pstate->arg = strlen(expression)/2;
973: free(normalized);
974: return NULL;
975: }
976:
977: /* allocate exact space for tokenized string array + strings */
978: tokens = 1;
979: for (dst = normalized; *dst; dst++) {
980: if (*dst == ' ') {
981: tokens++;
982: }
983: }
984: pstate->argv = malloc(tokens*sizeof(char*)+strlen(normalized)+1);
985: if (!pstate->argv) {
986: pstate->error = "alloc failed";
987: free(normalized);
988: return NULL;
989: }
990: /* and copy/tokenize... */
991: dst = (char*)(pstate->argv) + tokens*sizeof(char*);
992: strcpy(dst, normalized);
993: pstate->argv[0] = strtok(dst, " ");
994: for (i = 1; (dst = strtok(NULL, " ")); i++) {
995: pstate->argv[i] = dst;
996: }
997: assert(i == tokens);
998: pstate->argc = tokens;
999: #if DEBUG
1000: fprintf(stderr, "args->");
1001: for (i = 0; i < tokens; i++) {
1002: fprintf(stderr, " %d: %s,", i, pstate->argv[i]);
1003: }
1004: fprintf(stderr, "\n");
1005: #endif
1006: return normalized;
1007: }
1008:
1009:
1010: /**
1011: * Helper to set corrent breakpoint list and type name to given variables.
1012: * Return pointer to breakpoint list count
1013: */
1014: static int* BreakCond_GetListInfo(bc_breakpoint_t **bp,
1015: const char **name, bool bForDsp)
1016: {
1017: int *bcount;
1018: if (bForDsp) {
1019: bcount = &BreakPointDspCount;
1020: *bp = BreakPointsDsp;
1021: *name = "DSP";
1022: } else {
1023: bcount = &BreakPointCpuCount;
1024: *bp = BreakPointsCpu;
1025: *name = "CPU";
1026: }
1027: return bcount;
1028: }
1029:
1030:
1031: /**
1032: * Parse given breakpoint expression and store it.
1033: * Return true for success and false for failure.
1034: */
1035: static bool BreakCond_Parse(const char *expression, bool bForDsp)
1036: {
1037: parser_state_t pstate;
1038: bc_breakpoint_t *bp;
1039: const char *name;
1040: char *normalized;
1041: int *bcount;
1042: int ccount;
1043:
1044: bcount = BreakCond_GetListInfo(&bp, &name, bForDsp);
1045: if (*bcount >= BC_MAX_CONDITION_BREAKPOINTS) {
1046: fprintf(stderr, "ERROR: no free %s condition breakpoints left.\n", name);
1047: return false;
1048: }
1049: bp += *bcount;
1050:
1051: normalized = BreakCond_TokenizeExpression(expression, &pstate);
1052: if (normalized) {
1053: bp->expression = normalized;
1054: ccount = BreakCond_ParseCondition(&pstate, bForDsp,
1055: bp->conditions, 0);
1056: bp->ccount = ccount;
1057: } else {
1058: ccount = 0;
1059: }
1060: if (pstate.argv) {
1061: free(pstate.argv);
1062: }
1063: if (ccount > 0) {
1064: (*bcount)++;
1065: fprintf(stderr, "%s condition breakpoint %d with %d condition(s) added.\n",
1066: name, *bcount, ccount);
1067: } else {
1068: if (normalized) {
1069: int offset, i = 0;
1070: char *s = normalized;
1071: while (*s && i < pstate.arg) {
1072: if (*s++ == ' ') {
1073: i++;
1074: }
1075: }
1076: offset = s - normalized;
1077: /* show tokenized string and point out
1078: * the token where the error was encountered
1079: */
1080: fprintf(stderr, "ERROR in tokenized string:\n'%s'\n%*c-%s\n",
1081: normalized, offset+2, '^', pstate.error);
1082: free(normalized);
1083: bp->expression = NULL;
1084: } else {
1085: /* show original string and point out the character
1086: * where the error was encountered
1087: */
1088: fprintf(stderr, "ERROR in parsed string:\n'%s'\n%*c-%s\n",
1089: expression, pstate.arg+2, '^', pstate.error);
1090: }
1091: }
1092: return (ccount > 0);
1093: }
1094:
1095:
1096: /**
1097: * List condition breakpoints
1098: */
1099: static void BreakCond_List(bool bForDsp)
1100: {
1101: const char *name;
1102: bc_breakpoint_t *bp;
1103: int i, bcount;
1104:
1105: bcount = *BreakCond_GetListInfo(&bp, &name, bForDsp);
1106: if (!bcount) {
1107: fprintf(stderr, "No conditional %s breakpoints.\n", name);
1108: return;
1109: }
1110:
1111: fprintf(stderr, "%d conditional %s breakpoints:\n", bcount, name);
1112: for (i = 1; i <= bcount; bp++, i++) {
1113: fprintf(stderr, "%3d: %s\n", i, bp->expression);
1114: }
1115: }
1116:
1117: /**
1118: * Remove condition breakpoint at given position
1119: */
1120: static bool BreakCond_Remove(int position, bool bForDsp)
1121: {
1122: const char *name;
1123: bc_breakpoint_t *bp;
1124: int *bcount, offset;
1125:
1126: bcount = BreakCond_GetListInfo(&bp, &name, bForDsp);
1127: if (!*bcount) {
1128: fprintf(stderr, "No (more) breakpoints to remove.\n");
1129: return false;
1130: }
1131: if (position < 1 || position > *bcount) {
1132: fprintf(stderr, "ERROR: No such %s breakpoint.\n", name);
1133: return false;
1134: }
1135: offset = position - 1;
1136: fprintf(stderr, "Removed %s breakpoint %d:\n %s\n",
1137: name, position, bp[offset].expression);
1138: free(bp[offset].expression);
1139: bp[offset].expression = NULL;
1140: if (position < *bcount) {
1141: memmove(bp+offset, bp+position,
1142: (*bcount-position)*sizeof(bc_breakpoint_t));
1143: }
1144: (*bcount)--;
1145: return true;
1146: }
1147:
1148:
1149: /**
1150: * Remove all condition breakpoints
1151: */
1152: static void BreakCond_RemoveAll(bool bForDsp)
1153: {
1154: /* try removing everything from alternate ends */
1155: while (BreakCond_Remove(1, bForDsp))
1156: ;
1157: }
1158:
1159:
1160: /**
1161: * help
1162: */
1163: static void BreakCond_Help(void)
1164: {
1165: Uint32 value;
1166: int i;
1167: fputs(
1168: " breakpoint = <expression> [ && <expression> [ && <expression> ] ... ]\n"
1169: " expression = <value>[.mode] [& <number>] <condition> <value>[.mode]\n"
1170: "\n"
1171: " where:\n"
1172: " value = [(] <register-name | hatari-variable | number> [)]\n"
1173: " number = [#|$|%]<digits>\n"
1174: " condition = '<' | '>' | '=' | '!'\n"
1175: " addressing mode (width) = 'b' | 'w' | 'l'\n"
1176: " addressing mode (space) = 'p' | 'x' | 'y'\n"
1177: "\n"
1178: " If the value is in parenthesis like in '($ff820)' or '(a0)', then\n"
1179: " the used value will be read from the memory address pointed by it.\n"
1180: "\n"
1181: " M68k addresses can have byte (b), word (w) or long (l, default) width.\n"
1182: " DSP addresses belong to different address spaces: P, X or Y. Note that\n"
1183: " on DSP only R0-R7 registers can be used for memory addressing.\n"
1184: "\n"
1185: " Valid Hatari variable names (and their current values) are:\n", stderr);
1186: for (i = 0; i < ARRAYSIZE(hatari_vars); i++) {
1187: switch (hatari_vars[i].vtype) {
1188: case VALUE_TYPE_FUNCTION32:
1189: value = ((Uint32(*)(void))(hatari_vars[i].addr))();
1190: break;
1191: case VALUE_TYPE_VAR32:
1192: value = *(hatari_vars[i].addr);
1193: break;
1194: default:
1195: fprintf(stderr, "ERROR: variable '%s' has unsupported type '%d'\n",
1196: hatari_vars[i].name, hatari_vars[i].vtype);
1197: continue;
1198: }
1199: fprintf(stderr, " - %s (%d)", hatari_vars[i].name, value);
1200: if (hatari_vars[i].constraints) {
1201: fprintf(stderr, ", %s\n", hatari_vars[i].constraints);
1202: } else {
1203: fprintf(stderr, "\n");
1204: }
1205: }
1206: fputs(
1207: "\n"
1208: " Examples:\n"
1209: " pc = $64543 && ($ff820).w & 3 = (a0) && d0 = %1100\n"
1210: " (r0).x = 1 && (r0).y = 2\n", stderr);
1211: }
1212:
1213:
1214: /* ------------- breakpoint condition parsing, public API ------------ */
1215:
1216: /**
1217: * Parse given DebugUI command for Dsp and act accordingly
1218: */
1219: bool BreakCond_Command(const char *expression, bool bForDsp)
1220: {
1221: unsigned int position;
1222: const char *end;
1223:
1224: if (!expression) {
1225: BreakCond_List(bForDsp);
1226: return true;
1227: }
1228: while (*expression == ' ') {
1229: expression++;
1230: }
1231: if (strncmp(expression, "help", 4) == 0) {
1232: BreakCond_Help();
1233: return true;
1234: }
1235: if (strcmp(expression, "all") == 0) {
1236: BreakCond_RemoveAll(bForDsp);
1237: return true;
1238: }
1239: end = expression;
1240: while (isdigit(*end)) {
1241: end++;
1242: }
1243: if (end > expression && *end == '\0' &&
1244: sscanf(expression, "%u", &position) == 1) {
1245: return BreakCond_Remove(position, bForDsp);
1246: }
1247: return BreakCond_Parse(expression, bForDsp);
1248: }
1249:
1250:
1251: /* ---------------------------- test code ---------------------------- */
1252:
1253: /* Test building can be done in hatari/src/ with:
1254: * gcc -DTEST -I.. -Iincludes -Iuae-cpu -Ifalcon $(sdl-config --cflags) \
1255: * -O -Wall -g breakcond.c str.c
1256: *
1257: * TODO: Move test stuff to a separate file and add Valgrinding
1258: * and --fstack-protector Make test targets for it
1259: */
1260: #ifdef TEST
1261:
1262: /* fake Hatari configuration variables for str.c */
1263: #include "configuration.h"
1264: CNF_PARAMS ConfigureParams;
1265:
1266: /* fake ST RAM */
1267: Uint8 STRam[16*1024*1024];
1268: Uint32 STRamEnd = 4*1024*1024;
1269:
1270: /* fake Hatari variables */
1271: int nHBL = 20;
1272: int nVBLs = 71;
1273:
1274: /* fake video variables accessor */
1275: void Video_GetPosition(int *pFrameCycles, int *pHBL, int *pLineCycles)
1276: {
1277: *pFrameCycles = 2048;
1278: *pHBL = nHBL;
1279: *pFrameCycles = 508;
1280: }
1281:
1282: /* fake UAE core registers */
1283: struct regstruct regs;
1284:
1285: /* dummy UAE SR register tuning function */
1286: void MakeSR(void) { }
1287:
1288: /* fake AUE register accessors */
1289: int DebugUI_GetCpuRegisterAddress(const char *regname, Uint32 **addr)
1290: {
1291: const char *regnames[] = {
1292: /* must be in same order as in struct above! */
1293: "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
1294: "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7"
1295: };
1296: static Uint32 registers[ARRAYSIZE(regnames)];
1297: int i;
1298: for (i = 0; i < ARRAYSIZE(regnames); i++) {
1299: if (strcmp(regname, regnames[i]) == 0) {
1300: *addr = &(registers[i]);
1301: return 32;
1302: }
1303: }
1304: return 0;
1305: }
1306:
1307: static bool SetCpuRegister(const char *regname, Uint32 value)
1308: {
1309: Uint32 *addr;
1310:
1311: switch (DebugUI_GetCpuRegisterAddress(regname, &addr)) {
1312: case 32:
1313: *addr = value;
1314: break;
1315: case 16:
1316: *(Uint16*)addr = value;
1317: break;
1318: default:
1319: fprintf(stderr, "SETUP ERROR: Register '%s' to set (to %x) is unrecognized!\n", regname, value);
1320: return false;
1321: }
1322: return true;
1323: }
1324:
1325:
1326: /* fake DSP register accessors */
1327: int DSP_GetRegisterAddress(const char *regname, Uint32 **addr, Uint32 *mask)
1328: {
1329: const char *regnames[] = {
1330: "a0", "a1", "a2", "b0", "b1", "b2", "la", "lc",
1331: "m0", "m1", "m2", "m3", "m4", "m5", "m6", "m7",
1332: "n0", "n1", "n2", "n3", "n4", "n5", "n6", "n7",
1333: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
1334: "x0", "x1", "y0", "y1", "pc", "sr", "omr",
1335: "sp", "ssh", "ssl"
1336: };
1337: static Uint32 registers[ARRAYSIZE(regnames)];
1338: int i;
1339: for (i = 0; i < ARRAYSIZE(regnames); i++) {
1340: if (strcmp(regname, regnames[i]) == 0) {
1341: *addr = &(registers[i]);
1342: switch (regname[0]) {
1343: case 'a':
1344: case 'b':
1345: case 'x':
1346: case 'y':
1347: *mask = BITMASK(24);
1348: break;
1349: default:
1350: *mask = BITMASK(16);
1351: break;
1352: }
1353: if (regname[0] == 'p') {
1354: /* PC is 16-bit */
1355: return 16;
1356: }
1357: return 32;
1358: }
1359: }
1360: fprintf(stderr, "ERROR: unrecognized DSP register '%s', valid ones are:\n", regname);
1361: for (i = 0; i < ARRAYSIZE(regnames); i++) {
1362: fprintf(stderr, "- %s\n", regnames[i]);
1363: }
1364: return 0;
1365: }
1366:
1367: static bool SetDspRegister(const char *regname, Uint32 value)
1368: {
1369: Uint32 *addr, mask;
1370:
1371: switch (DSP_GetRegisterAddress(regname, &addr, &mask)) {
1372: case 32:
1373: *addr = value & mask;
1374: break;
1375: case 16:
1376: *(Uint16*)addr = value & mask;
1377: break;
1378: default:
1379: return false;
1380: }
1381: return true;
1382: }
1383:
1384: Uint32 DSP_ReadMemory(Uint16 addr, char space, const char **mem_str)
1385: {
1386: /* dummy */
1387: return 0;
1388: }
1389:
1390: void MemorySnapShot_Store(void *pData, int Size)
1391: {
1392: /* dummy */
1393: }
1394:
1395: int main(int argc, const char *argv[])
1396: {
1397: const char *parser_fail[] = {
1398: /* syntax & register name errors */
1399: "",
1400: " = ",
1401: " a0 d0 ",
1402: "gggg=a0",
1403: "=a=b=",
1404: "a0=d0=20",
1405: "a0=d || 0=20",
1406: "a0=d & 0=20",
1407: ".w&3=2",
1408: "d0 = %200",
1409: "d0 = \"ICE!BAR",
1410: "foo().w=bar()",
1411: "(a0.w=d0.l)",
1412: "(a0&3)=20",
1413: "20 = (a0.w)",
1414: "()&=d0",
1415: "d0=().w",
1416: "255 & 3 = (d0) & && 2 = 2",
1417: /* size and mask mismatches with numbers */
1418: "d0.w = $ffff0",
1419: "(a0).b & 3 < 100",
1420: /* more than BC_MAX_CONDITIONS_PER_BREAKPOINT conditions */
1421: "1=1 && 2=2 && 3=3 && 4=4 && 5=5",
1422: NULL
1423: };
1424: const char *parser_pass[] = {
1425: " ($200).w > 200 ",
1426: " ($200).w < 200 ",
1427: " (200).w = $200 ",
1428: " (200).w ! $200 ",
1429: "a0>d0",
1430: "a0<d0",
1431: "d0=d1",
1432: "d0!d1",
1433: "(a0)=(d0)",
1434: "(d0).w=(a0).b",
1435: "(a0).w&3=(d0)&&d0=1",
1436: " ( a 0 ) . w & 1 = ( d 0 ) & 1 && d 0 = 3 ",
1437: "a0=1 && (d0)&2=(a0).w && ($00ff00).w&1=1",
1438: " ($ff820a).b = 2",
1439: "hbl > 0 && vbl < 2000 && linecycles = 508",
1440: NULL
1441: };
1442: const char *match_tests[] = {
1443: "a0 = d0",
1444: "( $200 ) . b > 200", /* byte access to avoid endianess */
1445: "pc < $50000 && pc > $60000",
1446: "pc > $50000 && pc < $54000",
1447: #define FAILING_BC_TEST_MATCHES 4
1448: "pc > $50000 && pc < $60000",
1449: "( $200 ) . b > ( 200 ) . b",
1450: "d0 = d1",
1451: "a0 = pc",
1452: NULL
1453: };
1454: const char *test;
1455: int i, j, tests = 0, errors = 0;
1456: int remaining_matches;
1457: bool use_dsp;
1458:
1459: /* first automated tests... */
1460: use_dsp = false;
1461: fprintf(stderr, "\nShould FAIL for CPU:\n");
1462: for (i = 0; (test = parser_fail[i]); i++) {
1463: fprintf(stderr, "-----------------\n- parsing '%s'\n", test);
1464: if (BreakCond_Command(test, use_dsp)) {
1465: fprintf(stderr, "***ERROR***: should have failed\n");
1466: errors++;
1467: }
1468: }
1469: tests += i;
1470: fprintf(stderr, "-----------------\n\n");
1471: BreakCond_List(use_dsp);
1472:
1473: fprintf(stderr, "\nShould PASS for CPU:\n");
1474: for (i = 0; (test = parser_pass[i]); i++) {
1475: fprintf(stderr, "-----------------\n- parsing '%s'\n", test);
1476: if (!BreakCond_Command(test, use_dsp)) {
1477: fprintf(stderr, "***ERROR***: should have passed\n");
1478: errors++;
1479: }
1480: }
1481: tests += i;
1482: fprintf(stderr, "-----------------\n\n");
1483: BreakCond_List(use_dsp);
1484: fprintf(stderr, "\n");
1485: BreakCond_RemoveAll(use_dsp);
1486: BreakCond_List(use_dsp);
1487: fprintf(stderr, "-----------------\n");
1488:
1489: /* add conditions */
1490: fprintf(stderr, "\nLast one(s) should match, first one(s) shouldn't:\n");
1491: for (i = 0; (test = match_tests[i]); i++) {
1492: fprintf(stderr, "-----------------\n- parsing '%s'\n", test);
1493: if (!BreakCond_Command(test, use_dsp)) {
1494: fprintf(stderr, "***ERROR***: should have passed\n");
1495: errors++;
1496: }
1497: }
1498: tests += i;
1499: BreakCond_List(use_dsp);
1500: fprintf(stderr, "\n");
1501:
1502: /* set up registers etc */
1503:
1504: /* fail indirect equality checks with zerod regs */
1505: memset(STRam, 0, sizeof(STRam));
1506: STRam[0] = 1;
1507: /* !match: "( $200 ) > 200"
1508: * match: "( $200 ) . w > ( 200 ) . b"
1509: */
1510: STRam[0x200] = 100;
1511: STRam[200] = 0x20;
1512: /* match: "d0 = d1" */
1513: SetCpuRegister("d0", 4);
1514: SetCpuRegister("d1", 4);
1515: /* !match: "pc < $50000 && pc > $60000"
1516: * !match: "pc < $50000 && pc > $54000"
1517: * match: "pc > $50000 && pc < $60000"
1518: */
1519: regs.pc = 0x58000;
1520: /* !match: "d0 = a0"
1521: * match: "pc = a0"
1522: */
1523: SetCpuRegister("a0", 0x58000);
1524:
1525: /* check matches */
1526: while ((i = BreakCond_MatchCpu())) {
1527: fprintf(stderr, "Removing matching CPU breakpoint %d...\n", i);
1528: for (j = 0; (test = match_tests[j]); j++) {
1529: if (strcmp(test, BreakPointsCpu[i-1].expression) == 0) {
1530: break;
1531: }
1532: }
1533: if (test) {
1534: if (j < FAILING_BC_TEST_MATCHES) {
1535: fprintf(stderr, "ERROR: breakpoint should not have matched!\n");
1536: errors++;
1537: }
1538: } else {
1539: fprintf(stderr, "WARNING: canonized breakpoint form didn't match\n");
1540: errors++;
1541: }
1542: BreakCond_Remove(i, use_dsp);
1543: }
1544: remaining_matches = BreakCond_BreakPointCount(use_dsp);
1545: if (remaining_matches != FAILING_BC_TEST_MATCHES) {
1546: fprintf(stderr, "ERROR: wrong number of breakpoints left (%d instead of %d)!\n",
1547: remaining_matches, FAILING_BC_TEST_MATCHES);
1548: errors++;
1549: }
1550:
1551: fprintf(stderr, "\nOther breakpoints didn't match, removing the rest...\n");
1552: BreakCond_RemoveAll(use_dsp);
1553: BreakCond_List(use_dsp);
1554: fprintf(stderr, "-----------------\n");
1555:
1556: /* ...last parse cmd line args as DSP breakpoints */
1557: if (argc > 1) {
1558: use_dsp = true;
1559: fprintf(stderr, "\nCommand line DSP breakpoints:\n");
1560: for (argv++; --argc > 0; argv++) {
1561: fprintf(stderr, "-----------------\n- parsing '%s'\n", *argv);
1562: BreakCond_Command(*argv, use_dsp);
1563: }
1564: fprintf(stderr, "-----------------\n\n");
1565: BreakCond_List(use_dsp);
1566:
1567: while ((i = BreakCond_MatchDsp())) {
1568: fprintf(stderr, "Removing matching DSP breakpoint.\n");
1569: BreakCond_Remove(i, use_dsp);
1570: }
1571:
1572: BreakCond_RemoveAll(use_dsp);
1573: BreakCond_List(use_dsp);
1574: fprintf(stderr, "-----------------\n");
1575: }
1576: if (errors) {
1577: fprintf(stderr, "\n***Detected %d ERRORs in %d automated tests!***\n\n",
1578: errors, tests);
1579: } else {
1580: fprintf(stderr, "\nFinished without any errors!\n\n");
1581: }
1582: return 0;
1583: }
1584: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.