changeset 193:c66e4636f991

Implement support for self-modifying code
author Mike Pavone <pavone@retrodev.com>
date Tue, 15 Jan 2013 00:14:36 -0800
parents 1db07e112bf7
children 811163790e6c
files m68k_to_x86.c m68k_to_x86.h runtime.S
diffstat 3 files changed, 129 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/m68k_to_x86.c	Mon Jan 14 21:56:54 2013 -0800
+++ b/m68k_to_x86.c	Tue Jan 15 00:14:36 2013 -0800
@@ -44,6 +44,7 @@
 void m68k_native_addr_and_sync();
 void m68k_trap();
 void m68k_invalid();
+void m68k_retrans_stub();
 void set_sr();
 void set_ccr();
 void get_sr();
@@ -679,21 +680,38 @@
 uint8_t * get_native_address(native_map_slot * native_code_map, uint32_t address)
 {
 	address &= 0xFFFFFF;
-	if (address > 0x400000) {
-		printf("get_native_address: %X\n", address);
-	}
 	address /= 2;
 	uint32_t chunk = address / NATIVE_CHUNK_SIZE;
 	if (!native_code_map[chunk].base) {
 		return NULL;
 	}
 	uint32_t offset = address % NATIVE_CHUNK_SIZE;
-	if (native_code_map[chunk].offsets[offset] == INVALID_OFFSET) {
+	if (native_code_map[chunk].offsets[offset] == INVALID_OFFSET || native_code_map[chunk].offsets[offset] == EXTENSION_WORD) {
 		return NULL;
 	}
 	return native_code_map[chunk].base + native_code_map[chunk].offsets[offset];
 }
 
+uint32_t get_instruction_start(native_map_slot * native_code_map, uint32_t address)
+{
+	address &= 0xFFFFFF;
+	address /= 2;
+	uint32_t chunk = address / NATIVE_CHUNK_SIZE;
+	if (!native_code_map[chunk].base) {
+		return 0;
+	}
+	uint32_t offset = address % NATIVE_CHUNK_SIZE;
+	if (native_code_map[chunk].offsets[offset] == INVALID_OFFSET) {
+		return 0;
+	}
+	while (native_code_map[chunk].offsets[offset] == EXTENSION_WORD) {
+		--address;
+		chunk = address / NATIVE_CHUNK_SIZE;
+		offset = address % NATIVE_CHUNK_SIZE;
+	}
+	return address*2;
+}
+
 deferred_addr * defer_address(deferred_addr * old_head, uint32_t address, uint8_t *dest)
 {
 	deferred_addr * new_head = malloc(sizeof(deferred_addr));
@@ -755,6 +773,25 @@
 	}
 	uint32_t offset = address % NATIVE_CHUNK_SIZE;
 	native_code_map[chunk].offsets[offset] = native_addr-native_code_map[chunk].base;
+	for(address++,size-=2; size; address++,size-=2) {
+		chunk = address / NATIVE_CHUNK_SIZE;
+		offset = address % NATIVE_CHUNK_SIZE;
+		if (!native_code_map[chunk].base) {
+			native_code_map[chunk].base = native_addr;
+			native_code_map[chunk].offsets = malloc(sizeof(int32_t) * NATIVE_CHUNK_SIZE);
+			memset(native_code_map[chunk].offsets, 0xFF, sizeof(int32_t) * NATIVE_CHUNK_SIZE);
+		}
+		native_code_map[chunk].offsets[offset] = EXTENSION_WORD;
+	}
+}
+
+uint8_t get_native_inst_size(x86_68k_options * opts, uint32_t address)
+{
+	if (address < 0xE00000) {
+		return 0;
+	}
+	uint32_t slot = (address & 0xFFFF)/1024;
+	return opts->ram_inst_sizes[slot][((address & 0xFFFF)/2)%512];
 }
 
 uint8_t * translate_m68k_move(uint8_t * dst, m68kinst * inst, x86_68k_options * opts)
