# HG changeset patch # User Michael Pavone # Date 1698649676 25200 # Node ID 3350b3c8faa8d4547fd25c400fb214e944be4e7d # Parent 053ba4551c62591dbe2afb42f27b95d7e1d68833 Initial implementation of VDP register write breakpoints diff -r 053ba4551c62 -r 3350b3c8faa8 debug.c --- a/debug.c Sat Oct 28 16:04:58 2023 -0700 +++ b/debug.c Mon Oct 30 00:07:56 2023 -0700 @@ -45,10 +45,10 @@ return NULL; } -bp_def ** find_breakpoint(bp_def ** cur, uint32_t address) +bp_def ** find_breakpoint(bp_def ** cur, uint32_t address, uint8_t type) { while (*cur) { - if ((*cur)->address == address) { + if ((*cur)->type == type && (*cur)->address == (((*cur)->mask) & address)) { break; } cur = &((*cur)->next); @@ -2192,7 +2192,9 @@ return 1; } bp_def *tmp = *this_bp; - remove_breakpoint(root->cpu_context, tmp->address); + if (tmp->type == BP_TYPE_CPU) { + 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++) @@ -2211,12 +2213,72 @@ bp_def *new_bp = calloc(1, sizeof(bp_def)); new_bp->next = root->breakpoints; new_bp->address = cmd->args[0].value; + new_bp->mask = 0xFFFFFF; new_bp->index = root->bp_index++; + new_bp->type = BP_TYPE_CPU; root->breakpoints = new_bp; printf("68K Breakpoint %d set at %X\n", new_bp->index, cmd->args[0].value); return 1; } +static void on_vdp_reg_write(vdp_context *context, uint16_t reg, uint16_t value) +{ + value &= 0xFF; + if (context->regs[reg] == value) { + return; + } + genesis_context *gen = (genesis_context *)context->system; + debug_root *root = find_m68k_root(gen->m68k); + bp_def **this_bp = find_breakpoint(&root->breakpoints, reg, BP_TYPE_VDPREG); + int debugging = 1; + if (*this_bp) { + if ((*this_bp)->condition) { + uint32_t condres; + if (eval_expr(root, (*this_bp)->condition, &condres)) { + if (!condres) { + return; + } + } else { + fprintf(stderr, "Failed to eval condition for VDP Register Breakpoint %u\n", (*this_bp)->index); + free_expr((*this_bp)->condition); + (*this_bp)->condition = NULL; + } + } + for (uint32_t i = 0; debugging && i < (*this_bp)->num_commands; i++) + { + debugging = run_command(root, (*this_bp)->commands + i); + } + if (debugging) { + printf("VDP Register Breakpoint %d hit on register write %X - Old: %X, New: %X\n", (*this_bp)->index, reg, context->regs[reg], value); + gen->header.enter_debugger = 1; + if (gen->m68k->sync_cycle > gen->m68k->current_cycle + 1) { + gen->m68k->sync_cycle = gen->m68k->current_cycle + 1; + } + if (gen->m68k->target_cycle > gen->m68k->sync_cycle) { + gen->m68k->target_cycle = gen->m68k->sync_cycle; + } + } + } +} + +static uint8_t cmd_vdp_reg_break(debug_root *root, parsed_command *cmd) +{ + bp_def *new_bp = calloc(1, sizeof(bp_def)); + new_bp->next = root->breakpoints; + if (cmd->num_args) { + new_bp->address = cmd->args[0].value; + new_bp->mask = cmd->num_args > 1 ? cmd->args[1].value : 0xFF; + } + new_bp->index = root->bp_index++; + new_bp->type = BP_TYPE_VDPREG; + root->breakpoints = new_bp; + m68k_context *m68k = root->cpu_context; + genesis_context *gen = m68k->system; + gen->vdp->reg_hook = on_vdp_reg_write; + printf("VDP Register Breakpoint %d set\n", new_bp->index); + return 1; +} + static uint8_t cmd_advance_m68k(debug_root *root, parsed_command *cmd) { insert_breakpoint(root->cpu_context, cmd->args[0].value, debugger); @@ -2817,7 +2879,7 @@ }, { .names = (const char *[]){ - "vdpsregs", "vr", NULL + "vdpregs", "vr", NULL }, .usage = "vdpregs", .desc = "Print VDP register values with a short description", @@ -2825,6 +2887,16 @@ .min_args = 0, .max_args = 0 }, + { + .names = (const char *[]){ + "vdpregbreak", "vregbreak", "vrb", NULL + }, + .usage = "vdpregbreak [REGISTER [MASK]]", + .desc = "Enter debugger on VDP register write. If REGISTER is provided, breakpoint will only fire for writes to that register. If MASK is also provided, it will be applied to the register number before comparison with REGISTER", + .impl = cmd_vdp_reg_break, + .min_args = 0, + .max_args = 2 + }, #ifndef NO_Z80 { .names = (const char *[]){ @@ -2923,6 +2995,8 @@ bp_def *new_bp = calloc(1, sizeof(bp_def)); new_bp->next = root->breakpoints; new_bp->address = cmd->args[0].value; + new_bp->mask = 0xFFFF; + new_bp->type = BP_TYPE_CPU; new_bp->index = root->bp_index++; root->breakpoints = new_bp; printf("Z80 Breakpoint %d set at %X\n", new_bp->index, cmd->args[0].value); @@ -3829,7 +3903,7 @@ } root->address = address; //Check if this is a user set breakpoint, or just a temporary one - bp_def ** this_bp = find_breakpoint(&root->breakpoints, address); + bp_def ** this_bp = find_breakpoint(&root->breakpoints, address, BP_TYPE_CPU); if (*this_bp) { if ((*this_bp)->condition) { uint32_t condres; @@ -3902,13 +3976,13 @@ //probably not necessary, but let's play it safe address &= 0xFFFFFF; if (address == root->branch_t) { - bp_def ** f_bp = find_breakpoint(&root->breakpoints, root->branch_f); + bp_def ** f_bp = find_breakpoint(&root->breakpoints, root->branch_f, BP_TYPE_CPU); if (!*f_bp) { remove_breakpoint(context, root->branch_f); } root->branch_t = root->branch_f = 0; } else if(address == root->branch_f) { - bp_def ** t_bp = find_breakpoint(&root->breakpoints, root->branch_t); + bp_def ** t_bp = find_breakpoint(&root->breakpoints, root->branch_t, BP_TYPE_CPU); if (!*t_bp) { remove_breakpoint(context, root->branch_t); } @@ -3918,7 +3992,7 @@ root->address = address; 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); + bp_def ** this_bp = find_breakpoint(&root->breakpoints, address, BP_TYPE_CPU); if (*this_bp) { if ((*this_bp)->condition) { uint32_t condres; diff -r 053ba4551c62 -r 3350b3c8faa8 debug.h --- a/debug.h Sat Oct 28 16:04:58 2023 -0700 +++ b/debug.h Mon Oct 30 00:07:56 2023 -0700 @@ -98,6 +98,13 @@ command_block else_block; }; +enum { + BP_TYPE_CPU, + BP_TYPE_VDPREG, + BP_TYPE_VDPDMA, + BP_TYPE_VDPDATA +}; + typedef struct bp_def { struct bp_def *next; parsed_command *commands; @@ -105,6 +112,8 @@ uint32_t num_commands; uint32_t address; uint32_t index; + uint32_t mask; + uint8_t type; } bp_def; typedef struct debug_array debug_array; @@ -154,7 +163,7 @@ 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(bp_def ** cur, uint32_t address, uint8_t type); 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); void remove_display(disp_def ** head, uint32_t index); diff -r 053ba4551c62 -r 3350b3c8faa8 gdb_remote.c --- a/gdb_remote.c Sat Oct 28 16:04:58 2023 -0700 +++ b/gdb_remote.c Mon Oct 30 00:07:56 2023 -0700 @@ -227,6 +227,8 @@ bp_def *new_bp = malloc(sizeof(bp_def)); new_bp->next = root->breakpoints; new_bp->address = address; + new_bp->mask = 0xFFFFFF; + new_bp->type = BP_TYPE_CPU; new_bp->index = root->bp_index++; root->breakpoints = new_bp; gdb_send_command("OK"); @@ -241,7 +243,7 @@ if (type < '2') { uint32_t address = strtoul(command+3, NULL, 16); remove_breakpoint(context, address); - bp_def **found = find_breakpoint(&root->breakpoints, address); + bp_def **found = find_breakpoint(&root->breakpoints, address, BP_TYPE_CPU); if (*found) { bp_def * to_remove = *found; @@ -465,20 +467,20 @@ fatal_error("Could not find debug root for CPU %p\n", context); } if ((pc & 0xFFFFFF) == root->branch_t) { - bp_def ** f_bp = find_breakpoint(&root->breakpoints, root->branch_f); + bp_def ** f_bp = find_breakpoint(&root->breakpoints, root->branch_f, BP_TYPE_CPU); if (!*f_bp) { remove_breakpoint(context, root->branch_f); } root->branch_t = root->branch_f = 0; } else if((pc & 0xFFFFFF) == root->branch_f) { - bp_def ** t_bp = find_breakpoint(&root->breakpoints, root->branch_t); + bp_def ** t_bp = find_breakpoint(&root->breakpoints, root->branch_t, BP_TYPE_CPU); if (!*t_bp) { remove_breakpoint(context, root->branch_t); } root->branch_t = root->branch_f = 0; } //Check if this is a user set breakpoint, or just a temporary one - bp_def ** this_bp = find_breakpoint(&root->breakpoints, pc & 0xFFFFFF); + bp_def ** this_bp = find_breakpoint(&root->breakpoints, pc & 0xFFFFFF, BP_TYPE_CPU); if (!*this_bp) { remove_breakpoint(context, pc & 0xFFFFFF); } diff -r 053ba4551c62 -r 3350b3c8faa8 vdp.c --- a/vdp.c Sat Oct 28 16:04:58 2023 -0700 +++ b/vdp.c Mon Oct 30 00:07:56 2023 -0700 @@ -1126,6 +1126,9 @@ if (context->fifo_read == context->fifo_write) { if ((context->cd & 0x20) && (context->regs[REG_DMASRC_H] & DMA_TYPE_MASK) == DMA_FILL) { context->flags |= FLAG_DMA_RUN; + if (context->dma_hook) { + context->dma_hook(context); + } } context->fifo_read = -1; } @@ -4705,11 +4708,17 @@ //only captures are from a direct color DMA demo which will generally start DMA at a very specific point in display so other values are plausible //sticking with 3 slots for now until I can do some more captures vdp_run_context_full(context, context->cycles + 12 * ((context->regs[REG_MODE_2] & BIT_MODE_5) && (context->regs[REG_MODE_4] & BIT_H40) ? 4 : 5)); + vdp_dma_started(); context->flags |= FLAG_DMA_RUN; - vdp_dma_started(); + if (context->dma_hook) { + context->dma_hook(context); + } return 1; } else { context->flags |= FLAG_DMA_RUN; + if (context->dma_hook) { + context->dma_hook(context); + } //printf("DMA Copy Address: %X, New CD: %X, Source: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); } } else { @@ -4723,6 +4732,9 @@ if ((value & 0xC000) == 0x8000) { //Register write uint16_t reg = (value >> 8) & 0x1F; + if (context->reg_hook) { + context->reg_hook(context, reg, value); + } vdp_reg_write(context, reg, value); } else if (mode_5) { context->flags |= FLAG_PENDING; @@ -4774,6 +4786,9 @@ while (context->fifo_write == context->fifo_read) { vdp_run_context_full(context, context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)); } + if (context->data_hook) { + context->data_hook(context, value); + } fifo_entry * cur = context->fifo + context->fifo_write; cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY; cur->address = context->address; diff -r 053ba4551c62 -r 3350b3c8faa8 vdp.h --- a/vdp.h Sat Oct 28 16:04:58 2023 -0700 +++ b/vdp.h Mon Oct 30 00:07:56 2023 -0700 @@ -176,12 +176,9 @@ }; typedef struct vdp_context vdp_context; -typedef void (*vdp_hook_fun)(vdp_context *); - -typedef struct { - vdp_hook_fun handler; - void *data; -} vdp_hook; +typedef void (*vdp_hook)(vdp_context *); +typedef void (*vdp_reg_hook)(vdp_context *, uint16_t reg, uint16_t value); +typedef void (*vdp_data_hook)(vdp_context *, uint16_t value); struct vdp_context { system_header *system; @@ -193,8 +190,8 @@ uint32_t *debug_fbs[NUM_DEBUG_TYPES]; char *kmod_msg_buffer; vdp_hook dma_hook; - vdp_hook vdpreg_hook; - vdp_hook data_hook; + vdp_reg_hook reg_hook; + vdp_data_hook data_hook; uint32_t kmod_buffer_storage; uint32_t kmod_buffer_length; uint32_t timer_start_cycle;