diff m68k_to_x86.c @ 18:3e7bfde7606e

M68K to x86 translation works for a limited subset of instructions and addressing modes
author Mike Pavone <pavone@retrodev.com>
date Tue, 04 Dec 2012 19:13:12 -0800
parents 2bdad0f52f42
children 4717146a7606
line wrap: on
line diff
--- a/m68k_to_x86.c	Tue Nov 27 22:54:38 2012 -0800
+++ b/m68k_to_x86.c	Tue Dec 04 19:13:12 2012 -0800
@@ -1,11 +1,16 @@
 #include "gen_x86.h"
 #include "m68k_to_x86.h"
-
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
 
 #define BUS 4
+#define PREDEC_PENALTY 2
 #define CYCLES RAX
 #define LIMIT RBP
-#define SCRATCH RCX
+#define SCRATCH1 RCX
+#define SCRATCH2 RDI
 #define CONTEXT RSI
 
 #define FLAG_N RBX
@@ -22,16 +27,26 @@
 } x86_ea;
 
 void handle_cycle_limit();
+void m68k_read_word_scratch1();
+void m68k_read_long_scratch1();
+void m68k_read_byte_scratch1();
+void m68k_write_word();
+void m68k_write_long_lowfirst();
+void m68k_write_long_highfirst();
+void m68k_write_byte();
+void m68k_save_context();
+void m68k_modified_ret_addr();
+void m68k_start_context(uint8_t * addr, m68k_context * context);
 
 uint8_t * cycles(uint8_t * dst, uint32_t num)
 {
-	dst = add_i32r(dst, num, CYCLES);
+	dst = add_ir(dst, num, CYCLES, SZ_D);
 }
 
-uint8_t * check_cycles(uint8_t * dst) 	Ivds
+uint8_t * check_cycles(uint8_t * dst)
 {
 	dst = cmp_rr(dst, CYCLES, LIMIT, SZ_D);
-	dst = jcc(dst, CC_G, 5);
+	dst = jcc(dst, CC_G, dst+7);
 	dst = call(dst, (char *)handle_cycle_limit);
 }
 
@@ -46,37 +61,551 @@
 	return -1;
 }
 
