changeset 82:6331ddec228f

Initial stab at interrupt support. Make native code offsets bigger so I don't have to worry about overflowing the offset. Implement neg and not (untested).
author Mike Pavone <pavone@retrodev.com>
date Wed, 26 Dec 2012 11:09:04 -0800
parents 6d231dbe75ab
children 2c7267617d71
files gen_x86.c gen_x86.h m68k_to_x86.c m68k_to_x86.h runtime.S
diffstat 5 files changed, 292 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/gen_x86.c	Sat Dec 22 21:37:25 2012 -0800
+++ b/gen_x86.c	Wed Dec 26 11:09:04 2012 -0800
@@ -37,6 +37,7 @@
 #define OP_CALL 0xE8
 #define OP_JMP 0xE9
 #define OP_JMP_BYTE 0xEB
+#define OP_NOT_NEG 0xF6
 #define OP_SINGLE_EA 0xFF
 
 #define OP2_JCC 0x80
@@ -68,6 +69,10 @@
 #define OP_EX_BTR 0x6
 #define OP_EX_BTC 0x7
 
+#define OP_EX_TEST_I 0x0
+#define OP_EX_NOT    0x2
+#define OP_EX_NEG    0x3
+
 #define OP_EX_INC     0x0
 #define OP_EX_DEC     0x1
 #define OP_EX_CALL_EA 0x2
@@ -222,6 +227,61 @@
 	return out;
 }
 
+uint8_t * x86_r_size(uint8_t * out, uint8_t opcode, uint8_t opex, uint8_t dst, uint8_t size)
+{
+	uint8_t tmp;
+	if (size == SZ_W) {
+		*(out++) = PRE_SIZE;
+	}
+	if (size == SZ_Q || dst >= R8) {
+		*out = PRE_REX;
+		if (size == SZ_Q) {
+			*out |= REX_QUAD;
+		}
+		if (dst >= R8) {
+			*out |= REX_RM_FIELD;
+			dst -= (R8 - X86_R8);
+		}
+		out++;
+	}
+	if (size == SZ_B) {
+		if (dst >= AH && dst <= BH) {
+			dst -= (AH-X86_AH);
+		}
+	} else {
+		opcode |= BIT_SIZE;
+	}
+	*(out++) = opcode;
+	*(out++) = MODE_REG_DIRECT | dst | (opex << 3);
+	return out;
+}
+
+uint8_t * x86_rdisp8_size(uint8_t * out, uint8_t opcode, uint8_t opex, uint8_t dst, int8_t disp, uint8_t size)
+{
+	uint8_t tmp;
+	if (size == SZ_W) {
+		*(out++) = PRE_SIZE;
+	}
+	if (size == SZ_Q || dst >= R8) {
+		*out = PRE_REX;
+		if (size == SZ_Q) {
+			*out |= REX_QUAD;
+		}
+		if (dst >= R8) {
+			*out |= REX_RM_FIELD;
+			dst -= (R8 - X86_R8);
+		}
+		out++;
+	}
+	if (size != SZ_B) {
+		opcode |= BIT_SIZE;
+	}
+	*(out++) = opcode;
+	*(out++) = MODE_REG_DISPLACE8 | dst | (opex << 3);
+	*(out++) = disp;
+	return out;
+}
+
 uint8_t * x86_ir(uint8_t * out, uint8_t opcode, uint8_t op_ex, uint8_t al_opcode, int32_t val, uint8_t dst, uint8_t size)
 {
 	uint8_t sign_extend = 0;
@@ -297,9 +357,6 @@
 		}
 		out++;
 	}
-	if (dst >= AH && dst <= BH) {
-		dst -= (AH-X86_AH);
-	}
 	if (size != SZ_B) {
 		opcode |= BIT_SIZE;
 	}
@@ -718,6 +775,26 @@
 	return x86_rrdisp8_sizedir(out, OP_CMP, dst, src_base, disp, size, BIT_DIR);
 }
 
