# HG changeset patch # User Mike Pavone # Date 1370054593 25200 # Node ID 836585d389b8267df52d39a1214ffb56c619025e # Parent 3ba3b6656ffff500b1282b88977c8b32516319a2 Partial implementation of Z80 debugger diff -r 3ba3b6656fff -r 836585d389b8 blastem.c --- a/blastem.c Wed May 29 21:21:14 2013 -0700 +++ b/blastem.c Fri May 31 19:43:13 2013 -0700 @@ -973,7 +973,9 @@ } bp_def; bp_def * breakpoints = NULL; +bp_def * zbreakpoints = NULL; uint32_t bp_index = 0; +uint32_t zbp_index = 0; bp_def ** find_breakpoint(bp_def ** cur, uint32_t address) { @@ -1019,6 +1021,267 @@ } } +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; + //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; + if (address < 0x4000) { + pc = z80_ram + (address & 0x1FFF); + } else if (address >= 0x8000) { + if (context->bank_reg < (0x400000 >> 15)) { + fprintf(stderr, "Entered Z80 debugger in banked memory address %X, which is not yet supported\n", address); + exit(1); + } else { + fprintf(stderr, "Entered Z80 debugger in banked memory address %X, but the bank is not pointed to a cartridge address\n", address); + exit(1); + } + } else { + fprintf(stderr, "Entered Z80 debugger at address %X\n", address); + exit(1); + } + 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); + 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++; + zbreakpoints = new_bp; + printf("Z80 Breakpoint %d set at %X\n", new_bp->index, value); + break; + case 'c': + puts("Continuing"); + debugging = 0; + break; + case 'n': + //TODO: Handle branch instructions + zinsert_breakpoint(context, after, (uint8_t *)zdebugger); + break; + case 'p': + strcpy(format, "%s: %d\n"); + if (input_buf[1] == '/') { + switch (input_buf[2]) + { + case 'x': + case 'X': + case 'd': + case 'c': + format[5] = input_buf[2]; + break; + default: + fprintf(stderr, "Unrecognized format character: %c\n", input_buf[2]); + } + } + param = find_param(input_buf); + if (!param) { + fputs("p command requires a parameter\n", stderr); + break; + } + switch (param[0]) + { + 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 { + value = context->regs[Z80_B]; + } + break; + case 'c': + if(param[1] == '\'') { + value = context->alt_regs[Z80_C]; + } 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 { + value = context->im; + } + break; + case '0': + if (param[1] == 'x') { + uint16_t p_addr = strtol(param+2, NULL, 16); + if (p_addr < 0x4000) { + value = z80_ram[p_addr & 0x1FFF]; + } + } + break; + } + printf(format, param, value); + break; + case 'q': + puts("Quitting"); + exit(0); + break; + default: + fprintf(stderr, "Unrecognized debugger command %s\n", input_buf); + break; + } + } + return context; +} + m68k_context * debugger(m68k_context * context, uint32_t address) { static char last_cmd[1024]; @@ -1044,7 +1307,7 @@ //Check if this is a user set breakpoint, or just a temporary one bp_def ** this_bp = find_breakpoint(&breakpoints, address); if (*this_bp) { - printf("Breakpoint %d hit\n", (*this_bp)->index); + printf("68K Breakpoint %d hit\n", (*this_bp)->index); } else { remove_breakpoint(context, address); } @@ -1054,7 +1317,7 @@ } else if(address > 0xE00000) { pc = ram + (address & 0xFFFF)/2; } else { - fprintf(stderr, "Entered debugger at address %X\n", address); + fprintf(stderr, "Entered 68K debugger at address %X\n", address); exit(1); } uint16_t * after_pc = m68k_decode(pc, &inst, address); @@ -1098,7 +1361,7 @@ new_bp->address = value; new_bp->index = bp_index++; breakpoints = new_bp; - printf("Breakpoint %d set at %X\n", new_bp->index, value); + printf("68K Breakpoint %d set at %X\n", new_bp->index, value); break; case 'a': param = find_param(input_buf); @@ -1196,6 +1459,29 @@ } break; } + 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; + } + break; + } case 'q': puts("Quitting"); exit(0); diff -r 3ba3b6656fff -r 836585d389b8 z80_to_x86.c --- a/z80_to_x86.c Wed May 29 21:21:14 2013 -0700 +++ b/z80_to_x86.c Fri May 31 19:43:13 2013 -0700 @@ -36,6 +36,8 @@ void z80_io_read(); void z80_io_write(); void z80_halt(); +void z80_save_context(); +void z80_load_context(); uint8_t z80_size(z80inst * inst) { @@ -1953,4 +1955,59 @@ context->extra_pc = NULL; } +void zinsert_breakpoint(z80_context * context, uint16_t address, uint8_t * bp_handler) +{ + static uint8_t * bp_stub = NULL; + uint8_t * native = z80_get_native_address_trans(context, address); + uint8_t * start_native = native; + native = mov_ir(native, address, SCRATCH1, SZ_W); + if (!bp_stub) { + x86_z80_options * opts = context->options; + uint8_t * dst = opts->cur_code; + uint8_t * dst_end = opts->code_end; + if (dst_end - dst < 128) { + size_t size = 1024*1024; + dst = alloc_code(&size); + opts->code_end = dst_end = dst + size; + } + bp_stub = dst; + native = call(native, bp_stub); + + //Calculate length of prologue + dst = z80_check_cycles_int(dst, address); + int check_int_size = dst-bp_stub; + dst = bp_stub; + + //Save context and call breakpoint handler + dst = call(dst, (uint8_t *)z80_save_context); + dst = push_r(dst, SCRATCH1); + dst = mov_rr(dst, CONTEXT, RDI, SZ_Q); + dst = mov_rr(dst, SCRATCH1, RSI, SZ_W); + dst = call(dst, bp_handler); + dst = mov_rr(dst, RAX, CONTEXT, SZ_Q); + //Restore context + dst = call(dst, (uint8_t *)z80_load_context); + dst = pop_r(dst, SCRATCH1); + //do prologue stuff + dst = cmp_rr(dst, ZCYCLES, ZLIMIT, SZ_D); + uint8_t * jmp_off = dst+1; + dst = jcc(dst, CC_NC, dst + 7); + dst = call(dst, (uint8_t *)z80_handle_cycle_limit_int); + *jmp_off = dst - (jmp_off+1); + //jump back to body of translated instruction + dst = pop_r(dst, SCRATCH1); + dst = add_ir(dst, check_int_size - (native-start_native), SCRATCH1, SZ_Q); + dst = jmp_r(dst, SCRATCH1); + opts->cur_code = dst; + } else { + native = call(native, bp_stub); + } +} +void zremove_breakpoint(z80_context * context, uint16_t address) +{ + uint8_t * native = z80_get_native_address(context, address); + z80_check_cycles_int(native, address); +} + + diff -r 3ba3b6656fff -r 836585d389b8 z80_to_x86.h --- a/z80_to_x86.h Wed May 29 21:21:14 2013 -0700 +++ b/z80_to_x86.h Fri May 31 19:43:13 2013 -0700 @@ -59,6 +59,8 @@ z80_context * z80_handle_code_write(uint32_t address, z80_context * context); void z80_run(z80_context * context); void z80_reset(z80_context * context); +void zinsert_breakpoint(z80_context * context, uint16_t address, uint8_t * bp_handler); +void zremove_breakpoint(z80_context * context, uint16_t address); #endif //Z80_TO_X86_H_