-uint8_t * translate_m68k_ea(m68k_op_info * op, x86_ea * dst, uint8_t * out, x86_68k_options * opts)
+void print_regs_exit(m68k_context * context)
 {
-	int8_t reg = native_reg(op, opts);
+	for (int i = 0; i < 8; i++) {
+		printf("d%d: %X\n", i, context->dregs[i]);
+	}
+	for (int i = 0; i < 8; i++) {
+		printf("a%d: %X\n", i, context->aregs[i]);
+	}
+	exit(0);
+}
+
+uint8_t * translate_m68k_src(m68kinst * inst, x86_ea * ea, uint8_t * out, x86_68k_options * opts)
+{
+	int8_t reg = native_reg(&(inst->src), opts);
+	int32_t dec_amount,inc_amount;
 	if (reg >= 0) {
-		dst->mode = MODE_REG_DIRECT;
-		dst->base = reg;
-		return;
+		ea->mode = MODE_REG_DIRECT;
+		ea->base = reg;
+		return out;
 	}
-	switch (op->addr_mode)
+	switch (inst->src.addr_mode)
+	{
+	case MODE_REG:
+	case MODE_AREG:
+		//We only get one memory parameter, so if the dst operand is a register in memory,
+		//we need to copy this to a temp register first
+		reg = native_reg(&(inst->dst), opts);
+		if (reg >= 0 || inst->dst.addr_mode == MODE_UNUSED || (inst->dst.addr_mode != MODE_REG && inst->dst.addr_mode == MODE_AREG) 
+		    || inst->op == M68K_EXG) {
+		    
+			ea->mode = MODE_REG_DISPLACE8;
+			ea->base = CONTEXT;
+			ea->disp = (inst->src.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * inst->src.params.regs.pri;
+		} else {
+			out = mov_rdisp8r(out, CONTEXT, (inst->src.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * inst->src.params.regs.pri, SCRATCH1, inst->extra.size);
+			ea->mode = MODE_REG_DIRECT;
+			ea->base = SCRATCH1;
+		}
+		break;
+	case MODE_AREG_PREDEC:
+		dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1);
+		out = cycles(out, PREDEC_PENALTY);
+		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
+			out = sub_ir(out, inc_amount, opts->aregs[inst->src.params.regs.pri], SZ_D);
+		} else {
+			out = sub_irdisp8(out, inc_amount, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SZ_D);
+		}
+		out = check_cycles(out);
+	case MODE_AREG_INDIRECT:
+	case MODE_AREG_POSTINC:	
+		if (opts->aregs[inst->src.params.regs.pri] >= 0) {
+			out = mov_rr(out, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D);
+		} else {
+			out = mov_rdisp8r(out, CONTEXT,  offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D);
+		}
+		switch (inst->extra.size)
+		{
+		case OPSIZE_BYTE:
+			out = call(out, (char *)m68k_read_byte_scratch1);
+			break;
+		case OPSIZE_WORD:
+			out = call(out, (char *)m68k_read_word_scratch1);
+			break;
+		case OPSIZE_LONG:
+			out = call(out, (char *)m68k_read_long_scratch1);
+			break;
+		}
+		
+		if (inst->src.addr_mode == MODE_AREG_POSTINC) {
+			inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1);
+			if (opts->aregs[inst->src.params.regs.pri] >= 0) {
+				out = add_ir(out, inc_amount, opts->aregs[inst->src.params.regs.pri], SZ_D);
+			} else {
+				out = add_irdisp8(out, inc_amount, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SZ_D);
+			}
+		}
+		ea->mode = MODE_REG_DIRECT;
+		ea->base = SCRATCH1;
+		break;
+	case MODE_IMMEDIATE:
+		if (inst->variant != VAR_QUICK) {
+			if (inst->extra.size == OPSIZE_LONG) {
+				out = cycles(out, BUS);
+				out = check_cycles(out);
+			}
+			out = cycles(out, BUS);
+			out = check_cycles(out);
+		}
+		ea->mode = MODE_IMMED;
+		ea->disp = inst->src.params.immed;
+		break;
+	default:
+		printf("address mode %d not implemented (src)\n", inst->src.addr_mode);
+		exit(1);
+	}
+	return out;
+}
+
+uint8_t * translate_m68k_dst(m68kinst * inst, x86_ea * ea, uint8_t * out, x86_68k_options * opts)
+{
+	int8_t reg = native_reg(&(inst->dst), opts);
+	int32_t dec_amount, inc_amount;
+	if (reg >= 0) {
+		ea->mode = MODE_REG_DIRECT;
+		ea->base = reg;
+		return out;
+	}
+	switch (inst->dst.addr_mode)
 	{
 	case MODE_REG:
 	case MODE_AREG:
-		dst->mode = MODE_DISPLACE8;
-		dst->base = CONTEXT;
-		dst->disp = (op->addr_mode = MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * op->params.regs.pri;
+		ea->mode = MODE_REG_DISPLACE8;
+		ea->base = CONTEXT;
+		ea->disp = (inst->dst.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * inst->dst.params.regs.pri;
+		break;
+	case MODE_AREG_PREDEC:
+		dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1);
+		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
+			out = sub_ir(out, dec_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D);
+		} else {
+			out = sub_irdisp8(out, dec_amount, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D);
+		}
+	case MODE_AREG_INDIRECT:
+	case MODE_AREG_POSTINC:
+		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
+			out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], SCRATCH1, SZ_D);
+		} else {
+			out = mov_rdisp8r(out, CONTEXT,  offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SCRATCH1, SZ_D);
+		}
+		switch (inst->extra.size)
+		{
+		case OPSIZE_BYTE:
+			out = call(out, (char *)m68k_read_byte_scratch1);
+			break;
+		case OPSIZE_WORD:
+			out = call(out, (char *)m68k_read_word_scratch1);
+			break;
+		case OPSIZE_LONG:
+			out = call(out, (char *)m68k_read_long_scratch1);
+			break;
+		}
+		//save reg value in SCRATCH2 so we can use it to save the result in memory later
+		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
+			out = mov_rr(out, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D);
+		} else {
+			out = mov_rdisp8r(out, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SCRATCH2, SZ_D);
+		}
+		
+		if (inst->src.addr_mode == MODE_AREG_POSTINC) {
+			inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1);
+			if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
+				out = add_ir(out, inc_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D);
+			} else {
+				out = add_irdisp8(out, inc_amount, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D);
+			}
+		}
+		ea->mode = MODE_REG_DIRECT;
+		ea->base = SCRATCH1;
+		break;
+	default:
+		printf("address mode %d not implemented (dst)\n", inst->dst.addr_mode);
+		exit(1);
+	}
+	return out;
+}
+
+uint8_t * m68k_save_result(m68kinst * inst, uint8_t * out, x86_68k_options * opts)
+{
+	if (inst->dst.addr_mode != MODE_REG && inst->dst.addr_mode != MODE_AREG) {
+		switch (inst->extra.size)
+		{
+		case OPSIZE_BYTE:
+			out = call(out, (char *)m68k_write_byte);
+			break;
+		case OPSIZE_WORD:
+			out = call(out, (char *)m68k_write_word);
+			break;
+		case OPSIZE_LONG:
+			out = call(out, (char *)m68k_write_long_lowfirst);
+			break;
+		}
+	}
+	return out;
+}
+
+uint8_t * get_native_address(native_map_slot * native_code_map, uint32_t address)
+{
+	address &= 0xFFFFFF;
+	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) {
+		return NULL;
+	}
+	return native_code_map[chunk].base + native_code_map[chunk].offsets[offset];
+}
+
+deferred_addr * defer_address(deferred_addr * old_head, uint32_t address, uint8_t *dest)
+{
+	deferred_addr * new_head = malloc(sizeof(deferred_addr));
+	new_head->next = old_head;
+	new_head->address = address & 0xFFFFFF;
+	new_head->dest = dest;
+	return new_head;
+}
+
+void process_deferred(x86_68k_options * opts)
+{
+	deferred_addr * cur = opts->deferred;
+	deferred_addr **last_next = &(opts->deferred);
+	while(cur)
+	{
+		uint8_t * native = get_native_address(opts->native_code_map, cur->address);
+		if (native) {
+			int32_t disp = native - (cur->dest + 4);
+			printf("Native dest: %p, Offset address: %p, displacement: %X\n", native, cur->dest, disp);
+			uint8_t * out = cur->dest;
+			*(out++) = disp;
+			disp >>= 8;
+			*(out++) = disp;
+			disp >>= 8;
+			*(out++) = disp;
+			disp >>= 8;
+			*out = disp;
+			*last_next = cur->next;
+			free(cur);
+			cur = *last_next;
+		} else {
+			last_next = &(cur->next);
+			cur = cur->next;
+		}
+	}
+}
+
+void map_native_address(native_map_slot * native_code_map, uint32_t address, uint8_t * native_addr)
+{
+	//FIXME: This probably isn't going to work with real code in a lot of cases, no guarantee that
+	//all the code in 1KB block is going to be translated at the same time
+	address &= 0xFFFFFF;
+	uint32_t chunk = address / NATIVE_CHUNK_SIZE;
+	if (!native_code_map[chunk].base) {
+		native_code_map[chunk].base = native_addr;
+		native_code_map[chunk].offsets = malloc(sizeof(uint16_t) * NATIVE_CHUNK_SIZE);
+		memset(native_code_map[chunk].offsets, 0xFF, sizeof(uint16_t) * NATIVE_CHUNK_SIZE);
+	}
+	uint32_t offset = address % NATIVE_CHUNK_SIZE;
+	native_code_map[chunk].offsets[offset] = native_addr-native_code_map[chunk].base;
+}
+
+uint8_t * translate_m68k_move(uint8_t * dst, m68kinst * inst, x86_68k_options * opts)
+{
+	int8_t reg, flags_reg;
+	uint8_t dir = 0;
+	int32_t offset;
+	int32_t inc_amount, dec_amount;
+	x86_ea src;
+	dst = translate_m68k_src(inst, &src, dst, opts);
+	reg = native_reg(&(inst->dst), opts);
+	if (src.mode == MODE_REG_DIRECT) {
+		flags_reg = src.base;
+	} else {
+		if (reg >= 0) {
+			flags_reg = reg;
+		} else {
+			printf("moving %d to temp register %d\n", src.disp, SCRATCH1);
+			dst = mov_ir(dst, src.disp, SCRATCH1, SZ_D);
+			src.mode = MODE_REG_DIRECT;
+			flags_reg = src.base = SCRATCH1;
+		}
+	}
+	switch(inst->dst.addr_mode)
+	{
+	case MODE_REG:
+	case MODE_AREG:
+		if (reg >= 0) {
+			if (src.mode == MODE_REG_DIRECT) {
+				dst = mov_rr(dst, src.base, reg, inst->extra.size);
+			} else if (src.mode == MODE_REG_DISPLACE8) {
+				dst = mov_rdisp8r(dst, src.base, src.disp, reg, inst->extra.size);
+			} else {
+				dst = mov_ir(dst, src.disp, reg, inst->extra.size);
+			}
+		} else if(src.mode == MODE_REG_DIRECT) {
+			printf("mov_rrdisp8 from reg %d to offset %d from reg %d (%d)\n", src.base, (inst->dst.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * inst->dst.params.regs.pri, CONTEXT, inst->dst.params.regs.pri);
+			dst = mov_rrdisp8(dst, src.base, CONTEXT, (inst->dst.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * inst->dst.params.regs.pri, inst->extra.size);
+		} else {
+			dst = mov_irdisp8(dst, src.disp, CONTEXT, (inst->dst.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs)) + 4 * inst->dst.params.regs.pri, inst->extra.size);
+		}
 		break;
+	case MODE_AREG_PREDEC:
+		dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1);
+		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
+			dst = sub_ir(dst, dec_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D);
+		} else {
+			dst = sub_irdisp8(dst, dec_amount, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D);
+		}
 	case MODE_AREG_INDIRECT:
-		
+	case MODE_AREG_POSTINC:
+		if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
+			dst = mov_rr(dst, opts->aregs[inst->dst.params.regs.pri], SCRATCH2, SZ_D);
+		} else {
+			dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SCRATCH2, SZ_D);
+		}
+		if (src.mode == MODE_REG_DIRECT) {
+			if (src.base != SCRATCH1) {
+				dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size);
+			}
+		} else if (src.mode == MODE_REG_DISPLACE8) {
+			dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size);
+		} else {
+			dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
+		}
+		switch (inst->extra.size)
+		{
+		case OPSIZE_BYTE:
+			dst = call(dst, (char *)m68k_write_byte);
+			break;
+		case OPSIZE_WORD:
+			dst = call(dst, (char *)m68k_write_word);
+			break;
+		case OPSIZE_LONG:
+			dst = call(dst, (char *)m68k_write_long_highfirst);
+			break;
+		}
+		if (inst->dst.addr_mode == MODE_AREG_POSTINC) {
+			inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1);
+			if (opts->aregs[inst->dst.params.regs.pri] >= 0) {
+				dst = add_ir(dst, inc_amount, opts->aregs[inst->dst.params.regs.pri], SZ_D);
+			} else {
+				dst = add_irdisp8(dst, inc_amount, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D);
+			}
+		}
 		break;
