daily update
[external/binutils.git] / sim / ppc / interrupts.c
index 41e7d1e..dafb2b3 100644 (file)
@@ -1,6 +1,6 @@
 /*  This file is part of the program psim.
 
-    Copyright (C) 1994-1995, Andrew Cagney <cagney@highland.com.au>
+    Copyright 1994, 1995, 1996, 1997, 2003 Andrew Cagney
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -38,7 +38,8 @@
    interrupts */
 
 
-msreg STATIC_INLINE_INTERRUPTS
+STATIC_INLINE_INTERRUPTS\
+(msreg)
 interrupt_msr(msreg old_msr,
              msreg msr_clear,
              msreg msr_set)
@@ -63,20 +64,22 @@ interrupt_msr(msreg old_msr,
 }
 
 
-msreg STATIC_INLINE_INTERRUPTS
+STATIC_INLINE_INTERRUPTS\
+(msreg)
 interrupt_srr1(msreg old_msr,
               msreg srr1_clear,
               msreg srr1_set)
 {
   spreg srr1_mask = (MASK(0,32)
-                      | MASK(37, 41)
-                      | MASK(48, 63));
+                    | MASK(37, 41)
+                    | MASK(48, 63));
   spreg srr1 = (old_msr & srr1_mask & ~srr1_clear) | srr1_set;
   return srr1;
 }
 
 
