Fix 68hc11 timer device (accuracy, io, timer overflow)
authorStephane Carrez <stcarrez@nerim.fr>
Wed, 6 Sep 2000 19:33:12 +0000 (19:33 +0000)
committerStephane Carrez <stcarrez@nerim.fr>
Wed, 6 Sep 2000 19:33:12 +0000 (19:33 +0000)
sim/m68hc11/ChangeLog
sim/m68hc11/dv-m68hc11tim.c

index 8b5a69cae19a3e17882463f08d93cbd44a5252c2..be35547f983463d7527565b439f9f5218bbc65f8 100644 (file)
@@ -1,3 +1,13 @@
+2000-09-06  Stephane Carrez  <Stephane.Carrez@worldnet.fr>
+
+       * dv-m68hc11tim.c (m68hc11tim_timer_event): Compute the overflow
+       interrupt and compare events accurately.  Take into account the
+       pending ticks not processed by the simulator yet (introduced a shift).
+       (m68hc11_port_event): Reset the timer interrupt delays.
+       (m68hc11tim_io_read_buffer): Be able to read several bytes.
+       (m68hc11tim_io_write_buffer): Likewise for write.
+       (m68hc11tim_io_write_buffer): Recompute the timer overflow interrupt.
+
 2000-09-06  Stephane Carrez  <Stephane.Carrez@worldnet.fr>
 
        * dv-m68hc11spi.c (m68hc11spi_io_read_buffer): Clear the interrupts.
index 3a4b2cf3dde681d7c07436186ba0dcb65371e65f..c830c05ac91496b16e8d0b5c4b94b487bf0a6f2e 100644 (file)
@@ -77,6 +77,8 @@ struct m68hc11tim
   unsigned long ovf_delay;
   signed64      clock_prescaler;
   signed64      tcnt_adjust;
+  signed64      cop_prev_interrupt;
+  signed64      rti_prev_interrupt;
 
   /* Periodic timers.  */
   struct hw_event *rti_timer_event;