+	default:
+		printf("address mode %d not implemented (move dst)\n", inst->dst.addr_mode);
+		exit(1);
 	}
+
+	//add cycles for prefetch
+	dst = cycles(dst, BUS);
+	//update flags
+	dst = mov_ir(dst, 0, FLAG_V, SZ_B);
+	dst = mov_ir(dst, 0, FLAG_C, SZ_B);
+	dst = cmp_ir(dst, 0, flags_reg, inst->extra.size);
+	dst = setcc_r(dst, CC_Z, FLAG_Z);
+	dst = setcc_r(dst, CC_S, FLAG_N);
+	dst = check_cycles(dst);
+	return dst;
+}
+
+uint8_t * translate_m68k_lea(uint8_t * dst, m68kinst * inst, x86_68k_options * opts)
+{
+    int8_t dst_reg = native_reg(&(inst->dst), opts);
+    switch(inst->src.addr_mode)
+    {
+    case MODE_AREG_INDIRECT:
+        dst = cycles(dst, BUS);
+        if (opts->aregs[inst->src.params.regs.pri] >= 0) {
+            if (dst_reg >= 0) {
+                dst = mov_rr(dst, opts->aregs[inst->src.params.regs.pri], dst_reg, SZ_D);
+            } else {
+                dst = mov_rrdisp8(dst, opts->aregs[inst->src.params.regs.pri], CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D);
+            }
+        } else {
+            if (dst_reg >= 0) {
+                dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, dst_reg, SZ_D);
+            } else {
+                dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, SCRATCH1, SZ_D);
+                dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D);
+            }
+        }
+        dst = check_cycles(dst);
+        break;
+    case MODE_ABSOLUTE:
+        dst = cycles(dst, BUS);
+        dst = check_cycles(dst);
+    case MODE_ABSOLUTE_SHORT:
+        dst = cycles(dst, BUS);
+        dst = check_cycles(dst);
+        dst = cycles(dst, BUS);
+        if (dst_reg >= 0) {
+            dst = mov_ir(dst, inst->src.params.immed, dst_reg, SZ_D);
+        } else {
+            dst = mov_irdisp8(dst, inst->src.params.immed, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->dst.params.regs.pri, SZ_D);
+        }
+        dst = check_cycles(dst);
+        break;
+    }
+	return dst;
+}
+
+uint8_t * translate_m68k_bsr(uint8_t * dst, m68kinst * inst, x86_68k_options * opts)
+{
+	//TODO: Add cycles
+	int32_t disp = inst->src.params.immed;
+	uint32_t after = inst->address + (inst->variant == VAR_BYTE ? 2 : (inst->variant == VAR_WORD ? 4 : 6));
+	dst = mov_ir(dst, after, SCRATCH1, SZ_D);
+	dst = push_r(dst, SCRATCH1);
+	dst = sub_ir(dst, 4, opts->aregs[7], SZ_D);
+	dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
+	dst = call(dst, (char *)m68k_write_long_highfirst);
+	printf("bsr@%X: after=%X, disp=%X, dest=%X\n", inst->address, after, disp, after+disp);
+	uint8_t * dest_addr = get_native_address(opts->native_code_map, after + disp);
+	if (!dest_addr) {
+		opts->deferred = defer_address(opts->deferred, after + disp, dst + 1);
+		//dummy address to be replaced later
+		dest_addr = dst + 5;
+	}
+	dst = call(dst, (char *)dest_addr);
+	//would add_ir(dst, 8, RSP, SZ_Q) be faster here?
+	dst = pop_r(dst, SCRATCH1);
+	return dst;
+}
+
+uint8_t * translate_m68k_bcc(uint8_t * dst, m68kinst * inst, x86_68k_options * opts)
+{
+	//TODO: Add cycles
+	int32_t disp = inst->src.params.immed;
+	uint32_t after = inst->address + (inst->variant == VAR_BYTE ? 2 : (inst->variant == VAR_WORD ? 4 : 6));
+	printf("bcc@%X: after=%X, disp=%X, dest=%X\n", inst->address, after, disp, after+disp);
+	uint8_t * dest_addr = get_native_address(opts->native_code_map, after + disp);
+	if (inst->extra.cond == COND_TRUE) {
+		if (!dest_addr) {
+			opts->deferred = defer_address(opts->deferred, after + disp, dst + 1);
+			//dummy address to be replaced later, make sure it generates a 4-byte displacement
+			dest_addr = dst + 256;
+		}
+		dst = jmp(dst, dest_addr);
+	} else {
+		uint8_t cond = CC_NZ;
+		switch (inst->extra.cond)
+		{
+		case COND_HIGH:
+			cond = CC_Z;
+		case COND_LOW_SAME:
+			dst = mov_rr(dst, FLAG_Z, SCRATCH1, SZ_B);
+			dst = or_rr(dst, FLAG_C, SCRATCH1, SZ_B);
+			break;
+		case COND_CARRY_CLR:
+			cond = CC_Z;
+		case COND_CARRY_SET:
+			dst = cmp_ir(dst, 0, FLAG_C, SZ_B);
+			break;
+		case COND_NOT_EQ:
+			cond = CC_Z;
+		case COND_EQ:
+			dst = cmp_ir(dst, 0, FLAG_Z, SZ_B);
+			break;
+		case COND_OVERF_CLR:
+			cond = CC_Z;
+		case COND_OVERF_SET:
+			dst = cmp_ir(dst, 0, FLAG_V, SZ_B);
+			break;
+		case COND_PLUS:
+			cond = CC_Z;
+		case COND_MINUS:
+			dst = cmp_ir(dst, 0, FLAG_N, SZ_B);
+			break;
+		case COND_GREATER_EQ:
+			cond = CC_Z;
+		case COND_LESS:
+			dst = cmp_rr(dst, FLAG_N, FLAG_V, SZ_B);
+			break;
+		case COND_GREATER:
+			cond = CC_Z;
+		case COND_LESS_EQ:
+			dst = mov_rr(dst, FLAG_V, SCRATCH1, SZ_B);
+			dst = xor_rr(dst, FLAG_N, SCRATCH1, SZ_B);
+			dst = or_rr(dst, FLAG_Z, SCRATCH1, SZ_B);
+			break;
+		}
+		if (!dest_addr) {
+			opts->deferred = defer_address(opts->deferred, after + disp, dst + 2);
+			//dummy address to be replaced later, make sure it generates a 4-byte displacement
+			dest_addr = dst + 256;
+		}
+		dst = jcc(dst, cond, dest_addr);
+	}
+	return dst;
+}
+
+uint8_t * translate_m68k_rts(uint8_t * dst, m68kinst * inst, x86_68k_options * opts)
+{
+	//TODO: Add cycles
+	dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
+	dst = add_ir(dst, 4, opts->aregs[7], SZ_D);
+	dst = call(dst, (char *)m68k_read_long_scratch1);
+	dst = cmp_rdisp8r(dst, RSP, 8, SCRATCH1, SZ_D);
+	dst = jcc(dst, CC_NZ, dst+3);
+	dst = retn(dst);
+	dst = jmp(dst, (char *)m68k_modified_ret_addr);
+	return dst;
 }
 
 uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts)
 {
-	int8_t reg_a, reg_b, flags_reg;
-	uint8_t dir = 0;
-	int32_t offset;
+	map_native_address(opts->native_code_map, inst->address, dst);
+	if (inst->op == M68K_MOVE) {
+		return translate_m68k_move(dst, inst, opts);
+	} else if(inst->op == M68K_LEA) {
+		return translate_m68k_lea(dst, inst, opts);
+	} else if(inst->op == M68K_BSR) {
+		return translate_m68k_bsr(dst, inst, opts);
+	} else if(inst->op == M68K_BCC) {
+		return translate_m68k_bcc(dst, inst, opts);
+	} else if(inst->op == M68K_RTS) {
+		return translate_m68k_rts(dst, inst, opts);
+	}
+	x86_ea src_op, dst_op;
+	if (inst->src.addr_mode != MODE_UNUSED) {
+		dst = translate_m68k_src(inst, &src_op, dst, opts);
+	}
+	if (inst->dst.addr_mode != MODE_UNUSED) {
+		dst = translate_m68k_dst(inst, &dst_op, dst, opts);
+	}
 	switch(inst->op)
 	{
 	case M68K_ABCD:
+		break;
 	case M68K_ADD:
+		dst = cycles(dst, BUS);
+		if (src_op.mode == MODE_REG_DIRECT) {
+			if (dst_op.mode == MODE_REG_DIRECT) {
+				dst = add_rr(dst, src_op.base, dst_op.base, inst->extra.size);
+			} else {
+				dst = add_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size);
+			}
+		} else if (src_op.mode == MODE_REG_DISPLACE8) {
+			dst = add_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size);
+		} else {
+			if (dst_op.mode == MODE_REG_DIRECT) {
+				dst = add_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
+			} else {
+				dst = add_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
+			}
+		}
+		dst = setcc_r(dst, CC_C, FLAG_C);
+		dst = setcc_r(dst, CC_Z, FLAG_Z);
+		dst = setcc_r(dst, CC_S, FLAG_N);
+		dst = setcc_r(dst, CC_O, FLAG_V);
+		dst = mov_rrind(dst, FLAG_C, CONTEXT, SZ_B);
+		dst = check_cycles(dst);
+		break;
 	case M68K_ADDX:
 	case M68K_AND:
 	case M68K_ANDI_CCR:
