Mercurial > repos > blastem
comparison debug.c @ 2361:3350b3c8faa8
Initial implementation of VDP register write breakpoints
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Mon, 30 Oct 2023 00:07:56 -0700 |
parents | 053ba4551c62 |
children | b6c5a0fa3dfc |
comparison
equal
deleted
inserted
replaced
2360:053ba4551c62 | 2361:3350b3c8faa8 |
---|---|
43 return roots + num_roots - 1; | 43 return roots + num_roots - 1; |
44 } | 44 } |
45 return NULL; | 45 return NULL; |
46 } | 46 } |
47 | 47 |
48 bp_def ** find_breakpoint(bp_def ** cur, uint32_t address) | 48 bp_def ** find_breakpoint(bp_def ** cur, uint32_t address, uint8_t type) |
49 { | 49 { |
50 while (*cur) { | 50 while (*cur) { |
51 if ((*cur)->address == address) { | 51 if ((*cur)->type == type && (*cur)->address == (((*cur)->mask) & address)) { |
52 break; | 52 break; |
53 } | 53 } |
54 cur = &((*cur)->next); | 54 cur = &((*cur)->next); |
55 } | 55 } |
56 return cur; | 56 return cur; |
2190 if (!*this_bp) { | 2190 if (!*this_bp) { |
2191 fprintf(stderr, "Breakpoint %d does not exist\n", cmd->args[0].value); | 2191 fprintf(stderr, "Breakpoint %d does not exist\n", cmd->args[0].value); |
2192 return 1; | 2192 return 1; |
2193 } | 2193 } |
2194 bp_def *tmp = *this_bp; | 2194 bp_def *tmp = *this_bp; |
2195 remove_breakpoint(root->cpu_context, tmp->address); | 2195 if (tmp->type == BP_TYPE_CPU) { |
2196 remove_breakpoint(root->cpu_context, tmp->address); | |
2197 } | |
2196 *this_bp = (*this_bp)->next; | 2198 *this_bp = (*this_bp)->next; |
2197 if (tmp->commands) { | 2199 if (tmp->commands) { |
2198 for (uint32_t i = 0; i < tmp->num_commands; i++) | 2200 for (uint32_t i = 0; i < tmp->num_commands; i++) |
2199 { | 2201 { |
2200 free_parsed_command(tmp->commands + i); | 2202 free_parsed_command(tmp->commands + i); |
2209 { | 2211 { |
2210 insert_breakpoint(root->cpu_context, cmd->args[0].value, debugger); | 2212 insert_breakpoint(root->cpu_context, cmd->args[0].value, debugger); |
2211 bp_def *new_bp = calloc(1, sizeof(bp_def)); | 2213 bp_def *new_bp = calloc(1, sizeof(bp_def)); |
2212 new_bp->next = root->breakpoints; | 2214 new_bp->next = root->breakpoints; |
2213 new_bp->address = cmd->args[0].value; | 2215 new_bp->address = cmd->args[0].value; |
2216 new_bp->mask = 0xFFFFFF; | |
2214 new_bp->index = root->bp_index++; | 2217 new_bp->index = root->bp_index++; |
2218 new_bp->type = BP_TYPE_CPU; | |
2215 root->breakpoints = new_bp; | 2219 root->breakpoints = new_bp; |
2216 printf("68K Breakpoint %d set at %X\n", new_bp->index, cmd->args[0].value); | 2220 printf("68K Breakpoint %d set at %X\n", new_bp->index, cmd->args[0].value); |
2221 return 1; | |
2222 } | |
2223 | |
2224 static void on_vdp_reg_write(vdp_context *context, uint16_t reg, uint16_t value) | |
2225 { | |
2226 value &= 0xFF; | |
2227 if (context->regs[reg] == value) { | |
2228 return; | |
2229 } | |
2230 genesis_context *gen = (genesis_context *)context->system; | |
2231 debug_root *root = find_m68k_root(gen->m68k); | |
2232 bp_def **this_bp = find_breakpoint(&root->breakpoints, reg, BP_TYPE_VDPREG); | |
2233 int debugging = 1; | |
2234 if (*this_bp) { | |
2235 if ((*this_bp)->condition) { | |
2236 uint32_t condres; | |
2237 if (eval_expr(root, (*this_bp)->condition, &condres)) { | |
2238 if (!condres) { | |
2239 return; | |
2240 } | |
2241 } else { | |
2242 fprintf(stderr, "Failed to eval condition for VDP Register Breakpoint %u\n", (*this_bp)->index); | |
2243 free_expr((*this_bp)->condition); | |
2244 (*this_bp)->condition = NULL; | |
2245 } | |
2246 } | |
2247 for (uint32_t i = 0; debugging && i < (*this_bp)->num_commands; i++) | |
2248 { | |
2249 debugging = run_command(root, (*this_bp)->commands + i); | |
2250 } | |
2251 if (debugging) { | |
2252 printf("VDP Register Breakpoint %d hit on register write %X - Old: %X, New: %X\n", (*this_bp)->index, reg, context->regs[reg], value); | |
2253 gen->header.enter_debugger = 1; | |
2254 if (gen->m68k->sync_cycle > gen->m68k->current_cycle + 1) { | |
2255 gen->m68k->sync_cycle = gen->m68k->current_cycle + 1; | |
2256 } | |
2257 if (gen->m68k->target_cycle > gen->m68k->sync_cycle) { | |
2258 gen->m68k->target_cycle = gen->m68k->sync_cycle; | |
2259 } | |
2260 } | |
2261 } | |
2262 } | |
2263 | |
2264 static uint8_t cmd_vdp_reg_break(debug_root *root, parsed_command *cmd) | |
2265 { | |
2266 bp_def *new_bp = calloc(1, sizeof(bp_def)); | |
2267 new_bp->next = root->breakpoints; | |
2268 if (cmd->num_args) { | |
2269 new_bp->address = cmd->args[0].value; | |
2270 new_bp->mask = cmd->num_args > 1 ? cmd->args[1].value : 0xFF; | |
2271 } | |
2272 new_bp->index = root->bp_index++; | |
2273 new_bp->type = BP_TYPE_VDPREG; | |
2274 root->breakpoints = new_bp; | |
2275 m68k_context *m68k = root->cpu_context; | |
2276 genesis_context *gen = m68k->system; | |
2277 gen->vdp->reg_hook = on_vdp_reg_write; | |
2278 printf("VDP Register Breakpoint %d set\n", new_bp->index); | |
2217 return 1; | 2279 return 1; |
2218 } | 2280 } |
2219 | 2281 |
2220 static uint8_t cmd_advance_m68k(debug_root *root, parsed_command *cmd) | 2282 static uint8_t cmd_advance_m68k(debug_root *root, parsed_command *cmd) |
2221 { | 2283 { |
2815 .min_args = 0, | 2877 .min_args = 0, |
2816 .max_args = 0 | 2878 .max_args = 0 |
2817 }, | 2879 }, |
2818 { | 2880 { |
2819 .names = (const char *[]){ | 2881 .names = (const char *[]){ |
2820 "vdpsregs", "vr", NULL | 2882 "vdpregs", "vr", NULL |
2821 }, | 2883 }, |
2822 .usage = "vdpregs", | 2884 .usage = "vdpregs", |
2823 .desc = "Print VDP register values with a short description", | 2885 .desc = "Print VDP register values with a short description", |
2824 .impl = cmd_vdp_regs, | 2886 .impl = cmd_vdp_regs, |
2825 .min_args = 0, | 2887 .min_args = 0, |
2826 .max_args = 0 | 2888 .max_args = 0 |
2889 }, | |
2890 { | |
2891 .names = (const char *[]){ | |
2892 "vdpregbreak", "vregbreak", "vrb", NULL | |
2893 }, | |
2894 .usage = "vdpregbreak [REGISTER [MASK]]", | |
2895 .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", | |
2896 .impl = cmd_vdp_reg_break, | |
2897 .min_args = 0, | |
2898 .max_args = 2 | |
2827 }, | 2899 }, |
2828 #ifndef NO_Z80 | 2900 #ifndef NO_Z80 |
2829 { | 2901 { |
2830 .names = (const char *[]){ | 2902 .names = (const char *[]){ |
2831 "z80", NULL | 2903 "z80", NULL |
2921 { | 2993 { |
2922 zinsert_breakpoint(root->cpu_context, cmd->args[0].value, (uint8_t *)zdebugger); | 2994 zinsert_breakpoint(root->cpu_context, cmd->args[0].value, (uint8_t *)zdebugger); |
2923 bp_def *new_bp = calloc(1, sizeof(bp_def)); | 2995 bp_def *new_bp = calloc(1, sizeof(bp_def)); |
2924 new_bp->next = root->breakpoints; | 2996 new_bp->next = root->breakpoints; |
2925 new_bp->address = cmd->args[0].value; | 2997 new_bp->address = cmd->args[0].value; |
2998 new_bp->mask = 0xFFFF; | |
2999 new_bp->type = BP_TYPE_CPU; | |
2926 new_bp->index = root->bp_index++; | 3000 new_bp->index = root->bp_index++; |
2927 root->breakpoints = new_bp; | 3001 root->breakpoints = new_bp; |
2928 printf("Z80 Breakpoint %d set at %X\n", new_bp->index, cmd->args[0].value); | 3002 printf("Z80 Breakpoint %d set at %X\n", new_bp->index, cmd->args[0].value); |
2929 return 1; | 3003 return 1; |
2930 } | 3004 } |
3827 if (!root) { | 3901 if (!root) { |
3828 return context; | 3902 return context; |
3829 } | 3903 } |
3830 root->address = address; | 3904 root->address = address; |
3831 //Check if this is a user set breakpoint, or just a temporary one | 3905 //Check if this is a user set breakpoint, or just a temporary one |
3832 bp_def ** this_bp = find_breakpoint(&root->breakpoints, address); | 3906 bp_def ** this_bp = find_breakpoint(&root->breakpoints, address, BP_TYPE_CPU); |
3833 if (*this_bp) { | 3907 if (*this_bp) { |
3834 if ((*this_bp)->condition) { | 3908 if ((*this_bp)->condition) { |
3835 uint32_t condres; | 3909 uint32_t condres; |
3836 if (eval_expr(root, (*this_bp)->condition, &condres)) { | 3910 if (eval_expr(root, (*this_bp)->condition, &condres)) { |
3837 if (!condres) { | 3911 if (!condres) { |
3900 return; | 3974 return; |
3901 } | 3975 } |
3902 //probably not necessary, but let's play it safe | 3976 //probably not necessary, but let's play it safe |
3903 address &= 0xFFFFFF; | 3977 address &= 0xFFFFFF; |
3904 if (address == root->branch_t) { | 3978 if (address == root->branch_t) { |
3905 bp_def ** f_bp = find_breakpoint(&root->breakpoints, root->branch_f); | 3979 bp_def ** f_bp = find_breakpoint(&root->breakpoints, root->branch_f, BP_TYPE_CPU); |
3906 if (!*f_bp) { | 3980 if (!*f_bp) { |
3907 remove_breakpoint(context, root->branch_f); | 3981 remove_breakpoint(context, root->branch_f); |
3908 } | 3982 } |
3909 root->branch_t = root->branch_f = 0; | 3983 root->branch_t = root->branch_f = 0; |
3910 } else if(address == root->branch_f) { | 3984 } else if(address == root->branch_f) { |
3911 bp_def ** t_bp = find_breakpoint(&root->breakpoints, root->branch_t); | 3985 bp_def ** t_bp = find_breakpoint(&root->breakpoints, root->branch_t, BP_TYPE_CPU); |
3912 if (!*t_bp) { | 3986 if (!*t_bp) { |
3913 remove_breakpoint(context, root->branch_t); | 3987 remove_breakpoint(context, root->branch_t); |
3914 } | 3988 } |
3915 root->branch_t = root->branch_f = 0; | 3989 root->branch_t = root->branch_f = 0; |
3916 } | 3990 } |
3917 | 3991 |
3918 root->address = address; | 3992 root->address = address; |
3919 int debugging = 1; | 3993 int debugging = 1; |
3920 //Check if this is a user set breakpoint, or just a temporary one | 3994 //Check if this is a user set breakpoint, or just a temporary one |
3921 bp_def ** this_bp = find_breakpoint(&root->breakpoints, address); | 3995 bp_def ** this_bp = find_breakpoint(&root->breakpoints, address, BP_TYPE_CPU); |
3922 if (*this_bp) { | 3996 if (*this_bp) { |
3923 if ((*this_bp)->condition) { | 3997 if ((*this_bp)->condition) { |
3924 uint32_t condres; | 3998 uint32_t condres; |
3925 if (eval_expr(root, (*this_bp)->condition, &condres)) { | 3999 if (eval_expr(root, (*this_bp)->condition, &condres)) { |
3926 if (!condres) { | 4000 if (!condres) { |