+uint8_t * not_r(uint8_t * out, uint8_t dst, uint8_t size)
+{
+	return x86_r_size(out, OP_NOT_NEG, OP_EX_NOT, dst, size);
+}
+
+uint8_t * neg_r(uint8_t * out, uint8_t dst, uint8_t size)
+{
+	return x86_r_size(out, OP_NOT_NEG, OP_EX_NEG, dst, size);
+}
+
+uint8_t * not_rdisp8(uint8_t * out, uint8_t dst_base, int8_t disp, uint8_t size)
+{
+	return x86_rdisp8_size(out, OP_NOT_NEG, OP_EX_NOT, dst_base, disp, size);
+}
+
+uint8_t * neg_rdisp8(uint8_t * out, uint8_t dst_base, int8_t disp, uint8_t size)
+{
+	return x86_rdisp8_size(out, OP_NOT_NEG, OP_EX_NEG, dst_base, disp, size);
+}
+
 uint8_t * mov_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size)
 {
 	return x86_rr_sizedir(out, OP_MOV, src, dst, size);
@@ -1147,6 +1224,7 @@
 {
 	*(out++) = OP_SINGLE_EA;
 	*(out++) = MODE_REG_DIRECT | dst | (OP_EX_JMP_EA << 3);
+	return out;
 }
 
 uint8_t * call(uint8_t * out, uint8_t * fun)
--- a/gen_x86.h	Sat Dec 22 21:37:25 2012 -0800
+++ b/gen_x86.h	Wed Dec 26 11:09:04 2012 -0800
@@ -123,6 +123,10 @@
 uint8_t * sub_rdisp8r(uint8_t * out, uint8_t src_base, int8_t disp, uint8_t dst, uint8_t size);
 uint8_t * cmp_rrdisp8(uint8_t * out, uint8_t src, uint8_t dst_base, int8_t disp, uint8_t size);
 uint8_t * cmp_rdisp8r(uint8_t * out, uint8_t src_base, int8_t disp, uint8_t dst, uint8_t size);
+uint8_t * not_r(uint8_t * out, uint8_t dst, uint8_t size);
+uint8_t * neg_r(uint8_t * out, uint8_t dst, uint8_t size);
+uint8_t * not_rdisp8(uint8_t * out, uint8_t dst_base, int8_t disp, uint8_t size);
+uint8_t * neg_rdisp8(uint8_t * out, uint8_t dst_base, int8_t disp, uint8_t size);
 uint8_t * mov_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size);
 uint8_t * mov_rrdisp8(uint8_t * out, uint8_t src, uint8_t dst_base, int8_t disp, uint8_t size);
 uint8_t * mov_rdisp8r(uint8_t * out, uint8_t src_base, int8_t disp, uint8_t dst, uint8_t size);
--- a/m68k_to_x86.c	Sat Dec 22 21:37:25 2012 -0800
+++ b/m68k_to_x86.c	Wed Dec 26 11:09:04 2012 -0800
@@ -26,7 +26,7 @@
 	uint8_t cycles;
 } x86_ea;
 
-void handle_cycle_limit();
+void handle_cycle_limit_int();
 void m68k_read_word_scratch1();
 void m68k_read_long_scratch1();
 void m68k_read_byte_scratch1();
@@ -37,6 +37,10 @@
 void m68k_save_context();
 void m68k_modified_ret_addr();
 void m68k_native_addr();
+void m68k_native_addr_and_sync();
+void set_sr();
+void set_ccr();
+void get_sr();
 void m68k_start_context(uint8_t * addr, m68k_context * context);
 
 uint8_t * cycles(uint8_t * dst, uint32_t num)
@@ -44,6 +48,17 @@
 	dst = add_ir(dst, num, CYCLES, SZ_D);
 }
 
+uint8_t * check_cycles_int(uint8_t * dst, uint32_t address)
+{
+	dst = cmp_rr(dst, LIMIT, CYCLES, SZ_D);
+	uint8_t * jmp_off = dst+1;
+	dst = jcc(dst, CC_NC, dst + 7);
+	dst = mov_ir(dst, address, SCRATCH1, SZ_D);
+	dst = call(dst, (uint8_t *)handle_cycle_limit_int);
+	*jmp_off = dst - (jmp_off+1);
+	return dst;
+}
+
 int8_t native_reg(m68k_op_info * op, x86_68k_options * opts)
 {
 	if (op->addr_mode == MODE_REG) {
@@ -513,14 +528,12 @@
 
 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);
+		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);
 	}
 	uint32_t offset = address % NATIVE_CHUNK_SIZE;
 	native_code_map[chunk].offsets[offset] = native_addr-native_code_map[chunk].base;