@@ -92,6 +621,28 @@
 	case M68K_CHK:
 	case M68K_CLR:
 	case M68K_CMP:
+		dst = cycles(dst, BUS);
+		if (src_op.mode == MODE_REG_DIRECT) {
+			if (dst_op.mode == MODE_REG_DIRECT) {
+				dst = cmp_rr(dst, src_op.base, dst_op.base, inst->extra.size);
+			} else {
+				dst = cmp_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size);
+			}
+		} else if (src_op.mode == MODE_REG_DISPLACE8) {
+			dst = cmp_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size);
+		} else {
+			if (dst_op.mode == MODE_REG_DIRECT) {
+				dst = cmp_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
+			} else {
+				dst = cmp_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
+			}
+		}
+		dst = setcc_r(dst, CC_C, FLAG_C);
+		dst = setcc_r(dst, CC_Z, FLAG_Z);
+		dst = setcc_r(dst, CC_S, FLAG_N);
+		dst = setcc_r(dst, CC_O, FLAG_V);
+		dst = check_cycles(dst);
+		break;
 	case M68K_DBCC:
 	case M68K_DIVS:
 	case M68K_DIVU:
@@ -99,95 +650,41 @@
 	case M68K_EORI_CCR:
 	case M68K_EORI_SR:
 	case M68K_EXG:
