changeset 184:ebcbdd1c4cc8

Fix a bunch of bugs in the CPU core, add a 68K debugger
author Mike Pavone <pavone@retrodev.com>
date Sun, 13 Jan 2013 13:01:13 -0800
parents 2f08d9e90a4c
children b204fbed4efe
files 68kinst.c blastem.c m68k_to_x86.c m68k_to_x86.h
diffstat 4 files changed, 417 insertions(+), 64 deletions(-) [+]
line wrap: on
line diff
--- a/68kinst.c	Wed Jan 09 22:31:07 2013 -0800
+++ b/68kinst.c	Sun Jan 13 13:01:13 2013 -0800
@@ -479,13 +479,13 @@
 					break;
 #endif
 				}
+				decoded->dst.addr_mode = MODE_REG;
+				decoded->dst.addr_mode = m68k_reg_quick_field(*istream);
 				istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
 					return start+1;
 				}
-				decoded->dst.addr_mode = MODE_REG;
-				decoded->dst.addr_mode = m68k_reg_quick_field(*istream);
 			} else {
 				opmode = (*istream >> 3) & 0x7;
 				if ((*istream & 0xB80) == 0x880 && opmode != MODE_REG && opmode != MODE_AREG) {
@@ -959,13 +959,13 @@
 				//SUBX
 				decoded->op = M68K_SUBX;
 				decoded->extra.size = size;
-				istream = m68k_decode_op(istream, size, &(decoded->src));
-				if (!istream) {
-					decoded->op = M68K_INVALID;
-					return start+1;
+				decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream);
+				decoded->src.params.regs.pri = *istream & 0x7;
+				if (*istream & 0x8) {
+					decoded->dst.addr_mode = decoded->src.addr_mode = MODE_AREG_PREDEC;
+				} else {
+					decoded->dst.addr_mode = decoded->src.addr_mode = MODE_REG;
 				}
-				decoded->dst.addr_mode = decoded->src.addr_mode;
-				decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream);
 			}
 		} else {
 			if (size == OPSIZE_INVALID) {
@@ -1001,6 +1001,7 @@
 					return start+1;
 				}
 			} else {
+				reg = m68k_reg_quick_field(*istream);
 				istream = m68k_decode_op(istream, size, &(decoded->dst));
 				if (!istream) {
 					decoded->op = M68K_INVALID;
@@ -1011,12 +1012,12 @@
 					//CMPM
 					decoded->src.addr_mode = decoded->dst.addr_mode = MODE_AREG_POSTINC;
 					decoded->src.params.regs.pri = decoded->dst.params.regs.pri;
-					decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream);
+					decoded->dst.params.regs.pri = reg;
 				} else {
 					//EOR
 					decoded->op = M68K_EOR;
 					decoded->src.addr_mode = MODE_REG;
-					decoded->src.params.regs.pri = m68k_reg_quick_field(*istream);
+					decoded->src.params.regs.pri = reg;
 				}
 			}
 		} else {
@@ -1142,15 +1143,14 @@
 			} else {
 				//ADDX
 				decoded->op = M68K_ADDX;
-				//FIXME: Size is not technically correct
 				decoded->extra.size = size;
-				istream = m68k_decode_op(istream, size, &(decoded->src));
-				if (!istream) {
-					decoded->op = M68K_INVALID;
-					return start+1;
+				decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream);
+				decoded->src.params.regs.pri = *istream & 0x7;
+				if (*istream & 0x8) {
+					decoded->dst.addr_mode = decoded->src.addr_mode = MODE_AREG_PREDEC;
+				} else {
+					decoded->dst.addr_mode = decoded->src.addr_mode = MODE_REG;
 				}
-				decoded->dst.addr_mode = decoded->src.addr_mode;
-				decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream);
 			}
 		} else {
 			if (size == OPSIZE_INVALID) {
--- a/blastem.c	Wed Jan 09 22:31:07 2013 -0800
+++ b/blastem.c	Sun Jan 13 13:01:13 2013 -0800
@@ -6,6 +6,7 @@
 #include "blastem.h"
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 #define CARTRIDGE_WORDS 0x200000
 #define RAM_WORDS 32 * 1024
@@ -585,6 +586,232 @@
 	return context;
 }
 
+typedef struct bp_def {
+	struct bp_def * next;
+	uint32_t address;
+	uint32_t index;
+} bp_def;
+
+bp_def * breakpoints = NULL;
+uint32_t bp_index = 0;
+
+bp_def ** find_breakpoint(bp_def ** cur, uint32_t address)
+{
+	while (*cur) {
+		if ((*cur)->address == address) {
+			break;
+		}
+		cur = &((*cur)->next);
+	}
+	return cur;
+}
+
+bp_def ** find_breakpoint_idx(bp_def ** cur, uint32_t index)
+{
+	while (*cur) {
+		if ((*cur)->index == index) {
+			break;
+		}
+		cur = &((*cur)->next);
+	}
+	return cur;
+}
+
+char * find_param(char * buf)
+{
+	for (; *buf; buf++) {
+		if (*buf == ' ') {
+			if (*(buf+1)) {
+				return buf+1;
+			}
+		}
+	}
+	return NULL;
+}
+
+void strip_nl(char * buf)
+{
+	for(; *buf; buf++) {
+		if (*buf == '\n') {
+			*buf = 0;
+			return;
+		}
+	}
+}
+
+m68k_context * debugger(m68k_context * context, uint32_t address)
+{
+	static char last_cmd[1024];
+	char input_buf[1024];
+	static uint32_t branch_t;
+	static uint32_t branch_f;
+	m68kinst inst;
+	//probably not necessary, but let's play it safe
+	address &= 0xFFFFFF;
+	if (address == branch_t) {
+		bp_def ** f_bp = find_breakpoint(&breakpoints, branch_f);
+		if (!*f_bp) {
+			remove_breakpoint(context, branch_f);
+		}
+		branch_t = branch_f = 0;
+	} else if(address == branch_f) {
+		bp_def ** t_bp = find_breakpoint(&breakpoints, branch_t);
+		if (!*t_bp) {
+			remove_breakpoint(context, branch_t);
+		}
+		branch_t = branch_f = 0;
+	}
+	//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);
+	} else {
+		remove_breakpoint(context, address);
+	}
+	uint16_t * pc;
+	if (address < 0x400000) {
+		pc = cart + address/2;
+	} else if(address > 0xE00000) {
+		pc = ram + (address & 0xFFFF)/2;
+	} else {
+		fprintf(stderr, "Entered debugger at address %X\n", address);
+		exit(1);
+	}
+	uint16_t * after_pc = m68k_decode(pc, &inst, address);
+	m68k_disasm(&inst, input_buf);
+	printf("%X: %s\n", address, input_buf);
+	uint32_t after = address + (after_pc-pc)*2;
+	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 'c':
+				puts("Continuing");
+				debugging = 0;
+				break;
+			case 'b':
+				param = find_param(input_buf);
+				if (!param) {
+					fputs("b command requires a parameter\n", stderr);
+					break;
+				}
+				value = strtol(param, NULL, 16);
+				insert_breakpoint(context, value, (uint8_t *)debugger);
+				new_bp = malloc(sizeof(bp_def));
+				new_bp->next = breakpoints;
+				new_bp->address = value;
+				new_bp->index = bp_index++;
+				breakpoints = new_bp;
+				printf("Breakpoint %d set at %X\n", new_bp->index, value);
+				break;
+			case 'a':
+				param = find_param(input_buf);
+				if (!param) {
+					fputs("a command requires a parameter\n", stderr);
+					break;
+				}
+				value = strtol(param, NULL, 16);
+				insert_breakpoint(context, value, (uint8_t *)debugger);
+				debugging = 0;
+				break;
+			case 'd':
+				param = find_param(input_buf);
+				if (!param) {
+					fputs("b command requires a parameter\n", stderr);
+					break;
+				}
+				value = atoi(param);
+				this_bp = find_breakpoint_idx(&breakpoints, value);
+				if (!*this_bp) {
+					fprintf(stderr, "Breakpoint %d does not exist\n", value);
+					break;
+				}
+				new_bp = *this_bp;
+				*this_bp = (*this_bp)->next;
+				free(new_bp);
+				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;
+				}
+				if (param[0] == 'd' && param[1] >= '0' && param[1] <= '7') {
+					value = context->dregs[param[1]-'0'];
+				} else if (param[0] == 'a' && param[1] >= '0' && param[1] <= '7') {
+					value = context->aregs[param[1]-'0'];
+				} else if (param[0] == 'S' && param[1] == 'R') {
+					value = (context->status << 8);
+					for (int flag = 0; flag < 5; flag++) {
+						value |= context->flags[flag] << (4-flag);
+					}
+				} else if (param[0] == '0' && param[1] == 'x') {
+					uint32_t p_addr = strtol(param+2, NULL, 16);
+					value = read_dma_value(p_addr/2);
+				} else {
+					fprintf(stderr, "Unrecognized parameter to p: %s\n", param);
+					break;
+				}
+				printf(format, param, value);
+				break;
+			case 'n':
+				//TODO: Deal with jmp, dbcc, rtr and rte
+				if (inst.op == M68K_RTS) {
+					after = (read_dma_value(context->aregs[7]/2) << 16) | read_dma_value(context->aregs[7]/2 + 1);
+				} else if(inst.op == M68K_BCC && inst.extra.cond != COND_FALSE) {
+					if (inst.extra.cond = COND_TRUE) {
+						after = inst.address + 2 + inst.src.params.immed;
+					} else {
+						branch_f = after;
+						branch_t = inst.address + 2 + inst.src.params.immed;
+						insert_breakpoint(context, branch_t, (uint8_t *)debugger);
+					}
+				}
+				insert_breakpoint(context, after, (uint8_t *)debugger);
+				debugging = 0;
+				break;
+			case 'q':
+				puts("Quitting");
+				exit(0);
+				break;
+			default:
+				fprintf(stderr, "Unrecognized debugger command %s\n", input_buf);
+				break;
+		}
+	}
+	return context;
+}
+
 int main(int argc, char ** argv)
 {
 	if (argc < 2) {
@@ -595,16 +822,27 @@
 		fprintf(stderr, "Failed to open %s for reading\n", argv[1]);
 		return 1;
 	}
-	int width = 320;
-	int height = 240;
-	if (argc > 2) {
-		width = atoi(argv[2]);
-		if (argc > 3) {
-			height = atoi(argv[3]);
-		} else {
-			height = (width/320) * 240;
+	int width = -1;
+	int height = -1;
+	int debug = 0;
+	for (int i = 2; i < argc; i++) {
+		if (argv[i][0] == '-') {
+			switch(argv[i][1]) {
+			case 'd':
+				debug = 1;
+				break;
+			default:
+				fprintf(stderr, "Unrecognized switch %s\n", argv[i]);
+				return 1;
+			}
+		} else if (width < 0) {
+			width = atoi(argv[i]);
+		} else if (height < 0) {
+			height = atoi(argv[i]);
 		}
 	}
+	width = width < 320 ? 320 : width;
+	height = height < 240 ? (width/320) * 240 : height;
 	render_init(width, height);
 	
 	x86_68k_options opts;
@@ -629,6 +867,9 @@
 	translate_m68k_stream(address, &context);*/
 	address = cart[2] << 16 | cart[3];
 	translate_m68k_stream(address, &context);
+	if (debug) {
+		insert_breakpoint(&context, address, (uint8_t *)debugger);
+	}
 	m68k_reset(&context);
 	return 0;
 }
--- a/m68k_to_x86.c	Wed Jan 09 22:31:07 2013 -0800
+++ b/m68k_to_x86.c	Sun Jan 13 13:01:13 2013 -0800
@@ -38,6 +38,7 @@
 void m68k_write_long_highfirst();
 void m68k_write_byte();
 void m68k_save_context();
+void m68k_load_context();
 void m68k_modified_ret_addr();
 void m68k_native_addr();
 void m68k_native_addr_and_sync();
@@ -175,7 +176,7 @@
 			}
 		}
 		ea->mode = MODE_REG_DIRECT;
