This is proper interrupt handling. It suddenly starts to make more sense once you do remember to set F/I flags where appropriate. Duh, I guess.

Code
diff -Nru old/arm7.c new/arm7.c
--- old/arm7.c	2008-02-15 19:47:10.000000000 +0100
+++ new/arm7.c	2008-02-15 19:47:24.000000000 +0100
@@ -136,13 +136,10 @@
   // mode change could've enabled interrups, so we test for those and set
   // appropriate flag for the instruction loop to catch
   if (ARM7.fiq)
-    if (!(sr & ARM7_CPSR_F) && (ARM7_CPSR_M (sr) != ARM7_CPSR_M_fiq))
-      ARM7.flagi |= ARM7_FL_FIQ;
+    ARM7.flagi |= ARM7_FL_FIQ;
 #ifndef ARM7_DREAMCAST
   if (ARM7.irq)
-    if (!(sr & ARM7_CPSR_I) && (ARM7_CPSR_M (sr) != ARM7_CPSR_M_irq) &&\
- (ARM7_CPSR_M (sr) != ARM7_CPSR_M_fiq))
-      ARM7.flagi |= ARM7_FL_IRQ;
+    ARM7.flagi |= ARM7_FL_IRQ;
 #endif
   }
   //--------------------------------------------------------------------------
@@ -198,10 +195,10 @@
   // (FIQ can interrupt IRQ, but not the other way around)
   if (ARM7.fiq)
     {
-    if (!(sr & ARM7_CPSR_F) && (ARM7_CPSR_M (sr) != ARM7_CPSR_M_fiq))
+    if (!(sr & ARM7_CPSR_F))
       {
       // FIQ
-      ARM7_SetCPSR ((sr & 0xffffffc0) | ARM7_CPSR_M_fiq);
+      ARM7_SetCPSR (ARM7_CPSR_MX (sr, ARM7_CPSR_M_fiq) | ARM7_CPSR_F | ARM7_CPSR_I);
       ARM7.Rx [ARM7_SPSR] = sr;
       // set new PC (return from interrupt will subtract 4)
       ARM7.Rx [ARM7_LR] = ARM7.Rx [ARM7_PC] + 4;
@@ -211,11 +208,10 @@
 #ifndef ARM7_DREAMCAST
   if (ARM7.irq)
     {
-    if (!(sr & ARM7_CPSR_I) && (ARM7_CPSR_M (sr) != ARM7_CPSR_M_irq) &&\
- (ARM7_CPSR_M (sr) != ARM7_CPSR_M_irq))
+    if (!(sr & ARM7_CPSR_I))
       {
       // IRQ
-      ARM7_SetCPSR ((sr & 0xffffffc0) | ARM7_CPSR_M_irq);
+      ARM7_SetCPSR (ARM7_CPSR_MX (sr, ARM7_CPSR_M_irq) | ARM7_CPSR_I);
       ARM7.Rx [ARM7_SPSR] = sr;
       // set new PC (return from interrupt will subtract 4)
       ARM7.Rx [ARM7_LR] = ARM7.Rx [ARM7_PC] + 4;
diff -Nru old/arm7.h new/arm7.h
--- old/arm7.h	2008-02-15 19:27:49.000000000 +0100
+++ new/arm7.h	2008-02-15 19:27:49.000000000 +0100
@@ -42,7 +42,8 @@
 #define ARM7_CPSR_F (1 << 6)
 #define ARM7_CPSR_T (1 << 5)
   /** CPSR bit mask for current operating mode. */
-#define ARM7_CPSR_M(x) (x & 0x1f)
+#define ARM7_CPSR_M(x) ((x) & 0x1f)
+#define ARM7_CPSR_MX(sr,x) (((sr) & ~0x1f) | ((x) & 0x1f))
   /** Bit combinations for each operating mode. */
 #define ARM7_CPSR_M_usr 0x10
 #define ARM7_CPSR_M_fiq 0x11
diff -Nru old/arm7i.c new/arm7i.c
--- old/arm7i.c	2008-02-15 19:27:49.000000000 +0100
+++ new/arm7i.c	2008-02-15 19:27:49.000000000 +0100
@@ -1364,7 +1364,7 @@
     {
 //EMU_BLAD (BLAD_WEWNETRZNY, "BDT: user transfer");
     cpsr = ARM7.Rx [ARM7_CPSR];
-    ARM7_SetCPSR ((cpsr & 0xffffffe0) | ARM7_CPSR_M_usr);
+    ARM7_SetCPSR (ARM7_CPSR_MX (cpsr, ARM7_CPSR_M_usr));
     }
 
   if (ARM7.kod & BIT_L)
@@ -1551,8 +1551,12 @@
 	logerror("ARM7: G111 / Coprocessor data operation\n"); */
     }
   else
-   {
-  	ARM7_SetSWI();
-   }
+    {
+    uint32_t sr = ARM7.Rx [ARM7_CPSR];
+    ARM7_SetCPSR (ARM7_CPSR_MX (sr, ARM7_CPSR_M_svc) | ARM7_CPSR_I);
+    ARM7.Rx [ARM7_SPSR] = sr;
+    ARM7.Rx [ARM7_LR] = ARM7.Rx [ARM7_PC];
+    ARM7.Rx [ARM7_PC] = 0x00000008;
+    }
   }
   //--------------------------------------------------------------------------

I've also simplified group 0 decoder for non-Thumb cores, since it's only swap, multiply or data processing/PSR transfer in that case. I can create AO comptabile patch if anyone's interested.

EDIT: Oops, silly me. One change went into wrong file, corrected.

Last edited by Deunan Knute; 02/15/08 06:48 PM.