[uae] [PATCH] Fix for broken IRQs in WIP4

  • From: Richard Drummond <evilrich@xxxxxxxxxxxxxx>
  • To: uae@xxxxxxxxxxxxx
  • Date: Mon, 16 Jul 2007 23:35:27 -0400

Hi All

For those that're interested, here's a patch fixing the broken IRQ handling in 
WIP4. The fixes are also in CVS.

Cheers,
Rich
-- 
Richard Drummond

Web:  http://www.rcdrummond.net/
Mail: mailto:evilrich@xxxxxxxxxxxxxx
Index: src/custom.c
===================================================================
RCS file: /cvsroot/uaedev/uae/src/custom.c,v
retrieving revision 1.58
retrieving revision 1.59
diff -u -r1.58 -r1.59
--- src/custom.c        12 Mar 2007 13:15:20 -0000      1.58
+++ src/custom.c        17 Jul 2007 03:23:18 -0000      1.59
@@ -2550,86 +2550,103 @@
 
 #ifdef CPUEMU_6
 
-static int irq_pending[15];       /* If true, an IRQ is pending arrival at the 
CPU. */
-static unsigned long irq_due[15]; /* Cycle time that IRQ will arrive at CPU */
+static int           irq_pending[15];  /* If true, an IRQ is pending arrival 
at the CPU. If false,
+                                        * an IRQ has arrived or is disabled. */
+static unsigned long irq_time[15];     /* Cycle time an IRQ will arrive at the 
CPU if that IRQ is
+                                        * pending or has arrived; otherwise 0, 
if an IRQ is disabled. */
 
 /*
- * Handle interrupt delay in cycle-exact mode.
+ * Get priority level of IRQ (cycle-exact mode)
  */
-static int intlev_2 (void)
+STATIC_INLINE int intlev_exact (uae_u16 imask)
 {
-    uae_u16 imask = intreq & intena;
-    int il = -1;
-
-    if (imask && (intena & 0x4000)) {
-       unsigned long cycles = get_cycles ();
-       int i;
+#if 0
+    assert ((imask & 0x7FFF) != 0);
+#endif
 
-       for (i = 14; i >= 0; i--) {
-           if (imask & (1 << i)) {
-               if (irq_pending[i] && (cycles >= irq_due[i])) {
-                   irq_pending[i] = 0;
+    unsigned long curr_time = get_cycles ();
+    int i;
 
-                   if (i == 13 || i == 14)
-                       il = -1;
-                   else if (i == 11 || i == 12)
-                       return 5;
-                   else if (i >= 7 && i <= 10)
-                       return 4;
-                   else if (i >= 4 && i <= 6)
-                       return 3;
-                   else if (i == 3)
-                       return 2;
-                   else
-                       return 1;
-               }
+    for (i = 14; i >= 0; i--) {
+       if (imask & (1 << i)) {
+           if (irq_pending[i] == 0 || ((curr_time - irq_time[i]) > 4 * 
CYCLE_UNIT)) {
+               /* Mark IRQ as arrived. */
+               irq_pending[i] = 0;
+
+               /* Get priority level of IRQ. */
+               if (i == 13 || i == 14)
+                   return 6;
+               if (i == 11 || i == 12)
+                   return 5;
+               if (i >= 7 && i <= 10)
+                   return 4;
+               if (i >= 4 && i <= 6)
+                   return 3;
+               if (i == 3)
+                   return 2;
+               else
+                   return 1;
            }
        }
-    } else
-       unset_special (&regs, SPCFLAG_INT);
-
-    return il;
+    }
+    /* If we got here, then an at least one IRQ must be pending,
+     * but has not yet arrived at the CPU. */
+    return 0;
 }
 #endif
 
 /*
- * Get interrupt level of IRQ.
+ * Get priority level of IRQ (not cycle-exact mode)
+ */
+STATIC_INLINE int intlev_simple (uae_u16 imask)
+{
+#if 0
+    assert ((imask & 0x7FFF) != 0);
+#endif
+
+    if (imask & 0x6000)
+       return 6;
+    if (imask & 0x1800)
+       return 5;
+    if (imask & 0x0780)
+       return 4;
+    if (imask & 0x0070)
+       return 3;
+    if (imask & 0x0008)
+       return 2;
+    else
+       return 1;
+}
+
+/*
+ * Get level of interrupt request presented to CPU.
+ *
+ * If no IRQs are active and enabled, returns -1.
+ * If none of the active IRQs have yet reached the CPU, returns 0.
+ * Otherwise, returns the priority level of the highest priorty active IRQ.
  */
 int intlev (void)
 {
-    int il = -1;
+    uae_u16 imask = intreq & intena;
 
+    if (imask && (intena & 0x4000)) {
 #ifdef CPUEMU_6
-    if (currprefs.cpu_cycle_exact) {
-       il = intlev_2 ();
-       if (il >= 0 && il <= (int) regs.intmask)
-           unset_special (&regs, SPCFLAG_INT);
-    } else
+       if (currprefs.cpu_cycle_exact)
+           return intlev_exact (imask);
+       else
 #endif
-    {
-       uae_u16 imask = intreq & intena;
-       if (imask && (intena & 0x4000)) {
-           if (imask & 0x6000)
-               il = 6;
-           if (imask & 0x1800)
-               il = 5;
-           if (imask & 0x0780)
-               il = 4;
-           if (imask & 0x0070)
-               il = 3;
-           if (imask & 0x0008)
-               il = 2;
-           if (imask & 0x0007)
-               il = 1;
-       }
+           return intlev_simple (imask);
     }
-
-    return il;
+    return -1;
 }
 
+/*
+ * Enable/disable an IRQ.
+ */
 static void doint (void)
 {
-    set_special (&regs, SPCFLAG_INT);
+    if (intena & 0x4000)
+       set_special (&regs, SPCFLAG_INT);
 
 #ifdef CPUEMU_6
     if (currprefs.cpu_cycle_exact) {
@@ -2639,12 +2656,12 @@
        imask = intreq & intena;
 
        if (imask && (intena & 0x4000)) {
-           /* Set up delay for IRQ to arrive at the CPU. */
-           unsigned long cycle_irq_due = get_cycles () + 4 * CYCLE_UNIT;
+           /* Set up time for IRQ to arrive at the CPU. */
+           unsigned long curr_time = get_cycles ();
            for (i = 0; i < 15; i++) {
                if ((imask & (1 << i)) && irq_pending[i] == 0) {
                    irq_pending[i] = 1;
-                   irq_due[i]     = cycle_irq_due;
+                   irq_time[i]  = curr_time;
                }
            }
        }
@@ -2677,14 +2694,17 @@
             * pending status. */
            int i;
            for (i = 0; i < 15; i++) {
-               if (v & (1 << i))
+               if (v & (1 << i)) {
                    irq_pending[i] = 0;
+                   irq_time[i]    = 0;
+               }
            }
        }
     }
 #endif
 
-    doint ();
+    if (intena & 0x4000)
+       doint ();
 }
 
 void INTREQ (uae_u16 v)
Index: src/newcpu.c
===================================================================
RCS file: /cvsroot/uaedev/uae/src/newcpu.c,v
retrieving revision 1.46
retrieving revision 1.47
diff -u -r1.46 -r1.47
--- src/newcpu.c        28 Mar 2007 01:12:34 -0000      1.46
+++ src/newcpu.c        17 Jul 2007 03:23:18 -0000      1.47
@@ -687,7 +687,10 @@
        }
     }
 
