1 /*
2  * Code to test Hatari conditional breakpoints in src/debug/breakcond.c
3  * (both matching and setting CPU and DSP breakpoints)
4  */
5 #include "main.h"
6 #include "dsp.h"
7 #include "debugcpu.h"
8 #include "breakcond.h"
9 #include "stMemory.h"
10 #include "newcpu.h"
11 
12 #define BITMASK(x)      ((1<<(x))-1)
13 
14 /* BreakCond_Command() command strings */
15 #define CMD_LIST NULL
16 #define CMD_REMOVE_ALL "all"
17 
18 
SetCpuRegister(const char * regname,Uint32 value)19 static bool SetCpuRegister(const char *regname, Uint32 value)
20 {
21 	Uint32 *addr;
22 
23 	switch (DebugCpu_GetRegisterAddress(regname, &addr)) {
24 	case 32:
25 		*addr = value;
26 		break;
27 	case 16:
28 		*(Uint16*)addr = value;
29 		break;
30 	default:
31 		fprintf(stderr, "SETUP ERROR: Register '%s' to set (to %x) is unrecognized!\n", regname, value);
32 		return false;
33 	}
34 	return true;
35 }
36 
37 #if 0
38 static bool SetDspRegister(const char *regname, Uint32 value)
39 {
40 	Uint32 *addr, mask;
41 
42 	switch (DSP_GetRegisterAddress(regname, &addr, &mask)) {
43 	case 32:
44 		*addr = value & mask;
45 		break;
46 	case 16:
47 		*(Uint16*)addr = value & mask;
48 		break;
49 	default:
50 		return false;
51 	}
52 	return true;
53 }
54 #endif
55 
main(int argc,const char * argv[])56 int main(int argc, const char *argv[])
57 {
58 	const char *parser_fail[] = {
59 		/* syntax & register name errors */
60 		"",
61 		" = ",
62 		" a0 d0 ",
63 		"gggg=a0",
64 		"=a=b=",
65 		"a0=d0=20",
66 		"a0=d || 0=20",
67 		"a0=d & 0=20",
68 		".w&3=2",
69 		"d0 = %200",
70 		"d0 = \"ICE!BAR",
71 		"pc > $200 :foobar",
72 		"foo().w=bar()",
73 		"(a0.w=d0.l)",
74 		"(a0&3)=20",
75 		"20 = (a0.w)",
76 		"()&=d0",
77 		"d0=().w",
78 		"&& pc = 2",
79 		"pc = 2 &&",
80 		"255 & 3 = (d0) & && 2 = 2",
81 		/* missing options file */
82 		"pc>pc :file no-such-file",
83 		/* size and mask mismatches with numbers */
84 		"d0.w = $ffff0",
85 		"(a0).b & 3 < 100",
86 		NULL
87 	};
88 	const char *parser_pass[] = {
89 		/* comparisons with normal numbers + indrect addressing */
90 		" ($200).w > 200 ",
91 		" ($200).w < 200 ",
92 		" (200).w = $200 ",
93 		" (200).w ! $200 ",
94 		/* indirect addressing with registers */
95 		"(a0)=(d0)",
96 		"(d0).w=(a0).b",
97 		/* sizes + multiple conditions + spacing */
98 		"(a0).w&3=(d0)&&d0=1",
99 		" ( a 0 ) . w  &  1 = ( d 0 ) & 1 &&  d 0 = 3 ",
100 		"a0=1 && (d0)&2=(a0).w && ($00ff00).w&1=1",
101 		" ($ff820a).b = 2",
102 		/* variables */
103 		"hbl > 0 && vbl < 2000 && linecycles = 508",
104 		/* options */
105 		"($200).w ! ($200).w :trace",
106 		"($200).w > ($200).w :4 :lock",
107 		"pc>pc :file data/test.ini :once",
108 		NULL
109 	};
110 	/* address breakpoint + expression evalution with register */
111 	char addr_pass[] = "pc + ($200*16/2 & 0xffff)";
112 
113 	const char *nonmatching_tests[] = {
114 		"( $200 ) . b > 200", /* byte access to avoid endianness */
115 		"pc < $50000 && pc > $60000",
116 		"pc > $50000 && pc < $54000",
117 		"d0 = a0",
118 		"a0 = pc :trace",  /* matches, but :trace should hide that */
119 		"a0 = pc :3",      /* matches, but not yet */
120 		NULL
121 	};
122 	const char *matching_tests[] = {
123 		"a0 = pc",	   /* tested with all above */
124 		"( $200 ) . b > ( 200 ) . b :once",
125 		"pc > $50000 && pc < $60000",
126 		"d0 = d1 :once :quiet",
127 		"a0 = pc",	   /* tested alone */
128 		NULL
129 	};
130 	const char *test;
131 	int total_tests = 0, total_errors = 0;
132 	int i, errors;
133 	bool use_dsp;
134 
135 	/* first automated tests... */
136 	use_dsp = false;
137 	fprintf(stderr, "\nShould FAIL for CPU:\n");
138 	for (i = 0; (test = parser_fail[i]); i++) {
139 		fprintf(stderr, "-----------------\n- parsing '%s'\n", test);
140 		if (BreakCond_Command(test, use_dsp)) {
141 			fprintf(stderr, "***ERROR***: should have failed\n");
142 			total_errors++;
143 		}
144 	}
145 	total_tests += i;
146 	fprintf(stderr, "-----------------\n\n");
147 	BreakCond_Command(CMD_LIST, use_dsp);
148 
149 	fprintf(stderr, "\nShould PASS for CPU:\n");
150 	for (i = 0; (test = parser_pass[i]); i++) {
151 		fprintf(stderr, "-----------------\n- parsing '%s'\n", test);
152 		if (!BreakCond_Command(test, use_dsp)) {
153 			fprintf(stderr, "***ERROR***: should have passed\n");
154 			total_errors++;
155 		}
156 	}
157 	total_tests += i;
158 	fprintf(stderr, "\nAddress PASS test for CPU:\n");
159 	if (!BreakAddr_Command(addr_pass, use_dsp)) {
160 		fprintf(stderr, "***ERROR***: should have passed\n");
161 		total_errors++;
162 	}
163 	total_tests += 1;
164 
165 	fprintf(stderr, "-----------------\n\n");
166 	BreakCond_Command(CMD_LIST, use_dsp);
167 	fprintf(stderr, "\n");
168 	BreakCond_Command(CMD_REMOVE_ALL, use_dsp);
169 	BreakCond_Command(CMD_LIST, use_dsp);
170 	fprintf(stderr, "-----------------\n");
171 
172 	/* set up registers etc */
173 
174 	/* fail indirect equality checks with zeroed regs */
175 	memset(STRam, 0, STRamEnd);
176 	STMemory_WriteByte(0, 1);
177 	/* !match: "( $200 ) . b > 200"
178 	 *  match: "( $200 ) . b > ( 200 ) . b"
179 	 */
180 	STMemory_WriteByte(0x200, 100);
181 	STMemory_WriteByte(200, 0x20);
182 	/* !match: "pc < $50000  &&  pc > $60000"
183 	 * !match: "pc < $50000  &&  pc > $54000"
184 	 *  match: "pc > $50000  &&  pc < $60000"
185 	 */
186 	regs.pc = 0x58000;
187 	/*  match: "d0 = d1"
188 	 */
189 	SetCpuRegister("d0", 4);
190 	SetCpuRegister("d1", 4);
191 	/* !match: "d0 = a0"
192 	 *  match: "pc = a0"
193 	 */
194 	SetCpuRegister("a0", 0x58000);
195 
196 	/* add conditions */
197 	fprintf(stderr, "\nBreakpoints that should NOT match:\n");
198 	for (errors = i = 0; (test = nonmatching_tests[i]); i++) {
199 		fprintf(stderr, "-----------------\n- parsing '%s'\n", test);
200 		if (!BreakCond_Command(test, use_dsp)) {
201 			fprintf(stderr, "***ERROR***: should have passed\n");
202 			total_errors++;
203 		} else {
204 			/* does it match? */
205 			if (BreakCond_MatchCpu()) {
206 				fprintf(stderr, "***ERROR***: should NOT have matched\n");
207 				errors++;
208 				/* remove */
209 				BreakCond_Command("1", use_dsp);
210 			}
211 		}
212 	}
213 	fprintf(stderr, "-----------------\n\n");
214 	BreakCond_Command(CMD_LIST, use_dsp);
215 	if (errors) {
216 		total_errors += errors;
217 		fprintf(stderr, "\nERROR: %d out of %d breakpoints matched!\n",
218 			errors, i);
219 	}
220 	total_tests += i;
221 
222 	/* leave non-matching breakpoints, so that first matching
223 	 * breakpoint is at after those, and test rest of matching
224 	 * breakpoints as single breakpoints
225 	 */
226 
227 	/* add conditions */
228 	fprintf(stderr, "\nBreakpoints that should match:\n");
229 	for (errors = i = 0; (test = matching_tests[i]); i++) {
230 		fprintf(stderr, "-----------------\n- parsing '%s'\n", test);
231 		if (!BreakCond_Command(test, use_dsp)) {
232 			fprintf(stderr, "***ERROR***: should have passed\n");
233 			total_errors++;
234 		} else {
235 			/* does it match? */
236 			if (!BreakCond_MatchCpu()) {
237 				fprintf(stderr, "***ERROR***: should have matched\n");
238 				errors++;
239 			}
240 			/* remove all */
241 			BreakCond_Command(CMD_REMOVE_ALL, use_dsp);
242 		}
243 	}
244 	fprintf(stderr, "-----------------\n\n");
245 	if (errors) {
246 		total_errors += errors;
247 		fprintf(stderr, "ERROR: %d out of %d breakpoints didn't match!\n\n",
248 			errors, i);
249 	}
250 	total_tests += i;
251 
252 	/* ...last parse cmd line args as DSP breakpoints */
253 	if (argc > 1) {
254 		use_dsp = true;
255 		fprintf(stderr, "\nCommand line DSP breakpoints:\n");
256 		for (argv++; --argc > 0; argv++) {
257 			fprintf(stderr, "-----------------\n- parsing '%s'\n", *argv);
258 			BreakCond_Command(*argv, use_dsp);
259 		}
260 		fprintf(stderr, "-----------------\n\n");
261 		BreakCond_Command("", use_dsp); /* list */
262 
263 		if (BreakCond_MatchDsp()) {
264 			fprintf(stderr, "There were matching DSP breakpoint(s).\n");
265 		}
266 
267 		BreakCond_Command(CMD_REMOVE_ALL, use_dsp);
268 		BreakCond_Command(CMD_LIST, use_dsp);
269 		fprintf(stderr, "-----------------\n");
270 	}
271 	if (total_errors) {
272 		fprintf(stderr, "\n***Detected %d ERRORs in %d automated tests!***\n\n",
273 			total_errors, total_tests);
274 	} else {
275 		fprintf(stderr, "\nFinished without any errors!\n\n");
276 	}
277 	return total_errors;
278 }
279