@@ -1407,6 +1420,7 @@
 {
 	uint8_t * end_off;
 	map_native_address(opts->native_code_map, inst->address, dst);
+	dst = check_cycles_int(dst, inst->address);
 	if (inst->op == M68K_MOVE) {
 		return translate_m68k_move(dst, inst, opts);
 	} else if(inst->op == M68K_LEA) {
@@ -1653,9 +1667,7 @@
 		dst = mov_rr(dst, CONTEXT, RDI, SZ_Q);
 		dst = call(dst, (uint8_t *)print_regs_exit);
 		break;
-	/*case M68K_JSR:
-	case M68K_LEA:
-	case M68K_MOVE_FROM_SR:
+	/*case M68K_MOVE_FROM_SR:
 		break;*/
 	case M68K_MOVE_CCR:
 	case M68K_MOVE_SR:
@@ -1677,34 +1689,16 @@
 			}
 			dst = cycles(dst, 12);
 		} else {
-			if (src_op.mode == MODE_REG_DIRECT) {
-				dst = mov_rr(dst, src_op.base, FLAG_C, SZ_B);
-			} else {
-				dst = mov_rdisp8r(dst, src_op.base, src_op.disp, FLAG_C, SZ_B);
-			}
-			dst = mov_rr(dst, FLAG_C, FLAG_V, SZ_B);
-			dst = and_ir(dst, 1, FLAG_C, SZ_B);
-			dst = shr_ir(dst, 1, FLAG_V, SZ_B);
-			dst = mov_rr(dst, FLAG_V, FLAG_Z, SZ_B);
-			dst = and_ir(dst, 1, FLAG_V, SZ_B);
-			dst = shr_ir(dst, 1, FLAG_Z, SZ_B);
-			dst = mov_rr(dst, FLAG_Z, FLAG_N, SZ_B);
-			dst = and_ir(dst, 1, FLAG_Z, SZ_B);
-			dst = shr_ir(dst, 1, FLAG_N, SZ_B);
-			dst = mov_rr(dst, 1, SCRATCH2, SZ_B);
-			dst = shr_ir(dst, 1, SCRATCH2, SZ_B);
-			dst = and_ir(dst, 1, SCRATCH2, SZ_B);
-			dst = mov_rrind(dst, SCRATCH2, CONTEXT, SZ_B);
-			dst = cycles(dst, 12);
-			if (inst->op == M68K_MOVE_SR) {
+			if (src_op.base != SCRATCH1) {
 				if (src_op.mode == MODE_REG_DIRECT) {
-					dst = mov_rr(dst, src_op.base, SCRATCH2, SZ_W);
+					dst = mov_rr(dst, src_op.base, SCRATCH1, SZ_W);
 				} else {
-					dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH2, SZ_W);
+					dst = mov_rdisp8r(dst, src_op.base, src_op.disp, SCRATCH1, SZ_W);
 				}
 			}
-			dst = shr_ir(dst, 8, SCRATCH2, SZ_B);
-			dst = mov_rrdisp8(dst, SCRATCH2, CONTEXT, offsetof(m68k_context, status), SZ_B);
+			dst = call(dst, (uint8_t *)(inst->op == M68K_MOVE_SR ? set_sr : set_ccr));
+			dst = cycles(dst, 12);
+			
 		}
 		break;
 	case M68K_MOVE_USP:
@@ -1730,15 +1724,36 @@
 	/*case M68K_MOVEP:
 	case M68K_MULS:
 	case M68K_MULU:
-	case M68K_NBCD:
+	case M68K_NBCD:*/
 	case M68K_NEG:
-	case M68K_NEGX:
+		if (dst_op.mode == MODE_REG_DIRECT) {
+			dst = neg_r(dst, dst_op.base, inst->extra.size);
+		} else {
+			dst = not_rdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size);
+		}
+		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 = mov_ir(dst, 0, FLAG_V, SZ_B);
+		dst = m68k_save_result(inst, dst, opts);
+		break;
+	/*case M68K_NEGX:
 		break;*/
 	case M68K_NOP:
 		dst = cycles(dst, BUS);
 		break;