+   /* Interrupt priority level may have changed. Assert SPCFLAG_INT
+    * to check if there's an IRQ ready to go at the new level. */
     set_special (regs, SPCFLAG_INT);
+
     if (regs->t1 || regs->t0)
        set_special (regs, SPCFLAG_TRACE);
     else
@@ -973,15 +976,12 @@
 
 STATIC_INLINE void service_interrupt (unsigned int level, struct regstruct 
*regs)
 {
-    if (level > regs->intmask) {
-
-       regs->stopped = 0;
-       unset_special (regs, SPCFLAG_STOP);
+    regs->stopped = 0;
+    unset_special (regs, SPCFLAG_STOP);
 
-       Exception (level + 24, regs, 0);
+    Exception (level + 24, regs, 0);
 
-       regs->intmask = level;
-    }
+    regs->intmask = level;
 }
 
 /*
@@ -1694,12 +1694,14 @@
      * In non-cycle-exact mode we handle this by separating the interrupt 
request
      * pending (SPCFLAG_INT) and interrupt request arrived (SPCFLAG_DOINT) 
events.
      * This ensures that there's always a delay of one opcode (and so at least 
2
-     * machine cycles) between the interrupt controller requesting an interrupt
-     * and us servicing it here.
+     * machine cycles) between the interrupt controller requesting an 
interrupt or
+     * the processor changing its interrupt priority level and us servicing it 
here.
      *
      * In cycle-exact mode, there's just one event (SPCFLAG_INT) and the delay 
is
      * handled internally by the interrupt controller code in custom.c - 
intlev()
      * and friends.
+     *
+     * This stuff needs some tidying up!
      */
     if ((regs->spcflags & SPCFLAG_DOINT) ||
        (currprefs.cpu_cycle_exact && (regs->spcflags & SPCFLAG_INT))) {
@@ -1708,8 +1710,15 @@
 
        unset_special (regs, SPCFLAG_DOINT);
 
-       if (intr != -1)
+       if (intr > (int)regs->intmask) {
+           if (currprefs.cpu_cycle_exact)
+               unset_special(regs, SPCFLAG_INT);
+
            service_interrupt (intr, regs);
+       } else {
+           if (intr < 0 && currprefs.cpu_cycle_exact)
+               unset_special (regs, SPCFLAG_INT);
+       }
     }
 
     if ((regs->spcflags & SPCFLAG_INT) && !currprefs.cpu_cycle_exact) {

Other related posts:

  • » [uae] [PATCH] Fix for broken IRQs in WIP4