-		ea->base = SCRATCH1;
+		ea->base = (inst->dst.addr_mode == MODE_AREG_PREDEC && inst->op != M68K_MOVE) ? SCRATCH2 : SCRATCH1;
 		break;
 	case MODE_AREG_DISPLACE:
 		out = cycles(out, BUS);
@@ -392,6 +393,9 @@
 		ea->disp = reg_offset(&(inst->dst));
 		break;
 	case MODE_AREG_PREDEC:
+		if (inst->src.addr_mode == MODE_AREG_PREDEC) {
+			out = push_r(out, SCRATCH1);
+		}
 		dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->dst.params.regs.pri == 7 ? 2 : 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);
@@ -421,11 +425,16 @@
 				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);
+		if (inst->src.addr_mode == MODE_AREG_PREDEC) {
+			//restore src operand to SCRATCH2
+			out =pop_r(out, SCRATCH2);
 		} else {
-			out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D);
+			//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, reg_offset(&(inst->dst)), SCRATCH2, SZ_D);
+			}
 		}
 		
 		if (inst->dst.addr_mode == MODE_AREG_POSTINC) {
@@ -644,6 +653,13 @@
 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) {
+		if (inst->dst.addr_mode == MODE_AREG_PREDEC && inst->src.addr_mode == MODE_AREG_PREDEC && inst->op != M68K_MOVE) {
+			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, reg_offset(&(inst->dst)), SCRATCH2, SZ_D);
+			}
+		}
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
@@ -737,9 +753,11 @@
 	x86_ea src;
 	dst = translate_m68k_src(inst, &src, dst, opts);
 	reg = native_reg(&(inst->dst), opts);
