comparison debug.c @ 2395:ebca8ab02701

Basic string support in debugger language
author Michael Pavone <pavone@retrodev.com>
date Wed, 13 Dec 2023 20:09:18 -0800
parents 340299a76db7
children bf4f1a8d1d48
comparison
equal deleted inserted replaced
2394:340299a76db7 2395:ebca8ab02701
163 return NULL; 163 return NULL;
164 } 164 }
165 return arrays + val.v.u32; 165 return arrays + val.v.u32;
166 } 166 }
167 167
168 static debug_string **debug_strings;
169 static uint32_t num_debug_strings;
170 static uint32_t debug_string_storage;
171 static debug_val new_debug_string(char *str)
172 {
173 if (num_debug_strings == debug_string_storage) {
174 debug_string_storage = debug_string_storage ? 2 * debug_string_storage : 4;
175 debug_strings = realloc(debug_strings, debug_string_storage * sizeof(debug_string*));
176 }
177 debug_string *string = calloc(1, sizeof(debug_string));
178 string->size = string->storage = strlen(str);
179 string->buffer = calloc(1, string->size + 1);
180 memcpy(string->buffer, str, string->size + 1);
181 debug_strings[num_debug_strings] = string;
182 return (debug_val){
183 .type = DBG_VAL_STRING,
184 .v = {
185 .u32 = num_debug_strings++
186 }
187 };
188 }
189
190 static debug_string* get_string(debug_val val)
191 {
192 if (val.type != DBG_VAL_STRING) {
193 return NULL;
194 }
195 return debug_strings[val.v.u32];
196 }
197
198 static char* get_cstring(debug_val val)
199 {
200 debug_string *str = get_string(val);
201 if (!str) {
202 return NULL;
203 }
204 return str->buffer;
205 }
206
168 static uint8_t debug_cast_int(debug_val val, uint32_t *out) 207 static uint8_t debug_cast_int(debug_val val, uint32_t *out)
169 { 208 {
170 if (val.type == DBG_VAL_U32) { 209 if (val.type == DBG_VAL_U32) {
171 *out = val.v.u32; 210 *out = val.v.u32;
172 return 1; 211 return 1;
371 "TOKEN_OPER", 410 "TOKEN_OPER",
372 "TOKEN_SIZE", 411 "TOKEN_SIZE",
373 "TOKEN_LBRACKET", 412 "TOKEN_LBRACKET",
374 "TOKEN_RBRACKET", 413 "TOKEN_RBRACKET",
375 "TOKEN_LPAREN", 414 "TOKEN_LPAREN",
376 "TOKEN_RPAREN" 415 "TOKEN_RPAREN",
416 "TOKEN_STRING"
377 }; 417 };
378 418
419 static char *parse_string_literal(char *start, char **end)
420 {
421 uint32_t length = 0;
422 uint8_t is_escape = 0;
423 char *cur;
424 for (cur = start; *cur && *cur != '"'; cur++)
425 {
426 if (is_escape) {
427 switch (*cur)
428 {
429 case 't':
430 case 'n':
431 case 'r':
432 case '\\':
433 break;
434 default:
435 fprintf(stderr, "Unsupported escape character %c\n", *cur);
436 return NULL;
437 }
438 is_escape = 0;
439 } else if (*cur == '\\') {
440 is_escape = 1;
441 continue;
442 }
443 length++;
444 }
445 if (!*cur) {
446 fprintf(stderr, "Unterminated string literal: %s\n", start);
447 return NULL;
448 }
449 *end = cur + 1;
450 char *ret = calloc(1, length + 1);
451 char *dst = ret;
452 is_escape = 0;
453 for (cur = start; *cur != '"'; ++cur)
454 {
455 if (is_escape) {
456 switch (*cur)
457 {
458 case 't':
459 *(dst++) = '\t';
460 break;
461 case 'n':
462 *(dst++) = '\n';
463 break;
464 case 'r':
465 *(dst++) = '\r';
466 break;
467 case '\\':
468 *(dst++) = '\\';
469 break;
470 }
471 is_escape = 0;
472 } else if (*cur == '\\') {
473 is_escape = 1;
474 continue;
475 } else {
476 *(dst++) = *cur;
477 }
478 }
479 *dst = 0;
480 return ret;
481 }
482
379 static token parse_token(char *start, char **end) 483 static token parse_token(char *start, char **end)
380 { 484 {
381 while(*start && isblank(*start) && *start != '\n' && *start != '\r') 485 while(*start && isblank(*start) && *start != '\n' && *start != '\r')
382 { 486 {
383 ++start; 487 ++start;
384 } 488 }
385 if (!*start || *start == '\n' || *start == '\r') { 489 if (!*start || *start == '\n' || *start == '\r') {
490 *end = start;
386 return (token){ 491 return (token){
387 .type = TOKEN_NONE 492 .type = TOKEN_NONE
388 }; 493 };
389 *end = start;
390 } 494 }
391 if (*start == '$' || (*start == '0' && start[1] == 'x')) { 495 if (*start == '$' || (*start == '0' && start[1] == 'x')) {
392 return (token) { 496 return (token) {
393 .type = TOKEN_INT, 497 .type = TOKEN_INT,
394 .v = { 498 .v = {
395 .num = strtol(start + (*start == '$' ? 1 : 2), end, 16) 499 .num = strtol(start + (*start == '$' ? 1 : 2), end, 16)
500 }
501 };
502 }
503 if (*start == '"') {
504 char *str = parse_string_literal(start + 1, end);
505 if (!str) {
506 *end = start;
507 return (token){
508 .type = TOKEN_NONE
509 };
510 }
511 return (token){
512 .type = TOKEN_STRING,
513 .v = {
514 .str = str
396 } 515 }
397 }; 516 };
398 } 517 }
399 if (isdigit(*start)) { 518 if (isdigit(*start)) {
400 uint32_t ipart = strtol(start, end, 10); 519 uint32_t ipart = strtol(start, end, 10);
541 } 660 }
542 free(e->right); 661 free(e->right);
543 } else { 662 } else {
544 free_expr(e->right); 663 free_expr(e->right);
545 } 664 }
546 if (e->op.type == TOKEN_NAME || e->op.type == TOKEN_ARRAY) { 665 if (e->op.type == TOKEN_NAME || e->op.type == TOKEN_ARRAY || e->op.type == TOKEN_STRING) {
547 free(e->op.v.str); 666 free(e->op.v.str);
548 } 667 }
549 } 668 }
550 669
551 static void free_expr(expr *e) 670 static void free_expr(expr *e)
695 free_expr(ret); 814 free_expr(ret);
696 return NULL; 815 return NULL;
697 } 816 }
698 return ret; 817 return ret;
699 } 818 }
700 if (first.type != TOKEN_INT && first.type != TOKEN_DECIMAL && first.type != TOKEN_NAME) { 819 if (first.type != TOKEN_INT && first.type != TOKEN_DECIMAL && first.type != TOKEN_NAME && first.type != TOKEN_STRING) {
701 fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]); 820 fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]);
702 return NULL; 821 return NULL;
703 } 822 }
704 token second = parse_token(after_first, end); 823 token second = parse_token(after_first, end);
705 if (second.type != TOKEN_SIZE) { 824 if (second.type != TOKEN_SIZE) {
887 free_expr(ret); 1006 free_expr(ret);
888 return NULL; 1007 return NULL;
889 } 1008 }
890 return maybe_muldiv(ret, *end, end); 1009 return maybe_muldiv(ret, *end, end);
891 } 1010 }
892 if (first.type != TOKEN_INT && first.type != TOKEN_DECIMAL && first.type != TOKEN_NAME) { 1011 if (first.type != TOKEN_INT && first.type != TOKEN_DECIMAL && first.type != TOKEN_NAME && first.type != TOKEN_STRING) {
893 fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]); 1012 fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]);
894 return NULL; 1013 return NULL;
895 } 1014 }
896 char *after_second; 1015 char *after_second;
897 token second = parse_token(after_first, &after_second); 1016 token second = parse_token(after_first, &after_second);
1053 free_expr(ret); 1172 free_expr(ret);
1054 return NULL; 1173 return NULL;
1055 } 1174 }
1056 return maybe_binary(ret, *end, end); 1175 return maybe_binary(ret, *end, end);
1057 } 1176 }
1058 if (first.type != TOKEN_INT && first.type != TOKEN_DECIMAL && first.type != TOKEN_NAME) { 1177 if (first.type != TOKEN_INT && first.type != TOKEN_DECIMAL && first.type != TOKEN_NAME && first.type != TOKEN_STRING) {
1059 fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]); 1178 fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]);
1060 return NULL; 1179 return NULL;
1061 } 1180 }
1062 char *after_second; 1181 char *after_second;
1063 token second = parse_token(after_first, &after_second); 1182 token second = parse_token(after_first, &after_second);
1149 *out = var->get(var); 1268 *out = var->get(var);
1150 return 1; 1269 return 1;
1151 } else if (e->op.type == TOKEN_INT) { 1270 } else if (e->op.type == TOKEN_INT) {
1152 *out = debug_int(e->op.v.num); 1271 *out = debug_int(e->op.v.num);
1153 return 1; 1272 return 1;
1273 } else if (e->op.type == TOKEN_DECIMAL){
1274 *out = debug_float(e->op.v.f);
1275 return 1;
1154 } else { 1276 } else {
1155 *out = debug_float(e->op.v.f); 1277 *out = new_debug_string(e->op.v.str);
1156 return 1; 1278 return 1;
1157 } 1279 }
1158 case EXPR_UNARY: 1280 case EXPR_UNARY:
1159 if (!eval_expr(root, e->left, out)) { 1281 if (!eval_expr(root, e->left, out)) {
1160 return 0; 1282 return 0;
2388 return 1; 2510 return 1;
2389 } 2511 }
2390 2512
2391 static uint8_t cmd_printf(debug_root *root, parsed_command *cmd) 2513 static uint8_t cmd_printf(debug_root *root, parsed_command *cmd)
2392 { 2514 {
2393 char *param = cmd->raw; 2515 char *fmt = get_cstring(cmd->args[0].value);
2394 if (!param) { 2516 if (!fmt) {
2395 fputs("printf requires at least one parameter\n", stderr); 2517 fprintf(stderr, "First parameter to printf must be a string\n");
2396 return 1; 2518 return 1;
2397 } 2519 }
2398 while (isblank(*param)) 2520 char *cur = fmt;
2399 {
2400 ++param;
2401 }
2402 if (*param != '"') {
2403 fprintf(stderr, "First parameter to printf must be a string, found '%s'\n", param);
2404 return 1;
2405 }
2406 ++param;
2407 char *fmt = strdup(param);
2408 char *cur = param, *out = fmt;
2409 while (*cur && *cur != '"')
2410 {
2411 if (*cur == '\\') {
2412 switch (cur[1])
2413 {
2414 case 't':
2415 *(out++) = '\t';
2416 break;
2417 case 'n':
2418 *(out++) = '\n';
2419 break;
2420 case 'r':
2421 *(out++) = '\r';
2422 break;
2423 case '\\':
2424 *(out++) = '\\';
2425 break;
2426 default:
2427 fprintf(stderr, "Unsupported escape character %c in string %s\n", cur[1], fmt);
2428 free(fmt);
2429 return 1;
2430 }
2431 cur += 2;
2432 } else {
2433 *(out++) = *(cur++);
2434 }
2435 }
2436 *out = 0;
2437 ++cur;
2438 param = cur;
2439 cur = fmt;
2440 char format_str[3] = {'%', 'd', 0}; 2521 char format_str[3] = {'%', 'd', 0};
2522 uint32_t cur_param = 1;
2441 while (*cur) 2523 while (*cur)
2442 { 2524 {
2443 if (*cur == '%') { 2525 if (*cur == '%') {
2444 switch(cur[1]) 2526 switch(cur[1])
2445 { 2527 {
2450 case 's': 2532 case 's':
2451 case 'f': 2533 case 'f':
2452 break; 2534 break;
2453 default: 2535 default:
2454 fprintf(stderr, "Unsupported format character %c\n", cur[1]); 2536 fprintf(stderr, "Unsupported format character %c\n", cur[1]);
2455 free(fmt);
2456 return 1; 2537 return 1;
2457 } 2538 }
2458 format_str[1] = cur[1]; 2539 format_str[1] = cur[1];
2459 expr *arg = parse_expression(param, &param); 2540 if (cur_param == cmd->num_args) {
2460 if (!arg) { 2541 fprintf(stderr, "Not enough arguments for format char %c\n", *cur);
2461 free(fmt);
2462 return 1; 2542 return 1;
2463 } 2543 }
2464 debug_val val; 2544 debug_val val = cmd->args[cur_param++].value;
2465 if (!eval_expr(root, arg, &val)) {
2466 free(fmt);
2467 return 1;
2468 }
2469 if (cur[1] == 's') { 2545 if (cur[1] == 's') {
2470 if (val.type == DBG_VAL_STRING) { 2546 if (val.type == DBG_VAL_STRING) {
2471 //TODO: implement me 2547 printf(format_str, get_cstring(val));
2472 } else { 2548 } else {
2473 char tmp[128]; 2549 char tmp[128];
2474 uint32_t address; 2550 uint32_t address;
2475 if (!debug_cast_int(val, &address)) { 2551 if (!debug_cast_int(val, &address)) {
2476 fprintf(stderr, "Format char 's' accepts only integers and strings\n"); 2552 fprintf(stderr, "Format char 's' accepts only integers and strings\n");
2477 free(fmt);
2478 return 1; 2553 return 1;
2479 } 2554 }
2480 int j; 2555 int j;
2481 for (j = 0; j < sizeof(tmp)-1; j++, address++) 2556 for (j = 0; j < sizeof(tmp)-1; j++, address++)
2482 { 2557 {
2493 } 2568 }
2494 } else if (cur[1] == 'f') { 2569 } else if (cur[1] == 'f') {
2495 float fval; 2570 float fval;
2496 if (!debug_cast_float(val, &fval)) { 2571 if (!debug_cast_float(val, &fval)) {
2497 fprintf(stderr, "Format char '%c' only accepts floats\n", cur[1]); 2572 fprintf(stderr, "Format char '%c' only accepts floats\n", cur[1]);
2498 free(fmt);
2499 return 1; 2573 return 1;
2500 } 2574 }
2501 printf(format_str, fval); 2575 printf(format_str, fval);
2502 } else { 2576 } else {
2503 uint32_t ival; 2577 uint32_t ival;
2504 if (!debug_cast_int(val, &ival)) { 2578 if (!debug_cast_int(val, &ival)) {
2505 fprintf(stderr, "Format char '%c' only accepts integers\n", cur[1]); 2579 fprintf(stderr, "Format char '%c' only accepts integers\n", cur[1]);
2506 free(fmt);
2507 return 1; 2580 return 1;
2508 } 2581 }
2509 printf(format_str, ival); 2582 printf(format_str, ival);
2510 } 2583 }
2511 cur += 2; 2584 cur += 2;
2904 return 0; 2977 return 0;
2905 } 2978 }
2906 2979
2907 static uint8_t cmd_bindup(debug_root *root, parsed_command *cmd) 2980 static uint8_t cmd_bindup(debug_root *root, parsed_command *cmd)
2908 { 2981 {
2909 if (!bind_up(cmd->raw)) { 2982 char *bind = get_cstring(cmd->args[0].value);
2910 fprintf(stderr, "%s is not a valid binding name\n", cmd->raw); 2983 if (!bind) {
2984 fprintf(stderr, "Argument to bindup must be a string\n");
2985 return 1;
2986 }
2987 if (!bind_up(bind)) {
2988 fprintf(stderr, "%s is not a valid binding name\n", bind);
2911 } 2989 }
2912 return 1; 2990 return 1;
2913 } 2991 }
2914 2992
2915 static uint8_t cmd_binddown(debug_root *root, parsed_command *cmd) 2993 static uint8_t cmd_binddown(debug_root *root, parsed_command *cmd)
2916 { 2994 {
2917 if (!bind_down(cmd->raw)) { 2995 char *bind = get_cstring(cmd->args[0].value);
2918 fprintf(stderr, "%s is not a valid binding name\n", cmd->raw); 2996 if (!bind) {
2997 fprintf(stderr, "Argument to binddown must be a string\n");
2998 return 1;
2999 }
3000 if (!bind_down(bind)) {
3001 fprintf(stderr, "%s is not a valid binding name\n", bind);
2919 } 3002 }
2920 return 1; 3003 return 1;
2921 } 3004 }
2922 3005
2923 static uint8_t cmd_condition(debug_root *root, parsed_command *cmd) 3006 static uint8_t cmd_condition(debug_root *root, parsed_command *cmd)
2968 printf("$%X\n", (uint32_t)val.intval); 3051 printf("$%X\n", (uint32_t)val.intval);
2969 } 3052 }
2970 3053
2971 static uint8_t cmd_symbols(debug_root *root, parsed_command *cmd) 3054 static uint8_t cmd_symbols(debug_root *root, parsed_command *cmd)
2972 { 3055 {
2973 char *filename = cmd->raw ? strip_ws(cmd->raw) : NULL; 3056 if (cmd->num_args) {
2974 if (filename && *filename) { 3057 char *filename = get_cstring(cmd->args[0].value);
3058 if (!filename) {
3059 fprintf(stderr, "Argument to symbols must be a string if provided\n");
3060 return 1;
3061 }
2975 FILE *f = fopen(filename, "r"); 3062 FILE *f = fopen(filename, "r");
2976 if (!f) { 3063 if (!f) {
2977 fprintf(stderr, "Failed to open %s for reading\n", filename); 3064 fprintf(stderr, "Failed to open %s for reading\n", filename);
2978 return 1; 3065 return 1;
2979 } 3066 }
3007 char size = cmd->format ? cmd->format[0] : 'b'; 3094 char size = cmd->format ? cmd->format[0] : 'b';
3008 if (size != 'b' && size != 'w' && size != 'l') { 3095 if (size != 'b' && size != 'w' && size != 'l') {
3009 fprintf(stderr, "Invalid size %s\n", cmd->format); 3096 fprintf(stderr, "Invalid size %s\n", cmd->format);
3010 return 1; 3097 return 1;
3011 } 3098 }
3012 FILE *f = fopen(cmd->args[0].raw, "wb"); 3099 if (!eval_expr(root, cmd->args[0].parsed, &cmd->args[0].value)) {
3100 fprintf(stderr, "Failed to eval %s\n", cmd->args[0].raw);
3101 return 1;
3102 }
3103 char *fname = get_cstring(cmd->args[0].value);
3104 if (!fname) {
3105 fprintf(stderr, "First argument to save must be a string\n");
3106 return 1;
3107 }
3108 FILE *f = fopen(fname, "wb");
3013 if (!f) { 3109 if (!f) {
3014 fprintf(stderr, "Failed to open %s for writing\n", cmd->args[0].raw); 3110 fprintf(stderr, "Failed to open %s for writing\n", fname);
3015 return 1; 3111 return 1;
3016 } 3112 }
3017 uint32_t start = 0; 3113 uint32_t start = 0;
3018 debug_val val; 3114 debug_val val;
3019 debug_array * arr = NULL; 3115 debug_array * arr = NULL;
3161 char size = cmd->format ? cmd->format[0] : 'b'; 3257 char size = cmd->format ? cmd->format[0] : 'b';
3162 if (size != 'b' && size != 'w' && size != 'l') { 3258 if (size != 'b' && size != 'w' && size != 'l') {
3163 fprintf(stderr, "Invalid size %s\n", cmd->format); 3259 fprintf(stderr, "Invalid size %s\n", cmd->format);
3164 return 1; 3260 return 1;
3165 } 3261 }
3166 FILE *f = fopen(cmd->args[0].raw, "rb"); 3262 if (!eval_expr(root, cmd->args[0].parsed, &cmd->args[0].value)) {
3263 fprintf(stderr, "Failed to eval %s\n", cmd->args[0].raw);
3264 return 1;
3265 }
3266 char *fname = get_cstring(cmd->args[0].value);
3267 if (!fname) {
3268 fprintf(stderr, "First argument to load must be a string\n");
3269 return 1;
3270 }
3271 FILE *f = fopen(fname, "rb");
3167 if (!f) { 3272 if (!f) {
3168 fprintf(stderr, "Failed to open %s for reading\n", cmd->args[0].raw); 3273 fprintf(stderr, "Failed to open %s for reading\n", fname);
3169 return 1; 3274 return 1;
3170 } 3275 }
3171 uint32_t start = 0; 3276 uint32_t start = 0;
3172 debug_val val; 3277 debug_val val;
3173 debug_array * arr = NULL; 3278 debug_array * arr = NULL;
3774 }, 3879 },
3775 .usage = "printf FORMAT EXPRESSION...", 3880 .usage = "printf FORMAT EXPRESSION...",
3776 .desc = "Print a string with C-style formatting specifiers replaced with the value of the remaining arguments", 3881 .desc = "Print a string with C-style formatting specifiers replaced with the value of the remaining arguments",
3777 .impl = cmd_printf, 3882 .impl = cmd_printf,
3778 .min_args = 1, 3883 .min_args = 1,
3779 .max_args = -1, 3884 .max_args = -1
3780 .raw_args = 1
3781 }, 3885 },
3782 { 3886 {
3783 .names = (const char *[]){ 3887 .names = (const char *[]){
3784 "softreset", "sr", NULL 3888 "softreset", "sr", NULL
3785 }, 3889 },
3901 }, 4005 },
3902 .usage = "bindup NAME", 4006 .usage = "bindup NAME",
3903 .desc = "Simulate a keyup for binding NAME", 4007 .desc = "Simulate a keyup for binding NAME",
3904 .impl = cmd_bindup, 4008 .impl = cmd_bindup,
3905 .min_args = 1, 4009 .min_args = 1,
3906 .max_args = 1, 4010 .max_args = 1
3907 .raw_args = 1
3908 }, 4011 },
3909 { 4012 {
3910 .names = (const char *[]){ 4013 .names = (const char *[]){
3911 "binddown", NULL 4014 "binddown", NULL
3912 }, 4015 },
3913 .usage = "bindown NAME", 4016 .usage = "bindown NAME",
3914 .desc = "Simulate a keydown for binding NAME", 4017 .desc = "Simulate a keydown for binding NAME",
3915 .impl = cmd_binddown, 4018 .impl = cmd_binddown,
3916 .min_args = 1, 4019 .min_args = 1,
3917 .max_args = 1, 4020 .max_args = 1
3918 .raw_args = 1
3919 }, 4021 },
3920 { 4022 {
3921 .names = (const char *[]){ 4023 .names = (const char *[]){
3922 "condition", NULL 4024 "condition", NULL
3923 }, 4025 },
3958 }, 4060 },
3959 .usage = "symbols [FILENAME]", 4061 .usage = "symbols [FILENAME]",
3960 .desc = "Loads a list of symbols from the file indicated by FILENAME or lists currently loaded symbols if FILENAME is omitted", 4062 .desc = "Loads a list of symbols from the file indicated by FILENAME or lists currently loaded symbols if FILENAME is omitted",
3961 .impl = cmd_symbols, 4063 .impl = cmd_symbols,
3962 .min_args = 0, 4064 .min_args = 0,
3963 .max_args = 1, 4065 .max_args = 1
3964 .raw_args = 1
3965 }, 4066 },
3966 { 4067 {
3967 .names = (const char *[]){ 4068 .names = (const char *[]){
3968 "save", NULL 4069 "save", NULL
3969 }, 4070 },