# HG changeset patch # User Michael Pavone # Date 1660443390 25200 # Node ID f6d5bde4d07f4b4f2b4e9d5f2554ecd66d4a5193 # Parent 44596610b2a08f510c5b4df2b4256b1092d0a739 Finish debugger refactor started with expression parser changes diff -r 44596610b2a0 -r f6d5bde4d07f debug.c --- a/debug.c Sun Aug 07 01:16:47 2022 -0700 +++ b/debug.c Sat Aug 13 19:16:30 2022 -0700 @@ -20,8 +20,6 @@ #define Z80_OPTS options #endif - - static debug_root roots[5]; static uint32_t num_roots; #define MAX_DEBUG_ROOTS (sizeof(roots)/sizeof(*roots)) @@ -65,18 +63,6 @@ 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", @@ -89,22 +75,13 @@ "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') + while(*start && isblank(*start) && *start != '\n' && *start != '\r') { ++start; } - if (!*start || *start == '\n') { + if (!*start || *start == '\n' || *start == '\r') { return (token){ .type = TOKEN_NONE }; @@ -184,7 +161,7 @@ }; } *end = start + 1; - while (**end && !isblank(**end)) + while (**end && !isspace(**end)) { uint8_t done = 0; switch (**end) @@ -220,24 +197,6 @@ }; } -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) { @@ -619,6 +578,9 @@ value->left->op = first; return maybe_binary(value, after_second, end); } else { + if (second.type == TOKEN_NAME) { + free(second.v.str); + } expr *ret = calloc(1, sizeof(expr)); ret->type = EXPR_SCALAR; ret->op = first; @@ -719,30 +681,6 @@ } } -void add_display(disp_def ** head, uint32_t *index, char format_char, char * param) -{ - disp_def * ndisp = malloc(sizeof(*ndisp)); - ndisp->format_char = format_char; - ndisp->param = strdup(param); - ndisp->next = *head; - ndisp->index = *index++; - *head = ndisp; -} - -void remove_display(disp_def ** head, uint32_t index) -{ - while (*head) { - if ((*head)->index == index) { - disp_def * del_disp = *head; - *head = del_disp->next; - free(del_disp->param); - free(del_disp); - } else { - head = &(*head)->next; - } - } -} - char * find_param(char * buf) { for (; *buf; buf++) { @@ -787,13 +725,43 @@ if (size == 'b') { *out = m68k_read_byte(*out, context); } else if (size == 'l') { + if (*out & 1) { + fprintf(stderr, "Longword access to odd addresses (%X) is not allowed\n", *out); + return 0; + } *out = m68k_read_long(*out, context); } else { + if (*out & 1) { + fprintf(stderr, "Wword access to odd addresses (%X) is not allowed\n", *out); + return 0; + } *out = m68k_read_word(*out, context); } return 1; } +static uint8_t write_m68k(debug_root *root, uint32_t address, uint32_t value, char size) +{ + m68k_context *context = root->cpu_context; + if (size == 'b') { + write_byte(address, value, (void **)context->mem_pointers, &context->options->gen, context); + } else if (size == 'l') { + if (address & 1) { + fprintf(stderr, "Longword access to odd addresses (%X) is not allowed\n", address); + return 0; + } + write_word(address, value >> 16, (void **)context->mem_pointers, &context->options->gen, context); + write_word(address + 2, value, (void **)context->mem_pointers, &context->options->gen, context); + } else { + if (address & 1) { + fprintf(stderr, "Wword access to odd addresses (%X) is not allowed\n", address); + return 0; + } + write_word(address, value, (void **)context->mem_pointers, &context->options->gen, context); + } + return 1; +} + static uint8_t resolve_m68k(debug_root *root, const char *name, uint32_t *out) { m68k_context *context = root->cpu_context; @@ -820,6 +788,28 @@ return 1; } +static uint8_t set_m68k(debug_root *root, const char *name, uint32_t value) +{ + m68k_context *context = root->cpu_context; + if ((name[0] == 'd' || name[0] == 'D') && name[1] >= '0' && name[1] <= '7' && !name[2]) { + context->dregs[name[1]-'0'] = value; + } else if ((name[0] == 'a' || name[0] == 'A') && name[1] >= '0' && name[1] <= '7' && !name[2]) { + context->aregs[name[1]-'0'] = value; + } else if (!strcasecmp(name, "sr")) { + context->status = value >> 8; + for (int flag = 0; flag < 5; flag++) { + context->flags[flag] = (value & (1 << (4 - flag))) != 0; + } + } else if (!strcasecmp(name, "usp")) { + context->aregs[context->status & 0x20 ? 8 : 7] = value; + } else if (!strcasecmp(name, "ssp")) { + context->aregs[context->status & 0x20 ? 7 : 8] = value; + } 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)) { @@ -834,251 +824,1783 @@ return 0; } -void debugger_print(debug_root *root, char format_char, char *param) +void ambiguous_iter(char *key, tern_val val, uint8_t valtype, void *data) { - uint32_t value; - char format[8]; - strcpy(format, "%s: %d\n"); - switch (format_char) + char *prefix = data; + char * full = alloc_concat(prefix, key); + fprintf(stderr, "\t%s\n", full); + free(full); +} + +uint8_t parse_command(debug_root *root, char *text, parsed_command *out) +{ + char *cur = text; + while (*cur && *cur != '/' && !isspace(*cur)) { - case 'x': - case 'X': - case 'd': - case 'c': - case 's': - format[5] = format_char; - break; - case '\0': - break; - default: - fprintf(stderr, "Unrecognized format character: %c\n", format_char); + ++cur; + } + char *name = malloc(cur - text + 1); + memcpy(name, text, cur - text); + name[cur-text] = 0; + uint8_t ret = 0; + tern_node *prefix_res = tern_find_prefix(root->commands, name); + command_def *def = tern_find_ptr(prefix_res, ""); + if (!def) { + tern_node *node = prefix_res; + while (node) + { + if (node->left || node->right) { + break; + } + if (node->el) { + node = node->straight.next; + } else { + def = node->straight.value.ptrval; + break; + } + } + if (!def && prefix_res) { + fprintf(stderr, "%s is ambiguous. Matching commands:\n", name); + tern_foreach(prefix_res, ambiguous_iter, name); + goto cleanup_name; + } + } + if (!def) { + fprintf(stderr, "%s is not a recognized command\n", name); + goto cleanup_name; + } + char *format = NULL; + if (*cur == '/') { + ++cur; + text = cur; + while (*cur && !isspace(*cur)) + { + ++cur; + } + format = malloc(cur - text + 1); + memcpy(format, text, cur - text); + format[cur - text] = 0; + } + int num_args = 0; + command_arg *args = NULL; + if (*cur && *cur != '\n') { + ++cur; + } + text = cur; + if (def->raw_impl) { + while (*cur && *cur != '\n') + { + ++cur; + } + char *raw_param = NULL; + if (cur != text) { + raw_param = malloc(cur - text + 1); + memcpy(raw_param, text, cur - text); + raw_param[cur - text] = 0; + } + out->raw = raw_param; + out->args = NULL; + out->num_args = 0; + } else { + int arg_storage = 0; + if (def->max_args > 0) { + arg_storage = def->max_args; + } else if (def->max_args) { + arg_storage = def->min_args > 0 ? 2 * def->min_args : 2; + } + if (arg_storage) { + args = calloc(arg_storage, sizeof(command_arg)); + } + while (*text && *text != '\n') + { + char *after; + expr *e = parse_expression(text, &after); + if (e) { + if (num_args == arg_storage) { + if (def->max_args >= 0) { + free_expr(e); + fprintf(stderr, "Command %s takes a max of %d arguments, but at least %d provided\n", name, def->max_args, def->max_args+1); + goto cleanup_args; + } else { + arg_storage *= 2; + args = realloc(args, arg_storage * sizeof(command_arg)); + } + } + args[num_args].parsed = e; + args[num_args].raw = malloc(after - text + 1); + memcpy(args[num_args].raw, text, after - text); + args[num_args++].raw[after - text] = 0; + text = after; + } else { + goto cleanup_args; + } + } + if (num_args < def->min_args) { + fprintf(stderr, "Command %s requires at least %d arguments, but only %d provided\n", name, def->min_args, num_args); + goto cleanup_args; + } + out->raw = NULL; + out->args = args; + out->num_args = num_args; + } + out->def = def; + out->format = format; + + ret = 1; +cleanup_args: + if (!ret) { + for (int i = 0; i < num_args; i++) + { + free_expr(args[i].parsed); + free(args[i].raw); + } + free(args); } - char *after; - uint8_t at_least_one = 0; - while (*param && *param != '\n') +cleanup_name: + free(name); + return ret; +} + +static void free_parsed_command(parsed_command *cmd) +{ + free(cmd->format); + free(cmd->raw); + for (int i = 0; i < cmd->num_args; i++) { - 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); + free(cmd->args[i].raw); + free_expr(cmd->args[i].parsed); + } + free(cmd->args); +} + +static uint8_t cmd_quit(debug_root *root, char *format, int num_args, command_arg *args) +{ + exit(0); +} + +typedef struct { + size_t num_commands; + size_t longest_command; +} help_state; + +static void help_first_pass(char *key, tern_val val, uint8_t valtype, void *data) +{ + command_def *def = val.ptrval; + if (def->visited) { + return; + } + def->visited = 1; + help_state *state = data; + state->num_commands++; + size_t len = strlen(def->usage); + if (len > state->longest_command) { + state->longest_command = len; + } +} + +static void help_reset_visited(char *key, tern_val val, uint8_t valtype, void *data) +{ + command_def *def = val.ptrval; + def->visited = 0; +} + +static void help_second_pass(char *key, tern_val val, uint8_t valtype, void *data) +{ + command_def *def = val.ptrval; + if (def->visited) { + return; + } + def->visited = 1; + help_state *state = data; + size_t len = strlen(def->usage); + printf(" %s", def->usage); + while (len < state->longest_command) { + putchar(' '); + len++; + } + int remaining = 80 - state->longest_command - 5; + const char *extra_desc = NULL; + if (strlen(def->desc) <= remaining) { + printf(" - %s\n", def->desc); + } else { + char split[76]; + int split_point = remaining; + while (split_point > 0 && !isspace(def->desc[split_point])) + { + --split_point; + } + if (!split_point) { + split_point = remaining; + } + memcpy(split, def->desc, split_point); + extra_desc = def->desc + split_point + 1; + split[split_point] = 0; + printf(" - %s\n", split); + } + if (def->names[1]) { + fputs(" Aliases: ", stdout); + len = strlen(" Aliases: "); + const char **name = def->names + 1; + uint8_t first = 1; + while (*name) + { + if (first) { + first = 0; + } else { + putchar(','); + putchar(' '); + len += 2; } - free_expr(e); - } else { - fprintf(stderr, "Failed to parse %s\n", param); + fputs(*name, stdout); + len += strlen(*name); + ++name; + } + } else { + len = 0; + } + if (extra_desc) { + while (len < state->longest_command + 5) { + putchar(' '); + len++; } - char *tmp_param = malloc(after-param+1); - memcpy(tmp_param, param, after-param); - tmp_param[after-param] = 0; - param = after; - if (format_char == 's') { + fputs(extra_desc, stdout); + } + putchar('\n'); +} + +static uint8_t cmd_help(debug_root *root, char *format, char *param) +{ + help_state state = {0,0}; + tern_foreach(root->commands, help_first_pass, &state); + tern_foreach(root->commands, help_reset_visited, &state); + tern_foreach(root->commands, help_second_pass, &state); + tern_foreach(root->commands, help_reset_visited, &state); + return 1; +} + +static uint8_t cmd_continue(debug_root *root, char *format, int num_args, command_arg *args) +{ + return 0; +} + +static uint8_t cmd_print(debug_root *root, char *format, int num_args, command_arg *args) +{ + char format_str[8]; + strcpy(format_str, "%s: %d\n"); + if (format) { + switch (format[0]) + { + case 'x': + case 'X': + case 'd': + case 'c': + case 's': + format_str[5] = format[0]; + break; + default: + fprintf(stderr, "Unrecognized format character: %c\n", format[0]); + } + } + for (int i = 0; i < num_args; i++) + { + if (format && format[0] == 's') { char tmp[128]; - int i; - for (i = 0; i < sizeof(tmp)-1; i++, value++) + int j; + uint32_t addr = args[i].value; + for (j = 0; j < sizeof(tmp)-1; j++, addr++) { - uint32_t addr = value; - root->read_mem(root, &addr, 'b'); + uint32_t tmp_addr = addr; + root->read_mem(root, &tmp_addr, 'b'); char c = addr; if (c < 0x20 || c > 0x7F) { break; } - tmp[i] = c; + tmp[j] = c; + } + tmp[j] = 0; + printf(format_str, args[i].raw, tmp); + } else { + printf(format_str, args[i].raw, args[i].value); + } + } + return 1; +} + +static uint8_t cmd_display(debug_root *root, char *format, int num_args, command_arg *args) +{ + cmd_print(root, format, num_args, args); + disp_def *ndisp = calloc(1, sizeof(*ndisp)); + ndisp->next = root->displays; + ndisp->index = root->disp_index++; + ndisp->format = format ? strdup(format) : NULL; + ndisp->num_args = num_args; + ndisp->args = calloc(num_args, sizeof(command_arg)); + memcpy(ndisp->args, args, num_args * sizeof(command_arg)); + memset(args, 0, num_args * sizeof(command_arg)); + root->displays = ndisp; + printf("Added display %d\n", ndisp->index); + return 1; +} + +static uint8_t cmd_delete_display(debug_root *root, char *format, int num_args, command_arg *args) +{ + disp_def **cur = &root->displays; + while (*cur) + { + if ((*cur)->index == args[0].value) { + disp_def *del_disp = *cur; + *cur = del_disp->next; + free(del_disp->format); + for (int i = 0; i < del_disp->num_args; i++) + { + free(del_disp->args[i].raw); + free_expr(del_disp->args[i].parsed); } - tmp[i] = 0; - printf(format, tmp_param, tmp); + free(del_disp->args); + free(del_disp); + break; + } else { + cur = &(*cur)->next; + } + } + return 1; +} + +static uint8_t cmd_softreset(debug_root *root, char *format, int num_args, command_arg *args) +{ + if (current_system->soft_reset) { + current_system->soft_reset(current_system); + return 0; + } else { + fputs("Current system does not support soft reset", stderr); + return 1; + } +} + +static uint8_t cmd_command(debug_root *root, char *format, int num_args, command_arg *args) +{ + bp_def **target = find_breakpoint_idx(&root->breakpoints, args[0].value); + if (!target) { + fprintf(stderr, "Breakpoint %d does not exist!\n", args[0].value); + return 1; + } + printf("Enter commands for breakpoing %d, type end when done\n", args[0].value); + char cmd_buf[1024]; + char *commands = NULL; + uint32_t cmd_storage = 4; + for (uint32_t i = 0; i < (*target)->num_commands; i++) + { + free_parsed_command((*target)->commands + i); + } + free((*target)->commands); + (*target)->commands = calloc(cmd_storage, sizeof(parsed_command)); + for (;;) + { + fputs(">>", stdout); + fflush(stdout); + fgets(cmd_buf, sizeof(cmd_buf), stdin); + if (!strcmp(cmd_buf, "end\n")) { + return 1; + } else { + if ((*target)->num_commands == cmd_storage) { + cmd_storage *= 2; + (*target)->commands = realloc((*target)->commands, cmd_storage * sizeof(parsed_command)); + } + if (parse_command(root, cmd_buf, (*target)->commands + (*target)->num_commands)) { + ++(*target)->num_commands; + } + } + } +} + +const char *expr_type_names[] = { + "EXPR_NONE", + "EXPR_SCALAR", + "EXPR_UNARY", + "EXPR_BINARY", + "EXPR_SIZE", + "EXPR_MEM" +}; + +static uint8_t run_command(debug_root *root, parsed_command *cmd) +{ + if (cmd->def->raw_impl) { + return cmd->def->raw_impl(root, cmd->format, cmd->raw); + } else { + for (int i = 0; i < cmd->num_args; i++) + { + if (!eval_expr(root, cmd->args[i].parsed, &cmd->args[i].value)) { + fprintf(stderr, "Failed to eval %s\n", cmd->args[i].raw); + return 1; + } + } + return cmd->def->impl(root, cmd->format, cmd->num_args, cmd->args); + } +} + +static uint8_t cmd_set(debug_root *root, char *format, int num_args, command_arg *args) +{ + char *name = NULL; + char size = 0; + uint32_t address; + switch (args[0].parsed->type) + { + case EXPR_SCALAR: + if (args[0].parsed->op.type == TOKEN_NAME) { + name = args[0].parsed->op.v.str; + } else { + fputs("First argument to set must be a name or memory expression, not a number", stderr); + return 1; + } + break; + case EXPR_SIZE: + size = args[0].parsed->op.v.op[0]; + if (args[0].parsed->left->op.type == TOKEN_NAME) { + name = args[0].parsed->left->op.v.str; + } else { + fputs("First argument to set must be a name or memory expression, not a number", stderr); + return 1; + } + break; + case EXPR_MEM: + size = args[0].parsed->op.v.op[0]; + if (!eval_expr(root, args[0].parsed->left, &address)) { + fprintf(stderr, "Failed to eval %s\n", args[0].raw); + return 1; + } + break; + default: + fprintf(stderr, "First argument to set must be a name or memory expression, got %s\n", expr_type_names[args[0].parsed->type]); + return 1; + } + if (!eval_expr(root, args[1].parsed, &args[1].value)) { + fprintf(stderr, "Failed to eval %s\n", args[1].raw); + return 1; + } + uint32_t value = args[1].value; + if (name && size && size != 'l') { + uint32_t old; + if (!root->resolve(root, name, &old)) { + fprintf(stderr, "Failed to eval %s\n", name); + return 1; + } + if (size == 'b') { + old &= 0xFFFFFF00; + value &= 0xFF; + value |= old; } else { - printf(format, tmp_param, value); + old &= 0xFFFF0000; + value &= 0xFFFF; + value |= old; + } + } + if (name) { + if (!root->set(root, name, value)) { + fprintf(stderr, "Failed to set %s\n", name); + } + } else if (!root->write_mem(root, address, value, size)) { + fprintf(stderr, "Failed to write to address %X\n", address); + } + return 1; +} + +static uint8_t cmd_delete_m68k(debug_root *root, char *format, int num_args, command_arg *args) +{ + bp_def **this_bp = find_breakpoint_idx(&root->breakpoints, args[0].value); + if (!*this_bp) { + fprintf(stderr, "Breakpoint %d does not exist\n", args[0].value); + return 1; + } + bp_def *tmp = *this_bp; + remove_breakpoint(root->cpu_context, tmp->address); + *this_bp = (*this_bp)->next; + if (tmp->commands) { + for (uint32_t i = 0; i < tmp->num_commands; i++) + { + free_parsed_command(tmp->commands + i); + } + free(tmp->commands); + } + free(tmp); + return 1; +} + +static uint8_t cmd_breakpoint_m68k(debug_root *root, char *format, int num_args, command_arg *args) +{ + insert_breakpoint(root->cpu_context, args[0].value, debugger); + bp_def *new_bp = calloc(1, sizeof(bp_def)); + new_bp->next = root->breakpoints; + new_bp->address = args[0].value; + new_bp->index = root->bp_index++; + root->breakpoints = new_bp; + printf("68K Breakpoint %d set at %X\n", new_bp->index, args[0].value); + return 1; +} + +static uint8_t cmd_advance_m68k(debug_root *root, char *format, int num_args, command_arg *args) +{ + insert_breakpoint(root->cpu_context, args[0].value, debugger); + return 0; +} + +static uint8_t cmd_step_m68k(debug_root *root, char *format, int num_args, command_arg *args) +{ + m68kinst *inst = root->inst; + m68k_context *context = root->cpu_context; + uint32_t after = root->after; + if (inst->op == M68K_RTS) { + after = m68k_read_long(context->aregs[7], context); + } else if (inst->op == M68K_RTE || inst->op == M68K_RTR) { + after = m68k_read_long(context->aregs[7] + 2, context); + } else if(m68k_is_branch(inst)) { + if (inst->op == M68K_BCC && inst->extra.cond != COND_TRUE) { + root->branch_f = after; + root->branch_t = m68k_branch_target(inst, context->dregs, context->aregs) & 0xFFFFFF; + insert_breakpoint(context, root->branch_t, debugger); + } else if(inst->op == M68K_DBCC) { + if (inst->extra.cond == COND_FALSE) { + if (context->dregs[inst->dst.params.regs.pri] & 0xFFFF) { + after = m68k_branch_target(inst, context->dregs, context->aregs); + } + } else { + root->branch_t = after; + root->branch_f = m68k_branch_target(inst, context->dregs, context->aregs); + insert_breakpoint(context, root->branch_f, debugger); + } + } else { + after = m68k_branch_target(inst, context->dregs, context->aregs) & 0xFFFFFF; } - free(tmp_param); - while (*param && isblank(*param) && *param != '\n') - { - ++param; + } + insert_breakpoint(root->cpu_context, after, debugger); + return 0; +} + +static uint8_t cmd_over_m68k(debug_root *root, char *format, int num_args, command_arg *args) +{ + m68kinst *inst = root->inst; + m68k_context *context = root->cpu_context; + uint32_t after = root->after; + if (inst->op == M68K_RTS) { + after = m68k_read_long(context->aregs[7], context); + } else if (inst->op == M68K_RTE || inst->op == M68K_RTR) { + after = m68k_read_long(context->aregs[7] + 2, context); + } else if(m68k_is_noncall_branch(inst)) { + if (inst->op == M68K_BCC && inst->extra.cond != COND_TRUE) { + root->branch_t = m68k_branch_target(inst, context->dregs, context->aregs) & 0xFFFFFF; + if (root->branch_t < after) { + root->branch_t = 0; + } else { + root->branch_f = after; + insert_breakpoint(context, root->branch_t, debugger); + } + } else if(inst->op == M68K_DBCC) { + uint32_t target = m68k_branch_target(inst, context->dregs, context->aregs) & 0xFFFFFF; + if (target > after) { + if (inst->extra.cond == COND_FALSE) { + after = target; + } else { + root->branch_f = target; + root->branch_t = after; + insert_breakpoint(context, root->branch_f, debugger); + } + } + } else { + after = m68k_branch_target(inst, context->dregs, context->aregs) & 0xFFFFFF; + } + } + insert_breakpoint(root->cpu_context, after, debugger); + return 0; +} + +static uint8_t cmd_next_m68k(debug_root *root, char *format, int num_args, command_arg *args) +{ + m68kinst *inst = root->inst; + m68k_context *context = root->cpu_context; + uint32_t after = root->after; + if (inst->op == M68K_RTS) { + after = m68k_read_long(context->aregs[7], context); + } else if (inst->op == M68K_RTE || inst->op == M68K_RTR) { + after = m68k_read_long(context->aregs[7] + 2, context); + } else if(m68k_is_noncall_branch(inst)) { + if (inst->op == M68K_BCC && inst->extra.cond != COND_TRUE) { + root->branch_f = after; + root->branch_t = m68k_branch_target(inst, context->dregs, context->aregs); + insert_breakpoint(context, root->branch_t, debugger); + } else if(inst->op == M68K_DBCC) { + if ( inst->extra.cond == COND_FALSE) { + if (context->dregs[inst->dst.params.regs.pri] & 0xFFFF) { + after = m68k_branch_target(inst, context->dregs, context->aregs); + } + } else { + root->branch_t = after; + root->branch_f = m68k_branch_target(inst, context->dregs, context->aregs); + insert_breakpoint(context, root->branch_f, debugger); + } + } else { + after = m68k_branch_target(inst, context->dregs, context->aregs) & 0xFFFFFF; + } + } + insert_breakpoint(root->cpu_context, after, debugger); + return 0; +} + +static uint8_t cmd_backtrace_m68k(debug_root *root, char *format, int num_args, command_arg *args) +{ + m68k_context *context = root->cpu_context; + uint32_t stack = context->aregs[7]; + uint8_t non_adr_count = 0; + do { + uint32_t bt_address = m68k_instruction_fetch(stack, context); + bt_address = get_instruction_start(context->options, bt_address - 2); + if (bt_address) { + stack += 4; + non_adr_count = 0; + m68kinst inst; + char buf[128]; + m68k_decode(m68k_instruction_fetch, context, &inst, bt_address); + m68k_disasm(&inst, buf); + printf("%X: %s\n", bt_address, buf); + } else { + //non-return address value on stack can be word wide + stack += 2; + non_adr_count++; + } + //TODO: Make sure we don't wander into an invalid memory region + } while (stack && non_adr_count < 6); + return 1; +} + +static uint8_t cmd_vdp_sprites(debug_root *root, char *format, int num_args, command_arg *args) +{ + m68k_context *context = root->cpu_context; + genesis_context * gen = context->system; + vdp_print_sprite_table(gen->vdp); + return 1; +} + +static uint8_t cmd_vdp_regs(debug_root *root, char *format, int num_args, command_arg *args) +{ + m68k_context *context = root->cpu_context; + genesis_context * gen = context->system; + vdp_print_reg_explain(gen->vdp); + return 1; +} + +static uint8_t cmd_ym_channel(debug_root *root, char *format, int num_args, command_arg *args) +{ + m68k_context *context = root->cpu_context; + genesis_context * gen = context->system; + if (num_args) { + ym_print_channel_info(gen->ym, args[0].value - 1); + } else { + for (int i = 0; i < 6; i++) { + ym_print_channel_info(gen->ym, i); } } - if (!at_least_one) { - fprintf(stderr, "Missing argument to print/%c\n", format_char); + return 1; +} + +static uint8_t cmd_ym_timer(debug_root *root, char *format, int num_args, command_arg *args) +{ + m68k_context *context = root->cpu_context; + genesis_context * gen = context->system; + ym_print_timer_info(gen->ym); + return 1; +} + +static uint8_t cmd_sub(debug_root *root, char *format, char *param) +{ + while (param && *param && isblank(*param)) + { + ++param; + } + m68k_context *m68k = root->cpu_context; + genesis_context *gen = m68k->system; + segacd_context *cd = gen->expansion; + if (param && *param && !isspace(*param)) { + parsed_command cmd; + debug_root *sub_root = find_m68k_root(cd->m68k); + if (!sub_root) { + fputs("Failed to get debug root for Sub CPU\n", stderr); + return 1; + } + if (!parse_command(sub_root, param, &cmd)) { + return 1; + } + uint8_t ret = run_command(sub_root, &cmd); + free_parsed_command(&cmd); + return ret; + } else { + cd->enter_debugger = 1; + return 0; + } +} + +static uint8_t cmd_main(debug_root *root, char *format, char *param) +{ + while (param && *param && isblank(*param)) + { + ++param; + } + m68k_context *m68k = root->cpu_context; + segacd_context *cd = m68k->system; + + if (param && *param && !isspace(*param)) { + parsed_command cmd; + debug_root *main_root = find_m68k_root(cd->genesis->m68k); + if (!main_root) { + fputs("Failed to get debug root for Main CPU\n", stderr); + return 1; + } + if (!parse_command(main_root, param, &cmd)) { + return 1; + } + uint8_t ret = run_command(main_root, &cmd); + free_parsed_command(&cmd); + return ret; + } else { + cd->genesis->header.enter_debugger = 1; + return 0; + } +} + +static uint8_t cmd_gen_z80(debug_root *root, char *format, char *param) +{ + while (param && *param && isblank(*param)) + { + ++param; + } + m68k_context *m68k = root->cpu_context; + genesis_context *gen = m68k->system; + + if (param && *param && !isspace(*param)) { + parsed_command cmd; + debug_root *z80_root = find_z80_root(gen->z80); + if (!z80_root) { + fputs("Failed to get debug root for Z80\n", stderr); + return 1; + } + if (!parse_command(z80_root, param, &cmd)) { + return 1; + } + uint8_t ret = run_command(z80_root, &cmd); + free_parsed_command(&cmd); + return ret; + } else { + fputs("not implemented yet", stderr); + return 0; + } +} + +command_def common_commands[] = { + { + .names = (const char *[]){ + "quit", NULL + }, + .usage = "quit", + .desc = "Quit BlastEm", + .impl = cmd_quit, + .min_args = 0, + .max_args = 0, + }, + { + .names = (const char *[]){ + "help", "?", NULL + }, + .usage = "help", + .desc = "Print a list of available commands for the current debug context", + .raw_impl = cmd_help, + .min_args = 0, + .max_args = 1 + }, + { + .names = (const char *[]){ + "continue", "c", NULL + }, + .usage = "continue", + .desc = "Resume execution", + .impl = cmd_continue, + .min_args = 0, + .max_args = 0 + }, + { + .names = (const char *[]){ + "print", NULL + }, + .usage = "print[/FORMAT] EXPRESSION...", + .desc = "Print one or more expressions using the optional format character", + .impl = cmd_print, + .min_args = 1, + .max_args = -1 + }, + { + .names = (const char *[]){ + "softreset", "sr", NULL + }, + .usage = "softreset", + .desc = "Perform a soft-reset for the current system", + .impl = cmd_softreset, + .min_args = 0, + .max_args = 0, + }, + { + .names = (const char *[]){ + "display", NULL + }, + .usage = "display[/FORMAT] EXPRESSION...", + .desc = "Print one or more expressions every time the debugger is entered", + .impl = cmd_display, + .min_args = 1, + .max_args = -1 + }, + { + .names = (const char *[]){ + "deletedisplay", "dd", NULL + }, + .usage = "deletedisplay DISPLAYNUM", + .desc = "Remove expressions added with the `display` command", + .impl = cmd_delete_display, + .min_args = 1, + .max_args = 1 + }, + { + .names = (const char *[]){ + "commands", NULL + }, + .usage = "command BREAKPOINT", + .desc = "Set a list of debugger commands to be executed when the given breakpoint is hit", + .impl = cmd_command, + .min_args = 1, + .max_args = 1 + }, + { + .names = (const char *[]){ + "set", NULL + }, + .usage = "set MEM|NAME VALUE", + .desc = "Set a register, symbol or memory location to the result of evaluating VALUE", + .impl = cmd_set, + .min_args = 2, + .max_args = 2, + .skip_eval = 1 + } +}; +#define NUM_COMMON (sizeof(common_commands)/sizeof(*common_commands)) + +command_def m68k_commands[] = { + { + .names = (const char *[]){ + "breakpoint", "b", NULL + }, + .usage = "breakpoint ADDRESSS", + .desc = "Set a breakpoint at ADDRESS", + .impl = cmd_breakpoint_m68k, + .min_args = 1, + .max_args = 1 + }, + { + .names = (const char *[]){ + "advance", NULL + }, + .usage = "advance ADDRESS", + .desc = "Advance to ADDRESS", + .impl = cmd_advance_m68k, + .min_args = 1, + .max_args = 1 + }, + { + .names = (const char *[]){ + "step", "s", NULL + }, + .usage = "step", + .desc = "Advance to the next instruction, stepping into subroutines", + .impl = cmd_step_m68k, + .min_args = 0, + .max_args = 0 + }, + { + .names = (const char *[]){ + "over", NULL + }, + .usage = "over", + .desc = "Advance to the next instruction, ignoring branches to lower addresses", + .impl = cmd_over_m68k, + .min_args = 0, + .max_args = 0 + }, + { + .names = (const char *[]){ + "next", NULL + }, + .usage = "next", + .desc = "Advance to the next instruction", + .impl = cmd_next_m68k, + .min_args = 0, + .max_args = 0 + }, + { + .names = (const char *[]){ + "backtrace", "bt", NULL + }, + .usage = "backtrace", + .desc = "Print a backtrace", + .impl = cmd_backtrace_m68k, + .min_args = 0, + .max_args = 0 + }, + { + .names = (const char *[]){ + "delete", "d", NULL + }, + .usage = "delete BREAKPOINT", + .desc = "Remove breakpoint identified by BREAKPOINT", + .impl = cmd_delete_m68k, + .min_args = 1, + .max_args = 1 } +}; + +#define NUM_68K (sizeof(m68k_commands)/sizeof(*m68k_commands)) + +command_def genesis_commands[] = { + { + .names = (const char *[]){ + "vdpsprites", "vs", NULL + }, + .usage = "vdpsprites", + .desc = "Print the VDP sprite table", + .impl = cmd_vdp_sprites, + .min_args = 0, + .max_args = 0 + }, + { + .names = (const char *[]){ + "vdpsregs", "vr", NULL + }, + .usage = "vdpregs", + .desc = "Print VDP register values with a short description", + .impl = cmd_vdp_regs, + .min_args = 0, + .max_args = 0 + }, +#ifndef NO_Z80 + { + .names = (const char *[]){ + "z80", NULL + }, + .usage = "z80 [COMMAND]", + .desc = "Run a Z80 debugger command or switch to Z80 context when no command is given", + .raw_impl = cmd_gen_z80, + .min_args = 0, + .max_args = -1 + }, +#endif + { + .names = (const char *[]){ + "ymchannel", "yc", NULL + }, + .usage = "ymchannel [CHANNEL]", + .desc = "Print YM-2612 channel and operator params. Limited to CHANNEL if specified", + .impl = cmd_ym_channel, + .min_args = 0, + .max_args = 1 + }, + { + .names = (const char *[]){ + "ymtimer", "yt", NULL + }, + .usage = "ymtimer", + .desc = "Print YM-2612 timer info", + .impl = cmd_ym_timer, + .min_args = 0, + .max_args = 0 + } +}; + +#define NUM_GENESIS (sizeof(genesis_commands)/sizeof(*genesis_commands)) + +command_def scd_main_commands[] = { + { + .names = (const char *[]){ + "subcpu", NULL + }, + .usage = "subcpu [COMMAND]", + .desc = "Run a Sub-CPU debugger command or switch to Sub-CPU context when no command is given", + .raw_impl = cmd_sub, + .min_args = 0, + .max_args = -1 + } +}; + +#define NUM_SCD_MAIN (sizeof(scd_main_commands)/sizeof(*scd_main_commands)) + +command_def scd_sub_commands[] = { + { + .names = (const char *[]){ + "maincpu", NULL + }, + .usage = "maincpu [COMMAND]", + .desc = "Run a Main-CPU debugger command or switch to Main-CPU context when no command is given", + .raw_impl = cmd_main, + .min_args = 0, + .max_args = -1 + } +}; + +#define NUM_SCD_SUB (sizeof(scd_main_commands)/sizeof(*scd_main_commands)) + +#ifndef NO_Z80 + +static uint8_t cmd_delete_z80(debug_root *root, char *format, int num_args, command_arg *args) +{ + bp_def **this_bp = find_breakpoint_idx(&root->breakpoints, args[0].value); + if (!*this_bp) { + fprintf(stderr, "Breakpoint %d does not exist\n", args[0].value); + return 1; + } + bp_def *tmp = *this_bp; + zremove_breakpoint(root->cpu_context, tmp->address); + *this_bp = (*this_bp)->next; + if (tmp->commands) { + for (uint32_t i = 0; i < tmp->num_commands; i++) + { + free_parsed_command(tmp->commands + i); + } + free(tmp->commands); + } + free(tmp); + return 1; +} + +static uint8_t cmd_breakpoint_z80(debug_root *root, char *format, int num_args, command_arg *args) +{ + zinsert_breakpoint(root->cpu_context, args[0].value, (uint8_t *)zdebugger); + bp_def *new_bp = calloc(1, sizeof(bp_def)); + new_bp->next = root->breakpoints; + new_bp->address = args[0].value; + new_bp->index = root->bp_index++; + root->breakpoints = new_bp; + printf("Z80 Breakpoint %d set at %X\n", new_bp->index, args[0].value); + return 1; +} + +static uint8_t cmd_advance_z80(debug_root *root, char *format, int num_args, command_arg *args) +{ + zinsert_breakpoint(root->cpu_context, args[0].value, (uint8_t *)zdebugger); + return 0; +} + +static uint8_t cmd_step_z80(debug_root *root, char *format, int num_args, command_arg *args) +{ + z80inst *inst = root->inst; + z80_context *context = root->cpu_context; + uint32_t after = root->after; + //TODO: handle conditional branches + if (inst->op == Z80_JP || inst->op == Z80_CALL || inst->op == Z80_RST) { + if (inst->addr_mode == Z80_IMMED) { + after = inst->immed; + } else if (inst->ea_reg == Z80_HL) { +#ifndef NEW_CORE + after = context->regs[Z80_H] << 8 | context->regs[Z80_L]; + } else if (inst->ea_reg == Z80_IX) { + after = context->regs[Z80_IXH] << 8 | context->regs[Z80_IXL]; + } else if (inst->ea_reg == Z80_IY) { + after = context->regs[Z80_IYH] << 8 | context->regs[Z80_IYL]; +#endif + } + } else if(inst->op == Z80_JR) { + after += inst->immed; + } else if(inst->op == Z80_RET) { + uint8_t *sp = get_native_pointer(context->sp, (void **)context->mem_pointers, &context->Z80_OPTS->gen); + if (sp) { + after = *sp; + sp = get_native_pointer((context->sp + 1) & 0xFFFF, (void **)context->mem_pointers, &context->Z80_OPTS->gen); + if (sp) { + after |= *sp << 8; + } + } + } + zinsert_breakpoint(context, after, (uint8_t *)zdebugger); + return 0; +} + +static uint8_t cmd_over_z80(debug_root *root, char *format, int num_args, command_arg *args) +{ + fputs("not implemented yet\n", stderr); + return 1; +} + +static uint8_t cmd_next_z80(debug_root *root, char *format, int num_args, command_arg *args) +{ + z80inst *inst = root->inst; + z80_context *context = root->cpu_context; + uint32_t after = root->after; + //TODO: handle conditional branches + if (inst->op == Z80_JP) { + if (inst->addr_mode == Z80_IMMED) { + after = inst->immed; + } else if (inst->ea_reg == Z80_HL) { +#ifndef NEW_CORE + after = context->regs[Z80_H] << 8 | context->regs[Z80_L]; + } else if (inst->ea_reg == Z80_IX) { + after = context->regs[Z80_IXH] << 8 | context->regs[Z80_IXL]; + } else if (inst->ea_reg == Z80_IY) { + after = context->regs[Z80_IYH] << 8 | context->regs[Z80_IYL]; +#endif + } + } else if(inst->op == Z80_JR) { + after += inst->immed; + } else if(inst->op == Z80_RET) { + uint8_t *sp = get_native_pointer(context->sp, (void **)context->mem_pointers, &context->Z80_OPTS->gen); + if (sp) { + after = *sp; + sp = get_native_pointer((context->sp + 1) & 0xFFFF, (void **)context->mem_pointers, &context->Z80_OPTS->gen); + if (sp) { + after |= *sp << 8; + } + } + } + zinsert_breakpoint(context, after, (uint8_t *)zdebugger); + return 0; +} + +static uint8_t cmd_backtrace_z80(debug_root *root, char *format, int num_args, command_arg *args) +{ + z80_context *context = root->cpu_context; + uint32_t stack = context->sp; + uint8_t non_adr_count = 0; + do { + uint32_t bt_address = stack; + if (!root->read_mem(root, &bt_address, 'w')) { + break; + } + bt_address = z80_get_instruction_start(context, bt_address - 1); + if (bt_address != 0xFEEDFEED) { + stack += 4; + non_adr_count = 0; + z80inst inst; + char buf[128]; + uint8_t *pc = get_native_pointer(bt_address, (void **)context->mem_pointers, &context->Z80_OPTS->gen); + z80_decode(pc, &inst); + z80_disasm(&inst, buf, bt_address); + printf("%X: %s\n", bt_address, buf); + } else { + //non-return address value on stack can be byte wide + stack++; + non_adr_count++; + } + //TODO: Make sure we don't wander into an invalid memory region + } while (stack && non_adr_count < 6); + return 1; +} + +command_def z80_commands[] = { + { + .names = (const char *[]){ + "breakpoint", "b", NULL + }, + .usage = "breakpoint ADDRESSS", + .desc = "Set a breakpoint at ADDRESS", + .impl = cmd_breakpoint_z80, + .min_args = 1, + .max_args = 1 + }, + { + .names = (const char *[]){ + "advance", NULL + }, + .usage = "advance ADDRESS", + .desc = "Advance to ADDRESS", + .impl = cmd_advance_z80, + .min_args = 1, + .max_args = 1 + }, + { + .names = (const char *[]){ + "step", "s", NULL + }, + .usage = "step", + .desc = "Advance to the next instruction, stepping into subroutines", + .impl = cmd_step_z80, + .min_args = 0, + .max_args = 0 + }, + { + .names = (const char *[]){ + "over", NULL + }, + .usage = "over", + .desc = "Advance to the next instruction, ignoring branches to lower addresses", + .impl = cmd_over_z80, + .min_args = 0, + .max_args = 0 + }, + { + .names = (const char *[]){ + "next", NULL + }, + .usage = "next", + .desc = "Advance to the next instruction", + .impl = cmd_next_z80, + .min_args = 0, + .max_args = 0 + }, + { + .names = (const char *[]){ + "backtrace", "bt", NULL + }, + .usage = "backtrace", + .desc = "Print a backtrace", + .impl = cmd_backtrace_z80, + .min_args = 0, + .max_args = 0 + }, + { + .names = (const char *[]){ + "delete", "d", NULL + }, + .usage = "delete BREAKPOINT", + .desc = "Remove breakpoint identified by BREAKPOINT", + .impl = cmd_delete_z80, + .min_args = 1, + .max_args = 1 + } +}; + +#define NUM_Z80 (sizeof(z80_commands)/sizeof(*z80_commands)) + +#endif + +void add_commands(debug_root *root, command_def *defs, uint32_t num_commands) +{ + for (uint32_t i = 0; i < num_commands; i++) + { + for (int j = 0; defs[i].names[j]; j++) + { + root->commands = tern_insert_ptr(root->commands, defs[i].names[j], defs + i); + } + } +} + +debug_root *find_m68k_root(m68k_context *context) +{ + debug_root *root = find_root(context); + if (root && !root->commands) { + add_commands(root, common_commands, NUM_COMMON); + add_commands(root, m68k_commands, NUM_68K); + root->read_mem = read_m68k; + root->write_mem = write_m68k; + root->set = set_m68k; + switch (current_system->type) + { + case SYSTEM_GENESIS: + case SYSTEM_SEGACD: + //check if this is the main CPU + if (context->system == current_system) { + root->resolve = resolve_genesis; + add_commands(root, genesis_commands, NUM_GENESIS); + if (current_system->type == SYSTEM_SEGACD) { + add_commands(root, scd_main_commands, NUM_SCD_MAIN); + } + break; + } else { + add_commands(root, scd_sub_commands, NUM_SCD_SUB); + } + default: + root->resolve = resolve_m68k; + } + } + return root; } #ifndef NO_Z80 -void zdebugger_print(z80_context * context, char format_char, char * param) +static uint8_t read_z80(debug_root *root, uint32_t *out, char size) +{ + z80_context *context = root->cpu_context; + uint32_t address = *out; + *out = read_byte(address, (void **)context->mem_pointers, &context->options->gen, context); + if (size == 'w') { + *out |= read_byte(address + 1, (void **)context->mem_pointers, &context->options->gen, context) << 8; + } + return 1; +} + +static uint8_t write_z80(debug_root *root, uint32_t address, uint32_t value, char size) { - uint32_t value; - char format[8]; - strcpy(format, "%s: %d\n"); - genesis_context *system = context->system; - switch (format_char) + z80_context *context = root->cpu_context; + write_byte(address, value, (void **)context->mem_pointers, &context->options->gen, context); + if (size == 'w') { + write_byte(address + 1, value >> 8, (void **)context->mem_pointers, &context->options->gen, context); + } + return 1; +} + +static uint8_t resolve_z80(debug_root *root, const char *name, uint32_t *out) +{ + z80_context *context = root->cpu_context; + switch (tolower(name[0])) { - case 'x': - case 'X': - case 'd': - case 'c': - format[5] = format_char; - break; - case '\0': - break; - default: - fprintf(stderr, "Unrecognized format character: %c\n", format_char); - } - switch (param[0]) - { -#ifndef NEW_CORE case 'a': - if (param[1] == 'f') { - if(param[2] == '\'') { - value = context->alt_regs[Z80_A] << 8; - value |= context->alt_flags[ZF_S] << 7; - value |= context->alt_flags[ZF_Z] << 6; - value |= context->alt_flags[ZF_H] << 4; - value |= context->alt_flags[ZF_PV] << 2; - value |= context->alt_flags[ZF_N] << 1; - value |= context->alt_flags[ZF_C]; + if (!name[1]) { + *out = context->regs[Z80_A]; + } else if (name[1] == '\'' && !name[2]) { + *out = context->alt_regs[Z80_A]; + } else if (tolower(name[1]) == 'f') { + if (!name[2]) { + *out = context->regs[Z80_A] << 8; + *out |= context->flags[ZF_S] << 7; + *out |= context->flags[ZF_Z] << 6; + *out |= context->flags[ZF_H] << 4; + *out |= context->flags[ZF_PV] << 2; + *out |= context->flags[ZF_N] << 1; + *out |= context->flags[ZF_C]; + } else if (name[2] == '\'' && !name[3]) { + *out = context->alt_regs[Z80_A] << 8; + *out |= context->alt_flags[ZF_S] << 7; + *out |= context->alt_flags[ZF_Z] << 6; + *out |= context->alt_flags[ZF_H] << 4; + *out |= context->alt_flags[ZF_PV] << 2; + *out |= context->alt_flags[ZF_N] << 1; + *out |= context->alt_flags[ZF_C]; } else { - value = context->regs[Z80_A] << 8; - value |= context->flags[ZF_S] << 7; - value |= context->flags[ZF_Z] << 6; - value |= context->flags[ZF_H] << 4; - value |= context->flags[ZF_PV] << 2; - value |= context->flags[ZF_N] << 1; - value |= context->flags[ZF_C]; + return 0; } - } else if(param[1] == '\'') { - value = context->alt_regs[Z80_A]; } else { - value = context->regs[Z80_A]; + return 0; } break; case 'b': - if (param[1] == 'c') { - if(param[2] == '\'') { - value = context->alt_regs[Z80_B] << 8; - value |= context->alt_regs[Z80_C]; + if (!name[1]) { + *out = context->regs[Z80_B]; + } else if (name[1] == '\'' && !name[2]) { + *out = context->alt_regs[Z80_B]; + } else if (tolower(name[1]) == 'c') { + if (!name[2]) { + *out = context->regs[Z80_B] << 8 | context->regs[Z80_C]; + } else if (name[2] == '\'' && !name[3]) { + *out = context->alt_regs[Z80_B] << 8 | context->alt_regs[Z80_C]; } else { - value = context->regs[Z80_B] << 8; - value |= context->regs[Z80_C]; + return 0; } - } else if(param[1] == '\'') { - value = context->alt_regs[Z80_B]; - } else if(param[1] == 'a') { - value = context->bank_reg << 15; - } else { - value = context->regs[Z80_B]; } break; case 'c': - if(param[1] == '\'') { - value = context->alt_regs[Z80_C]; - } else if(param[1] == 'y') { - value = context->current_cycle; + if (!name[1]) { + *out = context->regs[Z80_C]; + } else if (name[1] == '\'' && !name[2]) { + *out = context->alt_regs[Z80_C]; } else { - value = context->regs[Z80_C]; + return 0; } break; case 'd': - if (param[1] == 'e') { - if(param[2] == '\'') { - value = context->alt_regs[Z80_D] << 8; - value |= context->alt_regs[Z80_E]; + if (!name[1]) { + *out = context->regs[Z80_D]; + } else if (name[1] == '\'' && !name[2]) { + *out = context->alt_regs[Z80_D]; + } else if (tolower(name[1]) == 'e') { + if (!name[2]) { + *out = context->regs[Z80_D] << 8 | context->regs[Z80_E]; + } else if (name[2] == '\'' && !name[3]) { + *out = context->alt_regs[Z80_D] << 8 | context->alt_regs[Z80_E]; } else { - value = context->regs[Z80_D] << 8; - value |= context->regs[Z80_E]; + return 0; } - } else if(param[1] == '\'') { - value = context->alt_regs[Z80_D]; - } else { - value = context->regs[Z80_D]; } break; case 'e': - if(param[1] == '\'') { - value = context->alt_regs[Z80_E]; + if (!name[1]) { + *out = context->regs[Z80_E]; + } else if (name[1] == '\'' && !name[2]) { + *out = context->alt_regs[Z80_E]; } else { - value = context->regs[Z80_E]; + return 0; } break; case 'f': - if(param[2] == '\'') { - value = context->alt_flags[ZF_S] << 7; - value |= context->alt_flags[ZF_Z] << 6; - value |= context->alt_flags[ZF_H] << 4; - value |= context->alt_flags[ZF_PV] << 2; - value |= context->alt_flags[ZF_N] << 1; - value |= context->alt_flags[ZF_C]; + if (!name[1]) { + *out = context->flags[ZF_S] << 7; + *out |= context->flags[ZF_Z] << 6; + *out |= context->flags[ZF_H] << 4; + *out |= context->flags[ZF_PV] << 2; + *out |= context->flags[ZF_N] << 1; + *out |= context->flags[ZF_C]; + } else if (name[1] == '\'' && !name[2]) { + *out = context->alt_flags[ZF_S] << 7; + *out |= context->alt_flags[ZF_Z] << 6; + *out |= context->alt_flags[ZF_H] << 4; + *out |= context->alt_flags[ZF_PV] << 2; + *out |= context->alt_flags[ZF_N] << 1; + *out |= context->alt_flags[ZF_C]; } else { - value = context->flags[ZF_S] << 7; - value |= context->flags[ZF_Z] << 6; - value |= context->flags[ZF_H] << 4; - value |= context->flags[ZF_PV] << 2; - value |= context->flags[ZF_N] << 1; - value |= context->flags[ZF_C]; + return 0; } break; case 'h': - if (param[1] == 'l') { - if(param[2] == '\'') { - value = context->alt_regs[Z80_H] << 8; - value |= context->alt_regs[Z80_L]; + if (!name[1]) { + *out = context->regs[Z80_H]; + } else if (name[1] == '\'' && !name[2]) { + *out = context->alt_regs[Z80_H]; + } else if (tolower(name[1]) == 'e') { + if (!name[2]) { + *out = context->regs[Z80_H] << 8 | context->regs[Z80_L]; + } else if (name[2] == '\'' && !name[3]) { + *out = context->alt_regs[Z80_H] << 8 | context->alt_regs[Z80_L]; + } else { + return 0; + } + } + break; + case 'i': + switch (tolower(name[1])) + { + case 0: + *out = context->regs[Z80_I]; + break; + case 'f': + if (name[2] != 'f' || name[3] < '1' || name[4]) { + return 0; + } + if (name[3] == '1') { + *out = context->iff1; + } else if (name[3] == '2') { + *out = context->iff2; } else { - value = context->regs[Z80_H] << 8; - value |= context->regs[Z80_L]; + return 0; + } + break; + case 'm': + if (name[2]) { + return 0; + } + *out = context->im; + break; + case 'n': + if (strcasecmp(name +2, "t_cycle")) { + return 0; + } + *out = context->int_cycle; + break; + case 'r': + if (name[2]) { + return 0; } - } else if(param[1] == '\'') { - value = context->alt_regs[Z80_H]; - } else { - value = context->regs[Z80_H]; + *out = context->regs[Z80_I] << 8 | context->regs[Z80_R]; + break; + case 'x': + switch (tolower(name[2])) + { + case 0: + *out = context->regs[Z80_IXH] << 8 | context->regs[Z80_IXL]; + break; + case 'h': + if (name[3]) { + return 0; + } + *out = context->regs[Z80_IXH]; + case 'l': + if (name[3]) { + return 0; + } + *out = context->regs[Z80_IXL]; + default: + return 0; + } + break; + case 'y': + switch (tolower(name[2])) + { + case 0: + *out = context->regs[Z80_IYH] << 8 | context->regs[Z80_IYL]; + break; + case 'h': + if (name[3]) { + return 0; + } + *out = context->regs[Z80_IYH]; + case 'l': + if (name[3]) { + return 0; + } + *out = context->regs[Z80_IYL]; + default: + return 0; + } + break; + default: + return 0; } break; case 'l': - if(param[1] == '\'') { - value = context->alt_regs[Z80_L]; + if (!name[1]) { + *out = context->regs[Z80_L]; + } else if (name[1] == '\'' && !name[2]) { + *out = context->alt_regs[Z80_L]; + } else { + return 0; + } + break; + case 'p': + if (tolower(name[1]) != 'c' || name[2]) { + return 0; + } + *out = root->address; + break; + case 'r': + if (name[1]) { + return 0; + } + *out = context->regs[Z80_R]; + case 's': + if (tolower(name[1]) != 'p' || name[2]) { + return 0; + } + *out = context->sp; + break; + default: + return 0; + } + return 1; +} + +static uint8_t set_z80(debug_root *root, const char *name, uint32_t value) +{ + z80_context *context = root->cpu_context; + switch (tolower(name[0])) + { + case 'a': + if (!name[1]) { + context->regs[Z80_A] = value; + } else if (name[1] == '\'' && !name[2]) { + context->alt_regs[Z80_A] = value; + } else if (tolower(name[1]) == 'f') { + if (!name[2]) { + context->regs[Z80_A] = value >> 8; + context->flags[ZF_S] = value >> 7 & 1; + context->flags[ZF_Z] = value >> 6 & 1; + context->flags[ZF_H] = value >> 4 & 1; + context->flags[ZF_PV] = value >> 2 & 1; + context->flags[ZF_N] = value >> 1 & 1; + context->flags[ZF_C] = value & 1; + } else if (name[2] == '\'' && !name[3]) { + context->alt_regs[Z80_A] = value >> 8; + context->alt_flags[ZF_S] = value >> 7 & 1; + context->alt_flags[ZF_Z] = value >> 6 & 1; + context->alt_flags[ZF_H] = value >> 4 & 1; + context->alt_flags[ZF_PV] = value >> 2 & 1; + context->alt_flags[ZF_N] = value >> 1 & 1; + context->alt_flags[ZF_C] = value & 1; + } else { + return 0; + } } else { - value = context->regs[Z80_L]; + return 0; + } + break; + case 'b': + if (!name[1]) { + context->regs[Z80_B] = value; + } else if (name[1] == '\'' && !name[2]) { + context->alt_regs[Z80_B] = value; + } else if (tolower(name[1]) == 'c') { + if (!name[2]) { + context->regs[Z80_B] = value >> 8; + context->regs[Z80_C] = value; + } else if (name[2] == '\'' && !name[3]) { + context->alt_regs[Z80_B] = value >> 8; + context->alt_regs[Z80_C] = value; + } else { + return 0; + } + } + break; + case 'c': + if (!name[1]) { + context->regs[Z80_C] = value; + } else if (name[1] == '\'' && !name[2]) { + context->alt_regs[Z80_C] = value; + } else { + return 0; + } + break; + case 'd': + if (!name[1]) { + context->regs[Z80_D] = value; + } else if (name[1] == '\'' && !name[2]) { + context->alt_regs[Z80_D] = value; + } else if (tolower(name[1]) == 'e') { + if (!name[2]) { + context->regs[Z80_D] = value >> 8; + context->regs[Z80_E] = value; + } else if (name[2] == '\'' && !name[3]) { + context->alt_regs[Z80_D] = value >> 8; + context->alt_regs[Z80_E] = value; + } else { + return 0; + } + } + break; + case 'e': + if (!name[1]) { + context->regs[Z80_E] = value; + } else if (name[1] == '\'' && !name[2]) { + context->alt_regs[Z80_E] = value; + } else { + return 0; + } + break; + case 'f': + if (!name[1]) { + context->flags[ZF_S] = value >> 7 & 1; + context->flags[ZF_Z] = value >> 6 & 1; + context->flags[ZF_H] = value >> 4 & 1; + context->flags[ZF_PV] = value >> 2 & 1; + context->flags[ZF_N] = value >> 1 & 1; + context->flags[ZF_C] = value & 1; + } else if (name[1] == '\'' && !name[2]) { + context->alt_flags[ZF_S] = value >> 7 & 1; + context->alt_flags[ZF_Z] = value >> 6 & 1; + context->alt_flags[ZF_H] = value >> 4 & 1; + context->alt_flags[ZF_PV] = value >> 2 & 1; + context->alt_flags[ZF_N] = value >> 1 & 1; + context->alt_flags[ZF_C] = value & 1; + } else { + return 0; + } + break; + case 'h': + if (!name[1]) { + context->regs[Z80_H] = value; + } else if (name[1] == '\'' && !name[2]) { + context->alt_regs[Z80_H] = value; + } else if (tolower(name[1]) == 'e') { + if (!name[2]) { + context->regs[Z80_H] = value >> 8; + context->regs[Z80_L] = value; + } else if (name[2] == '\'' && !name[3]) { + context->alt_regs[Z80_H] = value >> 8; + context->alt_regs[Z80_L] = value; + } else { + return 0; + } } break; case 'i': - if(param[1] == 'x') { - if (param[2] == 'h') { - value = context->regs[Z80_IXH]; - } else if(param[2] == 'l') { - value = context->regs[Z80_IXL]; + switch (tolower(name[1])) + { + case 0: + context->regs[Z80_I] = value; + break; + case 'f': + if (name[2] != 'f' || name[3] < '1' || name[4]) { + return 0; + } + if (name[3] == '1') { + context->iff1 = value != 0; + } else if (name[3] == '2') { + context->iff2 = value != 0; } else { - value = context->regs[Z80_IXH] << 8; - value |= context->regs[Z80_IXL]; + return 0; + } + break; + case 'm': + if (name[2]) { + return 0; + } + context->im = value & 3; + break; + case 'r': + if (name[2]) { + return 0; } - } else if(param[1] == 'y') { - if (param[2] == 'h') { - value = context->regs[Z80_IYH]; - } else if(param[2] == 'l') { - value = context->regs[Z80_IYL]; - } else { - value = context->regs[Z80_IYH] << 8; - value |= context->regs[Z80_IYL]; + context->regs[Z80_I] = value >> 8; + context->regs[Z80_R] = value; + break; + case 'x': + switch (tolower(name[2])) + { + case 0: + context->regs[Z80_IXH] = value >> 8; + context->regs[Z80_IXL] = value; + break; + case 'h': + if (name[3]) { + return 0; + } + context->regs[Z80_IXH] = value; + case 'l': + if (name[3]) { + return 0; + } + context->regs[Z80_IXL] = value; + default: + return 0; } - } else if(param[1] == 'n') { - value = context->int_cycle; - } else if(param[1] == 'f' && param[2] == 'f' && param[3] == '1') { - value = context->iff1; - } else if(param[1] == 'f' && param[2] == 'f' && param[3] == '2') { - value = context->iff2; - } else { - value = context->im; + break; + case 'y': + switch (tolower(name[2])) + { + case 0: + context->regs[Z80_IYH] = value >> 8; + context->regs[Z80_IYL] = value; + break; + case 'h': + if (name[3]) { + return 0; + } + context->regs[Z80_IYH] = value; + case 'l': + if (name[3]) { + return 0; + } + context->regs[Z80_IYL] = value; + default: + return 0; + } + break; + default: + return 0; } break; -#endif - case 's': - if (param[1] == 'p') { - value = context->sp; + case 'l': + if (!name[1]) { + context->regs[Z80_L] = value; + } else if (name[1] == '\'' && !name[2]) { + context->alt_regs[Z80_L] = value; + } else { + return 0; } break; - case '0': - if (param[1] == 'x') { - uint16_t p_addr = strtol(param+2, NULL, 16); - value = read_byte(p_addr, (void **)context->mem_pointers, &context->options->gen, context); + case 'r': + if (name[1]) { + return 0; + } + context->regs[Z80_R] = value; + case 's': + if (tolower(name[1]) != 'p' || name[2]) { + return 0; } + context->sp = value; break; + default: + return 0; } - printf(format, param, value); + return 1; +} + +debug_root *find_z80_root(z80_context *context) +{ + debug_root *root = find_root(context); + if (root && !root->commands) { + add_commands(root, common_commands, NUM_COMMON); + add_commands(root, z80_commands, NUM_Z80); + root->read_mem = read_z80; + root->write_mem = write_z80; + root->set = set_z80; + root->resolve = resolve_z80; + } + return root; } z80_context * zdebugger(z80_context * context, uint16_t address) @@ -1088,11 +2610,11 @@ z80inst inst; genesis_context *system = context->system; init_terminal(); - //Check if this is a user set breakpoint, or just a temporary one debug_root *root = find_root(context); if (!root) { return context; } + //Check if this is a user set breakpoint, or just a temporary one bp_def ** this_bp = find_breakpoint(&root->breakpoints, address); if (*this_bp) { printf("Z80 Breakpoint %d hit\n", (*this_bp)->index); @@ -1104,12 +2626,15 @@ fatal_error("Failed to get native pointer on entering Z80 debugger at address %X\n", address); } for (disp_def * cur = root->displays; cur; cur = cur->next) { - zdebugger_print(context, cur->format_char, cur->param); + cmd_print(root, cur->format, cur->num_args, cur->args); } uint8_t * after_pc = z80_decode(pc, &inst); z80_disasm(&inst, input_buf, address); printf("%X:\t%s\n", address, input_buf); uint16_t after = address + (after_pc-pc); + root->address = address; + root->after = after; + root->inst = &inst; int debugging = 1; while(debugging) { fputs(">", stdout); @@ -1124,173 +2649,10 @@ } else { strcpy(input_buf, last_cmd); } - char * param; - char format[8]; - uint32_t value; - bp_def * new_bp; - switch(input_buf[0]) - { - case 'a': - param = find_param(input_buf); - if (!param) { - fputs("a command requires a parameter\n", stderr); - break; - } - value = strtol(param, NULL, 16); - zinsert_breakpoint(context, value, (uint8_t *)zdebugger); - debugging = 0; - break; - case 'b': - param = find_param(input_buf); - if (!param) { - fputs("b command requires a parameter\n", stderr); - break; - } - value = strtol(param, NULL, 16); - zinsert_breakpoint(context, value, (uint8_t *)zdebugger); - new_bp = malloc(sizeof(bp_def)); - new_bp->next = root->breakpoints; - new_bp->address = value; - new_bp->index = root->bp_index++; - new_bp->commands = NULL; - root->breakpoints = new_bp; - printf("Z80 Breakpoint %d set at %X\n", new_bp->index, value); - break; - case 'c': - puts("Continuing"); - debugging = 0; - break; - case 'd': - if (input_buf[1] == 'i') { - char format_char = 0; - for(int i = 2; input_buf[i] != 0 && input_buf[i] != ' '; i++) { - if (input_buf[i] == '/') { - format_char = input_buf[i+1]; - break; - } - } - param = find_param(input_buf); - if (!param) { - fputs("display command requires a parameter\n", stderr); - break; - } - zdebugger_print(context, format_char, param); - add_display(&root->displays, &root->disp_index, format_char, param); - } else if (input_buf[1] == 'e' || input_buf[1] == ' ') { - param = find_param(input_buf); - if (!param) { - fputs("delete command requires a parameter\n", stderr); - break; - } - if (param[0] >= '0' && param[0] <= '9') { - value = atoi(param); - this_bp = find_breakpoint_idx(&root->breakpoints, value); - if (!*this_bp) { - fprintf(stderr, "Breakpoint %d does not exist\n", value); - break; - } - new_bp = *this_bp; - zremove_breakpoint(context, new_bp->address); - *this_bp = new_bp->next; - free(new_bp); - } else if (param[0] == 'd') { - param = find_param(param); - if (!param) { - fputs("delete display command requires a parameter\n", stderr); - break; - } - remove_display(&root->displays, atoi(param)); - } - } - break; - case 'n': - //TODO: Handle conditional branch instructions - if (inst.op == Z80_JP) { - if (inst.addr_mode == Z80_IMMED) { - after = inst.immed; - } else if (inst.ea_reg == Z80_HL) { -#ifndef NEW_CORE - after = context->regs[Z80_H] << 8 | context->regs[Z80_L]; - } else if (inst.ea_reg == Z80_IX) { - after = context->regs[Z80_IXH] << 8 | context->regs[Z80_IXL]; - } else if (inst.ea_reg == Z80_IY) { - after = context->regs[Z80_IYH] << 8 | context->regs[Z80_IYL]; -#endif - } - } else if(inst.op == Z80_JR) { - after += inst.immed; - } else if(inst.op == Z80_RET) { - uint8_t *sp = get_native_pointer(context->sp, (void **)context->mem_pointers, &context->Z80_OPTS->gen); - if (sp) { - after = *sp; - sp = get_native_pointer((context->sp + 1) & 0xFFFF, (void **)context->mem_pointers, &context->Z80_OPTS->gen); - if (sp) { - after |= *sp << 8; - } - } - } - zinsert_breakpoint(context, after, (uint8_t *)zdebugger); - debugging = 0; - break; - case 'p': - param = find_param(input_buf); - if (!param) { - fputs("p command requires a parameter\n", stderr); - break; - } - zdebugger_print(context, input_buf[1] == '/' ? input_buf[2] : 0, param); - break; - case 'q': - puts("Quitting"); - exit(0); - break; - case 's': { - param = find_param(input_buf); - if (!param) { - fputs("s command requires a file name\n", stderr); - break; - } - memmap_chunk const *ram_chunk = NULL; - for (int i = 0; i < context->Z80_OPTS->gen.memmap_chunks; i++) - { - memmap_chunk const *cur = context->Z80_OPTS->gen.memmap + i; - if (cur->flags & MMAP_WRITE) { - ram_chunk = cur; - break; - } - } - if (ram_chunk) { - uint32_t size = ram_chunk->end - ram_chunk->start; - if (size > ram_chunk->mask) { - size = ram_chunk->mask+1; - } - uint8_t *buf = get_native_pointer(ram_chunk->start, (void **)context->mem_pointers, &context->Z80_OPTS->gen); - FILE * f = fopen(param, "wb"); - if (f) { - if(fwrite(buf, 1, size, f) != size) { - fputs("Error writing file\n", stderr); - } - fclose(f); - printf("Wrote %d bytes to %s\n", size, param); - } else { - fprintf(stderr, "Could not open %s for writing\n", param); - } - } else { - fputs("Failed to find a RAM memory chunk\n", stderr); - } - break; - } - case '?': - print_z80_help(); - break; - default: - if ( - !context->Z80_OPTS->gen.debug_cmd_handler - || !context->Z80_OPTS->gen.debug_cmd_handler(&system->header, input_buf) - ) { - fprintf(stderr, "Unrecognized debugger command %s\nUse '?' for help.\n", input_buf); - } - break; + parsed_command cmd; + if (parse_command(root, input_buf, &cmd)) { + debugging = run_command(root, &cmd); + free_parsed_command(&cmd); } } return context; @@ -1298,476 +2660,6 @@ #endif -int run_debugger_command(m68k_context *context, uint32_t address, char *input_buf, m68kinst inst, uint32_t after); -int run_genesis_debugger_command(m68k_context *context, uint32_t address, char *input_buf) -{ - genesis_context * gen = context->system; - char *param; - uint32_t value; - bp_def *new_bp; - switch (input_buf[0]) - { - case 'v': - //VDP debug commands - switch(input_buf[1]) - { - case 's': - vdp_print_sprite_table(gen->vdp); - break; - case 'r': - vdp_print_reg_explain(gen->vdp); - break; - } - break; - case 'y': - //YM-2612 debug commands - switch(input_buf[1]) - { - case 'c': - if (input_buf[2] == ' ') { - int channel = atoi(input_buf+3)-1; - ym_print_channel_info(gen->ym, channel); - } else { - for (int i = 0; i < 6; i++) { - ym_print_channel_info(gen->ym, i); - } - } - break; - case 't': - ym_print_timer_info(gen->ym); - break; - } - break; - case 'u': - if (gen->expansion) { - segacd_context *cd = gen->expansion; - if (input_buf[1]) { - //TODO: filter out commands that are unsafe to run when we don't have the current Sub CPU address - run_debugger_command(cd->m68k, 0, input_buf + 1, (m68kinst){}, 0); - } else { - cd->enter_debugger = 1; - return 0; - } - } else { - fputs("u command only valid when Sega/Mega CD is active\n", stderr); - } - break; -#ifndef NO_Z80 - case 'z': - //Z80 debug commands - switch(input_buf[1]) - { - case 'b': { - param = find_param(input_buf); - if (!param) { - fputs("zb command requires a parameter\n", stderr); - break; - } - value = strtol(param, NULL, 16); - debug_root *zroot = find_root(gen->z80); - zinsert_breakpoint(gen->z80, value, (uint8_t *)zdebugger); - new_bp = malloc(sizeof(bp_def)); - new_bp->next = zroot->breakpoints; - new_bp->address = value; - new_bp->index = zroot->bp_index++; - zroot->breakpoints = new_bp; - printf("Z80 Breakpoint %d set at %X\n", new_bp->index, value); - break; - } - case 'p': - param = find_param(input_buf); - if (!param) { - fputs("zp command requires a parameter\n", stderr); - break; - } - zdebugger_print(gen->z80, input_buf[2] == '/' ? input_buf[3] : 0, param); - } - break; -#endif - default: - fprintf(stderr, "Unrecognized debugger command %s\nUse '?' for help.\n", input_buf); - break; - } - return 1; -} - -int run_subcpu_debugger_command(m68k_context *context, uint32_t address, char *input_buf) -{ - segacd_context *cd = context->system; - switch (input_buf[0]) - { - case 'm': - if (input_buf[1]) { - //TODO: filter out commands that are unsafe to run when we don't have the current Main CPU address - return run_debugger_command(cd->genesis->m68k, 0, input_buf + 1, (m68kinst){}, 0); - } else { - cd->genesis->header.enter_debugger = 1; - return 0; - } - break; - default: - fprintf(stderr, "Unrecognized debugger command %s\nUse '?' for help.\n", input_buf); - break; - } - return 1; -} - -int run_debugger_command(m68k_context *context, uint32_t address, char *input_buf, m68kinst inst, uint32_t after) -{ - char * param; - char format_char; - genesis_context *system = context->system; - uint32_t value; - bp_def *new_bp, **this_bp; - debug_root *root = find_root(context); - if (!root) { - return 0; - } - root->address = address; - switch(input_buf[0]) - { - case 'c': - if (input_buf[1] == 0 || input_buf[1] == 'o' && input_buf[2] == 'n') - { - puts("Continuing"); - return 0; - } else if (input_buf[1] == 'o' && input_buf[2] == 'm') { - param = find_param(input_buf); - if (!param) { - fputs("com command requires a parameter\n", stderr); - break; - } - bp_def **target = find_breakpoint_idx(&root->breakpoints, atoi(param)); - if (!target) { - fprintf(stderr, "Breakpoint %s does not exist!\n", param); - break; - } - printf("Enter commands for breakpoing %d, type end when done\n", atoi(param)); - char cmd_buf[1024]; - char *commands = NULL; - for (;;) - { - fputs(">>", stdout); - fflush(stdout); - fgets(cmd_buf, sizeof(cmd_buf), stdin); - if (strcmp(cmd_buf, "end\n")) { - if (commands) { - char *tmp = commands; - commands = alloc_concat(commands, cmd_buf); - free(tmp); - } else { - commands = strdup(cmd_buf); - } - } else { - break; - } - } - (*target)->commands = commands; - } else { - } - break; - case 'b': - if (input_buf[1] == 't') { - uint32_t stack = context->aregs[7]; - uint8_t non_adr_count = 0; - do { - uint32_t bt_address = m68k_instruction_fetch(stack, context); - bt_address = get_instruction_start(context->options, bt_address - 2); - if (bt_address) { - stack += 4; - non_adr_count = 0; - m68k_decode(m68k_instruction_fetch, context, &inst, bt_address); - m68k_disasm(&inst, input_buf); - printf("%X: %s\n", bt_address, input_buf); - } else { - //non-return address value on stack can be word wide - stack += 2; - non_adr_count++; - } - //TODO: Make sure we don't wander into an invalid memory region - } while (stack && non_adr_count < 6); - } else { - param = find_param(input_buf); - if (!param) { - fputs("b command requires a parameter\n", stderr); - break; - } - value = strtol(param, NULL, 16); - insert_breakpoint(context, value, debugger); - new_bp = malloc(sizeof(bp_def)); - new_bp->next = root->breakpoints; - new_bp->address = value; - new_bp->index = root->bp_index++; - new_bp->commands = NULL; - root->breakpoints = new_bp; - printf("68K Breakpoint %d set at %X\n", new_bp->index, value); - } - break; - case 'a': - param = find_param(input_buf); - if (!param) { - fputs("a command requires a parameter\n", stderr); - break; - } - value = strtol(param, NULL, 16); - insert_breakpoint(context, value, debugger); - return 0; - case 'd': - if (input_buf[1] == 'i') { - format_char = 0; - for(int i = 2; input_buf[i] != 0 && input_buf[i] != ' '; i++) { - if (input_buf[i] == '/') { - format_char = input_buf[i+1]; - break; - } - } - param = find_param(input_buf); - if (!param) { - fputs("display command requires a parameter\n", stderr); - break; - } - debugger_print(root, format_char, param); - add_display(&root->displays, &root->disp_index, format_char, param); - } else { - param = find_param(input_buf); - if (!param) { - fputs("d command requires a parameter\n", stderr); - break; - } - value = atoi(param); - this_bp = find_breakpoint_idx(&root->breakpoints, value); - if (!*this_bp) { - fprintf(stderr, "Breakpoint %d does not exist\n", value); - break; - } - new_bp = *this_bp; - *this_bp = (*this_bp)->next; - if (new_bp->commands) { - free(new_bp->commands); - } - free(new_bp); - } - break; - case 'p': - format_char = 0; - for(int i = 1; input_buf[i] != 0 && input_buf[i] != ' '; i++) { - if (input_buf[i] == '/') { - format_char = input_buf[i+1]; - break; - } - } - param = find_param(input_buf); - if (param) { - debugger_print(root, format_char, param); - } else { - m68k_disasm(&inst, input_buf); - printf("%X: %s\n", address, input_buf); - } - - break; - case 'n': - if (inst.op == M68K_RTS) { - after = m68k_read_long(context->aregs[7], context); - } else if (inst.op == M68K_RTE || inst.op == M68K_RTR) { - after = m68k_read_long(context->aregs[7] + 2, context); - } else if(m68k_is_noncall_branch(&inst)) { - if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) { - root->branch_f = after; - root->branch_t = m68k_branch_target(&inst, context->dregs, context->aregs); - insert_breakpoint(context, root->branch_t, debugger); - } else if(inst.op == M68K_DBCC) { - if ( inst.extra.cond == COND_FALSE) { - if (context->dregs[inst.dst.params.regs.pri] & 0xFFFF) { - after = m68k_branch_target(&inst, context->dregs, context->aregs); - } - } else { - root->branch_t = after; - root->branch_f = m68k_branch_target(&inst, context->dregs, context->aregs); - insert_breakpoint(context, root->branch_f, debugger); - } - } else { - after = m68k_branch_target(&inst, context->dregs, context->aregs); - } - } - insert_breakpoint(context, after, debugger); - return 0; - case 'o': - if (inst.op == M68K_RTS) { - after = m68k_read_long(context->aregs[7], context); - } else if (inst.op == M68K_RTE || inst.op == M68K_RTR) { - after = m68k_read_long(context->aregs[7] + 2, context); - } else if(m68k_is_noncall_branch(&inst)) { - if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) { - root->branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; - if (root->branch_t < after) { - root->branch_t = 0; - } else { - root->branch_f = after; - insert_breakpoint(context, root->branch_t, debugger); - } - } else if(inst.op == M68K_DBCC) { - uint32_t target = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; - if (target > after) { - if (inst.extra.cond == COND_FALSE) { - after = target; - } else { - root->branch_f = target; - root->branch_t = after; - insert_breakpoint(context, root->branch_f, debugger); - } - } - } else { - after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; - } - } - insert_breakpoint(context, after, debugger); - return 0; - case 's': - if (input_buf[1] == 'e') { - param = find_param(input_buf); - if (!param) { - fputs("Missing destination parameter for set\n", stderr); - return 1; - } - char *val = find_param(param); - if (!val) { - fputs("Missing value parameter for set\n", stderr); - return 1; - } - long int_val; - int reg_num; - switch (val[0]) - { - case 'd': - case 'a': - reg_num = val[1] - '0'; - if (reg_num < 0 || reg_num > 8) { - fprintf(stderr, "Invalid register %s\n", val); - return 1; - } - int_val = (val[0] == 'd' ? context->dregs : context->aregs)[reg_num]; - break; - case '$': - int_val = strtol(val+1, NULL, 16); - break; - case '0': - if (val[1] == 'x') { - int_val = strtol(val+2, NULL, 16); - break; - } - default: - int_val = strtol(val, NULL, 10); - } - switch(param[0]) - { - case 'd': - case 'a': - reg_num = param[1] - '0'; - if (reg_num < 0 || reg_num > 8) { - fprintf(stderr, "Invalid register %s\n", param); - return 1; - } - (param[0] == 'd' ? context->dregs : context->aregs)[reg_num] = int_val; - break; - default: - fprintf(stderr, "Invalid destinatino %s\n", param); - } - break; - } else if (input_buf[1] == 'r') { - system->header.soft_reset(&system->header); - return 0; - } else { - if (inst.op == M68K_RTS) { - after = m68k_read_long(context->aregs[7], context); - } else if (inst.op == M68K_RTE || inst.op == M68K_RTR) { - after = m68k_read_long(context->aregs[7] + 2, context); - } else if(m68k_is_branch(&inst)) { - if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) { - root->branch_f = after; - root->branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; - insert_breakpoint(context, root->branch_t, debugger); - } else if(inst.op == M68K_DBCC) { - if (inst.extra.cond == COND_FALSE) { - if (context->dregs[inst.dst.params.regs.pri] & 0xFFFF) { - after = m68k_branch_target(&inst, context->dregs, context->aregs); - } - } else { - root->branch_t = after; - root->branch_f = m68k_branch_target(&inst, context->dregs, context->aregs); - insert_breakpoint(context, root->branch_f, debugger); - } - } else { - after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; - } - } - insert_breakpoint(context, after, debugger); - return 0; - } - case '?': - print_m68k_help(); - break; - case 'q': - puts("Quitting"); - exit(0); - break; - default: { - if (context->system == current_system) { - //primary 68K for current system - return run_genesis_debugger_command(context, address, input_buf); - } else { - //presumably Sega CD sub CPU - //TODO: consider making this more generic - return run_subcpu_debugger_command(context, address, input_buf); - } - break; - } - } - return 1; -} - -void print_m68k_help() -{ - printf("M68k Debugger Commands\n"); - printf(" b ADDRESS - Set a breakpoint at ADDRESS\n"); - printf(" d BREAKPOINT - Delete a 68K breakpoint\n"); - printf(" co BREAKPOINT - Run a list of debugger commands each time\n"); - printf(" BREAKPOINT is hit\n"); - printf(" a ADDRESS - Advance to address\n"); - printf(" n - Advance to next instruction\n"); - printf(" o - Advance to next instruction ignoring branches to\n"); - printf(" lower addresses (good for breaking out of loops)\n"); - printf(" s - Advance to next instruction (follows bsr/jsr)\n"); - printf(" se REG|ADDRESS VALUE - Set value\n"); - printf(" sr - Soft reset\n"); - printf(" c - Continue\n"); - printf(" bt - Print a backtrace\n"); - printf(" p[/(x|X|d|c)] VALUE - Print a register or memory location\n"); - printf(" di[/(x|X|d|c)] VALUE - Print a register or memory location each time\n"); - printf(" a breakpoint is hit\n"); - printf(" vs - Print VDP sprite list\n"); - printf(" vr - Print VDP register info\n"); - printf(" yc [CHANNEL NUM] - Print YM-2612 channel info\n"); - printf(" yt - Print YM-2612 timer info\n"); - printf(" zb ADDRESS - Set a Z80 breakpoint\n"); - printf(" zp[/(x|X|d|c)] VALUE - Display a Z80 value\n"); - printf(" ? - Display help\n"); - printf(" q - Quit BlastEm\n"); -} - -void print_z80_help() -{ - printf("Z80 Debugger Commands\n"); - printf(" b ADDRESS - Set a breakpoint at ADDRESS\n"); - printf(" de BREAKPOINT - Delete a Z80 breakpoint\n"); - printf(" a ADDRESS - Advance to address\n"); - printf(" n - Advance to next instruction\n"); - printf(" c - Continue\n"); - printf(" p[/(x|X|d|c)] VALUE - Print a register or memory location\n"); - printf(" di[/(x|X|d|c)] VALUE - Print a register or memory location each time\n"); - printf(" a breakpoint is hit\n"); - printf(" q - Quit BlastEm\n"); -} - void debugger(m68k_context * context, uint32_t address) { static char last_cmd[1024]; @@ -1781,7 +2673,7 @@ genesis_context *gen = context->system; vdp_force_update_framebuffer(gen->vdp); } - debug_root *root = find_root(context); + debug_root *root = find_m68k_root(context); if (!root) { return; } @@ -1802,24 +2694,16 @@ } uint32_t after = m68k_decode(m68k_instruction_fetch, context, &inst, address); + root->address = address; + root->after = after; + root->inst = &inst; int debugging = 1; //Check if this is a user set breakpoint, or just a temporary one bp_def ** this_bp = find_breakpoint(&root->breakpoints, address); if (*this_bp) { - - if ((*this_bp)->commands) + for (uint32_t i = 0; debugging && i < (*this_bp)->num_commands; i++) { - char *commands = strdup((*this_bp)->commands); - char *copy = commands; - - while (debugging && *commands) - { - char *cmd = commands; - strip_nl(cmd); - commands += strlen(cmd) + 1; - debugging = run_debugger_command(context, address, cmd, inst, after); - } - free(copy); + debugging = run_command(root, (*this_bp)->commands + i); } if (debugging) { printf("68K Breakpoint %d hit\n", (*this_bp)->index); @@ -1830,7 +2714,7 @@ remove_breakpoint(context, address); } for (disp_def * cur = root->displays; cur; cur = cur->next) { - debugger_print(root, cur->format_char, cur->param); + cmd_print(root, cur->format, cur->num_args, cur->args); } m68k_disasm(&inst, input_buf); printf("%X: %s\n", address, input_buf); @@ -1870,7 +2754,11 @@ } else { strcpy(input_buf, last_cmd); } - debugging = run_debugger_command(context, address, input_buf, inst, after); + parsed_command cmd; + if (parse_command(root, input_buf, &cmd)) { + debugging = run_command(root, &cmd); + free_parsed_command(&cmd); + } } return; } diff -r 44596610b2a0 -r f6d5bde4d07f debug.h --- a/debug.h Sun Aug 07 01:16:47 2022 -0700 +++ b/debug.h Sat Aug 13 19:16:30 2022 -0700 @@ -2,6 +2,7 @@ #define DEBUG_H_ #include +#include "tern.h" #include "m68k_core.h" #ifdef NEW_CORE #include "z80.h" @@ -9,38 +10,117 @@ #include "z80_to_x86.h" #endif -typedef struct disp_def { - struct disp_def * next; - char * param; - uint32_t index; - char format_char; -} disp_def; +typedef enum { + TOKEN_NONE, + TOKEN_NUM, + TOKEN_NAME, + TOKEN_OPER, + TOKEN_SIZE, + TOKEN_LBRACKET, + TOKEN_RBRACKET, + TOKEN_LPAREN, + TOKEN_RPAREN +} token_type; + +typedef struct { + token_type type; + union { + char *str; + char op[3]; + uint32_t num; + } v; +} token; -typedef struct bp_def { - struct bp_def *next; - char *commands; - uint32_t address; - uint32_t index; -} bp_def; +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; +}; + +typedef struct { + char *raw; + expr *parsed; + uint32_t value; +} command_arg; typedef struct debug_root debug_root; +typedef uint8_t (*raw_cmd)(debug_root *root, char *format, char *param); +typedef uint8_t (*cmd)(debug_root *root, char *format, int num_args, command_arg *args); + +typedef struct { + const char **names; + const char *usage; + const char *desc; + raw_cmd raw_impl; + cmd impl; + int min_args; + int max_args; + uint8_t skip_eval; + uint8_t visited; +} command_def; + +typedef struct disp_def { + struct disp_def *next; + char *format; + int num_args; + command_arg *args; + uint32_t index; +} disp_def; + +typedef struct { + command_def *def; + char *format; + char *raw; + command_arg *args; + int num_args; +} parsed_command; + +typedef struct bp_def { + struct bp_def *next; + parsed_command *commands; + uint32_t num_commands; + uint32_t address; + uint32_t index; +} bp_def; + typedef uint8_t (*resolver)(debug_root *root, const char *name, uint32_t *out); +typedef uint8_t (*setter)(debug_root *root, const char *name, uint32_t value); typedef uint8_t (*reader)(debug_root *root, uint32_t *out, char size); +typedef uint8_t (*writer)(debug_root *root, uint32_t address, uint32_t value, 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; - uint32_t address; + void *cpu_context; + bp_def *breakpoints; + disp_def *displays; + tern_node *commands; + resolver resolve; + reader read_mem; + setter set; + writer write_mem; + uint32_t bp_index; + uint32_t disp_index; + uint32_t branch_t; + uint32_t branch_f; + void *inst; + uint32_t address; + uint32_t after; }; debug_root *find_root(void *cpu); +debug_root *find_m68k_root(m68k_context *context); +debug_root *find_z80_root(z80_context *context); bp_def ** find_breakpoint(bp_def ** cur, uint32_t address); bp_def ** find_breakpoint_idx(bp_def ** cur, uint32_t index); void add_display(disp_def ** head, uint32_t *index, char format_char, char * param); diff -r 44596610b2a0 -r f6d5bde4d07f z80_to_x86.h --- a/z80_to_x86.h Sun Aug 07 01:16:47 2022 -0700 +++ b/z80_to_x86.h Sat Aug 13 19:16:30 2022 -0700 @@ -112,6 +112,7 @@ void z80_adjust_cycles(z80_context * context, uint32_t deduction); void z80_serialize(z80_context *context, serialize_buffer *buf); void z80_deserialize(deserialize_buffer *buf, void *vcontext); +uint32_t z80_get_instruction_start(z80_context *context, uint32_t address); #endif //Z80_TO_X86_H_