Mercurial > repos > blastem
comparison debug.c @ 2045:b119e0de9a70 proprietary
Strip out mega wifi support and debugger
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Tue, 21 Sep 2021 23:17:34 -0700 |
parents | a7b753e260a2 |
children |
comparison
equal
deleted
inserted
replaced
2043:804954731e3f | 2045:b119e0de9a70 |
---|---|
111 return m68k_read_word(address, context) << 16 | m68k_read_word(address + 2, context); | 111 return m68k_read_word(address, context) << 16 | m68k_read_word(address + 2, context); |
112 } | 112 } |
113 | 113 |
114 void debugger_print(m68k_context *context, char format_char, char *param, uint32_t address) | 114 void debugger_print(m68k_context *context, char format_char, char *param, uint32_t address) |
115 { | 115 { |
116 uint32_t value; | |
117 char format[8]; | |
118 strcpy(format, "%s: %d\n"); | |
119 switch (format_char) | |
120 { | |
121 case 'x': | |
122 case 'X': | |
123 case 'd': | |
124 case 'c': | |
125 format[5] = format_char; | |
126 break; | |
127 case '\0': | |
128 break; | |
129 default: | |
130 fprintf(stderr, "Unrecognized format character: %c\n", format_char); | |
131 } | |
132 if (param[0] == 'd' && param[1] >= '0' && param[1] <= '7') { | |
133 value = context->dregs[param[1]-'0']; | |
134 if (param[2] == '.') { | |
135 if (param[3] == 'w') { | |
136 value &= 0xFFFF; | |
137 } else if (param[3] == 'b') { | |
138 value &= 0xFF; | |
139 } | |
140 } | |
141 } else if (param[0] == 'a' && param[1] >= '0' && param[1] <= '7') { | |
142 value = context->aregs[param[1]-'0']; | |
143 if (param[2] == '.') { | |
144 if (param[3] == 'w') { | |
145 value &= 0xFFFF; | |
146 } else if (param[3] == 'b') { | |
147 value &= 0xFF; | |
148 } | |
149 } | |
150 } else if (param[0] == 's' && param[1] == 'r') { | |
151 value = (context->status << 8); | |
152 for (int flag = 0; flag < 5; flag++) { | |
153 value |= context->flags[flag] << (4-flag); | |
154 } | |
155 } else if(param[0] == 'c') { | |
156 value = context->current_cycle; | |
157 } else if(param[0] == 'f') { | |
158 genesis_context *gen = context->system; | |
159 value = gen->vdp->frame; | |
160 } else if (param[0] == 'p' && param[1] == 'c') { | |
161 value = address; | |
162 } else if ((param[0] == '0' && param[1] == 'x') || param[0] == '$') { | |
163 char *after; | |
164 uint32_t p_addr = strtol(param+(param[0] == '0' ? 2 : 1), &after, 16); | |
165 if (after[0] == '.' && after[1] == 'l') { | |
166 value = m68k_read_long(p_addr, context); | |
167 } else if (after[0] == '.' && after[1] == 'b') { | |
168 value = m68k_read_byte(p_addr, context); | |
169 } else { | |
170 value = m68k_read_word(p_addr, context); | |
171 } | |
172 } else if(param[0] == '(' && (param[1] == 'a' || param[1] == 'd') && param[2] >= '0' && param[2] <= '7' && param[3] == ')') { | |
173 uint8_t reg = param[2] - '0'; | |
174 uint32_t p_addr = param[1] == 'a' ? context->aregs[reg] : context->dregs[reg]; | |
175 if (param[4] == '.' && param[5] == 'l') { | |
176 value = m68k_read_long(p_addr, context); | |
177 } else if (param[4] == '.' && param[5] == 'b') { | |
178 value = m68k_read_byte(p_addr, context); | |
179 } else { | |
180 value = m68k_read_word(p_addr, context); | |
181 } | |
182 } else { | |
183 fprintf(stderr, "Unrecognized parameter to p: %s\n", param); | |
184 return; | |
185 } | |
186 printf(format, param, value); | |
187 } | 116 } |
188 | 117 |
189 #ifndef NO_Z80 | 118 #ifndef NO_Z80 |
190 | 119 |
191 void zdebugger_print(z80_context * context, char format_char, char * param) | 120 void zdebugger_print(z80_context * context, char format_char, char * param) |
192 { | 121 { |
193 uint32_t value; | |
194 char format[8]; | |
195 strcpy(format, "%s: %d\n"); | |
196 genesis_context *system = context->system; | |
197 switch (format_char) | |
198 { | |
199 case 'x': | |
200 case 'X': | |
201 case 'd': | |
202 case 'c': | |
203 format[5] = format_char; | |
204 break; | |
205 case '\0': | |
206 break; | |
207 default: | |
208 fprintf(stderr, "Unrecognized format character: %c\n", format_char); | |
209 } | |
210 switch (param[0]) | |
211 { | |
212 #ifndef NEW_CORE | |
213 case 'a': | |
214 if (param[1] == 'f') { | |
215 if(param[2] == '\'') { | |
216 value = context->alt_regs[Z80_A] << 8; | |
217 value |= context->alt_flags[ZF_S] << 7; | |
218 value |= context->alt_flags[ZF_Z] << 6; | |
219 value |= context->alt_flags[ZF_H] << 4; | |
220 value |= context->alt_flags[ZF_PV] << 2; | |
221 value |= context->alt_flags[ZF_N] << 1; | |
222 value |= context->alt_flags[ZF_C]; | |
223 } else { | |
224 value = context->regs[Z80_A] << 8; | |
225 value |= context->flags[ZF_S] << 7; | |
226 value |= context->flags[ZF_Z] << 6; | |
227 value |= context->flags[ZF_H] << 4; | |
228 value |= context->flags[ZF_PV] << 2; | |
229 value |= context->flags[ZF_N] << 1; | |
230 value |= context->flags[ZF_C]; | |
231 } | |
232 } else if(param[1] == '\'') { | |
233 value = context->alt_regs[Z80_A]; | |
234 } else { | |
235 value = context->regs[Z80_A]; | |
236 } | |
237 break; | |
238 case 'b': | |
239 if (param[1] == 'c') { | |
240 if(param[2] == '\'') { | |
241 value = context->alt_regs[Z80_B] << 8; | |
242 value |= context->alt_regs[Z80_C]; | |
243 } else { | |
244 value = context->regs[Z80_B] << 8; | |
245 value |= context->regs[Z80_C]; | |
246 } | |
247 } else if(param[1] == '\'') { | |
248 value = context->alt_regs[Z80_B]; | |
249 } else if(param[1] == 'a') { | |
250 value = context->bank_reg << 15; | |
251 } else { | |
252 value = context->regs[Z80_B]; | |
253 } | |
254 break; | |
255 case 'c': | |
256 if(param[1] == '\'') { | |
257 value = context->alt_regs[Z80_C]; | |
258 } else if(param[1] == 'y') { | |
259 value = context->current_cycle; | |
260 } else { | |
261 value = context->regs[Z80_C]; | |
262 } | |
263 break; | |
264 case 'd': | |
265 if (param[1] == 'e') { | |
266 if(param[2] == '\'') { | |
267 value = context->alt_regs[Z80_D] << 8; | |
268 value |= context->alt_regs[Z80_E]; | |
269 } else { | |
270 value = context->regs[Z80_D] << 8; | |
271 value |= context->regs[Z80_E]; | |
272 } | |
273 } else if(param[1] == '\'') { | |
274 value = context->alt_regs[Z80_D]; | |
275 } else { | |
276 value = context->regs[Z80_D]; | |
277 } | |
278 break; | |
279 case 'e': | |
280 if(param[1] == '\'') { | |
281 value = context->alt_regs[Z80_E]; | |
282 } else { | |
283 value = context->regs[Z80_E]; | |
284 } | |
285 break; | |
286 case 'f': | |
287 if(param[2] == '\'') { | |
288 value = context->alt_flags[ZF_S] << 7; | |
289 value |= context->alt_flags[ZF_Z] << 6; | |
290 value |= context->alt_flags[ZF_H] << 4; | |
291 value |= context->alt_flags[ZF_PV] << 2; | |
292 value |= context->alt_flags[ZF_N] << 1; | |
293 value |= context->alt_flags[ZF_C]; | |
294 } else { | |
295 value = context->flags[ZF_S] << 7; | |
296 value |= context->flags[ZF_Z] << 6; | |
297 value |= context->flags[ZF_H] << 4; | |
298 value |= context->flags[ZF_PV] << 2; | |
299 value |= context->flags[ZF_N] << 1; | |
300 value |= context->flags[ZF_C]; | |
301 } | |
302 break; | |
303 case 'h': | |
304 if (param[1] == 'l') { | |
305 if(param[2] == '\'') { | |
306 value = context->alt_regs[Z80_H] << 8; | |
307 value |= context->alt_regs[Z80_L]; | |
308 } else { | |
309 value = context->regs[Z80_H] << 8; | |
310 value |= context->regs[Z80_L]; | |
311 } | |
312 } else if(param[1] == '\'') { | |
313 value = context->alt_regs[Z80_H]; | |
314 } else { | |
315 value = context->regs[Z80_H]; | |
316 } | |
317 break; | |
318 case 'l': | |
319 if(param[1] == '\'') { | |
320 value = context->alt_regs[Z80_L]; | |
321 } else { | |
322 value = context->regs[Z80_L]; | |
323 } | |
324 break; | |
325 case 'i': | |
326 if(param[1] == 'x') { | |
327 if (param[2] == 'h') { | |
328 value = context->regs[Z80_IXH]; | |
329 } else if(param[2] == 'l') { | |
330 value = context->regs[Z80_IXL]; | |
331 } else { | |
332 value = context->regs[Z80_IXH] << 8; | |
333 value |= context->regs[Z80_IXL]; | |
334 } | |
335 } else if(param[1] == 'y') { | |
336 if (param[2] == 'h') { | |
337 value = context->regs[Z80_IYH]; | |
338 } else if(param[2] == 'l') { | |
339 value = context->regs[Z80_IYL]; | |
340 } else { | |
341 value = context->regs[Z80_IYH] << 8; | |
342 value |= context->regs[Z80_IYL]; | |
343 } | |
344 } else if(param[1] == 'n') { | |
345 value = context->int_cycle; | |
346 } else if(param[1] == 'f' && param[2] == 'f' && param[3] == '1') { | |
347 value = context->iff1; | |
348 } else if(param[1] == 'f' && param[2] == 'f' && param[3] == '2') { | |
349 value = context->iff2; | |
350 } else { | |
351 value = context->im; | |
352 } | |
353 break; | |
354 #endif | |
355 case 's': | |
356 if (param[1] == 'p') { | |
357 value = context->sp; | |
358 } | |
359 break; | |
360 case '0': | |
361 if (param[1] == 'x') { | |
362 uint16_t p_addr = strtol(param+2, NULL, 16); | |
363 value = read_byte(p_addr, (void **)context->mem_pointers, &context->options->gen, context); | |
364 } | |
365 break; | |
366 } | |
367 printf(format, param, value); | |
368 } | 122 } |
369 | 123 |
370 z80_context * zdebugger(z80_context * context, uint16_t address) | 124 z80_context * zdebugger(z80_context * context, uint16_t address) |
371 { | 125 { |
372 static char last_cmd[1024]; | |
373 char input_buf[1024]; | |
374 static uint16_t branch_t; | |
375 static uint16_t branch_f; | |
376 z80inst inst; | |
377 genesis_context *system = context->system; | |
378 init_terminal(); | |
379 //Check if this is a user set breakpoint, or just a temporary one | |
380 bp_def ** this_bp = find_breakpoint(&zbreakpoints, address); | |
381 if (*this_bp) { | |
382 printf("Z80 Breakpoint %d hit\n", (*this_bp)->index); | |
383 } else { | |
384 zremove_breakpoint(context, address); | |
385 } | |
386 uint8_t * pc = get_native_pointer(address, (void **)context->mem_pointers, &context->Z80_OPTS->gen); | |
387 if (!pc) { | |
388 fatal_error("Failed to get native pointer on entering Z80 debugger at address %X\n", address); | |
389 } | |
390 for (disp_def * cur = zdisplays; cur; cur = cur->next) { | |
391 zdebugger_print(context, cur->format_char, cur->param); | |
392 } | |
393 uint8_t * after_pc = z80_decode(pc, &inst); | |
394 z80_disasm(&inst, input_buf, address); | |
395 printf("%X:\t%s\n", address, input_buf); | |
396 uint16_t after = address + (after_pc-pc); | |
397 int debugging = 1; | |
398 while(debugging) { | |
399 fputs(">", stdout); | |
400 if (!fgets(input_buf, sizeof(input_buf), stdin)) { | |
401 fputs("fgets failed", stderr); | |
402 break; | |
403 } | |
404 strip_nl(input_buf); | |
405 //hitting enter repeats last command | |
406 if (input_buf[0]) { | |
407 strcpy(last_cmd, input_buf); | |
408 } else { | |
409 strcpy(input_buf, last_cmd); | |
410 } | |
411 char * param; | |
412 char format[8]; | |
413 uint32_t value; | |
414 bp_def * new_bp; | |
415 switch(input_buf[0]) | |
416 { | |
417 case 'a': | |
418 param = find_param(input_buf); | |
419 if (!param) { | |
420 fputs("a command requires a parameter\n", stderr); | |
421 break; | |
422 } | |
423 value = strtol(param, NULL, 16); | |
424 zinsert_breakpoint(context, value, (uint8_t *)zdebugger); | |
425 debugging = 0; | |
426 break; | |
427 case 'b': | |
428 param = find_param(input_buf); | |
429 if (!param) { | |
430 fputs("b command requires a parameter\n", stderr); | |
431 break; | |
432 } | |
433 value = strtol(param, NULL, 16); | |
434 zinsert_breakpoint(context, value, (uint8_t *)zdebugger); | |
435 new_bp = malloc(sizeof(bp_def)); | |
436 new_bp->next = zbreakpoints; | |
437 new_bp->address = value; | |
438 new_bp->index = zbp_index++; | |
439 new_bp->commands = NULL; | |
440 zbreakpoints = new_bp; | |
441 printf("Z80 Breakpoint %d set at %X\n", new_bp->index, value); | |
442 break; | |
443 case 'c': | |
444 puts("Continuing"); | |
445 debugging = 0; | |
446 break; | |
447 case 'd': | |
448 if (input_buf[1] == 'i') { | |
449 char format_char = 0; | |
450 for(int i = 2; input_buf[i] != 0 && input_buf[i] != ' '; i++) { | |
451 if (input_buf[i] == '/') { | |
452 format_char = input_buf[i+1]; | |
453 break; | |
454 } | |
455 } | |
456 param = find_param(input_buf); | |
457 if (!param) { | |
458 fputs("display command requires a parameter\n", stderr); | |
459 break; | |
460 } | |
461 zdebugger_print(context, format_char, param); | |
462 add_display(&zdisplays, &zdisp_index, format_char, param); | |
463 } else if (input_buf[1] == 'e' || input_buf[1] == ' ') { | |
464 param = find_param(input_buf); | |
465 if (!param) { | |
466 fputs("delete command requires a parameter\n", stderr); | |
467 break; | |
468 } | |
469 if (param[0] >= '0' && param[0] <= '9') { | |
470 value = atoi(param); | |
471 this_bp = find_breakpoint_idx(&zbreakpoints, value); | |
472 if (!*this_bp) { | |
473 fprintf(stderr, "Breakpoint %d does not exist\n", value); | |
474 break; | |
475 } | |
476 new_bp = *this_bp; | |
477 zremove_breakpoint(context, new_bp->address); | |
478 *this_bp = new_bp->next; | |
479 free(new_bp); | |
480 } else if (param[0] == 'd') { | |
481 param = find_param(param); | |
482 if (!param) { | |
483 fputs("delete display command requires a parameter\n", stderr); | |
484 break; | |
485 } | |
486 remove_display(&zdisplays, atoi(param)); | |
487 } | |
488 } | |
489 break; | |
490 case 'n': | |
491 //TODO: Handle conditional branch instructions | |
492 if (inst.op == Z80_JP) { | |
493 if (inst.addr_mode == Z80_IMMED) { | |
494 after = inst.immed; | |
495 } else if (inst.ea_reg == Z80_HL) { | |
496 #ifndef NEW_CORE | |
497 after = context->regs[Z80_H] << 8 | context->regs[Z80_L]; | |
498 } else if (inst.ea_reg == Z80_IX) { | |
499 after = context->regs[Z80_IXH] << 8 | context->regs[Z80_IXL]; | |
500 } else if (inst.ea_reg == Z80_IY) { | |
501 after = context->regs[Z80_IYH] << 8 | context->regs[Z80_IYL]; | |
502 #endif | |
503 } | |
504 } else if(inst.op == Z80_JR) { | |
505 after += inst.immed; | |
506 } else if(inst.op == Z80_RET) { | |
507 uint8_t *sp = get_native_pointer(context->sp, (void **)context->mem_pointers, &context->Z80_OPTS->gen); | |
508 if (sp) { | |
509 after = *sp; | |
510 sp = get_native_pointer((context->sp + 1) & 0xFFFF, (void **)context->mem_pointers, &context->Z80_OPTS->gen); | |
511 if (sp) { | |
512 after |= *sp << 8; | |
513 } | |
514 } | |
515 } | |
516 zinsert_breakpoint(context, after, (uint8_t *)zdebugger); | |
517 debugging = 0; | |
518 break; | |
519 case 'p': | |
520 param = find_param(input_buf); | |
521 if (!param) { | |
522 fputs("p command requires a parameter\n", stderr); | |
523 break; | |
524 } | |
525 zdebugger_print(context, input_buf[1] == '/' ? input_buf[2] : 0, param); | |
526 break; | |
527 case 'q': | |
528 puts("Quitting"); | |
529 exit(0); | |
530 break; | |
531 case 's': { | |
532 param = find_param(input_buf); | |
533 if (!param) { | |
534 fputs("s command requires a file name\n", stderr); | |
535 break; | |
536 } | |
537 memmap_chunk const *ram_chunk = NULL; | |
538 for (int i = 0; i < context->Z80_OPTS->gen.memmap_chunks; i++) | |
539 { | |
540 memmap_chunk const *cur = context->Z80_OPTS->gen.memmap + i; | |
541 if (cur->flags & MMAP_WRITE) { | |
542 ram_chunk = cur; | |
543 break; | |
544 } | |
545 } | |
546 if (ram_chunk) { | |
547 uint32_t size = ram_chunk->end - ram_chunk->start; | |
548 if (size > ram_chunk->mask) { | |
549 size = ram_chunk->mask+1; | |
550 } | |
551 uint8_t *buf = get_native_pointer(ram_chunk->start, (void **)context->mem_pointers, &context->Z80_OPTS->gen); | |
552 FILE * f = fopen(param, "wb"); | |
553 if (f) { | |
554 if(fwrite(buf, 1, size, f) != size) { | |
555 fputs("Error writing file\n", stderr); | |
556 } | |
557 fclose(f); | |
558 printf("Wrote %d bytes to %s\n", size, param); | |
559 } else { | |
560 fprintf(stderr, "Could not open %s for writing\n", param); | |
561 } | |
562 } else { | |
563 fputs("Failed to find a RAM memory chunk\n", stderr); | |
564 } | |
565 break; | |
566 } | |
567 case '?': | |
568 print_z80_help(); | |
569 break; | |
570 default: | |
571 if ( | |
572 !context->Z80_OPTS->gen.debug_cmd_handler | |
573 || !context->Z80_OPTS->gen.debug_cmd_handler(&system->header, input_buf) | |
574 ) { | |
575 fprintf(stderr, "Unrecognized debugger command %s\nUse '?' for help.\n", input_buf); | |
576 } | |
577 break; | |
578 } | |
579 } | |
580 return context; | 126 return context; |
581 } | 127 } |
582 | 128 |
583 #endif | 129 #endif |
584 | 130 |
585 static uint32_t branch_t; | |
586 static uint32_t branch_f; | |
587 | |
588 int run_debugger_command(m68k_context *context, uint32_t address, char *input_buf, m68kinst inst, uint32_t after) | |
589 { | |
590 char * param; | |
591 char format_char; | |
592 genesis_context *system = context->system; | |
593 uint32_t value; | |
594 bp_def *new_bp, **this_bp; | |
595 switch(input_buf[0]) | |
596 { | |
597 case 'c': | |
598 if (input_buf[1] == 0 || input_buf[1] == 'o' && input_buf[2] == 'n') | |
599 { | |
600 puts("Continuing"); | |
601 return 0; | |
602 } else if (input_buf[1] == 'o' && input_buf[2] == 'm') { | |
603 param = find_param(input_buf); | |
604 if (!param) { | |
605 fputs("com command requires a parameter\n", stderr); | |
606 break; | |
607 } | |
608 bp_def **target = find_breakpoint_idx(&breakpoints, atoi(param)); | |
609 if (!target) { | |
610 fprintf(stderr, "Breakpoint %s does not exist!\n", param); | |
611 break; | |
612 } | |
613 printf("Enter commands for breakpoing %d, type end when done\n", atoi(param)); | |
614 char cmd_buf[1024]; | |
615 char *commands = NULL; | |
616 for (;;) | |
617 { | |
618 fputs(">>", stdout); | |
619 fflush(stdout); | |
620 fgets(cmd_buf, sizeof(cmd_buf), stdin); | |
621 if (strcmp(cmd_buf, "end\n")) { | |
622 if (commands) { | |
623 char *tmp = commands; | |
624 commands = alloc_concat(commands, cmd_buf); | |
625 free(tmp); | |
626 } else { | |
627 commands = strdup(cmd_buf); | |
628 } | |
629 } else { | |
630 break; | |
631 } | |
632 } | |
633 (*target)->commands = commands; | |
634 } else { | |
635 } | |
636 break; | |
637 case 'b': | |
638 if (input_buf[1] == 't') { | |
639 uint32_t stack = context->aregs[7]; | |
640 if (stack >= 0xE00000) { | |
641 stack &= 0xFFFF; | |
642 uint8_t non_adr_count = 0; | |
643 do { | |
644 uint32_t bt_address = system->work_ram[stack/2] << 16 | system->work_ram[stack/2+1]; | |
645 bt_address = get_instruction_start(context->options, bt_address - 2); | |
646 if (bt_address) { | |
647 stack += 4; | |
648 non_adr_count = 0; | |
649 uint16_t *bt_pc = NULL; | |
650 if (bt_address < 0x400000) { | |
651 bt_pc = system->cart + bt_address/2; | |
652 } else if(bt_address > 0xE00000) { | |
653 bt_pc = system->work_ram + (bt_address & 0xFFFF)/2; | |
654 } | |
655 m68k_decode(bt_pc, &inst, bt_address); | |
656 m68k_disasm(&inst, input_buf); | |
657 printf("%X: %s\n", bt_address, input_buf); | |
658 } else { | |
659 //non-return address value on stack can be word wide | |
660 stack += 2; | |
661 non_adr_count++; | |
662 } | |
663 stack &= 0xFFFF; | |
664 } while (stack && non_adr_count < 6); | |
665 } | |
666 } else { | |
667 param = find_param(input_buf); | |
668 if (!param) { | |
669 fputs("b command requires a parameter\n", stderr); | |
670 break; | |
671 } | |
672 value = strtol(param, NULL, 16); | |
673 insert_breakpoint(context, value, debugger); | |
674 new_bp = malloc(sizeof(bp_def)); | |
675 new_bp->next = breakpoints; | |
676 new_bp->address = value; | |
677 new_bp->index = bp_index++; | |
678 new_bp->commands = NULL; | |
679 breakpoints = new_bp; | |
680 printf("68K Breakpoint %d set at %X\n", new_bp->index, value); | |
681 } | |
682 break; | |
683 case 'a': | |
684 param = find_param(input_buf); | |
685 if (!param) { | |
686 fputs("a command requires a parameter\n", stderr); | |
687 break; | |
688 } | |
689 value = strtol(param, NULL, 16); | |
690 insert_breakpoint(context, value, debugger); | |
691 return 0; | |
692 case 'd': | |
693 if (input_buf[1] == 'i') { | |
694 format_char = 0; | |
695 for(int i = 2; input_buf[i] != 0 && input_buf[i] != ' '; i++) { | |
696 if (input_buf[i] == '/') { | |
697 format_char = input_buf[i+1]; | |
698 break; | |
699 } | |
700 } | |
701 param = find_param(input_buf); | |
702 if (!param) { | |
703 fputs("display command requires a parameter\n", stderr); | |
704 break; | |
705 } | |
706 debugger_print(context, format_char, param, address); | |
707 add_display(&displays, &disp_index, format_char, param); | |
708 } else { | |
709 param = find_param(input_buf); | |
710 if (!param) { | |
711 fputs("d command requires a parameter\n", stderr); | |
712 break; | |
713 } | |
714 value = atoi(param); | |
715 this_bp = find_breakpoint_idx(&breakpoints, value); | |
716 if (!*this_bp) { | |
717 fprintf(stderr, "Breakpoint %d does not exist\n", value); | |
718 break; | |
719 } | |
720 new_bp = *this_bp; | |
721 *this_bp = (*this_bp)->next; | |
722 if (new_bp->commands) { | |
723 free(new_bp->commands); | |
724 } | |
725 free(new_bp); | |
726 } | |
727 break; | |
728 case 'p': | |
729 format_char = 0; | |
730 for(int i = 1; input_buf[i] != 0 && input_buf[i] != ' '; i++) { | |
731 if (input_buf[i] == '/') { | |
732 format_char = input_buf[i+1]; | |
733 break; | |
734 } | |
735 } | |
736 param = find_param(input_buf); | |
737 if (param) { | |
738 debugger_print(context, format_char, param, address); | |
739 } else { | |
740 m68k_disasm(&inst, input_buf); | |
741 printf("%X: %s\n", address, input_buf); | |
742 } | |
743 | |
744 break; | |
745 case 'n': | |
746 if (inst.op == M68K_RTS) { | |
747 after = m68k_read_long(context->aregs[7], context); | |
748 } else if (inst.op == M68K_RTE || inst.op == M68K_RTR) { | |
749 after = m68k_read_long(context->aregs[7] + 2, context); | |
750 } else if(m68k_is_noncall_branch(&inst)) { | |
751 if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) { | |
752 branch_f = after; | |
753 branch_t = m68k_branch_target(&inst, context->dregs, context->aregs); | |
754 insert_breakpoint(context, branch_t, debugger); | |
755 } else if(inst.op == M68K_DBCC) { | |
756 if ( inst.extra.cond == COND_FALSE) { | |
757 if (context->dregs[inst.dst.params.regs.pri] & 0xFFFF) { | |
758 after = m68k_branch_target(&inst, context->dregs, context->aregs); | |
759 } | |
760 } else { | |
761 branch_t = after; | |
762 branch_f = m68k_branch_target(&inst, context->dregs, context->aregs); | |
763 insert_breakpoint(context, branch_f, debugger); | |
764 } | |
765 } else { | |
766 after = m68k_branch_target(&inst, context->dregs, context->aregs); | |
767 } | |
768 } | |
769 insert_breakpoint(context, after, debugger); | |
770 return 0; | |
771 case 'o': | |
772 if (inst.op == M68K_RTS) { | |
773 after = m68k_read_long(context->aregs[7], context); | |
774 } else if (inst.op == M68K_RTE || inst.op == M68K_RTR) { | |
775 after = m68k_read_long(context->aregs[7] + 2, context); | |
776 } else if(m68k_is_noncall_branch(&inst)) { | |
777 if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) { | |
778 branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; | |
779 if (branch_t < after) { | |
780 branch_t = 0; | |
781 } else { | |
782 branch_f = after; | |
783 insert_breakpoint(context, branch_t, debugger); | |
784 } | |
785 } else if(inst.op == M68K_DBCC) { | |
786 uint32_t target = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; | |
787 if (target > after) { | |
788 if (inst.extra.cond == COND_FALSE) { | |
789 after = target; | |
790 } else { | |
791 branch_f = target; | |
792 branch_t = after; | |
793 insert_breakpoint(context, branch_f, debugger); | |
794 } | |
795 } | |
796 } else { | |
797 after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; | |
798 } | |
799 } | |
800 insert_breakpoint(context, after, debugger); | |
801 return 0; | |
802 case 's': | |
803 if (input_buf[1] == 'e') { | |
804 param = find_param(input_buf); | |
805 if (!param) { | |
806 fputs("Missing destination parameter for set\n", stderr); | |
807 return 1; | |
808 } | |
809 char *val = find_param(param); | |
810 if (!val) { | |
811 fputs("Missing value parameter for set\n", stderr); | |
812 return 1; | |
813 } | |
814 long int_val; | |
815 int reg_num; | |
816 switch (val[0]) | |
817 { | |
818 case 'd': | |
819 case 'a': | |
820 reg_num = val[1] - '0'; | |
821 if (reg_num < 0 || reg_num > 8) { | |
822 fprintf(stderr, "Invalid register %s\n", val); | |
823 return 1; | |
824 } | |
825 int_val = (val[0] == 'd' ? context->dregs : context->aregs)[reg_num]; | |
826 break; | |
827 case '$': | |
828 int_val = strtol(val+1, NULL, 16); | |
829 break; | |
830 case '0': | |
831 if (val[1] == 'x') { | |
832 int_val = strtol(val+2, NULL, 16); | |
833 break; | |
834 } | |
835 default: | |
836 int_val = strtol(val, NULL, 10); | |
837 } | |
838 switch(param[0]) | |
839 { | |
840 case 'd': | |
841 case 'a': | |
842 reg_num = param[1] - '0'; | |
843 if (reg_num < 0 || reg_num > 8) { | |
844 fprintf(stderr, "Invalid register %s\n", param); | |
845 return 1; | |
846 } | |
847 (param[0] == 'd' ? context->dregs : context->aregs)[reg_num] = int_val; | |
848 break; | |
849 default: | |
850 fprintf(stderr, "Invalid destinatino %s\n", param); | |
851 } | |
852 break; | |
853 } else if (input_buf[1] == 'r') { | |
854 system->header.soft_reset(&system->header); | |
855 return 0; | |
856 } else { | |
857 if (inst.op == M68K_RTS) { | |
858 after = m68k_read_long(context->aregs[7], context); | |
859 } else if (inst.op == M68K_RTE || inst.op == M68K_RTR) { | |
860 after = m68k_read_long(context->aregs[7] + 2, context); | |
861 } else if(m68k_is_branch(&inst)) { | |
862 if (inst.op == M68K_BCC && inst.extra.cond != COND_TRUE) { | |
863 branch_f = after; | |
864 branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; | |
865 insert_breakpoint(context, branch_t, debugger); | |
866 } else if(inst.op == M68K_DBCC) { | |
867 if (inst.extra.cond == COND_FALSE) { | |
868 if (context->dregs[inst.dst.params.regs.pri] & 0xFFFF) { | |
869 after = m68k_branch_target(&inst, context->dregs, context->aregs); | |
870 } | |
871 } else { | |
872 branch_t = after; | |
873 branch_f = m68k_branch_target(&inst, context->dregs, context->aregs); | |
874 insert_breakpoint(context, branch_f, debugger); | |
875 } | |
876 } else { | |
877 after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; | |
878 } | |
879 } | |
880 insert_breakpoint(context, after, debugger); | |
881 return 0; | |
882 } | |
883 case 'v': { | |
884 genesis_context * gen = context->system; | |
885 //VDP debug commands | |
886 switch(input_buf[1]) | |
887 { | |
888 case 's': | |
889 vdp_print_sprite_table(gen->vdp); | |
890 break; | |
891 case 'r': | |
892 vdp_print_reg_explain(gen->vdp); | |
893 break; | |
894 } | |
895 break; | |
896 } | |
897 case 'y': { | |
898 genesis_context * gen = context->system; | |
899 //YM-2612 debug commands | |
900 switch(input_buf[1]) | |
901 { | |
902 case 'c': | |
903 if (input_buf[2] == ' ') { | |
904 int channel = atoi(input_buf+3)-1; | |
905 ym_print_channel_info(gen->ym, channel); | |
906 } else { | |
907 for (int i = 0; i < 6; i++) { | |
908 ym_print_channel_info(gen->ym, i); | |
909 } | |
910 } | |
911 break; | |
912 case 't': | |
913 ym_print_timer_info(gen->ym); | |
914 break; | |
915 } | |
916 break; | |
917 } | |
918 #ifndef NO_Z80 | |
919 case 'z': { | |
920 genesis_context * gen = context->system; | |
921 //Z80 debug commands | |
922 switch(input_buf[1]) | |
923 { | |
924 case 'b': | |
925 param = find_param(input_buf); | |
926 if (!param) { | |
927 fputs("zb command requires a parameter\n", stderr); | |
928 break; | |
929 } | |
930 value = strtol(param, NULL, 16); | |
931 zinsert_breakpoint(gen->z80, value, (uint8_t *)zdebugger); | |
932 new_bp = malloc(sizeof(bp_def)); | |
933 new_bp->next = zbreakpoints; | |
934 new_bp->address = value; | |
935 new_bp->index = zbp_index++; | |
936 zbreakpoints = new_bp; | |
937 printf("Z80 Breakpoint %d set at %X\n", new_bp->index, value); | |
938 break; | |
939 case 'p': | |
940 param = find_param(input_buf); | |
941 if (!param) { | |
942 fputs("zp command requires a parameter\n", stderr); | |
943 break; | |
944 } | |
945 zdebugger_print(gen->z80, input_buf[2] == '/' ? input_buf[3] : 0, param); | |
946 } | |
947 break; | |
948 } | |
949 #endif | |
950 case '?': | |
951 print_m68k_help(); | |
952 break; | |
953 case 'q': | |
954 puts("Quitting"); | |
955 exit(0); | |
956 break; | |
957 default: | |
958 fprintf(stderr, "Unrecognized debugger command %s\nUse '?' for help.\n", input_buf); | |
959 break; | |
960 } | |
961 return 1; | |
962 } | |
963 | |
964 void print_m68k_help() | |
965 { | |
966 printf("M68k Debugger Commands\n"); | |
967 printf(" b ADDRESS - Set a breakpoint at ADDRESS\n"); | |
968 printf(" d BREAKPOINT - Delete a 68K breakpoint\n"); | |
969 printf(" co BREAKPOINT - Run a list of debugger commands each time\n"); | |
970 printf(" BREAKPOINT is hit\n"); | |
971 printf(" a ADDRESS - Advance to address\n"); | |
972 printf(" n - Advance to next instruction\n"); | |
973 printf(" o - Advance to next instruction ignoring branches to\n"); | |
974 printf(" lower addresses (good for breaking out of loops)\n"); | |
975 printf(" s - Advance to next instruction (follows bsr/jsr)\n"); | |
976 printf(" se REG|ADDRESS VALUE - Set value\n"); | |
977 printf(" sr - Soft reset\n"); | |
978 printf(" c - Continue\n"); | |
979 printf(" bt - Print a backtrace\n"); | |
980 printf(" p[/(x|X|d|c)] VALUE - Print a register or memory location\n"); | |
981 printf(" di[/(x|X|d|c)] VALUE - Print a register or memory location each time\n"); | |
982 printf(" a breakpoint is hit\n"); | |
983 printf(" vs - Print VDP sprite list\n"); | |
984 printf(" vr - Print VDP register info\n"); | |
985 printf(" yc [CHANNEL NUM] - Print YM-2612 channel info\n"); | |
986 printf(" yt - Print YM-2612 timer info\n"); | |
987 printf(" zb ADDRESS - Set a Z80 breakpoint\n"); | |
988 printf(" zp[/(x|X|d|c)] VALUE - Display a Z80 value\n"); | |
989 printf(" ? - Display help\n"); | |
990 printf(" q - Quit BlastEm\n"); | |
991 } | |
992 | |
993 void print_z80_help() | |
994 { | |
995 printf("Z80 Debugger Commands\n"); | |
996 printf(" b ADDRESS - Set a breakpoint at ADDRESS\n"); | |
997 printf(" de BREAKPOINT - Delete a Z80 breakpoint\n"); | |
998 printf(" a ADDRESS - Advance to address\n"); | |
999 printf(" n - Advance to next instruction\n"); | |
1000 printf(" c - Continue\n"); | |
1001 printf(" p[/(x|X|d|c)] VALUE - Print a register or memory location\n"); | |
1002 printf(" di[/(x|X|d|c)] VALUE - Print a register or memory location each time\n"); | |
1003 printf(" a breakpoint is hit\n"); | |
1004 printf(" q - Quit BlastEm\n"); | |
1005 } | |
1006 | |
1007 void debugger(m68k_context * context, uint32_t address) | 131 void debugger(m68k_context * context, uint32_t address) |
1008 { | 132 { |
1009 static char last_cmd[1024]; | |
1010 char input_buf[1024]; | |
1011 m68kinst inst; | |
1012 | |
1013 init_terminal(); | |
1014 | |
1015 sync_components(context, 0); | |
1016 genesis_context *gen = context->system; | |
1017 vdp_force_update_framebuffer(gen->vdp); | |
1018 //probably not necessary, but let's play it safe | |
1019 address &= 0xFFFFFF; | |
1020 if (address == branch_t) { | |
1021 bp_def ** f_bp = find_breakpoint(&breakpoints, branch_f); | |
1022 if (!*f_bp) { | |
1023 remove_breakpoint(context, branch_f); | |
1024 } | |
1025 branch_t = branch_f = 0; | |
1026 } else if(address == branch_f) { | |
1027 bp_def ** t_bp = find_breakpoint(&breakpoints, branch_t); | |
1028 if (!*t_bp) { | |
1029 remove_breakpoint(context, branch_t); | |
1030 } | |
1031 branch_t = branch_f = 0; | |
1032 } | |
1033 | |
1034 uint16_t * pc = get_native_pointer(address, (void **)context->mem_pointers, &context->options->gen); | |
1035 if (!pc) { | |
1036 fatal_error("Entered 68K debugger at address %X\n", address); | |
1037 } | |
1038 uint16_t * after_pc = m68k_decode(pc, &inst, address); | |
1039 uint32_t after = address + (after_pc-pc)*2; | |
1040 int debugging = 1; | |
1041 //Check if this is a user set breakpoint, or just a temporary one | |
1042 bp_def ** this_bp = find_breakpoint(&breakpoints, address); | |
1043 if (*this_bp) { | |
1044 | |
1045 if ((*this_bp)->commands) | |
1046 { | |
1047 char *commands = strdup((*this_bp)->commands); | |
1048 char *copy = commands; | |
1049 | |
1050 while (debugging && *commands) | |
1051 { | |
1052 char *cmd = commands; | |
1053 strip_nl(cmd); | |
1054 commands += strlen(cmd) + 1; | |
1055 debugging = run_debugger_command(context, address, cmd, inst, after); | |
1056 } | |
1057 free(copy); | |
1058 } | |
1059 if (debugging) { | |
1060 printf("68K Breakpoint %d hit\n", (*this_bp)->index); | |
1061 } else { | |
1062 return; | |
1063 } | |
1064 } else { | |
1065 remove_breakpoint(context, address); | |
1066 } | |
1067 for (disp_def * cur = displays; cur; cur = cur->next) { | |
1068 debugger_print(context, cur->format_char, cur->param, address); | |
1069 } | |
1070 m68k_disasm(&inst, input_buf); | |
1071 printf("%X: %s\n", address, input_buf); | |
1072 #ifdef _WIN32 | |
1073 #define prompt 1 | |
1074 #else | |
1075 int prompt = 1; | |
1076 fd_set read_fds; | |
1077 FD_ZERO(&read_fds); | |
1078 struct timeval timeout; | |
1079 #endif | |
1080 while (debugging) { | |
1081 if (prompt) { | |
1082 fputs(">", stdout); | |
1083 fflush(stdout); | |
1084 } | |
1085 process_events(); | |
1086 #ifndef _WIN32 | |
1087 timeout.tv_sec = 0; | |
1088 timeout.tv_usec = 16667; | |
1089 FD_SET(fileno(stdin), &read_fds); | |
1090 if(select(fileno(stdin) + 1, &read_fds, NULL, NULL, &timeout) < 1) { | |
1091 prompt = 0; | |
1092 continue; | |
1093 } else { | |
1094 prompt = 1; | |
1095 } | |
1096 #endif | |
1097 if (!fgets(input_buf, sizeof(input_buf), stdin)) { | |
1098 fputs("fgets failed", stderr); | |
1099 break; | |
1100 } | |
1101 strip_nl(input_buf); | |
1102 //hitting enter repeats last command | |
1103 if (input_buf[0]) { | |
1104 strcpy(last_cmd, input_buf); | |
1105 } else { | |
1106 strcpy(input_buf, last_cmd); | |
1107 } | |
1108 debugging = run_debugger_command(context, address, input_buf, inst, after); | |
1109 } | |
1110 return; | 133 return; |
1111 } | 134 } |