@@ -165,11 +167,13 @@ m68hc11tim_port_event (struct hw *me,
           {
             hw_event_queue_deschedule (me, controller->rti_timer_event);
             controller->rti_timer_event = 0;
+            controller->rti_prev_interrupt = 0;
           }
         if (controller->cop_timer_event)
           {
             hw_event_queue_deschedule (me, controller->cop_timer_event);
             controller->cop_timer_event = 0;
+            controller->cop_prev_interrupt = 0;
           }
         if (controller->tof_timer_event)
           {
@@ -220,13 +224,16 @@ m68hc11tim_timer_event (struct hw *me, void *data)
   int check_interrupt = 0;
   unsigned mask;
   unsigned flags;
+  unsigned long tcnt_internal;
   unsigned long tcnt;
   int i;
+  sim_events *events;
   
   controller = hw_data (me);
   sd         = hw_system (me);
   cpu        = STATE_CPU (sd, 0);
   type       = (enum event_type) ((long) data) & 0x0FF;
+  events     = STATE_EVENTS (sd);
 
   delay = 0;
   switch (type)
@@ -234,43 +241,82 @@ m68hc11tim_timer_event (struct hw *me, void *data)
     case COP_EVENT:
       eventp = &controller->cop_timer_event;
       delay  = controller->cop_delay;
+      delay  = controller->cop_prev_interrupt + controller->cop_delay;
+      controller->cop_prev_interrupt = delay;
+      delay  = delay - cpu->cpu_absolute_cycle;
       check_interrupt = 1;
+      delay += events->nr_ticks_to_process;
       break;
 
     case RTI_EVENT:
       eventp = &controller->rti_timer_event;
-      delay  = controller->rti_delay;
+      delay  = controller->rti_prev_interrupt + controller->rti_delay;
+      
       if (((long) (data) & 0x0100) == 0)
         {
           cpu->ios[M6811_TFLG2] |= M6811_RTIF;
           check_interrupt = 1;
+          controller->rti_prev_interrupt = delay;
+          delay += controller->rti_delay;
         }
+      delay = delay - cpu->cpu_absolute_cycle;
+      delay += events->nr_ticks_to_process;
       break;
 
     case OVERFLOW_EVENT:
+      /* Compute the 68HC11 internal free running counter.
+         There may be 'nr_ticks_to_process' pending cycles that are
+         not (yet) taken into account by 'sim_events_time'.  */
+      tcnt_internal = sim_events_time (sd) - controller->tcnt_adjust;
+      tcnt_internal += events->nr_ticks_to_process;
+
+      /* We must take into account the prescaler that comes
+         before the counter (it's a power of 2).  */
+      tcnt_internal &= 0x0ffff * controller->clock_prescaler;
+
+      /* Compute the time when the overflow will occur.  It occurs when
+         the counter increments from 0x0ffff to 0x10000 (and thus resets).  */
+      delay = (0x10000 * controller->clock_prescaler) - tcnt_internal;
+
+      /* The 'nr_ticks_to_process' will be subtracted when the event
+         is scheduled.  */
+      delay += events->nr_ticks_to_process;
+
       eventp = &controller->tof_timer_event;
-      delay  = controller->ovf_delay;
-      cpu->ios[M6811_TFLG2] |= M6811_TOF;
+      if (((long) (data) & 0x100) == 0)
+        {
+          cpu->ios[M6811_TFLG2] |= M6811_TOF;
+          check_interrupt = 1;
+        }
       break;
 
     case COMPARE_EVENT:
       eventp = &controller->cmp_timer_event;
 
-      /* Get current free running counter.  */
-      tcnt = ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
-              / controller->clock_prescaler);
-      tcnt &= 0x0ffffL;
+      /* Compute the 68HC11 internal free running counter.
+         There may be 'nr_ticks_to_process' pending cycles that are
+         not (yet) taken into account by 'sim_events_time'.  */
+      events = STATE_EVENTS (sd);
+      tcnt_internal = sim_events_time (sd) - controller->tcnt_adjust;
+      tcnt_internal += events->nr_ticks_to_process;
+
+      /* We must take into account the prescaler that comes
+         before the counter (it's a power of 2).  */
+      tcnt_internal &= 0x0ffff * controller->clock_prescaler;
+
+      /* Get current visible TCNT register value.  */
+      tcnt = tcnt_internal / controller->clock_prescaler;
       
       flags = cpu->ios[M6811_TMSK1];
       mask  = 0x80;
-      delay = 65536;
+      delay = 65536 * controller->clock_prescaler;
 
       /* Scan each output compare register to see if one matches
          the free running counter.  Set the corresponding OCi flag
          if the output compare is enabled.  */
       for (i = M6811_TOC1; i <= M6811_TOC5; i += 2, mask >>= 1)
         {
-          unsigned short compare;
+          unsigned long compare;
           
           compare = (cpu->ios[i] << 8) + cpu->ios[i+1];
           if (compare == tcnt && (flags & mask))
@@ -279,16 +325,19 @@ m68hc11tim_timer_event (struct hw *me, void *data)
               check_interrupt++;
             }
 
-          /* Compute how many times for the next match.  */
-          if (compare > tcnt)
-            compare = compare - tcnt;
+          /* Compute how many times for the next match.
+             Use the internal counter value to take into account the
+             prescaler accurately.  */
+          compare = compare * controller->clock_prescaler;
+          if (compare > tcnt_internal)
+            compare = compare - tcnt_internal;
           else
-            compare = compare - tcnt + 65536;
+            compare = compare - tcnt_internal
+              + 65536 * controller->clock_prescaler;
           
           if (compare < delay)
             delay = compare;
         }
-      delay = delay * controller->clock_prescaler;
 
       /* Deactivate the compare timer if no output compare is enabled.  */
       if ((flags & 0xF0) == 0)
@@ -442,6 +491,7 @@ m68hc11tim_io_read_buffer (struct hw *me,
   struct m68hc11tim *controller;
   sim_cpu *cpu;
   unsigned8 val;
+  unsigned cnt = 0;
   
   HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes));
 