@@ -3611,7 +3648,7 @@
 	}
 	do {
 		do {
-			if (dst_end-dst < 128) {
+			if (dst_end-dst < MAX_NATIVE_SIZE) {
 				if (dst_end-dst < 5) {
 					puts("out of code memory, not enough space for jmp to next chunk");
 					exit(1);
@@ -3673,6 +3710,60 @@
 	return ret;
 }
 
+void * m68k_retranslate_inst(uint32_t address, m68k_context * context)
+{
+	x86_68k_options * opts = context->options;
+	uint8_t orig_size = get_native_inst_size(opts, address);
+	uint8_t * orig_start = get_native_address(context->native_code_map, address);
+	uint32_t orig = address;
+	address &= 0xFFFF;
+	uint8_t * dst = opts->cur_code;
+	uint8_t * dst_end = opts->code_end;
+	uint16_t *after, *inst = context->mem_pointers[1] + address/2;
+	m68kinst instbuf;
+	after = m68k_decode(inst, &instbuf, orig);
+	if (orig_size != MAX_NATIVE_SIZE) {
+		if (dst_end - dst < 128) {
+			size_t size = 1024*1024;
+			dst = alloc_code(&size);
+			opts->code_end = dst_end = dst + size;
+			opts->cur_code = dst;
+		}
+		uint8_t * native_end = translate_m68k(dst, &instbuf, opts);
+		if ((native_end - dst) <= orig_size) {
+			native_end = translate_m68k(orig_start, &instbuf, opts);
+			while (native_end < orig_start + orig_size) {
+				*(native_end++) = 0x90; //NOP
+			}
+			return orig_start;
+		} else {
+			map_native_address(context, instbuf.address, dst, (after-inst)*2, MAX_NATIVE_SIZE);
+			opts->code_end = dst+MAX_NATIVE_SIZE;
+			if (instbuf.op != M68K_RTS && instbuf.op != M68K_RTE && instbuf.op != M68K_RTR && instbuf.op != M68K_JMP && (instbuf.op != M68K_BCC || instbuf.extra.cond != COND_TRUE)) {
+				jmp(native_end, get_native_address(context->native_code_map, address + (after-inst)*2));
+			}
+			return dst;
+		}
+	} else {
+		dst = translate_m68k(orig_start, &instbuf, opts);
+		if (instbuf.op != M68K_RTS && instbuf.op != M68K_RTE && instbuf.op != M68K_RTR && instbuf.op != M68K_JMP && (instbuf.op != M68K_BCC || instbuf.extra.cond != COND_TRUE)) {
+			dst = jmp(dst, get_native_address(context->native_code_map, address + (after-inst)*2));
+		}
+		return orig_start;
+	}
+}
+
+m68k_context * m68k_handle_code_write(uint32_t address, m68k_context * context)
+{
+	uint32_t inst_start = get_instruction_start(context->native_code_map, address | 0xFF0000);
+	if (inst_start) {
+		uint8_t * dst = get_native_address(context->native_code_map, inst_start);
+		dst = mov_ir(dst, inst_start, SCRATCH2, SZ_D);
+		dst = jmp(dst, (uint8_t *)m68k_retrans_stub);
+	}
+	return context;
+}
+
 void insert_breakpoint(m68k_context * context, uint32_t address, uint8_t * bp_handler)
 {
 	static uint8_t * bp_stub = NULL;
@@ -3760,6 +3851,7 @@
 	opts->cur_code = alloc_code(&size);
 	opts->code_end = opts->cur_code + size;
 	opts->ram_inst_sizes = malloc(sizeof(uint8_t *) * 64);
+	memset(opts->ram_inst_sizes, 0, sizeof(uint8_t *) * 64);
 }
 
 void init_68k_context(m68k_context * context, native_map_slot * native_code_map, void * opts)
--- a/m68k_to_x86.h	Mon Jan 14 21:56:54 2013 -0800
+++ b/m68k_to_x86.h	Tue Jan 15 00:14:36 2013 -0800
@@ -5,6 +5,8 @@
 #define NATIVE_MAP_CHUNKS (64*1024)
 #define NATIVE_CHUNK_SIZE ((16 * 1024 * 1024 / NATIVE_MAP_CHUNKS)/2)
 #define INVALID_OFFSET 0xFFFFFFFF
+#define EXTENSION_WORD 0xFFFFFFFE
+#define MAX_NATIVE_SIZE 255
 
 #define OPT_NATIVE_CALL_STACK 0x1
  
--- a/runtime.S	Mon Jan 14 21:56:54 2013 -0800
+++ b/runtime.S	Tue Jan 15 00:14:36 2013 -0800
@@ -240,8 +240,17 @@
 not_io_w:
 	ret
 workram_w:
-	and $0xFFFF, %rdi
+	and $0xFFFF, %edi
 	mov %cx, (%r9, %rdi)
+	mov %edi, %ecx
+	shr $10, %ecx
+	bt %ecx, 160(%rsi)
+	jnc not_code
+	call m68k_save_context
+	call m68k_handle_code_write
+	mov %rax, %rsi
+	call m68k_load_context
+not_code:
 	ret
 cart_w:
 	mov %cx, (%r8, %rdi)
@@ -322,6 +331,16 @@
 	xor $1, %edi
 	and $0xFFFF, %rdi
 	mov %cl, (%r9, %rdi)
+	mov %edi, %ecx
+	shr $10, %ecx
+	bt %ecx, 160(%rsi)
+	jnc not_code_b
+	xor $1, %edi
+	call m68k_save_context
+	call m68k_handle_code_write
+	mov %rax, %rsi
+	call m68k_load_context
+not_code_b:
 	ret
 cart_wb:
 	/* deal with byte swapping */
@@ -498,6 +517,16 @@
 	pop %rsi
 	call m68k_load_context
 	ret
+	
+	.global m68k_retrans_stub
+m68k_retrans_stub:
+	call m68k_save_context
+	push %rsi
+	call m68k_retranslate_inst
+	pop %rsi
+	mov %rax, %rcx
+	call m68k_load_context
+	jmp *%rcx
 
 	.global m68k_save_context
 m68k_save_context: