Mercurial > repos > blastem
view debug.c @ 1925:039553703c20
Don't apply address and cd register changes to the 'live' registers until pending flag is cleared, but do preserve the upper address bits in the latch. Fixes regression in Overdrive 2 while preserving fix to Mona in 344 bytes
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Mon, 13 Apr 2020 20:43:25 -0700 |
parents | 23394a890508 |
children | 374a5ae694e8 495569c1dc61 |
line wrap: on
line source
#include "debug.h" #include "genesis.h" #include "68kinst.h" #include <stdlib.h> #include <string.h> #ifndef _WIN32 #include <sys/select.h> #endif #include "render.h" #include "util.h" #include "terminal.h" #include "z80inst.h" #ifdef NEW_CORE #define Z80_OPTS opts #else #define Z80_OPTS options #endif static bp_def * breakpoints = NULL; static bp_def * zbreakpoints = NULL; static uint32_t bp_index = 0; static uint32_t zbp_index = 0; bp_def ** find_breakpoint(bp_def ** cur, uint32_t address) { while (*cur) { if ((*cur)->address == address) { break; } cur = &((*cur)->next); } return cur; } bp_def ** find_breakpoint_idx(bp_def ** cur, uint32_t index) { while (*cur) { if ((*cur)->index == index) { break; } cur = &((*cur)->next); } return cur; } disp_def * displays = NULL; disp_def * zdisplays = NULL; uint32_t disp_index = 0; uint32_t zdisp_index = 0; 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++) { if (*buf == ' ') { if (*(buf+1)) { return buf+1; } } } return NULL; } void strip_nl(char * buf) { for(; *buf; buf++) { if (*buf == '\n') { *buf = 0; return; } } } uint16_t m68k_read_word(uint32_t address, m68k_context *context) { return read_word(address, (void **)context->mem_pointers, &context->options->gen, context); } uint32_t m68k_read_long(uint32_t address, m68k_context *context) { return m68k_read_word(address, context) << 16 | m68k_read_word(address + 2, context); } void debugger_print(m68k_context *context, char format_char, char *param, uint32_t address) { uint32_t value; char format[8]; strcpy(format, "%s: %d\n"); switch (format_char) { 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); } if (param[0] == 'd' && param[1] >= '0' && param[1] <= '7') { value = context->dregs[param[1]-'0']; if (param[2] == '.') { if (param[3] == 'w') { value &= 0xFFFF; } else if (param[3] == 'b') { value &= 0xFF; } } } else if (param[0] == 'a' && param[1] >= '0' && param[1] <= '7') { value = context->aregs[param[1]-'0']; if (param[2] == '.') { if (param[3] == 'w') { value &= 0xFFFF; } else if (param[3] == 'b') { value &= 0xFF; } } } else if (param[0] == 's' && param[1] == 'r') { value = (context->status << 8); for (int flag = 0; flag < 5; flag++) { value |= context->flags[flag] << (4-flag); } } else if(param[0] == 'c') { value = context->current_cycle; } else if(param[0] == 'f') { genesis_context *gen = context->system; value = gen->vdp->frame; } else if (param[0] == 'p' && param[1] == 'c') { value = address; } else if ((param[0] == '0' && param[1] == 'x') || param[0] == '$') { char *after; uint32_t p_addr = strtol(param+(param[0] == '0' ? 2 : 1), &after, 16); if (after[0] == '.' && after[1] == 'l') { value = m68k_read_long(p_addr, context); } else { value = m68k_read_word(p_addr, context); } } else if(param[0] == '(' && (param[1] == 'a' || param[1] == 'd') && param[2] >= '0' && param[2] <= '7' && param[3] == ')') { uint8_t reg = param[2] - '0'; uint32_t p_addr = param[1] == 'a' ? context->aregs[reg] : context->dregs[reg]; if (param[4] == '.' && param[5] == 'l') { value = m68k_read_long(p_addr, context); } else { value = m68k_read_word(p_addr, context); } } else { fprintf(stderr, "Unrecognized parameter to p: %s\n", param); return; } printf(format, param, value); } #ifndef NO_Z80 void zdebugger_print(z80_context * context, char format_char, char * param) { uint32_t value; char format[8]; strcpy(format, "%s: %d\n"); genesis_context *system = context->system; switch (format_char) { 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]; } 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]; } } else if(param[1] == '\'') { value = context->alt_regs[Z80_A]; } else { value = context->regs[Z80_A]; } break; case 'b': if (param[1] == 'c') { if(param[2] == '\'') { value = context->alt_regs[Z80_B] << 8; value |= context->alt_regs[Z80_C]; } else { value = context->regs[Z80_B] << 8; value |= context->regs[Z80_C]; } } 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; } else { value = context->regs[Z80_C]; } break; case 'd': if (param[1] == 'e') { if(param[2] == '\'') { value = context->alt_regs[Z80_D] << 8; value |= context->alt_regs[Z80_E]; } else { value = context->regs[Z80_D] << 8; value |= context->regs[Z80_E]; } } 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]; } else { value = context->regs[Z80_E]; } 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]; } 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]; } break; case 'h': if (param[1] == 'l') { if(param[2] == '\'') { value = context->alt_regs[Z80_H] << 8; value |= context->alt_regs[Z80_L]; } else { value = context->regs[Z80_H] << 8; value |= context->regs[Z80_L]; } } else if(param[1] == '\'') { value = context->alt_regs[Z80_H]; } else { value = context->regs[Z80_H]; } break; case 'l': if(param[1] == '\'') { value = context->alt_regs[Z80_L]; } else { value = context->regs[Z80_L]; } 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]; } else { value = context->regs[Z80_IXH] << 8; value |= context->regs[Z80_IXL]; } } 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]; } } 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; #endif case 's': if (param[1] == 'p') { value = context->sp; } 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); } break; } printf(format, param, value); } z80_context * zdebugger(z80_context * context, uint16_t address) { static char last_cmd[1024]; char input_buf[1024]; static uint16_t branch_t; static uint16_t branch_f; z80inst inst; genesis_context *system = context->system; init_terminal(); //Check if this is a user set breakpoint, or just a temporary one bp_def ** this_bp = find_breakpoint(&zbreakpoints, address); if (*this_bp) { printf("Z80 Breakpoint %d hit\n", (*this_bp)->index); } else { zremove_breakpoint(context, address); } uint8_t * pc = get_native_pointer(address, (void **)context->mem_pointers, &context->Z80_OPTS->gen); if (!pc) { fatal_error("Failed to get native pointer on entering Z80 debugger at address %X\n", address); } for (disp_def * cur = zdisplays; cur; cur = cur->next) { zdebugger_print(context, cur->format_char, cur->param); } 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); int debugging = 1; while(debugging) { fputs(">", stdout); if (!fgets(input_buf, sizeof(input_buf), stdin)) { fputs("fgets failed", stderr); break; } strip_nl(input_buf); //hitting enter repeats last command if (input_buf[0]) { strcpy(last_cmd, input_buf); } 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 = zbreakpoints; new_bp->address = value; new_bp->index = zbp_index++; new_bp->commands = NULL; zbreakpoints = 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(&zdisplays, &zdisp_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(&zbreakpoints, 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(&zdisplays, 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; } } return context; } #endif static uint32_t branch_t; static uint32_t branch_f; 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; 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(&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]; if (stack >= 0xE00000) { stack &= 0xFFFF; uint8_t non_adr_count = 0; do { uint32_t bt_address = system->work_ram[stack/2] << 16 | system->work_ram[stack/2+1]; bt_address = get_instruction_start(context->options, bt_address - 2); if (bt_address) { stack += 4; non_adr_count = 0; uint16_t *bt_pc = NULL; if (bt_address < 0x400000) { bt_pc = system->cart + bt_address/2; } else if(bt_address > 0xE00000) { bt_pc = system->work_ram + (bt_address & 0xFFFF)/2; } m68k_decode(bt_pc, &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++; } stack &= 0xFFFF; } 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 = breakpoints; new_bp->address = value; new_bp->index = bp_index++; new_bp->commands = NULL; 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(context, format_char, param, address); add_display(&displays, &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(&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(context, format_char, param, address); } 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) { branch_f = after; branch_t = m68k_branch_target(&inst, context->dregs, context->aregs); insert_breakpoint(context, 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 { branch_t = after; branch_f = m68k_branch_target(&inst, context->dregs, context->aregs); insert_breakpoint(context, 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) { branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; if (branch_t < after) { branch_t = 0; } else { branch_f = after; insert_breakpoint(context, 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 { branch_f = target; branch_t = after; insert_breakpoint(context, 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) { branch_f = after; branch_t = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; insert_breakpoint(context, 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 { branch_t = after; branch_f = m68k_branch_target(&inst, context->dregs, context->aregs); insert_breakpoint(context, branch_f, debugger); } } else { after = m68k_branch_target(&inst, context->dregs, context->aregs) & 0xFFFFFF; } } insert_breakpoint(context, after, debugger); return 0; } case 'v': { genesis_context * gen = context->system; //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': { genesis_context * gen = context->system; //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; } #ifndef NO_Z80 case 'z': { genesis_context * gen = context->system; //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); zinsert_breakpoint(gen->z80, value, (uint8_t *)zdebugger); new_bp = malloc(sizeof(bp_def)); new_bp->next = zbreakpoints; new_bp->address = value; new_bp->index = zbp_index++; zbreakpoints = 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 case '?': print_m68k_help(); break; case 'q': puts("Quitting"); exit(0); break; default: fprintf(stderr, "Unrecognized debugger command %s\nUse '?' for help.\n", 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]; char input_buf[1024]; m68kinst inst; init_terminal(); sync_components(context, 0); genesis_context *gen = context->system; vdp_force_update_framebuffer(gen->vdp); //probably not necessary, but let's play it safe address &= 0xFFFFFF; if (address == branch_t) { bp_def ** f_bp = find_breakpoint(&breakpoints, branch_f); if (!*f_bp) { remove_breakpoint(context, branch_f); } branch_t = branch_f = 0; } else if(address == branch_f) { bp_def ** t_bp = find_breakpoint(&breakpoints, branch_t); if (!*t_bp) { remove_breakpoint(context, branch_t); } branch_t = branch_f = 0; } uint16_t * pc = get_native_pointer(address, (void **)context->mem_pointers, &context->options->gen); if (!pc) { fatal_error("Entered 68K debugger at address %X\n", address); } uint16_t * after_pc = m68k_decode(pc, &inst, address); uint32_t after = address + (after_pc-pc)*2; int debugging = 1; //Check if this is a user set breakpoint, or just a temporary one bp_def ** this_bp = find_breakpoint(&breakpoints, address); if (*this_bp) { if ((*this_bp)->commands) { 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); } if (debugging) { printf("68K Breakpoint %d hit\n", (*this_bp)->index); } else { return; } } else { remove_breakpoint(context, address); } for (disp_def * cur = displays; cur; cur = cur->next) { debugger_print(context, cur->format_char, cur->param, address); } m68k_disasm(&inst, input_buf); printf("%X: %s\n", address, input_buf); #ifdef _WIN32 #define prompt 1 #else int prompt = 1; fd_set read_fds; FD_ZERO(&read_fds); struct timeval timeout; #endif while (debugging) { if (prompt) { fputs(">", stdout); fflush(stdout); } process_events(); #ifndef _WIN32 timeout.tv_sec = 0; timeout.tv_usec = 16667; FD_SET(fileno(stdin), &read_fds); if(select(fileno(stdin) + 1, &read_fds, NULL, NULL, &timeout) < 1) { prompt = 0; continue; } else { prompt = 1; } #endif if (!fgets(input_buf, sizeof(input_buf), stdin)) { fputs("fgets failed", stderr); break; } strip_nl(input_buf); //hitting enter repeats last command if (input_buf[0]) { strcpy(last_cmd, input_buf); } else { strcpy(input_buf, last_cmd); } debugging = run_debugger_command(context, address, input_buf, inst, after); } return; }