@@ -449,27 +499,34 @@ m68hc11tim_io_read_buffer (struct hw *me,
   cpu = STATE_CPU (sd, 0);
   controller = hw_data (me);
 
-  switch (base)
+  while (nr_bytes)
     {
-      /* The cpu_absolute_cycle is updated after each instruction.
-         Reading in a 16-bit register will be split in two accesses
-         but this will be atomic within the simulator.  */
-    case M6811_TCTN_H:
-      val = (uint8) ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
-                     / (controller->clock_prescaler * 256));
-      break;
+      switch (base)
+        {
+          /* The cpu_absolute_cycle is updated after each instruction.
+             Reading in a 16-bit register will be split in two accesses
+             but this will be atomic within the simulator.  */
+        case M6811_TCTN_H:
+          val = (uint8) ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
+                         / (controller->clock_prescaler * 256));
+          break;
 
-    case M6811_TCTN_L:
-      val = (uint8) ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
-                     / controller->clock_prescaler);
-      break;
+        case M6811_TCTN_L:
+          val = (uint8) ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
+                         / controller->clock_prescaler);
+          break;
 
-    default:
-      val = cpu->ios[base];
-      break;
+        default:
+          val = cpu->ios[base];
+          break;
+        }
+      *((unsigned8*) dest) = val;
+      dest++;
+      base++;
+      nr_bytes--;
+      cnt++;
     }
-  *((unsigned8*) dest) = val;
-  return 1;
+  return cnt;
 }
 
 static unsigned
@@ -485,109 +542,120 @@ m68hc11tim_io_write_buffer (struct hw *me,
   unsigned8 val, n;
   signed64 adj;
   int reset_compare = 0;
+  int reset_overflow = 0;
+  int cnt = 0;
   
   HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes));
 
   sd  = hw_system (me);
   cpu = STATE_CPU (sd, 0);
   controller = hw_data (me);
-  
-  val = *((const unsigned8*) source);
-  switch (base)
+
+  while (nr_bytes)
     {
-      /* Set the timer counter low part, trying to preserve the low part.
-         We compute the absolute cycle adjustment that we have to apply
-         to obtain the timer current value.  Computation must be made
-         in 64-bit to avoid overflow problems.  */
-    case M6811_TCTN_L:
-      adj = ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
-             / (controller->clock_prescaler * (signed64) 256)) & 0x0FF;
-      adj = cpu->cpu_absolute_cycle
-        - (adj * controller->clock_prescaler * (signed64) 256)
-        - ((signed64) adj * controller->clock_prescaler);
-      controller->tcnt_adjust = adj;
-      reset_compare = 1;
-      break;
+      val = *((const unsigned8*) source);
+      switch (base)
+        {
+          /* Set the timer counter low part, trying to preserve the low part.
+             We compute the absolute cycle adjustment that we have to apply
+             to obtain the timer current value.  Computation must be made
+             in 64-bit to avoid overflow problems.  */
+        case M6811_TCTN_L:
+          adj = ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
+                 / (controller->clock_prescaler * (signed64) 256)) & 0x0FF;
+          adj = cpu->cpu_absolute_cycle
+            - (adj * controller->clock_prescaler * (signed64) 256)
+            - ((signed64) adj * controller->clock_prescaler);
+          controller->tcnt_adjust = adj;
+          reset_compare = 1;
+          reset_overflow = 1;
+          break;
 
-    case M6811_TCTN_H:
-      adj = ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
-             / controller->clock_prescaler) & 0x0ff;
-      adj = cpu->cpu_absolute_cycle
-        - ((signed64) val * controller->clock_prescaler * (signed64) 256)
-        - (adj * controller->clock_prescaler);
-      controller->tcnt_adjust = adj;
-      reset_compare = 1;
-      break;
+        case M6811_TCTN_H:
+          adj = ((cpu->cpu_absolute_cycle - controller->tcnt_adjust)
+                 / controller->clock_prescaler) & 0x0ff;
+          adj = cpu->cpu_absolute_cycle
+            - ((signed64) val * controller->clock_prescaler * (signed64) 256)
+            - (adj * controller->clock_prescaler);
+          controller->tcnt_adjust = adj;
+          reset_compare = 1;
+          reset_overflow = 1;
+          break;
 
-    case M6811_TMSK2:
+        case M6811_TMSK2:
 
       /* Timer prescaler cannot be changed after 64 bus cycles.  */
