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