commit 61271e5c2d3eff4cc0c4845be291172c2015b6f1
Author: bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Date:   Thu Jul 7 21:45:18 2005 +0000

    more precise cuda timers
    
    
    git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1508 c046a42c-6fe2-441c-8c8c-71466251a162

diff --git a/hw/cuda.c b/hw/cuda.c
index 5ef2637..98a2637 100644
--- a/hw/cuda.c
+++ b/hw/cuda.c
@@ -23,6 +23,8 @@
  */
 #include "vl.h"
 
+/* XXX: implement all timer modes */
+
 //#define DEBUG_CUDA
 //#define DEBUG_CUDA_PACKET
 
@@ -41,6 +43,7 @@
 #define IER_CLR		0		/* clear bits in IER */
 #define SR_INT		0x04		/* Shift register full/empty */
 #define T1_INT          0x40            /* Timer 1 interrupt */
+#define T2_INT          0x20            /* Timer 2 interrupt */
 
 /* Bits in ACR */
 #define T1MODE          0xc0            /* Timer 1 mode */
@@ -91,7 +94,8 @@
 #define RTC_OFFSET                      2082844800
 
 typedef struct CUDATimer {
-    unsigned int latch;
+    int index; 
+    uint16_t latch;
     uint16_t counter_value; /* counter value at load time */
     int64_t load_time;
     int64_t next_irq_time;
@@ -154,10 +158,16 @@ static unsigned int get_counter(CUDATimer *s)
 
     d = muldiv64(qemu_get_clock(vm_clock) - s->load_time, 
                  CUDA_TIMER_FREQ, ticks_per_sec);
-    if (d <= s->counter_value) {
-        counter = d;
+    if (s->index == 0) {
+        /* the timer goes down from latch to -1 (period of latch + 2) */
+        if (d <= (s->counter_value + 1)) {
+            counter = (s->counter_value - d) & 0xffff;
+        } else {
+            counter = (d - (s->counter_value + 1)) % (s->latch + 2);
+            counter = (s->latch - counter) & 0xffff; 
+        }
     } else {
-        counter = s->latch - 1 - ((d - s->counter_value) % s->latch);
+        counter = (s->counter_value - d) & 0xffff;
     }
     return counter;
 }
@@ -175,17 +185,27 @@ static void set_counter(CUDAState *s, CUDATimer *ti, unsigned int val)
 
 static int64_t get_next_irq_time(CUDATimer *s, int64_t current_time)
 {
-    int64_t d, next_time, base;
+    int64_t d, next_time;
+    unsigned int counter;
+
     /* current counter value */
     d = muldiv64(current_time - s->load_time, 
                  CUDA_TIMER_FREQ, ticks_per_sec);
-    if (d < s->counter_value) {
-        next_time = s->counter_value + 1;
-    } else
-    {
-        base = ((d - s->counter_value + 1) / s->latch);
-        base = (base * s->latch) + s->counter_value;
-        next_time = base + s->latch;
+    /* the timer goes down from latch to -1 (period of latch + 2) */
+    if (d <= (s->counter_value + 1)) {
+        counter = (s->counter_value - d) & 0xffff;
+    } else {
+        counter = (d - (s->counter_value + 1)) % (s->latch + 2);
+        counter = (s->latch - counter) & 0xffff; 
+    }
+    
+    /* Note: we consider the irq is raised on 0 */
+    if (counter == 0xffff) {
+        next_time = d + s->latch + 1;
+    } else if (counter == 0) {
+        next_time = d + s->latch + 2;
+    } else {
+        next_time = d + counter;
     }
 #if 0
 #ifdef DEBUG_CUDA
@@ -249,17 +269,18 @@ static uint32_t cuda_readb(void *opaque, target_phys_addr_t addr)
         break;
     case 5:
         val = get_counter(&s->timers[0]) >> 8;
-        s->ifr &= ~T1_INT;
         cuda_update_irq(s);
         break;
     case 6:
         val = s->timers[0].latch & 0xff;
         break;
     case 7:
+        /* XXX: check this */
         val = (s->timers[0].latch >> 8) & 0xff;
         break;
     case 8:
         val = get_counter(&s->timers[1]) & 0xff;
+        s->ifr &= ~T2_INT;
         break;
     case 9:
         val = get_counter(&s->timers[1]) >> 8;
@@ -317,12 +338,13 @@ static void cuda_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
         s->dira = val;
         break;
     case 4:
-        val = val | (get_counter(&s->timers[0]) & 0xff00);
-        set_counter(s, &s->timers[0], val);
+        s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
+        cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
         break;
     case 5:
-        val = (val << 8) |  (get_counter(&s->timers[0]) & 0xff);
-        set_counter(s, &s->timers[0], val);
+        s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
+        s->ifr &= ~T1_INT;
+        set_counter(s, &s->timers[0], s->timers[0].latch);
         break;
     case 6:
         s->timers[0].latch = (s->timers[0].latch & 0xff00) | val;
@@ -330,15 +352,15 @@ static void cuda_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
         break;
     case 7:
         s->timers[0].latch = (s->timers[0].latch & 0xff) | (val << 8);
+        s->ifr &= ~T1_INT;
         cuda_timer_update(s, &s->timers[0], qemu_get_clock(vm_clock));
         break;
     case 8:
-        val = val | (get_counter(&s->timers[1]) & 0xff00);
+        s->timers[1].latch = val;
         set_counter(s, &s->timers[1], val);
         break;
     case 9:
-        val = (val << 8) |  (get_counter(&s->timers[1]) & 0xff);
-        set_counter(s, &s->timers[1], val);
+        set_counter(s, &s->timers[1], (val << 8) | s->timers[1].latch);
         break;
     case 10:
         s->sr = val;
@@ -620,10 +642,13 @@ int cuda_init(SetIRQFunc *set_irq, void *irq_opaque, int irq)
     s->irq_opaque = irq_opaque;
     s->irq = irq;
 
+    s->timers[0].index = 0;
     s->timers[0].timer = qemu_new_timer(vm_clock, cuda_timer1, s);
-    s->timers[0].latch = 0x10000;
+    s->timers[0].latch = 0xffff;
     set_counter(s, &s->timers[0], 0xffff);
-    s->timers[1].latch = 0x10000;
+
+    s->timers[1].index = 1;
+    s->timers[1].latch = 0;
     //    s->ier = T1_INT | SR_INT;
     s->ier = 0;
     set_counter(s, &s->timers[1], 0xffff);