-unsigned_word STATIC_INLINE_INTERRUPTS
+STATIC_INLINE_INTERRUPTS\
+(unsigned_word)
 interrupt_base_ea(msreg msr)
 {
   if (msr & msr_interrupt_prefix)
@@ -89,7 +92,8 @@ interrupt_base_ea(msreg msr)
 /* finish off an interrupt for the OEA model, updating all registers
    and forcing a restart of the processor */
 
-unsigned_word STATIC_INLINE_INTERRUPTS
+STATIC_INLINE_INTERRUPTS\
+(unsigned_word)
 perform_oea_interrupt(cpu *processor,
                      unsigned_word cia,
                      unsigned_word vector_offset,
@@ -101,19 +105,27 @@ perform_oea_interrupt(cpu *processor,
   msreg old_msr = MSR;
   msreg new_msr = interrupt_msr(old_msr, msr_clear, msr_set);
   unsigned_word nia;
-  if (!(old_msr & msr_recoverable_interrupt))
-    error("perform_oea_interrupt() recoverable_interrupt bit clear, cia=0x%x, msr=0x%x\n",
-         cia, old_msr);
+  if (!(old_msr & msr_recoverable_interrupt)) {
+    cpu_error(processor, cia,
+             "double interrupt - MSR[RI] bit clear when attempting to deliver interrupt, cia=0x%lx, msr=0x%lx; srr0=0x%lx(cia), srr1=0x%lx(msr); trap-vector=0x%lx, trap-msr=0x%lx",
+             (unsigned long)cia,
+             (unsigned long)old_msr,
+             (unsigned long)SRR0,
+             (unsigned long)SRR1,
+             (unsigned long)vector_offset,
+             (unsigned long)new_msr);
+  }
   SRR0 = (spreg)(cia);
   SRR1 = interrupt_srr1(old_msr, srr1_clear, srr1_set);
   MSR = new_msr;
   nia = interrupt_base_ea(new_msr) + vector_offset;
-  cpu_synchronize_context(processor);
+  cpu_synchronize_context(processor, cia);
   return nia;
 }
 
 
-void INLINE_INTERRUPTS
+INLINE_INTERRUPTS\
+(void)
 machine_check_interrupt(cpu *processor,
                        unsigned_word cia)
 {
@@ -121,21 +133,23 @@ machine_check_interrupt(cpu *processor,
 
   case USER_ENVIRONMENT:
   case VIRTUAL_ENVIRONMENT:
-    error("%s - cia=0x%x\n",
-         "machine_check_interrupt", cia);
+    cpu_error(processor, cia, "machine-check interrupt");
 
   case OPERATING_ENVIRONMENT:
+    TRACE(trace_interrupts, ("machine-check interrupt - cia=0x%lx\n",
+                            (unsigned long)cia));
     cia = perform_oea_interrupt(processor, cia, 0x00200, 0, 0, 0, 0);
     cpu_restart(processor, cia);
 
   default:
-    error("machine_check_interrupt() - internal error\n");
+    error("internal error - machine_check_interrupt - bad switch");
 
   }
 }
 
 
-void INLINE_INTERRUPTS
+INLINE_INTERRUPTS\
+(void)
 data_storage_interrupt(cpu *processor,
                       unsigned_word cia,
                       unsigned_word ea,
@@ -146,7 +160,8 @@ data_storage_interrupt(cpu *processor,
 
   case USER_ENVIRONMENT:
   case VIRTUAL_ENVIRONMENT:
-    error("data_storage_interrupt() should not be called in VEA mode\n");
+    error("internal error - data_storage_interrupt - should not be called in VEA mode");
+    break;
 
   case OPERATING_ENVIRONMENT:
     {
@@ -171,22 +186,27 @@ data_storage_interrupt(cpu *processor,
        DSISR = dsisr_earwax_disabled | direction;
        break;
       default:
-       error("data_storage_interrupt: unknown reason %d\n", reason);
+       error("internal error - data_storage_interrupt - reason %d not implemented", reason);
        break;
       }
       DAR = (spreg)ea;
+      TRACE(trace_interrupts, ("data storage interrupt - cia=0x%lx DAR=0x%lx DSISR=0x%lx\n",
+                              (unsigned long)cia,
+                              (unsigned long)DAR,
+                              (unsigned long)DSISR));
       cia = perform_oea_interrupt(processor, cia, 0x00300, 0, 0, 0, 0);
       cpu_restart(processor, cia);
     }
 
   default:
-    error("data_storage_interrupt() - internal error\n");
+    error("internal error - data_storage_interrupt - bad switch");
 
   }
 }
 
 
-void INLINE_INTERRUPTS
+INLINE_INTERRUPTS\
+(void)
 instruction_storage_interrupt(cpu *processor,
                              unsigned_word cia,
                              storage_interrupt_reasons reason)
@@ -195,8 +215,7 @@ instruction_storage_interrupt(cpu *processor,
 
   case USER_ENVIRONMENT:
   case VIRTUAL_ENVIRONMENT:
-    error("instruction_storage_interrupt - cia=0x%x - not implemented\n",
-         cia);
+    error("internal error - instruction_storage_interrupt - should not be called in VEA mode");
 
   case OPERATING_ENVIRONMENT:
     {
@@ -216,22 +235,26 @@ instruction_storage_interrupt(cpu *processor,
        break;
       default:
        srr1_set = 0;
-       error("instruction_storage_interrupt: unknown reason %d\n", reason);
+       error("internal error - instruction_storage_interrupt - reason %d not implemented");
        break;
       }
+      TRACE(trace_interrupts, ("instruction storage interrupt - cia=0x%lx SRR1|=0x%lx\n",
+                              (unsigned long)cia,
+                              (unsigned long)srr1_set));
       cia = perform_oea_interrupt(processor, cia, 0x00400, 0, 0, 0, srr1_set);
       cpu_restart(processor, cia);
     }
 
   default:
-    error("instruction_storage_interrupt() - internal error\n");
+    error("internal error - instruction_storage_interrupt - bad switch");
 
   }
 }
 
 
 
-void INLINE_INTERRUPTS
+INLINE_INTERRUPTS\
+(void)
 alignment_interrupt(cpu *processor,
                    unsigned_word cia,
                    unsigned_word ra)
@@ -240,17 +263,20 @@ alignment_interrupt(cpu *processor,
 
   case USER_ENVIRONMENT:
   case VIRTUAL_ENVIRONMENT:
-    error("%s - cia=0x%x, ra=0x%x\n",
-         "alignment_interrupt", cia, ra);
+    cpu_error(processor, cia, "alignment interrupt - ra=0x%lx", ra);
     
   case OPERATING_ENVIRONMENT:
     DAR = (spreg)ra;
     DSISR = 0; /* FIXME */
+    TRACE(trace_interrupts, ("alignment interrupt - cia=0x%lx DAR=0x%lx DSISR=0x%lx\n",
+                            (unsigned long)cia,
+                            (unsigned long)DAR,
+                            (unsigned long)DSISR));
     cia = perform_oea_interrupt(processor, cia, 0x00600, 0, 0, 0, 0);
     cpu_restart(processor, cia);
 
   default:
-    error("alignment_interrupt() - internal error\n");
+    error("internal error - alignment_interrupt - bad switch");
     
   }
 }