-	//case M68K_NOT:
-	//	break;
+	case M68K_NOT:
+		if (dst_op.mode == MODE_REG_DIRECT) {
+			dst = not_r(dst, dst_op.base, inst->extra.size);
+		} else {
+			dst = not_rdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size);
+		}
+		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 = mov_ir(dst, 0, FLAG_V, SZ_B);
+		dst = m68k_save_result(inst, dst, opts);
+		break;
 	case M68K_OR:
 		dst = cycles(dst, BUS);
 		if (src_op.mode == MODE_REG_DIRECT) {
@@ -1769,9 +1784,28 @@
 	case M68K_ROL:
 	case M68K_ROR:
 	case M68K_ROXL:
-	case M68K_ROXR:
+	case M68K_ROXR:*/
 	case M68K_RTE:
-	case M68K_RTR:
+		dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
+		dst = call(dst, (uint8_t *)m68k_read_long_scratch1);
+		dst = push_r(dst, SCRATCH1);
+		dst = add_ir(dst, 4, opts->aregs[7], SZ_D);
+		dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D);
+		dst = call(dst, (uint8_t *)m68k_read_word_scratch1);
+		dst = add_ir(dst, 2, opts->aregs[7], SZ_D);
+		dst = call(dst, (uint8_t *)set_sr);
+		dst = pop_r(dst, SCRATCH1);
+		dst = bt_irdisp8(dst, 5, CONTEXT, offsetof(m68k_context, status), SZ_B);
+		end_off = dst+1;
+		dst = jcc(dst, CC_NC, dst+2);
+		dst = mov_rr(dst, opts->aregs[7], SCRATCH2, SZ_D);
+		dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, opts->aregs[7], SZ_D);
+		dst = mov_rrdisp8(dst, SCRATCH2, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D);
+		*end_off = dst - (end_off+1);
+		dst = call(dst, (uint8_t *)m68k_native_addr_and_sync);
+		dst = jmp_r(dst, SCRATCH1);
+		break;
+	/*case M68K_RTR:
 	case M68K_SBCD:
 	case M68K_SCC:
 	case M68K_STOP:
@@ -1857,6 +1891,9 @@
 {
 	m68kinst instbuf;
 	x86_68k_options * opts = context->options;
+	if(get_native_address(opts->native_code_map, address)) {
+		return dst;
+	}
 	char disbuf[1024];
 	uint16_t *encoded = context->mem_pointers[0] + address/2, *next;
 	do {
@@ -1918,5 +1955,6 @@
 	memset(context, 0, sizeof(m68k_context));
 	context->native_code_map = native_code_map;
 	context->options = opts;
+	context->int_cycle = 0xFFFFFFFF;
 }
 
--- a/m68k_to_x86.h	Sat Dec 22 21:37:25 2012 -0800
+++ b/m68k_to_x86.h	Wed Dec 26 11:09:04 2012 -0800
@@ -4,11 +4,11 @@
 #define NUM_MEM_AREAS 4
 #define NATIVE_MAP_CHUNKS (32*1024)
 #define NATIVE_CHUNK_SIZE ((16 * 1024 * 1024 / NATIVE_MAP_CHUNKS)/2)
-#define INVALID_OFFSET 0xFFFF
+#define INVALID_OFFSET 0xFFFFFFFF
 
 typedef struct {
 	uint8_t  *base;
-	uint16_t *offsets;
+	int32_t *offsets;
 } native_map_slot;
 
 typedef struct deferred_addr {
@@ -35,9 +35,12 @@
 	uint32_t		target_cycle; //cycle at which the next synchronization or interrupt occurs
 	uint32_t		current_cycle;
 	uint32_t        sync_cycle;
+	uint32_t        int_cycle;
+	uint32_t        int_num;
 	uint16_t        *mem_pointers[NUM_MEM_AREAS];
 	void            *next_context;
 	uint16_t        value;
+	
 	native_map_slot *native_code_map;
 	void            *options;
 } m68k_context;
--- a/runtime.S	Sat Dec 22 21:37:25 2012 -0800
+++ b/runtime.S	Wed Dec 26 11:09:04 2012 -0800
@@ -1,11 +1,107 @@
 
 	.global handle_cycle_limit
 handle_cycle_limit:
+	cmp 84(%rsi), %eax
+	jb skip_sync
 	call m68k_save_context
 	mov %rsi, %rdi
 	call sync_components
 	mov %rax, %rsi
 	call m68k_load_context
+skip_sync:
+	ret
+
+	.global handle_cycle_limit_int
+handle_cycle_limit_int:
+	cmp 88(%rsi), %eax
+	jb skip_int
+	push %rcx
+	/* swap USP and SSP if not already in supervisor mode */
+	bt $5, 5(%rsi)
+	jc already_supervisor
+	mov 72(%rsi), %edi
+	mov %r15d, 72(%rsi)
+	mov %edi, %r15d
+already_supervisor:
+	/* save status register on stack */
+	sub $2, %r15d
+	mov %r15d, %edi
+	call get_sr
+	call m68k_write_word
+	/* update status register */
+	andb $0xF8, 5(%rsi)
+	mov 92(%rsi), %cl
+	or $0x20, %cl
+	or %cl, 5(%rsi)
+	/* save PC */
+	sub $4, %r15d
+	mov %r15d, %edi
+	pop %rcx
+	call m68k_write_long_lowfirst
+	/* calculate interrupt vector address */
+	mov 92(%rsi), %ecx
+	shl $2, %ecx
+	add $0x60, %ecx
+	call m68k_read_long_scratch1
+	call m68k_native_addr_and_sync
+	add $24, %eax
+	/* discard function return address */
+	pop %rdi
+	jmp *%rcx
+skip_int:
+	ret
+
+	.global get_sr
+get_sr:
+	mov 5(%rsi), %cl
+	shl $8, %cx
+	mov (%rsi), %cl
+	shl $1, %cl
+	or %bl, %cl
+	shl $1, %cl
+	or %dl, %cl
+	shl $1, %cl
+	or %bh, %cl
+	shl $1, %cl
+	or %dh, %cl
+	ret
+
+	.global set_sr
+set_sr:
+	mov %cl, %dh
+	and $1, %dh
+	shr $1, %cl
+	mov %cl, %bh
+	and $1, %bh
+	shr $1, %cl
+	mov %cl, %dl
+	and $1, %dl
+	shr $1, %cl
+	mov %cl, %bl
+	and $1, %bl
+	shr $1, %cl
+	and $1, %cl
+	mov %cl, (%rsi)
+	shr $8, %cx
+	mov %cl, 5(%rsi)
+	ret
+
+	.global set_ccr
+set_ccr:
+	mov %cl, %dh
+	and $1, %dh
+	shr $1, %cl
+	mov %cl, %bh
+	and $1, %bh
+	shr $1, %cl
+	mov %cl, %dl
+	and $1, %dl
+	shr $1, %cl
+	mov %cl, %bl
+	and $1, %bl
+	shr $1, %cl
+	and $1, %cl
+	mov %cl, (%rsi)
 	ret
 
 do_vdp_port_write:
