changeset 996:784bc1e45e80

Fix 68K interrupt handling some more. Fatal Rewind is working again.
author Michael Pavone <pavone@retrodev.com>
date Sat, 30 Apr 2016 09:45:53 -0700
parents 2bc27415565b
children 560da2e455c2
files blastem.c m68k_core.h m68k_core_x86.c
diffstat 3 files changed, 26 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/blastem.c	Sat Apr 30 08:37:55 2016 -0700
+++ b/blastem.c	Sat Apr 30 09:45:53 2016 -0700
@@ -175,7 +175,7 @@
 			}
 		}
 	}
-	if (context->int_cycle > context->current_cycle) {
+	if (context->int_cycle > context->current_cycle && context->int_pending == INT_PENDING_SR_CHANGE) {
 		context->int_pending = 0;
 	}
 	/*if (context->int_cycle != old_int_cycle) {
--- a/m68k_core.h	Sat Apr 30 08:37:55 2016 -0700
+++ b/m68k_core.h	Sat Apr 30 09:45:53 2016 -0700
@@ -18,6 +18,8 @@
 
 #define M68K_OPT_BROKEN_READ_MODIFY 1
 
+#define INT_PENDING_SR_CHANGE 8
+
 typedef void (*start_fun)(uint8_t * addr, void * context);
 
 typedef struct {
--- a/m68k_core_x86.c	Sat Apr 30 08:37:55 2016 -0700
+++ b/m68k_core_x86.c	Sat Apr 30 09:45:53 2016 -0700
@@ -2016,7 +2016,7 @@
 		    || (inst->op == M68K_ORI_SR && inst->src.params.immed & 0x700)) {
 			if (inst->op == M68K_ANDI_SR) {
 				//set int pending flag in case we trigger an interrupt as a result of the mask change
-				mov_irdisp(code, 1, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
+				mov_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
 			}
 			call(code, opts->do_sync);
 		}
@@ -2049,7 +2049,7 @@
 		xor_irdisp(code, inst->src.params.immed >> 8, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
 		if (inst->src.params.immed & 0x700) {
 			//set int pending flag in case we trigger an interrupt as a result of the mask change
-			mov_irdisp(code, 1, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
+			mov_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
 			call(code, opts->do_sync);
 		}
 	}
@@ -2081,7 +2081,7 @@
 			}
 			if (((src_op->disp >> 8) & 7) < 7) {
 				//set int pending flag in case we trigger an interrupt as a result of the mask change
-				mov_irdisp(code, 1, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
+				mov_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
 			}
 			call(code, opts->do_sync);
 		}
@@ -2133,7 +2133,7 @@
 		cmp_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_cycle), opts->gen.cycles, SZ_D);
 	jcc(code, CC_C, loop_top);
 	//set int pending flag so interrupt fires immediately after stop is done
-	mov_irdisp(code, 1, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
+	mov_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
 }
 
 void translate_m68k_trapv(m68k_options *opts, m68kinst *inst)
@@ -2560,7 +2560,7 @@
 	shr_ir(code, 8, opts->gen.scratch1, SZ_W);
 	mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B);
 	//set int pending flag in case we trigger an interrupt as a result of the mask change
-	mov_irdisp(code, 1, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
+	mov_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
 	retn(code);
 
 	opts->set_ccr = code->cur;
@@ -2743,11 +2743,20 @@
 	cmp_irdisp(code, 0, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
 	do_int = code->cur + 1;
 	jcc(code, CC_NZ, do_int);
-	mov_irdisp(code, 1, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
+	//store current interrupt number so it doesn't change before we start processing the vector
+	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_num), opts->gen.scratch1, SZ_B);
+	mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
 	retn(code);
 	*do_int = code->cur - (do_int + 1);
-	//save interrupt number so it can't change during interrupt processing
-	push_rdisp(code, opts->gen.context_reg, offsetof(m68k_context, int_num));
+	//Check if int_pending has an actual interrupt priority in it
+	cmp_irdisp(code, INT_PENDING_SR_CHANGE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
+	code_ptr already_int_num = code->cur + 1;
+	jcc(code, CC_NZ, already_int_num);
+	
+	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_num), opts->gen.scratch2, SZ_B);
+	mov_rrdisp(code, opts->gen.scratch2, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
+	
+	*already_int_num = code->cur - (already_int_num + 1);
 	//save PC as stored in scratch1 for later
 	push_r(code, opts->gen.scratch1);
 	//set target cycle to sync cycle
@@ -2803,13 +2812,17 @@
 	add_ir(code, 2, opts->gen.scratch2, SZ_D);
 	call(code, opts->write_32_lowfirst);
 
-	//restore saved interrupt number
-	pop_r(code, opts->gen.scratch1);
+	//grab saved interrupt number
+	xor_rr(code, opts->gen.scratch1, opts->gen.scratch1, SZ_D);
+	mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_pending), opts->gen.scratch1, SZ_B);
 	//ack the interrupt (happens earlier on hardware, but shouldn't be an observable difference)
 	mov_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, int_ack), SZ_W);
 	//calculate the vector address
 	shl_ir(code, 2, opts->gen.scratch1, SZ_D);
 	add_ir(code, 0x60, opts->gen.scratch1, SZ_D);
+	//clear out pending flag
+	mov_irdisp(code, 0, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B);
+	//read vector
 	call(code, opts->read_32);
 	call(code, opts->native_addr_and_sync);
 	//2 prefetch bus operations + 2 idle bus cycles