+	    dst = cycles(dst, 6);
+	    if (dst_op.mode == MODE_REG_DIRECT) {
+	        dst = mov_rr(dst, dst_op.base, SCRATCH2, SZ_D);
+	        if (src_op.mode == MODE_REG_DIRECT) {
+	            dst = mov_rr(dst, src_op.base, dst_op.base, SZ_D);
+	            dst = mov_rr(dst, SCRATCH2, src_op.base, SZ_D);
+	        } else {
+	            dst = mov_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, SZ_D);
+	            dst = mov_rrdisp8(dst, SCRATCH2, src_op.base, src_op.disp, SZ_D);
+	        }
+	    } else {
+	        dst = mov_rdisp8r(dst, dst_op.base, dst_op.disp, SCRATCH2, SZ_D);
+	        if (src_op.mode == MODE_REG_DIRECT) {
+	            dst = mov_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, SZ_D);
+	            dst = mov_rr(dst, SCRATCH2, src_op.base, SZ_D);
+	        } else {
+	            dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_D);
+	            dst = mov_rrdisp8(dst, SCRATCH1, dst_op.base, dst_op.disp, SZ_D);
+	            dst = mov_rrdisp8(dst, SCRATCH2, src_op.base, src_op.disp, SZ_D);
+	        }
+	    }
+	    dst = check_cycles(dst);
+	    break;
 	case M68K_EXT:
 	case M68K_ILLEGAL:
