# HG changeset patch # User Michael Pavone # Date 1403232616 25200 # Node ID 7c46891a29b1e9845f8b62965b5eb63dbf9044e7 # Parent 6aa2a8ab9c70042c9ef3276ee3060b86fac8df3d Properly handle Z80 breakpoints on self-modifying code and setting Z80 breakpoints before the Z80 program has been loaded diff -r 6aa2a8ab9c70 -r 7c46891a29b1 z80_to_x86.c --- a/z80_to_x86.c Thu Jun 19 08:14:35 2014 -0700 +++ b/z80_to_x86.c Thu Jun 19 19:50:16 2014 -0700 @@ -44,6 +44,8 @@ void z80_save_context(); void z80_load_context(); +uint8_t * zbreakpoint_patch(z80_context * context, uint16_t address, uint8_t * native); + uint8_t z80_size(z80inst * inst) { uint8_t reg = (inst->reg & 0x1F); @@ -334,6 +336,9 @@ x86_z80_options *opts = context->options; uint8_t * start = dst; dst = z80_check_cycles_int(dst, address); + if (context->breakpoint_flags[address / sizeof(uint8_t)] & (1 << (address % sizeof(uint8_t)))) { + zbreakpoint_patch(context, address, start); + } switch(inst->op) { case Z80_LD: @@ -1975,62 +1980,80 @@ context->extra_pc = NULL; } +uint8_t * zbreakpoint_patch(z80_context * context, uint16_t address, uint8_t * native) +{ + native = mov_ir(native, address, SCRATCH1, SZ_W); + native = call(native, context->bp_stub); + return native; +} + +void zcreate_stub(z80_context * context) +{ + 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; + } + context->bp_stub = dst; + + //Calculate length of prologue + int check_int_size = z80_check_cycles_int(dst, 0) - dst; + + //Calculate length of patch + int patch_size = zbreakpoint_patch(context, 0, dst) - dst; + + //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, context->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 = pop_r(dst, SCRATCH1); + dst = add_ir(dst, check_int_size - patch_size, SCRATCH1, SZ_Q); + dst = push_r(dst, SCRATCH1); + dst = jmp(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 - patch_size, SCRATCH1, SZ_Q); + dst = jmp_r(dst, SCRATCH1); + opts->cur_code = dst; +} + 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; + context->bp_handler = bp_handler; + uint8_t bit = 1 << (address % sizeof(uint8_t)); + if (!(bit & context->breakpoint_flags[address / sizeof(uint8_t)])) { + context->breakpoint_flags[address / sizeof(uint8_t)] |= bit; + if (!context->bp_stub) { + zcreate_stub(context); } - 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 = pop_r(dst, SCRATCH1); - dst = add_ir(dst, check_int_size - (native-start_native), SCRATCH1, SZ_Q); - dst = push_r(dst, SCRATCH1); - dst = jmp(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); + uint8_t * native = z80_get_native_address(context, address); + if (native) { + zbreakpoint_patch(context, address, native); + } } } void zremove_breakpoint(z80_context * context, uint16_t address) { + context->breakpoint_flags[address / sizeof(uint8_t)] &= 1 << (address % sizeof(uint8_t)); uint8_t * native = z80_get_native_address(context, address); - z80_check_cycles_int(native, address); + if (native) { + z80_check_cycles_int(native, address); + } } diff -r 6aa2a8ab9c70 -r 7c46891a29b1 z80_to_x86.h --- a/z80_to_x86.h Thu Jun 19 08:14:35 2014 -0700 +++ b/z80_to_x86.h Thu Jun 19 19:50:16 2014 -0700 @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #ifndef Z80_TO_X86_H_ @@ -55,6 +55,9 @@ void * system; uint8_t ram_code_flags[(8 * 1024)/128/8]; uint32_t int_enable_cycle; + uint8_t breakpoint_flags[(16 * 1024)/sizeof(uint8_t)]; + uint8_t * bp_handler; + uint8_t * bp_stub; uint16_t pc; } z80_context;