@@ -258,7 +284,8 @@ alignment_interrupt(cpu *processor,
 
 
 
-void INLINE_INTERRUPTS
+INLINE_INTERRUPTS\
+(void)
 program_interrupt(cpu *processor,
                  unsigned_word cia,
                  program_interrupt_reasons reason)
@@ -268,15 +295,42 @@ program_interrupt(cpu *processor,
   case USER_ENVIRONMENT:
   case VIRTUAL_ENVIRONMENT:
     switch (reason) {
+    case floating_point_enabled_program_interrupt:
+      cpu_error(processor, cia, "program interrupt - %s",
+               "floating point enabled");
+      break;
+    case illegal_instruction_program_interrupt:
+      cpu_error(processor, cia, "program interrupt - %s",
+               "illegal instruction");
+      break;
+    case privileged_instruction_program_interrupt:
+      cpu_error(processor, cia, "program interrupt - %s",
+               "privileged instruction");
+      break;
+    case trap_program_interrupt:
+      cpu_error(processor, cia, "program interrupt - %s",
+               "trap");
+      break;
+    case optional_instruction_program_interrupt:
+      cpu_error(processor, cia, "program interrupt - %s",
+               "illegal instruction (optional instruction not supported)");
+      break;
+    case mpc860c0_instruction_program_interrupt:
+      cpu_error(processor, cia, "program interrupt - %s",
+               "problematic branch detected, see MPC860 C0 errata");
+      break;
     default:
-      error("%s - cia=0x%x, reason=%d - not implemented\n",
-           "program_interrupt", cia, reason);
+      error("internal error - program_interrupt - reason %d not implemented", reason);
     }
 
   case OPERATING_ENVIRONMENT:
     {
       msreg srr1_set;
       switch (reason) {
+      case floating_point_enabled_program_interrupt:
+       srr1_set = srr1_floating_point_enabled;
+       break;
+      case optional_instruction_program_interrupt:
       case illegal_instruction_program_interrupt:
        srr1_set = srr1_illegal_instruction;
        break;
@@ -286,23 +340,32 @@ program_interrupt(cpu *processor,
       case trap_program_interrupt:
        srr1_set = srr1_trap;
        break;
+      case mpc860c0_instruction_program_interrupt:
+        srr1_set = 0;
+        cpu_error(processor, cia, "program interrupt - %s",
+              "problematic branch detected, see MPC860 C0 errata");
+        break;
       default:
        srr1_set = 0;
-       error("program_interrupt - cia=0x%x, reason=%d(%s) - not implemented\n",
-             cia, reason);
+       error("internal error - program_interrupt - reason %d not implemented", reason);
+       break;
       }
+      TRACE(trace_interrupts, ("program interrupt - cia=0x%lx SRR1|=0x%lx\n",
+                              (unsigned long)cia,
+                              (unsigned long)srr1_set));
       cia = perform_oea_interrupt(processor, cia, 0x00700, 0, 0, 0, srr1_set);
       cpu_restart(processor, cia);
     }
 
   default:
-    error("program_interrupt() - internal error\n");
+    error("internal error - program_interrupt - bad switch");
 
   }
 }
 
 
-void INLINE_INTERRUPTS
+INLINE_INTERRUPTS\
+(void)
 floating_point_unavailable_interrupt(cpu *processor,
                                     unsigned_word cia)
 {
@@ -310,24 +373,28 @@ floating_point_unavailable_interrupt(cpu *processor,
     
   case USER_ENVIRONMENT:
   case VIRTUAL_ENVIRONMENT:
-    error("%s - cia=0x%x - not implemented\n",
-         "floating_point_unavailable_interrupt", cia);
+    cpu_error(processor, cia, "floating-point unavailable interrupt");
 
   case OPERATING_ENVIRONMENT:
+    TRACE(trace_interrupts, ("floating-point unavailable interrupt - cia=0x%lx\n",
+                            (unsigned long)cia));
     cia = perform_oea_interrupt(processor, cia, 0x00800, 0, 0, 0, 0);
     cpu_restart(processor, cia);
 
   default:
-    error("floating_point_unavailable_interrupt() - internal error\n");
+    error("internal error - floating_point_unavailable_interrupt - bad switch");
 
   }
 }
 
 
-void INLINE_INTERRUPTS
+INLINE_INTERRUPTS\
+(void)
 system_call_interrupt(cpu *processor,
                      unsigned_word cia)
 {
+  TRACE(trace_interrupts, ("system-call interrupt - cia=0x%lx\n", (unsigned long)cia));
+
   switch (CURRENT_ENVIRONMENT) {
 
   case USER_ENVIRONMENT:
@@ -340,16 +407,13 @@ system_call_interrupt(cpu *processor,
     cpu_restart(processor, cia);
 
   default:
-    error("system_call_interrupt() - internal error\n");
+    error("internal error - system_call_interrupt - bad switch");
 
   }
 }
 
-void INLINE_INTERRUPTS
-trace_interrupt(cpu *processor,
-               unsigned_word cia);
-
-void INLINE_INTERRUPTS
+INLINE_INTERRUPTS\
+(void)
 floating_point_assist_interrupt(cpu *processor,
                                unsigned_word cia)
 {
@@ -357,52 +421,121 @@ floating_point_assist_interrupt(cpu *processor,
 
   case USER_ENVIRONMENT:
   case VIRTUAL_ENVIRONMENT:
-    error("%s - cia=0x%x - not implemented\n",
-         "floating_point_assist_interrupt", cia);
+    cpu_error(processor, cia, "floating-point assist interrupt");
 
   case OPERATING_ENVIRONMENT:
+    TRACE(trace_interrupts, ("floating-point assist interrupt - cia=0x%lx\n", (unsigned long)cia));
     cia = perform_oea_interrupt(processor, cia, 0x00e00, 0, 0, 0, 0);
     cpu_restart(processor, cia);
 
   default:
-    error("floating_point_assist_interrupt() - internal error\n");
+    error("internal error - floating_point_assist_interrupt - bad switch");
 
   }
 }
 
 
 
-/* handle an externally generated event */
+/* handle an externally generated event or an interrupt that has just
+   been enabled through changes to the MSR. */
 
-int INLINE_INTERRUPTS
-decrementer_interrupt(cpu *processor)
+STATIC_INLINE_INTERRUPTS\
+(void)
+deliver_hardware_interrupt(void *data)
 {
-  if (cpu_registers(processor)->msr & msr_external_interrupt_enable) {
+  cpu *processor = (cpu*)data;
+  interrupts *ints = cpu_interrupts(processor);
+  ints->delivery_scheduled = NULL;
+  if ((cpu_registers(processor)->msr & (msr_floating_point_exception_mode_0
+                                       | msr_floating_point_exception_mode_1))
+      && cpu_registers(processor)->fpscr & fpscr_fex) {
+    msreg srr1_set = srr1_floating_point_enabled | srr1_subsequent_instruction;
     unsigned_word cia = cpu_get_program_counter(processor);
     unsigned_word nia = perform_oea_interrupt(processor,
-                                             cia, 0x00900, 0, 0, 0, 0);
+                                             cia, 0x00700, 0, 0, 0, srr1_set);
     cpu_set_program_counter(processor, nia);
-    return 1;
   }
-  else {
-    return 0;
+  else if (cpu_registers(processor)->msr & msr_external_interrupt_enable) {
+    /* external interrupts have a high priority and remain pending */
+    if (ints->pending_interrupts & external_interrupt_pending) {
+      unsigned_word cia = cpu_get_program_counter(processor);
+      unsigned_word nia = perform_oea_interrupt(processor,
+                                               cia, 0x00500, 0, 0, 0, 0);
+      TRACE(trace_interrupts, ("external interrupt - cia=0x%lx\n", (unsigned long)cia));
+      cpu_set_program_counter(processor, nia);
+    }
+    /* decrementer interrupts have a lower priority and are once only */
+    else if (ints->pending_interrupts & decrementer_interrupt_pending) {
+      unsigned_word cia = cpu_get_program_counter(processor);
+      unsigned_word nia = perform_oea_interrupt(processor,
+                                               cia, 0x00900, 0, 0, 0, 0);
+      TRACE(trace_interrupts, ("decrementer interrupt - cia 0x%lx, time %ld\n",
+                              (unsigned long)cia,
+                              (unsigned long)event_queue_time(psim_event_queue(cpu_system(processor)))
+                              ));
+      cpu_set_program_counter(processor, nia);
+      ints->pending_interrupts &= ~decrementer_interrupt_pending;
+    }
+  }
+}
+
+STATIC_INLINE_INTERRUPTS\
+(void)
+schedule_hardware_interrupt_delivery(cpu *processor) 
+{
+  interrupts *ints = cpu_interrupts(processor);
+  if (ints->delivery_scheduled == NULL) {
+    ints->delivery_scheduled =
+      event_queue_schedule(psim_event_queue(cpu_system(processor)),
+                          0, deliver_hardware_interrupt, processor);
   }
 }
 
-int INLINE_INTERRUPTS
-external_interrupt(cpu *processor)
+
+INLINE_INTERRUPTS\
+(void)
+check_masked_interrupts(cpu *processor)
+{
+  if (((cpu_registers(processor)->msr & (msr_floating_point_exception_mode_0
+                                        | msr_floating_point_exception_mode_1))
+       && cpu_registers(processor)->fpscr & fpscr_fex)
+      || ((cpu_registers(processor)->msr & msr_external_interrupt_enable)
+         && (cpu_interrupts(processor)->pending_interrupts)))
+    schedule_hardware_interrupt_delivery(processor);
+}
+
+INLINE_INTERRUPTS\
+(void)
+decrementer_interrupt(cpu *processor)
 {
+  interrupts *ints = cpu_interrupts(processor);
+  ints->pending_interrupts |= decrementer_interrupt_pending;
   if (cpu_registers(processor)->msr & msr_external_interrupt_enable) {
-    unsigned_word cia = cpu_get_program_counter(processor);
-    unsigned_word nia = perform_oea_interrupt(processor,
-                                             cia, 0x00500, 0, 0, 0, 0);
-    cpu_set_program_counter(processor, nia);
-    return 1;
+    schedule_hardware_interrupt_delivery(processor);
+  }
+}
+
+INLINE_INTERRUPTS\
+(void)
+external_interrupt(cpu *processor,
+                  int is_asserted)
+{
+  interrupts *ints = cpu_interrupts(processor);
+  if (is_asserted) {
+    if (!(ints->pending_interrupts & external_interrupt_pending)) {
+      ints->pending_interrupts |= external_interrupt_pending;
+      if (cpu_registers(processor)->msr & msr_external_interrupt_enable)
+       schedule_hardware_interrupt_delivery(processor);
+    }
+    else {
+      /* check that we haven't missed out on a chance to deliver an
+         interrupt */
+      ASSERT(!(cpu_registers(processor)->msr & msr_external_interrupt_enable));
+    }
   }
   else {
-    return 0; /* not delivered */
+    ints->pending_interrupts &= ~external_interrupt_pending;
   }
 }
 
-
 #endif /* _INTERRUPTS_C_ */