-	//update statically set flags
-	dst = mov_ir(dst, 0, FLAG_V, SZ_B);
-	dst = mov_ir(dst, 0, FLAG_C, SZ_B);
+	if (inst->dst.addr_mode != MODE_AREG) {
+		//update statically set flags
+		dst = mov_ir(dst, 0, FLAG_V, SZ_B);
+		dst = mov_ir(dst, 0, FLAG_C, SZ_B);
+	}
 	
 	if (src.mode == MODE_REG_DIRECT) {
 		flags_reg = src.base;
@@ -775,9 +793,11 @@
 		} else {
 			dst = mov_irdisp8(dst, src.disp, CONTEXT, reg_offset(&(inst->dst)), size);
 		}
-		dst = cmp_ir(dst, 0, flags_reg, size);
-		dst = setcc_r(dst, CC_Z, FLAG_Z);
-		dst = setcc_r(dst, CC_S, FLAG_N);
+		if (inst->dst.addr_mode != MODE_AREG) {
+			dst = cmp_ir(dst, 0, flags_reg, size);
+			dst = setcc_r(dst, CC_Z, FLAG_Z);
+			dst = setcc_r(dst, CC_S, FLAG_N);
+		}
 		break;
 	case MODE_AREG_PREDEC:
 		dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->dst.params.regs.pri == 7 ? 2 : 1));