+		dst = call(dst, (uint8_t *)m68k_save_context);
+		dst = mov_rr(dst, CONTEXT, RDI, SZ_Q);
+		dst = call(dst, (uint8_t *)print_regs_exit);
+		break;
 	case M68K_JMP:
 	case M68K_JSR:
 	case M68K_LEA:
 	case M68K_LINK:
 	case M68K_LSL:
 	case M68K_LSR:
-	case M68K_MOVE:
-		
-		if ((inst->src.addr_mode == MODE_REG || inst->src.addr_mode == MODE_AREG || (inst->src.addr_mode == MODE_IMMEDIATE && inst->src.variant == VAR_QUICK)) && (inst->dst.addr_mode == MODE_REG || inst->dst.addr_mode == MODE_AREG)) {
-			dst = cycles(dst, BUS);
-			reg_a = native_reg(&(inst->src), opts);
-			reg_b = native_reg(&(inst->dst), opts);
-			dst = cycles(dst, BUS);
-			if (reg_a >= 0 && reg_b >= 0) {
-				dst = mov_rr(dst, reg_a, reg_b, inst->extra.size);
-				flags_reg = reg_b;
-			} else if(reg_a >= 0) {
-				offset = inst->dst.addr_mode == MODE_REG ? offsetof(m68k_context, dregs) : offsetof(m68k_context, aregs);
-				dst = mov_rrdisp8(dst, reg_a, CONTEXT, offset + 4 * inst->dst.params.regs.pri, inst->extra.size);
-				flags_reg = reg_a;
-			} else if(reg_b >= 0) {
-				if (inst->src.addr_mode == MODE_REG) {
-					dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, dregs) + 4 * inst->src.params.regs.pri, reg_b, inst->extra.size);
-				} else if(inst->src.addr_mode == MODE_AREG) {
-					dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + 4 * inst->src.params.regs.pri, reg_b, inst->extra.size);
-				} else {
-					dst = mov_i32r(dst, inst->src.params.u32, reg_b);
-				}
-				flags_reg = reg_b;
-			} else {
-				
-			}
-			dst = mov_i8r(dst, 0, FLAG_V);
-			dst = mov_i8r(dst, 0, FLAG_C);
-			switch (inst->extra.size)
-			{
-			case OPSIZE_BYTE:
-				dst = cmp_i8r(dst, 0, reg_b, SZ_B);
-				break;
-			case OPSIZE_WORD:
-				dst = cmp_i8r(dst, 0, reg_b, SZ_W);
-				break;
-			case OPSIZE_LONG:
-				dst = cmp_i8r(dst, 0, reg_b, SZ_D);
-				break;
-			}
-			dst = setcc_r(dst, CC_Z, FLAG_Z);
-			dst = setcc_r(dst, CC_S, FLAG_N);
-			dst = check_cycles(dst);
-		}
-		
-		if (reg_a >= 0 && reg_b >= 0) {
-			dst = cycles(dst, BUS);
-			dst = mov_rr(dst, reg_a, reg_b, inst->extra.size);
-			dst = mov_i8r(dst, 0, FLAG_V);
-			dst = mov_i8r(dst, 0, FLAG_C);
-			switch (inst->extra.size)
-			{
-			case OPSIZE_BYTE:
-				dst = cmp_i8r(dst, 0, reg_b, SZ_B);
-				break;
-			case OPSIZE_WORD:
-				dst = cmp_i8r(dst, 0, reg_b, SZ_W);
-				break;
-			case OPSIZE_LONG:
-				dst = cmp_i8r(dst, 0, reg_b, SZ_D);
-				break;
-			}
-			dst = setcc_r(dst, CC_Z, FLAG_Z);
-			dst = setcc_r(dst, CC_S, FLAG_N);
-			dst = check_cycles(dst);
-		} else if(reg_a >= 0 || reg_b >= 0) {
-			if (reg_a >= 0) {
-				switch (inst->dst.addr_mode)
-				{
-				case MODE_REG:
-					dst = cycles(dst, BUS);
-					dst = mov_rr(dst, reg_a, reg_b, inst->extra.size);
-					dst = check_cycles(dst);
-					break;
-				case MODE_AREG:
-					break;
-				}
-			} else {
-			}
-		}
-		break;
 	case M68K_MOVE_CCR:
 	case M68K_MOVE_FROM_SR:
 	case M68K_MOVE_SR:
