Mercurial > repos > blastem
comparison debug.c @ 2169:cb9572145f8e
WIP support for expression parsing in debugger
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 06 Aug 2022 14:14:15 -0700 |
parents | 8554751f17b5 |
children | ada3130b1396 |
comparison
equal
deleted
inserted
replaced
2167:fe68a9ad509f | 2169:cb9572145f8e |
---|---|
62 cur = &((*cur)->next); | 62 cur = &((*cur)->next); |
63 } | 63 } |
64 return cur; | 64 return cur; |
65 } | 65 } |
66 | 66 |
67 typedef enum { | |
68 TOKEN_NONE, | |
69 TOKEN_NUM, | |
70 TOKEN_NAME, | |
71 TOKEN_OPER, | |
72 TOKEN_SIZE | |
73 } token_type; | |
74 | |
75 static const char *token_type_names[] = { | |
76 "TOKEN_NONE", | |
77 "TOKEN_NUM", | |
78 "TOKEN_NAME", | |
79 "TOKEN_OPER", | |
80 "TOKEN_SIZE" | |
81 }; | |
82 | |
83 typedef struct { | |
84 token_type type; | |
85 union { | |
86 char *str; | |
87 char op[3]; | |
88 uint32_t num; | |
89 } v; | |
90 } token; | |
91 | |
92 static token parse_token(char *start, char **end) | |
93 { | |
94 while(*start && isblank(*start) && *start != '\n') | |
95 { | |
96 ++start; | |
97 } | |
98 if (!*start || *start == '\n') { | |
99 return (token){ | |
100 .type = TOKEN_NONE | |
101 }; | |
102 *end = start; | |
103 } | |
104 if (*start == '$' || (*start == '0' && start[1] == 'x')) { | |
105 return (token) { | |
106 .type = TOKEN_NUM, | |
107 .v = { | |
108 .num = strtol(start + (*start == '$' ? 1 : 2), end, 16) | |
109 } | |
110 }; | |
111 } | |
112 if (isdigit(*start)) { | |
113 return (token) { | |
114 .type = TOKEN_NUM, | |
115 .v = { | |
116 .num = strtol(start, end, 10) | |
117 } | |
118 }; | |
119 } | |
120 switch (*start) | |
121 { | |
122 case '+': | |
123 case '-': | |
124 case '*': | |
125 case '/': | |
126 case '&': | |
127 case '|': | |
128 case '^': | |
129 case '~': | |
130 case '=': | |
131 case '!': | |
132 if (*start == '!' && start[1] == '=') { | |
133 *end = start + 2; | |
134 return (token) { | |
135 .type = TOKEN_OPER, | |
136 .v = { | |
137 .op = {*start, start[1], 0} | |
138 } | |
139 }; | |
140 } | |
141 *end = start + 1; | |
142 return (token) { | |
143 .type = TOKEN_OPER, | |
144 .v = { | |
145 .op = {*start, 0} | |
146 } | |
147 }; | |
148 case '.': | |
149 *end = start + 2; | |
150 return (token) { | |
151 .type = TOKEN_SIZE, | |
152 .v = { | |
153 .op = {start[1], 0} | |
154 } | |
155 }; | |
156 } | |
157 *end = start + 1; | |
158 while (**end && !isblank(**end) && **end != '.') | |
159 { | |
160 ++*end; | |
161 } | |
162 char *name = malloc(*end - start + 1); | |
163 memcpy(name, start, *end - start); | |
164 name[*end-start] = 0; | |
165 return (token) { | |
166 .type = TOKEN_NAME, | |
167 .v = { | |
168 .str = name | |
169 } | |
170 }; | |
171 } | |
172 | |
173 typedef enum { | |
174 EXPR_NONE, | |
175 EXPR_SCALAR, | |
176 EXPR_UNARY, | |
177 EXPR_BINARY, | |
178 EXPR_SIZE | |
179 } expr_type; | |
180 | |
181 typedef struct expr expr; | |
182 | |
183 struct expr { | |
184 expr_type type; | |
185 expr *left; | |
186 expr *right; | |
187 token op; | |
188 }; | |
189 | |
190 static void free_expr(expr *e) | |
191 { | |
192 if (!e) { | |
193 return; | |
194 } | |
195 free_expr(e->left); | |
196 free_expr(e->right); | |
197 if (e->op.type == TOKEN_NAME) { | |
198 free(e->op.v.str); | |
199 } | |
200 free(e); | |
201 } | |
202 | |
203 static expr *parse_scalar(char *start, char **end) | |
204 { | |
205 char *after_first; | |
206 token first = parse_token(start, &after_first); | |
207 if (!first.type) { | |
208 return NULL; | |
209 } | |
210 if (first.type == TOKEN_SIZE) { | |
211 fprintf(stderr, "Unexpected TOKEN_SIZE '.%s'\n", first.v.op); | |
212 return NULL; | |
213 } | |
214 if (first.type == TOKEN_OPER) { | |
215 expr *target = parse_scalar(after_first, end); | |
216 if (!target) { | |
217 fprintf(stderr, "Unary expression %s needs value\n", first.v.op); | |
218 return NULL; | |
219 } | |
220 expr *ret = calloc(1, sizeof(expr)); | |
221 ret->type = EXPR_UNARY; | |
222 ret->op = first; | |
223 ret->left = target; | |
224 *end = after_first; | |
225 return ret; | |
226 } | |
227 token second = parse_token(after_first, end); | |
228 if (second.type != TOKEN_SIZE) { | |
229 expr *ret = calloc(1, sizeof(expr)); | |
230 ret->type = EXPR_SCALAR; | |
231 ret->op = first; | |
232 *end = after_first; | |
233 return ret; | |
234 } | |
235 expr *ret = calloc(1, sizeof(expr)); | |
236 ret->type = EXPR_SIZE; | |
237 ret->left = calloc(1, sizeof(expr)); | |
238 ret->left->type = EXPR_SCALAR; | |
239 ret->left->op = second; | |
240 ret->op = first; | |
241 return ret; | |
242 } | |
243 | |
244 static expr *parse_scalar_or_muldiv(char *start, char **end); | |
245 static expr *parse_expression(char *start, char **end); | |
246 | |
247 static expr *maybe_binary(expr *left, char *start, char **end) | |
248 { | |
249 char *after_first; | |
250 token first = parse_token(start, &after_first); | |
251 if (first.type != TOKEN_OPER) { | |
252 *end = start; | |
253 return left; | |
254 } | |
255 expr *bin = calloc(1, sizeof(expr)); | |
256 bin->left = left; | |
257 bin->op = first; | |
258 bin->type = EXPR_BINARY; | |
259 switch (first.v.op[0]) | |
260 { | |
261 case '*': | |
262 case '/': | |
263 case '&': | |
264 case '|': | |
265 case '^': | |
266 bin->right = parse_scalar(after_first, end); | |
267 return maybe_binary(bin, *end, end); | |
268 case '+': | |
269 case '-': | |
270 bin->right = parse_scalar_or_muldiv(after_first, end); | |
271 return maybe_binary(bin, *end, end); | |
272 case '=': | |
273 case '!': | |
274 bin->right = parse_expression(after_first, end); | |
275 return bin; | |
276 default: | |
277 bin->left = NULL; | |
278 free(bin); | |
279 return left; | |
280 } | |
281 } | |
282 | |
283 static expr *maybe_muldiv(expr *left, char *start, char **end) | |
284 { | |
285 char *after_first; | |
286 token first = parse_token(start, &after_first); | |
287 if (first.type != TOKEN_OPER) { | |
288 *end = start; | |
289 return left; | |
290 } | |
291 expr *bin = calloc(1, sizeof(expr)); | |
292 bin->left = left; | |
293 bin->op = first; | |
294 bin->type = EXPR_BINARY; | |
295 switch (first.v.op[0]) | |
296 { | |
297 case '*': | |
298 case '/': | |
299 case '&': | |
300 case '|': | |
301 case '^': | |
302 bin->right = parse_scalar(after_first, end); | |
303 return maybe_binary(bin, *end, end); | |
304 default: | |
305 bin->left = NULL; | |
306 free(bin); | |
307 return left; | |
308 } | |
309 } | |
310 | |
311 static expr *parse_scalar_or_muldiv(char *start, char **end) | |
312 { | |
313 char *after_first; | |
314 token first = parse_token(start, &after_first); | |
315 if (!first.type) { | |
316 return NULL; | |
317 } | |
318 if (first.type == TOKEN_SIZE) { | |
319 fprintf(stderr, "Unexpected TOKEN_SIZE '.%s'\n", first.v.op); | |
320 return NULL; | |
321 } | |
322 if (first.type == TOKEN_OPER) { | |
323 expr *target = parse_scalar(after_first, end); | |
324 if (!target) { | |
325 fprintf(stderr, "Unary expression %s needs value\n", first.v.op); | |
326 return NULL; | |
327 } | |
328 expr *ret = calloc(1, sizeof(expr)); | |
329 ret->type = EXPR_UNARY; | |
330 ret->op = first; | |
331 ret->left = target; | |
332 return ret; | |
333 } | |
334 char *after_second; | |
335 token second = parse_token(after_first, &after_second); | |
336 if (!second.type) { | |
337 expr *ret = calloc(1, sizeof(expr)); | |
338 ret->type = EXPR_SCALAR; | |
339 ret->op = first; | |
340 *end = after_first; | |
341 return ret; | |
342 } | |
343 if (second.type == TOKEN_OPER) { | |
344 expr *ret; | |
345 expr *bin = calloc(1, sizeof(expr)); | |
346 bin->type = EXPR_BINARY; | |
347 bin->left = calloc(1, sizeof(expr)); | |
348 bin->left->type = EXPR_SCALAR; | |
349 bin->left->op = first; | |
350 bin->op = second; | |
351 switch (second.v.op[0]) | |
352 { | |
353 case '*': | |
354 case '/': | |
355 case '&': | |
356 case '|': | |
357 case '^': | |
358 bin->right = parse_scalar(after_second, end); | |
359 return maybe_muldiv(bin, *end, end); | |
360 case '+': | |
361 case '-': | |
362 case '=': | |
363 case '!': | |
364 ret = bin->left; | |
365 bin->left = NULL; | |
366 free_expr(bin); | |
367 return ret; | |
368 default: | |
369 fprintf(stderr, "%s is not a valid binary operator\n", second.v.op); | |
370 free(bin->left); | |
371 free(bin); | |
372 return NULL; | |
373 } | |
374 } else if (second.type == TOKEN_SIZE) { | |
375 expr *value = calloc(1, sizeof(expr)); | |
376 value->type = EXPR_SIZE; | |
377 value->op = second; | |
378 value->left = calloc(1, sizeof(expr)); | |
379 value->left->type = EXPR_SCALAR; | |
380 value->left->op = first; | |
381 return maybe_muldiv(value, after_second, end); | |
382 } else { | |
383 fprintf(stderr, "Unexpected %s after scalar\n", token_type_names[second.type]); | |
384 return NULL; | |
385 } | |
386 } | |
387 | |
388 static expr *parse_expression(char *start, char **end) | |
389 { | |
390 char *after_first; | |
391 token first = parse_token(start, &after_first); | |
392 if (!first.type) { | |
393 return NULL; | |
394 } | |
395 if (first.type == TOKEN_SIZE) { | |
396 fprintf(stderr, "Unexpected TOKEN_SIZE '.%s'\n", first.v.op); | |
397 return NULL; | |
398 } | |
399 if (first.type == TOKEN_OPER) { | |
400 expr *target = parse_scalar(after_first, end); | |
401 if (!target) { | |
402 fprintf(stderr, "Unary expression %s needs value\n", first.v.op); | |
403 return NULL; | |
404 } | |
405 expr *ret = calloc(1, sizeof(expr)); | |
406 ret->type = EXPR_UNARY; | |
407 ret->op = first; | |
408 ret->left = target; | |
409 return ret; | |
410 } | |
411 char *after_second; | |
412 token second = parse_token(after_first, &after_second); | |
413 if (!second.type) { | |
414 expr *ret = calloc(1, sizeof(expr)); | |
415 ret->type = EXPR_SCALAR; | |
416 ret->op = first; | |
417 *end = after_first; | |
418 return ret; | |
419 } | |
420 if (second.type == TOKEN_OPER) { | |
421 expr *bin = calloc(1, sizeof(expr)); | |
422 bin->type = EXPR_BINARY; | |
423 bin->left = calloc(1, sizeof(expr)); | |
424 bin->left->type = EXPR_SCALAR; | |
425 bin->left->op = first; | |
426 bin->op = second; | |
427 switch (second.v.op[0]) | |
428 { | |
429 case '*': | |
430 case '/': | |
431 case '&': | |
432 case '|': | |
433 case '^': | |
434 bin->right = parse_scalar(after_second, end); | |
435 return maybe_binary(bin, *end, end); | |
436 case '+': | |
437 case '-': | |
438 bin->right = parse_scalar_or_muldiv(after_second, end); | |
439 return maybe_binary(bin, *end, end); | |
440 case '=': | |
441 case '!': | |
442 bin->right = parse_expression(after_second, end); | |
443 return bin; | |
444 default: | |
445 fprintf(stderr, "%s is not a valid binary operator\n", second.v.op); | |
446 free(bin->left); | |
447 free(bin); | |
448 return NULL; | |
449 } | |
450 } else if (second.type == TOKEN_SIZE) { | |
451 expr *value = calloc(1, sizeof(expr)); | |
452 value->type = EXPR_SIZE; | |
453 value->op = second; | |
454 value->left = calloc(1, sizeof(expr)); | |
455 value->left->type = EXPR_SCALAR; | |
456 value->left->op = first; | |
457 return maybe_binary(value, after_second, end); | |
458 } else { | |
459 fprintf(stderr, "Unexpected %s after scalar\n", token_type_names[second.type]); | |
460 return NULL; | |
461 } | |
462 } | |
463 | |
464 typedef struct debug_context debug_context; | |
465 typedef uint8_t (*resolver)(debug_context *context, const char *name, uint32_t *out); | |
466 | |
467 struct debug_context { | |
468 resolver resolve; | |
469 void *system; | |
470 }; | |
471 | |
472 uint8_t eval_expr(debug_context *context, expr *e, uint32_t *out) | |
473 { | |
474 uint32_t right; | |
475 switch(e->type) | |
476 { | |
477 case EXPR_SCALAR: | |
478 if (e->op.type == TOKEN_NAME) { | |
479 return context->resolve(context, e->op.v.str, out); | |
480 } else { | |
481 *out = e->op.v.num; | |
482 return 1; | |
483 } | |
484 case EXPR_UNARY: | |
485 if (!eval_expr(context, e->left, out)) { | |
486 return 0; | |
487 } | |
488 switch (e->op.v.op[0]) | |
489 { | |
490 case '!': | |
491 *out = !*out; | |
492 break; | |
493 case '~': | |
494 *out = ~*out; | |
495 break; | |
496 case '-': | |
497 *out = -*out; | |
498 break; | |
499 default: | |
500 return 0; | |
501 } | |
502 return 1; | |
503 case EXPR_BINARY: | |
504 if (!eval_expr(context, e->left, out) || !eval_expr(context, e->right, &right)) { | |
505 return 0; | |
506 } | |
507 switch (e->op.v.op[0]) | |
508 { | |
509 case '+': | |
510 *out += right; | |
511 break; | |
512 case '-': | |
513 *out -= right; | |
514 break; | |
515 case '*': | |
516 *out *= right; | |
517 break; | |
518 case '/': | |
519 *out /= right; | |
520 break; | |
521 case '&': | |
522 *out &= right; | |
523 break; | |
524 case '|': | |
525 *out |= right; | |
526 break; | |
527 case '^': | |
528 *out ^= right; | |
529 break; | |
530 case '=': | |
531 *out = *out == right; | |
532 break; | |
533 case '!': | |
534 *out = *out != right; | |
535 break; | |
536 default: | |
537 return 0; | |
538 } | |
539 return 1; | |
540 case EXPR_SIZE: | |
541 if (!eval_expr(context, e->left, out)) { | |
542 return 0; | |
543 } | |
544 switch (e->op.v.op[0]) | |
545 { | |
546 case 'b': | |
547 *out &= 0xFF; | |
548 break; | |
549 case 'w': | |
550 *out &= 0xFFFF; | |
551 break; | |
552 } | |
553 return 1; | |
554 default: | |
555 return 0; | |
556 } | |
557 } | |
558 | |
67 void add_display(disp_def ** head, uint32_t *index, char format_char, char * param) | 559 void add_display(disp_def ** head, uint32_t *index, char format_char, char * param) |
68 { | 560 { |
69 disp_def * ndisp = malloc(sizeof(*ndisp)); | 561 disp_def * ndisp = malloc(sizeof(*ndisp)); |
70 ndisp->format_char = format_char; | 562 ndisp->format_char = format_char; |
71 ndisp->param = strdup(param); | 563 ndisp->param = strdup(param); |
122 } | 614 } |
123 | 615 |
124 uint32_t m68k_read_long(uint32_t address, m68k_context *context) | 616 uint32_t m68k_read_long(uint32_t address, m68k_context *context) |
125 { | 617 { |
126 return m68k_read_word(address, context) << 16 | m68k_read_word(address + 2, context); | 618 return m68k_read_word(address, context) << 16 | m68k_read_word(address + 2, context); |
619 } | |
620 | |
621 uint8_t resolve_m68k(m68k_context *context, const char *name, uint32_t *out) | |
622 { | |
623 if (name[0] == 'd' && name[1] >= '0' && name[1] <= '7') { | |
624 *out = context->dregs[name[1]-'0']; | |
625 } else if (name[0] == 'a' && name[1] >= '0' && name[1] <= '7') { | |
626 *out = context->aregs[name[1]-'0']; | |
627 } else if (name[0] == 's' && name[1] == 'r') { | |
628 *out = context->status << 8; | |
629 for (int flag = 0; flag < 5; flag++) { | |
630 *out |= context->flags[flag] << (4-flag); | |
631 } | |
632 } else if(name[0] == 'c') { | |
633 *out = context->current_cycle; | |
634 } else if (name[0] == 'p' && name[1] == 'c') { | |
635 //FIXME | |
636 //*out = address; | |
637 } else { | |
638 return 0; | |
639 } | |
640 return 1; | |
641 } | |
642 | |
643 uint8_t resolve_genesis(debug_context *context, const char *name, uint32_t *out) | |
644 { | |
645 genesis_context *gen = context->system; | |
646 if (resolve_m68k(gen->m68k, name, out)) { | |
647 return 1; | |
648 } | |
649 if (!strcmp(name, "f") || !strcmp(name, "frame")) { | |
650 *out = gen->vdp->frame; | |
651 return 1; | |
652 } | |
653 return 0; | |
127 } | 654 } |
128 | 655 |
129 void debugger_print(m68k_context *context, char format_char, char *param, uint32_t address) | 656 void debugger_print(m68k_context *context, char format_char, char *param, uint32_t address) |
130 { | 657 { |
131 uint32_t value; | 658 uint32_t value; |
143 case '\0': | 670 case '\0': |
144 break; | 671 break; |
145 default: | 672 default: |
146 fprintf(stderr, "Unrecognized format character: %c\n", format_char); | 673 fprintf(stderr, "Unrecognized format character: %c\n", format_char); |
147 } | 674 } |
675 debug_context c = { | |
676 .resolve = resolve_genesis, | |
677 .system = context->system | |
678 }; | |
679 char *after; | |
680 expr *e = parse_expression(param, &after); | |
681 if (e) { | |
682 if (!eval_expr(&c, e, &value)) { | |
683 fprintf(stderr, "Failed to eval %s\n", param); | |
684 } | |
685 free_expr(e); | |
686 } else { | |
687 fprintf(stderr, "Failed to parse %s\n", param); | |
688 } | |
689 /* | |
148 if (param[0] == 'd' && param[1] >= '0' && param[1] <= '7') { | 690 if (param[0] == 'd' && param[1] >= '0' && param[1] <= '7') { |
149 value = context->dregs[param[1]-'0']; | 691 value = context->dregs[param[1]-'0']; |
150 if (param[2] == '.') { | 692 if (param[2] == '.') { |
151 if (param[3] == 'w') { | 693 if (param[3] == 'w') { |
152 value &= 0xFFFF; | 694 value &= 0xFFFF; |
196 value = m68k_read_word(p_addr, context); | 738 value = m68k_read_word(p_addr, context); |
197 } | 739 } |
198 } else { | 740 } else { |
199 fprintf(stderr, "Unrecognized parameter to p: %s\n", param); | 741 fprintf(stderr, "Unrecognized parameter to p: %s\n", param); |
200 return; | 742 return; |
201 } | 743 }*/ |
202 if (format_char == 's') { | 744 if (format_char == 's') { |
203 char tmp[128]; | 745 char tmp[128]; |
204 int i; | 746 int i; |
205 for (i = 0; i < sizeof(tmp)-1; i++, value++) | 747 for (i = 0; i < sizeof(tmp)-1; i++, value++) |
206 { | 748 { |