@@ -802,9 +822,11 @@
 		} else {
 			dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
 		}
-		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);
+		if (inst->dst.addr_mode != MODE_AREG) {
+			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);
+		}
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
@@ -843,9 +865,11 @@
 		} else {
 			dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
 		}
-		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);
+		if (inst->dst.addr_mode != MODE_AREG) {
+			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);
+		}
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
@@ -906,9 +930,20 @@
 		if (inst->dst.params.regs.displacement) {
 			dst = add_ir(dst, inst->dst.params.regs.displacement, SCRATCH2, SZ_D);
 		}
-		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);
+		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);
+		}
+		if (inst->dst.addr_mode != MODE_AREG) {
+			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);
+		}
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
@@ -934,9 +969,11 @@
 		} else {
 			dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
 		}
-		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);
+		if (inst->dst.addr_mode != MODE_AREG) {
+			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);
+		}
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
@@ -967,9 +1004,11 @@
 			dst = cycles(dst, BUS);
 		}
 		dst = mov_ir(dst, inst->dst.params.immed, SCRATCH2, SZ_D);
-		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);
+		if (inst->dst.addr_mode != MODE_AREG) {
+			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);
+		}
 		switch (inst->extra.size)
 		{
 		case OPSIZE_BYTE:
@@ -2621,11 +2660,13 @@
 				dst = add_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, 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);
+		if (inst->dst.addr_mode != MODE_AREG) {
+			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 = m68k_save_result(inst, dst, opts);
 		break;
 	case M68K_ADDX:
@@ -2647,7 +2688,8 @@
 			}
 		}
 		dst = setcc_r(dst, CC_C, FLAG_C);
-		dst = setcc_r(dst, CC_Z, FLAG_Z);
+		dst = jcc(dst, CC_Z, dst+4);
+		dst = mov_ir(dst, 0, FLAG_Z, SZ_B);
 		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);
@@ -2710,6 +2752,7 @@
 		break;
 	case M68K_ASL:
 	case M68K_LSL:
+		//TODO: Check overflow flag behavior
 		dst = translate_shift(dst, inst, &src_op, &dst_op, opts, shl_ir, shl_irdisp8, shl_clr, shl_clrdisp8, shr_ir, shr_irdisp8);
 		break;
 	case M68K_ASR:
@@ -3294,6 +3337,7 @@
 			dst = cmp_ir(dst, 0, dst_op.base, inst->extra.size);
 			dst = setcc_r(dst, CC_Z, FLAG_Z);
 			dst = setcc_r(dst, CC_S, FLAG_N);