@@ -217,6 +714,29 @@
 	case M68K_SCC:
 	case M68K_STOP:
 	case M68K_SUB:
+		dst = cycles(dst, BUS);
+		if (src_op.mode == MODE_REG_DIRECT) {
+			if (dst_op.mode == MODE_REG_DIRECT) {
+				dst = sub_rr(dst, src_op.base, dst_op.base, inst->extra.size);
+			} else {
+				dst = sub_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size);
+			}
+		} else if (src_op.mode == MODE_REG_DISPLACE8) {
+			dst = sub_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, inst->extra.size);
+		} else {
+			if (dst_op.mode == MODE_REG_DIRECT) {
+				dst = sub_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
+			} else {
+				dst = sub_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
+			}
+		}
+		dst = setcc_r(dst, CC_C, FLAG_C);
+		dst = setcc_r(dst, CC_Z, FLAG_Z);
+		dst = setcc_r(dst, CC_S, FLAG_N);
+		dst = setcc_r(dst, CC_O, FLAG_V);
+		dst = mov_rrind(dst, FLAG_C, CONTEXT, SZ_B);
+		dst = check_cycles(dst);
+		break;
 	case M68K_SUBX:
 	case M68K_SWAP:
 	case M68K_TAS:
@@ -227,5 +747,65 @@
 	case M68K_INVALID:
 		break;
 	}
