changeset 181:3b4ef459aa8d

Fix signed division with negative result, fix address reg destination with word-sized operand, fix cmpm decoding and code generation, fix unbalanced pop in bit instructions
author Mike Pavone <pavone@retrodev.com>
date Wed, 09 Jan 2013 21:08:37 -0800
parents 8b846bcff6a2
children 924af8b2f7a0
files 68kinst.c m68k_to_x86.c
diffstat 2 files changed, 104 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/68kinst.c	Tue Jan 08 09:34:46 2013 -0800
+++ b/68kinst.c	Wed Jan 09 21:08:37 2013 -0800
@@ -1006,7 +1006,8 @@
 					decoded->op = M68K_INVALID;
 					return start+1;
 				}
-				if (decoded->src.addr_mode == MODE_AREG) {
+				decoded->extra.size = size;
+				if (decoded->dst.addr_mode == MODE_AREG) {
 					//CMPM
 					decoded->src.addr_mode = decoded->dst.addr_mode = MODE_AREG_POSTINC;
 					decoded->src.params.regs.pri = decoded->dst.params.regs.pri;
@@ -1014,7 +1015,6 @@
 				} else {
 					//EOR
 					decoded->op = M68K_EOR;
-					decoded->extra.size = size;
 					decoded->src.addr_mode = MODE_REG;
 					decoded->src.params.regs.pri = m68k_reg_quick_field(*istream);
 				}
--- a/m68k_to_x86.c	Tue Jan 08 09:34:46 2013 -0800
+++ b/m68k_to_x86.c	Wed Jan 09 21:08:37 2013 -0800
@@ -105,7 +105,12 @@
 	int32_t dec_amount,inc_amount;
 	if (reg >= 0) {
 		ea->mode = MODE_REG_DIRECT;
-		ea->base = reg;
+		if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) {
+			out = movsx_rr(out, reg, SCRATCH1, SZ_W, SZ_D);
+			ea->base = SCRATCH1;
+		} else {
+			ea->base = reg;
+		}
 		return out;
 	}
 	switch (inst->src.addr_mode)
@@ -122,9 +127,15 @@
 			ea->base = CONTEXT;
 			ea->disp = reg_offset(&(inst->src));
 		} else {
-			out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, inst->extra.size);
+			if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) {
+				out = movsx_rdisp8r(out, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, SZ_W, SZ_D);
+			} else {
+				out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->src)), SCRATCH1, inst->extra.size);
+			}
 			ea->mode = MODE_REG_DIRECT;
 			ea->base = SCRATCH1;
+			//we're explicitly handling the areg dest here, so we exit immediately
+			return out;
 		}
 		break;
 	case MODE_AREG_PREDEC:
@@ -345,12 +356,21 @@
 		}
 		ea->mode = MODE_IMMED;
 		ea->disp = inst->src.params.immed;
-		break;
+		return out;
 	default:
 		m68k_disasm(inst, disasm_buf);
 		printf("%X: %s\naddress mode %d not implemented (src)\n", inst->address, disasm_buf, inst->src.addr_mode);
 		exit(1);
 	}
+	if (inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) {
+		if (ea->mode == MODE_REG_DIRECT) {
+			out = movsx_rr(out, ea->base, SCRATCH1, SZ_W, SZ_D);
+		} else {
+			out = movsx_rdisp8r(out, ea->base, ea->disp, SCRATCH1, SZ_W, SZ_D);
+			ea->mode = MODE_REG_DIRECT;
+		}
+		ea->base = SCRATCH1;
+	}
 	return out;
 }
 
@@ -736,24 +756,26 @@
 			flags_reg = src.base = SCRATCH1;
 		}
 	}
+	uint8_t size = inst->extra.size;
 	switch(inst->dst.addr_mode)
 	{
+	case MODE_AREG:
+		size = OPSIZE_LONG;
 	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);
