Mercurial > repos > blastem
changeset 2176:035a54aa633f
Merge
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 06 Aug 2022 22:10:54 -0700 |
parents | 8c28c5466d70 (diff) 42a0f6db81c3 (current diff) |
children | 44596610b2a0 |
files | |
diffstat | 2 files changed, 761 insertions(+), 71 deletions(-) [+] |
line wrap: on
line diff
--- a/debug.c Thu Aug 04 23:40:13 2022 -0700 +++ b/debug.c Sat Aug 06 22:10:54 2022 -0700 @@ -64,6 +64,660 @@ return cur; } +typedef enum { + TOKEN_NONE, + TOKEN_NUM, + TOKEN_NAME, + TOKEN_OPER, + TOKEN_SIZE, + TOKEN_LBRACKET, + TOKEN_RBRACKET, + TOKEN_LPAREN, + TOKEN_RPAREN +} token_type; + +static const char *token_type_names[] = { + "TOKEN_NONE", + "TOKEN_NUM", + "TOKEN_NAME", + "TOKEN_OPER", + "TOKEN_SIZE", + "TOKEN_LBRACKET", + "TOKEN_RBRACKET", + "TOKEN_LPAREN", + "TOKEN_RPAREN" +}; + +typedef struct { + token_type type; + union { + char *str; + char op[3]; + uint32_t num; + } v; +} token; + +static token parse_token(char *start, char **end) +{ + while(*start && isblank(*start) && *start != '\n') + { + ++start; + } + if (!*start || *start == '\n') { + return (token){ + .type = TOKEN_NONE + }; + *end = start; + } + if (*start == '$' || (*start == '0' && start[1] == 'x')) { + return (token) { + .type = TOKEN_NUM, + .v = { + .num = strtol(start + (*start == '$' ? 1 : 2), end, 16) + } + }; + } + if (isdigit(*start)) { + return (token) { + .type = TOKEN_NUM, + .v = { + .num = strtol(start, end, 10) + } + }; + } + switch (*start) + { + case '+': + case '-': + case '*': + case '/': + case '&': + case '|': + case '^': + case '~': + case '=': + case '!': + if (*start == '!' && start[1] == '=') { + *end = start + 2; + return (token) { + .type = TOKEN_OPER, + .v = { + .op = {*start, start[1], 0} + } + }; + } + *end = start + 1; + return (token) { + .type = TOKEN_OPER, + .v = { + .op = {*start, 0} + } + }; + case '.': + *end = start + 2; + return (token) { + .type = TOKEN_SIZE, + .v = { + .op = {start[1], 0} + } + }; + case '[': + *end = start + 1; + return (token) { + .type = TOKEN_LBRACKET + }; + case ']': + *end = start + 1; + return (token) { + .type = TOKEN_RBRACKET + }; + case '(': + *end = start + 1; + return (token) { + .type = TOKEN_LPAREN + }; + case ')': + *end = start + 1; + return (token) { + .type = TOKEN_RPAREN + }; + } + *end = start + 1; + while (**end && !isblank(**end)) + { + uint8_t done = 0; + switch (**end) + { + case '+': + case '-': + case '*': + case '/': + case '&': + case '|': + case '^': + case '~': + case '=': + case '!': + case '.': + done = 1; + break; + } + if (done) { + break; + } + + ++*end; + } + char *name = malloc(*end - start + 1); + memcpy(name, start, *end - start); + name[*end-start] = 0; + return (token) { + .type = TOKEN_NAME, + .v = { + .str = name + } + }; +} + +typedef enum { + EXPR_NONE, + EXPR_SCALAR, + EXPR_UNARY, + EXPR_BINARY, + EXPR_SIZE, + EXPR_MEM +} expr_type; + +typedef struct expr expr; + +struct expr { + expr_type type; + expr *left; + expr *right; + token op; +}; + +static void free_expr(expr *e) +{ + if (!e) { + return; + } + free_expr(e->left); + free_expr(e->right); + if (e->op.type == TOKEN_NAME) { + free(e->op.v.str); + } + free(e); +} + +static expr *parse_scalar_or_muldiv(char *start, char **end); +static expr *parse_expression(char *start, char **end); + +static expr *parse_scalar(char *start, char **end) +{ + char *after_first; + token first = parse_token(start, &after_first); + if (!first.type) { + return NULL; + } + if (first.type == TOKEN_SIZE) { + fprintf(stderr, "Unexpected TOKEN_SIZE '.%s'\n", first.v.op); + return NULL; + } + if (first.type == TOKEN_OPER) { + expr *target = parse_scalar(after_first, end); + if (!target) { + fprintf(stderr, "Unary expression %s needs value\n", first.v.op); + return NULL; + } + expr *ret = calloc(1, sizeof(expr)); + ret->type = EXPR_UNARY; + ret->op = first; + ret->left = target; + *end = after_first; + return ret; + } + if (first.type == TOKEN_LBRACKET) { + expr *ret = calloc(1, sizeof(expr)); + ret->type = EXPR_MEM; + ret->left = parse_expression(after_first, end); + if (!ret->left) { + fprintf(stderr, "Expression expected after `[`\n"); + free(ret); + return NULL; + } + token rbrack = parse_token(*end, end); + if (rbrack.type != TOKEN_RBRACKET) { + fprintf(stderr, "Missing closing `]`"); + free_expr(ret); + return NULL; + } + char *after_size; + token size = parse_token(*end, &after_size); + if (size.type == TOKEN_SIZE) { + *end = after_size; + ret->op = size; + } + return ret; + } + if (first.type == TOKEN_LPAREN) { + expr *ret = parse_expression(after_first, end); + if (!ret) { + fprintf(stderr, "Expression expected after `(`\n"); + return NULL; + } + token rparen = parse_token(*end, end); + if (rparen.type != TOKEN_RPAREN) { + fprintf(stderr, "Missing closing `)`"); + free_expr(ret); + return NULL; + } + return ret; + } + if (first.type != TOKEN_NUM && first.type != TOKEN_NAME) { + fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]); + return NULL; + } + token second = parse_token(after_first, end); + if (second.type != TOKEN_SIZE) { + expr *ret = calloc(1, sizeof(expr)); + ret->type = EXPR_SCALAR; + ret->op = first; + *end = after_first; + return ret; + } + expr *ret = calloc(1, sizeof(expr)); + ret->type = EXPR_SIZE; + ret->left = calloc(1, sizeof(expr)); + ret->left->type = EXPR_SCALAR; + ret->left->op = second; + ret->op = first; + return ret; +} + +static expr *maybe_binary(expr *left, char *start, char **end) +{ + char *after_first; + token first = parse_token(start, &after_first); + if (first.type != TOKEN_OPER) { + *end = start; + return left; + } + expr *bin = calloc(1, sizeof(expr)); + bin->left = left; + bin->op = first; + bin->type = EXPR_BINARY; + switch (first.v.op[0]) + { + case '*': + case '/': + case '&': + case '|': + case '^': + bin->right = parse_scalar(after_first, end); + return maybe_binary(bin, *end, end); + case '+': + case '-': + bin->right = parse_scalar_or_muldiv(after_first, end); + return maybe_binary(bin, *end, end); + case '=': + case '!': + bin->right = parse_expression(after_first, end); + return bin; + default: + bin->left = NULL; + free(bin); + return left; + } +} + +static expr *maybe_muldiv(expr *left, char *start, char **end) +{ + char *after_first; + token first = parse_token(start, &after_first); + if (first.type != TOKEN_OPER) { + *end = start; + return left; + } + expr *bin = calloc(1, sizeof(expr)); + bin->left = left; + bin->op = first; + bin->type = EXPR_BINARY; + switch (first.v.op[0]) + { + case '*': + case '/': + case '&': + case '|': + case '^': + bin->right = parse_scalar(after_first, end); + return maybe_binary(bin, *end, end); + default: + bin->left = NULL; + free(bin); + return left; + } +} + +static expr *parse_scalar_or_muldiv(char *start, char **end) +{ + char *after_first; + token first = parse_token(start, &after_first); + if (!first.type) { + return NULL; + } + if (first.type == TOKEN_SIZE) { + fprintf(stderr, "Unexpected TOKEN_SIZE '.%s'\n", first.v.op); + return NULL; + } + if (first.type == TOKEN_OPER) { + expr *target = parse_scalar(after_first, end); + if (!target) { + fprintf(stderr, "Unary expression %s needs value\n", first.v.op); + return NULL; + } + expr *ret = calloc(1, sizeof(expr)); + ret->type = EXPR_UNARY; + ret->op = first; + ret->left = target; + return ret; + } + if (first.type == TOKEN_LBRACKET) { + expr *ret = calloc(1, sizeof(expr)); + ret->type = EXPR_MEM; + ret->left = parse_expression(after_first, end); + if (!ret->left) { + fprintf(stderr, "Expression expected after `[`\n"); + free(ret); + return NULL; + } + token rbrack = parse_token(*end, end); + if (rbrack.type != TOKEN_RBRACKET) { + fprintf(stderr, "Missing closing `]`"); + free_expr(ret); + return NULL; + } + char *after_size; + token size = parse_token(*end, &after_size); + if (size.type == TOKEN_SIZE) { + *end = after_size; + ret->op = size; + } + return maybe_muldiv(ret, *end, end); + } + if (first.type == TOKEN_LPAREN) { + expr *ret = parse_expression(after_first, end); + if (!ret) { + fprintf(stderr, "Expression expected after `(`\n"); + return NULL; + } + token rparen = parse_token(*end, end); + if (rparen.type != TOKEN_RPAREN) { + fprintf(stderr, "Missing closing `)`"); + free_expr(ret); + return NULL; + } + return maybe_muldiv(ret, *end, end); + } + if (first.type != TOKEN_NUM && first.type != TOKEN_NAME) { + fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]); + return NULL; + } + char *after_second; + token second = parse_token(after_first, &after_second); + if (second.type == TOKEN_OPER) { + expr *ret; + expr *bin = calloc(1, sizeof(expr)); + bin->type = EXPR_BINARY; + bin->left = calloc(1, sizeof(expr)); + bin->left->type = EXPR_SCALAR; + bin->left->op = first; + bin->op = second; + switch (second.v.op[0]) + { + case '*': + case '/': + case '&': + case '|': + case '^': + bin->right = parse_scalar(after_second, end); + return maybe_muldiv(bin, *end, end); + case '+': + case '-': + case '=': + case '!': + ret = bin->left; + bin->left = NULL; + free_expr(bin); + return ret; + default: + fprintf(stderr, "%s is not a valid binary operator\n", second.v.op); + free(bin->left); + free(bin); + return NULL; + } + } else if (second.type == TOKEN_SIZE) { + expr *value = calloc(1, sizeof(expr)); + value->type = EXPR_SIZE; + value->op = second; + value->left = calloc(1, sizeof(expr)); + value->left->type = EXPR_SCALAR; + value->left->op = first; + return maybe_muldiv(value, after_second, end); + } else { + expr *ret = calloc(1, sizeof(expr)); + ret->type = EXPR_SCALAR; + ret->op = first; + *end = after_first; + return ret; + } +} + +static expr *parse_expression(char *start, char **end) +{ + char *after_first; + token first = parse_token(start, &after_first); + if (!first.type) { + return NULL; + } + if (first.type == TOKEN_SIZE) { + fprintf(stderr, "Unexpected TOKEN_SIZE '.%s'\n", first.v.op); + return NULL; + } + if (first.type == TOKEN_OPER) { + expr *target = parse_scalar(after_first, end); + if (!target) { + fprintf(stderr, "Unary expression %s needs value\n", first.v.op); + return NULL; + } + expr *ret = calloc(1, sizeof(expr)); + ret->type = EXPR_UNARY; + ret->op = first; + ret->left = target; + return ret; + } + if (first.type == TOKEN_LBRACKET) { + expr *ret = calloc(1, sizeof(expr)); + ret->type = EXPR_MEM; + ret->left = parse_expression(after_first, end); + if (!ret->left) { + fprintf(stderr, "Expression expected after `[`\n"); + free(ret); + return NULL; + } + token rbrack = parse_token(*end, end); + if (rbrack.type != TOKEN_RBRACKET) { + fprintf(stderr, "Missing closing `]`"); + free_expr(ret); + return NULL; + } + char *after_size; + token size = parse_token(*end, &after_size); + if (size.type == TOKEN_SIZE) { + *end = after_size; + ret->op = size; + } + return maybe_binary(ret, *end, end); + } + if (first.type == TOKEN_LPAREN) { + expr *ret = parse_expression(after_first, end); + if (!ret) { + fprintf(stderr, "Expression expected after `(`\n"); + return NULL; + } + token rparen = parse_token(*end, end); + if (rparen.type != TOKEN_RPAREN) { + fprintf(stderr, "Missing closing `)`"); + free_expr(ret); + return NULL; + } + return maybe_binary(ret, *end, end); + } + if (first.type != TOKEN_NUM && first.type != TOKEN_NAME) { + fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]); + return NULL; + } + char *after_second; + token second = parse_token(after_first, &after_second); + if (second.type == TOKEN_OPER) { + expr *bin = calloc(1, sizeof(expr)); + bin->type = EXPR_BINARY; + bin->left = calloc(1, sizeof(expr)); + bin->left->type = EXPR_SCALAR; + bin->left->op = first; + bin->op = second; + switch (second.v.op[0]) + { + case '*': + case '/': + case '&': + case '|': + case '^': + bin->right = parse_scalar(after_second, end); + return maybe_binary(bin, *end, end); + case '+': + case '-': + bin->right = parse_scalar_or_muldiv(after_second, end); + return maybe_binary(bin, *end, end); + case '=': + case '!': + bin->right = parse_expression(after_second, end); + return bin; + default: + fprintf(stderr, "%s is not a valid binary operator\n", second.v.op); + free(bin->left); + free(bin); + return NULL; + } + } else if (second.type == TOKEN_SIZE) { + expr *value = calloc(1, sizeof(expr)); + value->type = EXPR_SIZE; + value->op = second; + value->left = calloc(1, sizeof(expr)); + value->left->type = EXPR_SCALAR; + value->left->op = first; + return maybe_binary(value, after_second, end); + } else { + expr *ret = calloc(1, sizeof(expr)); + ret->type = EXPR_SCALAR; + ret->op = first; + *end = after_first; + return ret; + } +} + +uint8_t eval_expr(debug_root *root, expr *e, uint32_t *out) +{ + uint32_t right; + switch(e->type) + { + case EXPR_SCALAR: + if (e->op.type == TOKEN_NAME) { + return root->resolve(root, e->op.v.str, out); + } else { + *out = e->op.v.num; + return 1; + } + case EXPR_UNARY: + if (!eval_expr(root, e->left, out)) { + return 0; + } + switch (e->op.v.op[0]) + { + case '!': + *out = !*out; + break; + case '~': + *out = ~*out; + break; + case '-': + *out = -*out; + break; + default: + return 0; + } + return 1; + case EXPR_BINARY: + if (!eval_expr(root, e->left, out) || !eval_expr(root, e->right, &right)) { + return 0; + } + switch (e->op.v.op[0]) + { + case '+': + *out += right; + break; + case '-': + *out -= right; + break; + case '*': + *out *= right; + break; + case '/': + *out /= right; + break; + case '&': + *out &= right; + break; + case '|': + *out |= right; + break; + case '^': + *out ^= right; + break; + case '=': + *out = *out == right; + break; + case '!': + *out = *out != right; + break; + default: + return 0; + } + return 1; + case EXPR_SIZE: + if (!eval_expr(root, e->left, out)) { + return 0; + } + switch (e->op.v.op[0]) + { + case 'b': + *out &= 0xFF; + break; + case 'w': + *out &= 0xFFFF; + break; + } + return 1; + case EXPR_MEM: + if (!eval_expr(root, e->left, out)) { + return 0; + } + return root->read_mem(root, out, e->op.v.op[0]); + default: + return 0; + } +} + void add_display(disp_def ** head, uint32_t *index, char format_char, char * param) { disp_def * ndisp = malloc(sizeof(*ndisp)); @@ -116,17 +770,70 @@ return read_byte(address, (void **)context->mem_pointers, &context->options->gen, context); } -uint16_t m68k_read_word(uint32_t address, m68k_context *context) +static uint16_t m68k_read_word(uint32_t address, m68k_context *context) { return read_word(address, (void **)context->mem_pointers, &context->options->gen, context); } -uint32_t m68k_read_long(uint32_t address, m68k_context *context) +static uint32_t m68k_read_long(uint32_t address, m68k_context *context) { return m68k_read_word(address, context) << 16 | m68k_read_word(address + 2, context); } -void debugger_print(m68k_context *context, char format_char, char *param, uint32_t address) +static uint8_t read_m68k(debug_root *root, uint32_t *out, char size) +{ + m68k_context *context = root->cpu_context; + if (size == 'b') { + *out = m68k_read_byte(*out, context); + } else if (size == 'l') { + *out = m68k_read_long(*out, context); + } else { + *out = m68k_read_word(*out, context); + } + return 1; +} + +static uint8_t resolve_m68k(debug_root *root, const char *name, uint32_t *out) +{ + m68k_context *context = root->cpu_context; + if ((name[0] == 'd' || name[0] == 'D') && name[1] >= '0' && name[1] <= '7' && !name[2]) { + *out = context->dregs[name[1]-'0']; + } else if ((name[0] == 'a' || name[0] == 'A') && name[1] >= '0' && name[1] <= '7' && !name[2]) { + *out = context->aregs[name[1]-'0']; + } else if (!strcasecmp(name, "sr")) { + *out = context->status << 8; + for (int flag = 0; flag < 5; flag++) { + *out |= context->flags[flag] << (4-flag); + } + } else if(!strcasecmp(name, "cycle")) { + *out = context->current_cycle; + } else if (!strcasecmp(name, "pc")) { + *out = root->address; + } else if (!strcasecmp(name, "usp")) { + *out = context->status & 0x20 ? context->aregs[8] : context->aregs[7]; + } else if (!strcasecmp(name, "ssp")) { + *out = context->status & 0x20 ? context->aregs[7] : context->aregs[8]; + } else { + return 0; + } + return 1; +} + +static uint8_t resolve_genesis(debug_root *root, const char *name, uint32_t *out) +{ + if (resolve_m68k(root, name, out)) { + return 1; + } + m68k_context *m68k = root->cpu_context; + genesis_context *gen = m68k->system; + if (!strcmp(name, "f") || !strcmp(name, "frame")) { + *out = gen->vdp->frame; + return 1; + } + return 0; +} + +void debugger_print(debug_root *root, char format_char, char *param) { uint32_t value; char format[8]; @@ -145,75 +852,50 @@ default: fprintf(stderr, "Unrecognized format character: %c\n", format_char); } - if (param[0] == 'd' && param[1] >= '0' && param[1] <= '7') { - value = context->dregs[param[1]-'0']; - if (param[2] == '.') { - if (param[3] == 'w') { - value &= 0xFFFF; - } else if (param[3] == 'b') { - value &= 0xFF; + char *after; + uint8_t at_least_one = 0; + while (*param && *param != '\n') + { + at_least_one = 1; + expr *e = parse_expression(param, &after); + if (e) { + if (!eval_expr(root, e, &value)) { + fprintf(stderr, "Failed to eval %s\n", param); } - } - } else if (param[0] == 'a' && param[1] >= '0' && param[1] <= '7') { - value = context->aregs[param[1]-'0']; - if (param[2] == '.') { - if (param[3] == 'w') { - value &= 0xFFFF; - } else if (param[3] == 'b') { - value &= 0xFF; - } - } - } else if (param[0] == 's' && param[1] == 'r') { - value = (context->status << 8); - for (int flag = 0; flag < 5; flag++) { - value |= context->flags[flag] << (4-flag); + free_expr(e); + } else { + fprintf(stderr, "Failed to parse %s\n", param); } - } else if(param[0] == 'c') { - value = context->current_cycle; - } else if(param[0] == 'f') { - genesis_context *gen = context->system; - value = gen->vdp->frame; - } else if (param[0] == 'p' && param[1] == 'c') { - value = address; - } else if ((param[0] == '0' && param[1] == 'x') || param[0] == '$') { - char *after; - uint32_t p_addr = strtol(param+(param[0] == '0' ? 2 : 1), &after, 16); - if (after[0] == '.' && after[1] == 'l') { - value = m68k_read_long(p_addr, context); - } else if (after[0] == '.' && after[1] == 'b') { - value = m68k_read_byte(p_addr, context); + char *tmp_param = malloc(after-param+1); + memcpy(tmp_param, param, after-param); + tmp_param[after-param] = 0; + param = after; + if (format_char == 's') { + char tmp[128]; + int i; + for (i = 0; i < sizeof(tmp)-1; i++, value++) + { + uint32_t addr = value; + root->read_mem(root, &addr, 'b'); + char c = addr; + if (c < 0x20 || c > 0x7F) { + break; + } + tmp[i] = c; + } + tmp[i] = 0; + printf(format, tmp_param, tmp); } else { - value = m68k_read_word(p_addr, context); + printf(format, tmp_param, value); } - } else if(param[0] == '(' && (param[1] == 'a' || param[1] == 'd') && param[2] >= '0' && param[2] <= '7' && param[3] == ')') { - uint8_t reg = param[2] - '0'; - uint32_t p_addr = param[1] == 'a' ? context->aregs[reg] : context->dregs[reg]; - if (param[4] == '.' && param[5] == 'l') { - value = m68k_read_long(p_addr, context); - } else if (param[4] == '.' && param[5] == 'b') { - value = m68k_read_byte(p_addr, context); - } else { - value = m68k_read_word(p_addr, context); + free(tmp_param); + while (*param && isblank(*param) && *param != '\n') + { + ++param; } - } else { - fprintf(stderr, "Unrecognized parameter to p: %s\n", param); - return; } - if (format_char == 's') { - char tmp[128]; - int i; - for (i = 0; i < sizeof(tmp)-1; i++, value++) - { - char c = m68k_read_byte(value, context); - if (c < 0x20 || c > 0x7F) { - break; - } - tmp[i] = c; - } - tmp[i] = 0; - printf(format, param, tmp); - } else { - printf(format, param, value); + if (!at_least_one) { + fprintf(stderr, "Missing argument to print/%c\n", format_char); } } @@ -740,6 +1422,7 @@ if (!root) { return 0; } + root->address = address; switch(input_buf[0]) { case 'c': @@ -842,7 +1525,7 @@ fputs("display command requires a parameter\n", stderr); break; } - debugger_print(context, format_char, param, address); + debugger_print(root, format_char, param); add_display(&root->displays, &root->disp_index, format_char, param); } else { param = find_param(input_buf); @@ -874,7 +1557,7 @@ } param = find_param(input_buf); if (param) { - debugger_print(context, format_char, param, address); + debugger_print(root, format_char, param); } else { m68k_disasm(&inst, input_buf); printf("%X: %s\n", address, input_buf); @@ -1146,7 +1829,7 @@ remove_breakpoint(context, address); } for (disp_def * cur = root->displays; cur; cur = cur->next) { - debugger_print(context, cur->format_char, cur->param, address); + debugger_print(root, cur->format_char, cur->param); } m68k_disasm(&inst, input_buf); printf("%X: %s\n", address, input_buf);
--- a/debug.h Thu Aug 04 23:40:13 2022 -0700 +++ b/debug.h Sat Aug 06 22:10:54 2022 -0700 @@ -23,15 +23,22 @@ uint32_t index; } bp_def; -typedef struct { +typedef struct debug_root debug_root; +typedef uint8_t (*resolver)(debug_root *root, const char *name, uint32_t *out); +typedef uint8_t (*reader)(debug_root *root, uint32_t *out, char size); + +struct debug_root { void *cpu_context; bp_def *breakpoints; disp_def *displays; + resolver resolve; + reader read_mem; uint32_t bp_index; uint32_t disp_index; uint32_t branch_t; uint32_t branch_f; -} debug_root; + uint32_t address; +}; debug_root *find_root(void *cpu); bp_def ** find_breakpoint(bp_def ** cur, uint32_t address);