+	return dst;
 }
 
+uint8_t * translate_m68k_stream(uint8_t * dst, uint8_t * dst_end, uint32_t address, m68k_context * context)
+{
+	m68kinst instbuf;
+	x86_68k_options * opts = context->options;
+	char disbuf[1024];
+	uint16_t *encoded = context->mem_pointers[0] + address/2, *next;
+	do {
+		do {
+			if (dst_end-dst < 128) {
+				puts("out of code memory");
+				exit(1);
+			}
+			next = m68k_decode(encoded, &instbuf, address);
+			address += (next-encoded)*2;
+			encoded = next;
+			m68k_disasm(&instbuf, disbuf);
+			printf("%X: %s\n", instbuf.address, disbuf);
+			dst = translate_m68k(dst, &instbuf, opts);
+		} while(instbuf.op != M68K_ILLEGAL && instbuf.op != M68K_RTS && instbuf.op != M68K_RTE && !(instbuf.op == M68K_BCC && instbuf.extra.cond == COND_TRUE) && instbuf.op != M68K_JMP);
+		process_deferred(opts);
+		if (opts->deferred) {
+			address = opts->deferred->address;
+			encoded = context->mem_pointers[0] + address/2;
+		} else {
+			encoded = NULL;
+		}
+	} while(encoded != NULL);
+	return dst;
+}
+
+void start_68k_context(m68k_context * context, uint32_t address)
+{
+	uint8_t * addr = get_native_address(context->native_code_map, address);
+	m68k_start_context(addr, context);
+}
+
+void init_x86_68k_opts(x86_68k_options * opts)
+{
+	opts->flags = 0;
+	for (int i = 0; i < 8; i++)
+		opts->dregs[i] = opts->aregs[i] = -1;
+	opts->dregs[0] = R10;
+	opts->dregs[1] = R11;
+	opts->dregs[2] = R12;
+	opts->aregs[0] = R13;
+	opts->aregs[1] = R14;
+	opts->aregs[7] = R15;
+	opts->native_code_map = malloc(sizeof(native_map_slot) * NATIVE_MAP_CHUNKS);
+	memset(opts->native_code_map, 0, sizeof(native_map_slot) * NATIVE_MAP_CHUNKS);
+	opts->deferred = NULL;
+}
+
+void init_68k_context(m68k_context * context, native_map_slot * native_code_map, void * opts)
+{
+	memset(context, 0, sizeof(m68k_context));
+	context->native_code_map = native_code_map;
+	context->options = opts;
+}
+