+				dst = mov_rr(dst, src.base, reg, size);
 			} else if (src.mode == MODE_REG_DISPLACE8) {
-				dst = mov_rdisp8r(dst, src.base, src.disp, reg, inst->extra.size);
+				dst = mov_rdisp8r(dst, src.base, src.disp, reg, size);
 			} else {
-				dst = mov_ir(dst, src.disp, reg, inst->extra.size);
+				dst = mov_ir(dst, src.disp, reg, size);
 			}
 		} else if(src.mode == MODE_REG_DIRECT) {
-			dst = mov_rrdisp8(dst, src.base, CONTEXT, reg_offset(&(inst->dst)), inst->extra.size);
+			dst = mov_rrdisp8(dst, src.base, CONTEXT, reg_offset(&(inst->dst)), size);
 		} else {
-			dst = mov_irdisp8(dst, src.disp, CONTEXT, reg_offset(&(inst->dst)), inst->extra.size);
+			dst = mov_irdisp8(dst, src.disp, CONTEXT, reg_offset(&(inst->dst)), size);
 		}
-		dst = cmp_ir(dst, 0, flags_reg, inst->extra.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);
 		break;
@@ -2386,6 +2408,45 @@
 	return dst;
 }
 
+uint8_t * translate_m68k_cmp(uint8_t * dst, m68kinst * inst, x86_68k_options * opts)
+{
+	uint8_t size = inst->extra.size;
+	x86_ea src_op, dst_op;
+	dst = translate_m68k_src(inst, &src_op, dst, opts);
+	if (inst->dst.addr_mode == MODE_AREG_POSTINC) {
+		dst = push_r(dst, SCRATCH1);
+		dst = translate_m68k_dst(inst, &dst_op, dst, opts, 0);
+		dst = pop_r(dst, SCRATCH2);
+		src_op.base = SCRATCH2;
+	} else {
+		dst = translate_m68k_dst(inst, &dst_op, dst, opts, 0);
+		if (inst->dst.addr_mode == MODE_AREG && size == OPSIZE_WORD) {
+			size = OPSIZE_LONG;
+		}
+	}
+	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, size);
+		} else {
+			dst = cmp_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, size);
+		}
+	} else if (src_op.mode == MODE_REG_DISPLACE8) {
+		dst = cmp_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, size);
+	} else {
+		if (dst_op.mode == MODE_REG_DIRECT) {
+			dst = cmp_ir(dst, src_op.disp, dst_op.base, size);
+		} else {
+			dst = cmp_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);
+	return dst;
+}
+
 typedef uint8_t * (*shift_ir_t)(uint8_t * out, uint8_t val, uint8_t dst, uint8_t size);
 typedef uint8_t * (*shift_irdisp8_t)(uint8_t * out, uint8_t val, uint8_t dst_base, int8_t disp, uint8_t size);
 typedef uint8_t * (*shift_clr_t)(uint8_t * out, uint8_t dst, uint8_t size);
@@ -2527,6 +2588,8 @@
 	} else if(inst->op == M68K_INVALID) {
 		dst = mov_ir(dst, inst->address, SCRATCH1, SZ_D);
 		return call(dst, (uint8_t *)m68k_invalid);
+	} else if(inst->op == M68K_CMP) {
+		return translate_m68k_cmp(dst, inst, opts);
 	}
 	x86_ea src_op, dst_op;
 	if (inst->src.addr_mode != MODE_UNUSED) {
@@ -2535,25 +2598,27 @@
 	if (inst->dst.addr_mode != MODE_UNUSED) {
 		dst = translate_m68k_dst(inst, &dst_op, dst, opts, 0);
 	}
+	uint8_t size;
 	switch(inst->op)
 	{
 	//case M68K_ABCD:
 	//	break;
 	case M68K_ADD:
 		dst = cycles(dst, BUS);
+		size = inst->dst.addr_mode == MODE_AREG ? OPSIZE_LONG : inst->extra.size;
 		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);
+				dst = add_rr(dst, src_op.base, dst_op.base, size);
 			} else {
-				dst = add_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size);
+				dst = add_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, 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);
+			dst = add_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, size);
 		} else {
 			if (dst_op.mode == MODE_REG_DIRECT) {
-				dst = add_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
+				dst = add_ir(dst, src_op.disp, dst_op.base, size);
 			} else {
-				dst = add_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
+				dst = add_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, size);
 			}
 		}
 		dst = setcc_r(dst, CC_C, FLAG_C);