+			dst = mov_rrind(dst, FLAG_C, CONTEXT, SZ_B);
 			dst = m68k_save_result(inst, dst, opts);
 		} else {
 			if (src_op.mode == MODE_IMMED) {
@@ -3313,6 +3357,7 @@
 					}
 				}
 				dst = setcc_r(dst, CC_C, FLAG_C);
+				dst = mov_rrind(dst, FLAG_C, CONTEXT, SZ_B);
 			} else {
 				if (src_op.mode == MODE_REG_DIRECT) {
 					if (src_op.base != SCRATCH1) {
@@ -3364,10 +3409,12 @@
 					}
 				}
 				dst = setcc_r(dst, CC_C, FLAG_C);
+				dst = mov_rr(dst, FLAG_C, CONTEXT, SZ_B);
 				end_off = dst + 1;
 				dst = jmp(dst, dst+2);
 				*zero_off = dst - (zero_off+1);
-				dst = mov_ir(dst, 0, FLAG_C, SZ_B);
+				//Carry flag is set to X flag when count is 0, this is different from ROR/ROL
+				dst = mov_rindr(dst, CONTEXT, FLAG_C, SZ_B);
 				*end_off = dst - (end_off+1);
 			}
 			if (dst_op.mode == MODE_REG_DIRECT) {
@@ -3437,11 +3484,13 @@
 				dst = sub_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, 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);
+		if (inst->dst.addr_mode != MODE_AREG) {
+			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 = m68k_save_result(inst, dst, opts);
 		break;
 	case M68K_SUBX:
@@ -3463,7 +3512,8 @@
 			}
 		}
 		dst = setcc_r(dst, CC_C, FLAG_C);
-		dst = setcc_r(dst, CC_Z, FLAG_Z);
+		dst = jcc(dst, CC_Z, dst+4);
+		dst = mov_ir(dst, 0, FLAG_Z, SZ_B);
 		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);
@@ -3562,6 +3612,11 @@
 				dst = call(dst, (uint8_t *)exit);
 				break;
 			}
+			uint8_t * existing = get_native_address(opts->native_code_map, address);
+			if (existing) {
+				dst = jmp(dst, existing);
+				break;
+			}
 			next = m68k_decode(encoded, &instbuf, address);
 			address += (next-encoded)*2;
 			encoded = next;
@@ -3599,6 +3654,61 @@
 	return ret;
 }
 
+void insert_breakpoint(m68k_context * context, uint32_t address, uint8_t * bp_handler)
+{
+	static uint8_t * bp_stub = NULL;
+	uint8_t * native = get_native_address_trans(context, address);
+	uint8_t * start_native = native;
+	native = mov_ir(native, address, SCRATCH1, SZ_D);
+	if (!bp_stub) {
+		x86_68k_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 = 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 *)m68k_save_context);
+		dst = push_r(dst, SCRATCH1);
+		dst = mov_rr(dst, CONTEXT, RDI, SZ_Q);
+		dst = mov_rr(dst, SCRATCH1, RSI, SZ_D);
+		dst = call(dst, bp_handler);
+		dst = mov_rr(dst, RAX, CONTEXT, SZ_Q);
+		//Restore context
+		dst = call(dst, (uint8_t *)m68k_load_context);
+		dst = pop_r(dst, SCRATCH1);
+		//do prologue stuff
+		dst = cmp_rr(dst, CYCLES, LIMIT, SZ_D);
+		uint8_t * jmp_off = dst+1;
+		dst = jcc(dst, CC_NC, dst + 7);
+		dst = call(dst, (uint8_t *)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 remove_breakpoint(m68k_context * context, uint32_t address)
+{
+	uint8_t * native = get_native_address(context->native_code_map, address);
+	check_cycles_int(native, address);
+}
+
 void start_68k_context(m68k_context * context, uint32_t address)
 {
 	uint8_t * addr = get_native_address(context->native_code_map, address);
--- a/m68k_to_x86.h	Wed Jan 09 22:31:07 2013 -0800
+++ b/m68k_to_x86.h	Sun Jan 13 13:01:13 2013 -0800
@@ -54,4 +54,6 @@
 void init_x86_68k_opts(x86_68k_options * opts);
 void init_68k_context(m68k_context * context, native_map_slot * native_code_map, void * opts);
 void m68k_reset(m68k_context * context);
+void insert_breakpoint(m68k_context * context, uint32_t address, uint8_t * bp_handler);
+void remove_breakpoint(m68k_context * context, uint32_t address);