-      if (cpu->cpu_absolute_cycle >= 64)
-        {
-          val &= ~(M6811_PR1 | M6811_PR0);
-          val |= cpu->ios[M6811_TMSK2] & (M6811_PR1 | M6811_PR0);
-        }
-      switch (val & (M6811_PR1 | M6811_PR0))
-        {
-        case 0:
-          n = 1;
-          break;
-        case M6811_PR0:
-          n = 4;
-          break;
-        case M6811_PR1:
-          n = 8;
-          break;
-        default:
-        case M6811_PR1 | M6811_PR0:
-          n = 16;
+          if (cpu->cpu_absolute_cycle >= 64)
+            {
+              val &= ~(M6811_PR1 | M6811_PR0);
+              val |= cpu->ios[M6811_TMSK2] & (M6811_PR1 | M6811_PR0);
+            }
+          switch (val & (M6811_PR1 | M6811_PR0))
+            {
+            case 0:
+              n = 1;
+              break;
+            case M6811_PR0:
+              n = 4;
+              break;
+            case M6811_PR1:
+              n = 8;
+              break;
+            default:
+            case M6811_PR1 | M6811_PR0:
+              n = 16;
+              break;
+            }
+          if (cpu->cpu_absolute_cycle < 64)
+            {
+              reset_overflow = 1;
+              controller->clock_prescaler = n;
+            }
+          cpu->ios[base] = val;
+          interrupts_update_pending (&cpu->cpu_interrupts);
           break;
-        }
-      if (controller->clock_prescaler != n)
-        {
-          controller->clock_prescaler = n;
-          controller->ovf_delay = n * 65536;
-          m68hc11tim_timer_event (me, (void*) (OVERFLOW_EVENT| 0x100));
-        }
-      cpu->ios[base] = val;
-      interrupts_update_pending (&cpu->cpu_interrupts);
-      break;
 
-    case M6811_PACTL:
-      n = (1 << ((val & (M6811_RTR1 | M6811_RTR0))));
-      cpu->ios[base] = val;
+        case M6811_PACTL:
+          n = (1 << ((val & (M6811_RTR1 | M6811_RTR0))));
+          cpu->ios[base] = val;
 
-      controller->rti_delay = (long) (n) * 8192;
-      m68hc11tim_timer_event (me, (void*) (RTI_EVENT| 0x100));
-      break;
+          controller->rti_delay = (long) (n) * 8192;
+          m68hc11tim_timer_event (me, (void*) (RTI_EVENT| 0x100));
+          break;
       
-    case M6811_TFLG2:
-      if (val & M6811_TOF)
-        val &= ~M6811_TOF;
-      else
-        val |= cpu->ios[M6811_TFLG2] & M6811_TOF;
+        case M6811_TFLG2:
+          if (val & M6811_TOF)
+            val &= ~M6811_TOF;
+          else
+            val |= cpu->ios[M6811_TFLG2] & M6811_TOF;
 
       /* Clear the Real Time interrupt flag. */
-      if (val & M6811_RTIF)
-        val &= ~M6811_RTIF;
-      else
-        val |= cpu->ios[M6811_TFLG2] & M6811_RTIF;
+          if (val & M6811_RTIF)
+            val &= ~M6811_RTIF;
+          else
+            val |= cpu->ios[M6811_TFLG2] & M6811_RTIF;
       
-      cpu->ios[base] = val;
-      interrupts_update_pending (&cpu->cpu_interrupts);
-      break;
+          cpu->ios[base] = val;
+          interrupts_update_pending (&cpu->cpu_interrupts);
+          break;
 
-    case M6811_TOC1:
-    case M6811_TOC2:
-    case M6811_TOC3:
-    case M6811_TOC4:
-    case M6811_TOC5:
-      cpu->ios[base] = val;
-      reset_compare = 1;
-      break;
+        case M6811_TOC1:
+        case M6811_TOC2:
+        case M6811_TOC3:
+        case M6811_TOC4:
+        case M6811_TOC5:
+          cpu->ios[base] = val;
+          reset_compare = 1;
+          break;
       
-    default:
-      return 0;
+        default:
+          break;
+        }
+
+      base++;
+      nr_bytes--;
+      cnt++;
+      source++;
     }
 
   /* Re-compute the next timer compare event.  */
@@ -595,7 +663,11 @@ m68hc11tim_io_write_buffer (struct hw *me,
     {
       m68hc11tim_timer_event (me, (void*) (COMPARE_EVENT));
     }
-  return nr_bytes;
+  if (reset_overflow)
+    {
+      m68hc11tim_timer_event (me, (void*) (OVERFLOW_EVENT| 0x100));
+    }
+  return cnt;
 }