@@ -2735,9 +2800,9 @@
 					dst = btc_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size);
 				}
 			}
-		}
-		if (src_op.base == SCRATCH2) {
-			dst = pop_r(dst, SCRATCH2);
+			if (src_op.base == SCRATCH2) {
+				dst = pop_r(dst, SCRATCH2);
+			}
 		}
 		//x86 sets the carry flag to the value of the bit tested
 		//68K sets the zero flag to the complement of the bit tested
@@ -2748,28 +2813,6 @@
 		break;
 	/*case M68K_CHK:
 		break;*/
-	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);
-		break;
 	case M68K_DIVS:
 	case M68K_DIVU:
 		//TODO: Trap on division by zero
@@ -2807,8 +2850,17 @@
 			dst = div_r(dst, SCRATCH2, SZ_D);
 		}
 		dst = cmp_ir(dst, 0x10000, RAX, SZ_D);
-		norm_off = dst+1;
-		dst = jcc(dst, CC_NC, dst+2);
+		if (inst->op == M68K_DIVS) {
+			uint8_t * skip_sec_check = dst + 1;
+			dst = jcc(dst, CC_C, dst+2);
+			dst = cmp_ir(dst, -0x10000, RAX, SZ_D);
+			norm_off = dst+1;
+			dst = jcc(dst, CC_LE, dst+2);
+			*skip_sec_check = dst - (skip_sec_check+1);
+		} else {
+			norm_off = dst+1;
+			dst = jcc(dst, CC_NC, dst+2);
+		}
 		if (dst_op.mode == MODE_REG_DIRECT) {
 			dst = mov_rr(dst, RDX, dst_op.base, SZ_W);
 			dst = shl_ir(dst, 16, dst_op.base, SZ_D);
@@ -3368,20 +3420,21 @@
 	case M68K_STOP:
 		break;*/
 	case M68K_SUB:
+		size = inst->dst.addr_mode == MODE_AREG ? OPSIZE_LONG : inst->extra.size;
 		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);
+				dst = sub_rr(dst, src_op.base, dst_op.base, size);
 			} else {
-				dst = sub_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, inst->extra.size);
+				dst = sub_rrdisp8(dst, src_op.base, dst_op.base, dst_op.disp, 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);
+			dst = sub_rdisp8r(dst, src_op.base, src_op.disp, dst_op.base, size);
 		} else {
 			if (dst_op.mode == MODE_REG_DIRECT) {
-				dst = sub_ir(dst, src_op.disp, dst_op.base, inst->extra.size);
+				dst = sub_ir(dst, src_op.disp, dst_op.base, size);
 			} else {
-				dst = sub_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, inst->extra.size);
+				dst = sub_irdisp8(dst, src_op.disp, dst_op.base, dst_op.disp, size);
 			}
 		}
 		dst = setcc_r(dst, CC_C, FLAG_C);
@@ -3442,10 +3495,10 @@
 		} else { //M68000 doesn't support immedate operand for tst, so this must be MODE_REG_DISPLACE8
 			dst = cmp_irdisp8(dst, 0, src_op.base, src_op.disp, inst->extra.size);
 		}
-		dst = setcc_r(dst, CC_C, FLAG_C);
+		dst = mov_ir(dst, 0, FLAG_C, SZ_B);
 		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_ir(dst, 0, FLAG_V, SZ_B);
 		break;
 	case M68K_UNLK:
 		dst = cycles(dst, BUS);