diff --git a/hw/mips/cputimer.c b/hw/mips/cputimer.c
index 94a29df..849d62c 100644
--- a/hw/mips/cputimer.c
+++ b/hw/mips/cputimer.c
@@ -54,7 +54,13 @@ static void cpu_mips_timer_expire(CPUMIPSState *env)
     if (env->insn_flags & ISA_MIPS32R2) {
         env->CP0_Cause |= 1 << CP0Ca_TI;
     }
-    qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
+    if (env->CP0_Config3 & (1 << CP0C3_VEIC)) {
+        /* External interrupt controller mode. */
+        env->eic_timer_irq(env, 1);
+    } else {
+        /* Legacy or vectored interrupt mode. */
+        qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
+    }
 }
 
 uint32_t cpu_mips_get_count (CPUMIPSState *env)
@@ -102,7 +108,14 @@ void cpu_mips_store_compare (CPUMIPSState *env, uint32_t value)
         cpu_mips_timer_update(env);
     if (env->insn_flags & ISA_MIPS32R2)
         env->CP0_Cause &= ~(1 << CP0Ca_TI);
-    qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
+
+    if (env->CP0_Config3 & (1 << CP0C3_VEIC)) {
+        /* External interrupt controller mode. */
+        env->eic_timer_irq(env, 0);
+    } else {
+        /* Legacy or vectored interrupt mode. */
+        qemu_irq_lower(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]);
+    }
 }
 
 void cpu_mips_start_count(CPUMIPSState *env)
diff --git a/hw/mips/mips_int.c b/hw/mips/mips_int.c
index d740046..3452866 100644
--- a/hw/mips/mips_int.c
+++ b/hw/mips/mips_int.c
@@ -32,7 +32,7 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level)
     CPUMIPSState *env = &cpu->env;
     CPUState *cs = CPU(cpu);
 
-    if (irq < 0 || irq > 7)
+    if (irq < 0 || irq > 7 || (env->CP0_Config3 & (1 << CP0C3_VEIC)))
         return;
 
     if (level) {
@@ -74,5 +74,13 @@ void cpu_mips_soft_irq(CPUMIPSState *env, int irq, int level)
         return;
     }
 
-    qemu_set_irq(env->irq[irq], level);
+    if (env->CP0_Config3 & (1 << CP0C3_VEIC)) {
+        /* External interrupt controller mode. */
+        if (level > 0) {
+            env->eic_soft_irq(env, irq);
+        }
+    } else {
+        /* Legacy or vectored interrupt mode. */
+        qemu_set_irq(env->irq[irq], level);
+    }
 }
diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index c476166..ab830ee 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -596,6 +596,11 @@ struct CPUMIPSState {
     void *irq[8];
     QEMUTimer *timer; /* Internal timer */
     unsigned count_freq; /* rate of Count register */
+
+    /* Fields for external interrupt controller. */
+    void *eic_context;
+    void (*eic_timer_irq)(CPUMIPSState *env, int raise);
+    void (*eic_soft_irq)(CPUMIPSState *env, int num);
 };
 
 #include "cpu-qom.h"
@@ -664,7 +669,9 @@ static inline int cpu_mips_hw_interrupts_pending(CPUMIPSState *env)
     if (env->CP0_Config3 & (1 << CP0C3_VEIC)) {
         /* A MIPS configured with a vectorizing external interrupt controller
            will feed a vector into the Cause pending lines. The core treats
-           the status lines as a vector level, not as indiviual masks.  */
+           the status lines as a vector level, not as individual masks.  */
+        pending >>= CP0Ca_IP + 2;
+        status >>= CP0Ca_IP + 2;
         r = pending > status;
     } else {
         /* A MIPS configured with compatibility or VInt (Vectored Interrupts)
diff --git a/target-mips/helper.c b/target-mips/helper.c
index 8e3204a..7e25998 100644
--- a/target-mips/helper.c
+++ b/target-mips/helper.c
@@ -574,23 +574,31 @@ void mips_cpu_do_interrupt(CPUState *cs)
             unsigned int vector;
             unsigned int pending = (env->CP0_Cause & CP0Ca_IP_mask) >> 8;
 
-            pending &= env->CP0_Status >> 8;
             /* Compute the Vector Spacing.  */
             spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & ((1 << 6) - 1);
             spacing <<= 5;
 
-            if (env->CP0_Config3 & (1 << CP0C3_VInt)) {
+            if (env->CP0_Config3 & (1 << CP0C3_VEIC)) {
+                /* For VEIC mode, the external interrupt controller feeds the
+                 * vector through the CP0Cause IP lines. */
+                vector = pending;
+
+                /* Architecturally, this is chip-specific behavior.
+                 * TODO: some processors, like PIC32MZ,
+                 * provide vector in a different way.
+                 * Some processors, like PIC32, have a separate
+                 * bit INTCON.MVEC to explicitly enable vectored mode,
+                 * disabled by default. */
+                spacing = 0;
+            } else {
                 /* For VInt mode, the MIPS computes the vector internally.  */
+                pending &= env->CP0_Status >> 8;
                 for (vector = 7; vector > 0; vector--) {
                     if (pending & (1 << vector)) {
                         /* Found it.  */
                         break;
                     }
                 }
-            } else {
-                /* For VEIC mode, the external interrupt controller feeds the
-                   vector through the CP0Cause IP lines.  */
-                vector = pending;
             }
             offset = 0x200 + vector * spacing;
         }