@@ -22,7 +118,7 @@
 	call vdp_port_read
 	mov %rax, %rsi
 	call m68k_load_context
-	mov 128(%rsi), %cx
+	mov 136(%rsi), %cx
 	ret
 	
 do_io_write:
@@ -40,7 +136,7 @@
 	call io_read
 	mov %rax, %rsi
 	call m68k_load_context
-	mov 128(%rsi), %cl
+	mov 136(%rsi), %cl
 	ret
 	
 bad_access_msg:
@@ -76,7 +172,7 @@
 	push %rdx
 	push %rbx
 	/* fetch VDP context pointer from 68K context */
-	mov 120(%rsi), %rdx
+	mov 128(%rsi), %rdx
 	/* get fifo_cur and compare it to fifo_end */
 	mov (%rdx), %rbx
 	cmp %rbx, 8(%rdx)
@@ -165,7 +261,7 @@
 	
 inccycles:
 	cmp %rbp, %rax
-	jge do_limit
+	jnb do_limit
 	add $4, %rax
 	ret
 do_limit:
@@ -271,10 +367,30 @@
 	
 	.global m68k_native_addr
 m68k_native_addr:
-	lea dyn_addr_msg(%rip), %rdi
-	call puts
-	mov $1, %rdi
-	call exit
+	call m68k_save_context
+	push %rcx
+	mov %rsi, %rdi
+	call sync_components
+	pop %rsi
+	push %rax
+	mov 144(%rax), %rdi
+	call get_native_address
+	mov %rax, %rcx
+	pop %rsi
+	call m68k_load_context
+	ret
+
+	.global m68k_native_addr_and_sync
+m68k_native_addr_and_sync:
+	call m68k_save_context
+	push %rsi
+	mov 144(%rsi), %rdi
+	mov %ecx, %esi
+	call get_native_address
+	mov %rax, %rcx
+	pop %rsi
+	call m68k_load_context
+	ret
 
 	.global m68k_save_context
 m68k_save_context:
@@ -305,8 +421,8 @@
 	mov 68(%rsi), %r15d /* a7 */
 	mov 76(%rsi), %ebp /* target cycle count */
 	mov 80(%rsi), %eax /* current cycle count */
-	mov 88(%rsi), %r8d /* cartridge address */
-	mov 96(%rsi), %r9d /* work ram address */
+	mov 96(%rsi), %r8d /* cartridge address */
+	mov 104(%rsi), %r9d /* work ram address */
 	ret
 
 	.global m68k_start_context