changeset 626:7c46891a29b1

Properly handle Z80 breakpoints on self-modifying code and setting Z80 breakpoints before the Z80 program has been loaded
author Michael Pavone <pavone@retrodev.com>
date Thu, 19 Jun 2014 19:50:16 -0700
parents 6aa2a8ab9c70
children c5820734a5b6
files z80_to_x86.c z80_to_x86.h
diffstat 2 files changed, 74 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- 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);
+	}
 }
 
 
--- 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;