diff --git a/hw/sun4u.c b/hw/sun4u.c
index 84a8043..35f4c6b 100644
--- a/hw/sun4u.c
+++ b/hw/sun4u.c
@@ -281,6 +281,12 @@ void cpu_check_irqs(CPUState *env)
    }
 }

+static void cpu_kick_irq(CPUState *env)
+{
+    env->halted = 0;
+    cpu_check_irqs(env);
+}
+
 static void cpu_set_irq(void *opaque, int irq, int level)
 {
    CPUState *env = opaque;
@@ -302,6 +308,41 @@ typedef struct ResetData {
    uint64_t prom_addr;
 } ResetData;

+struct sun4u_timer
+{
+    const char *name;
+    uint32_t    frequency;
+    int      disabled;
+    uint64_t disabled_mask;
+    QEMUTimer *qtimer;
+};
+
+typedef struct sun4u_timer sun4u_timer;
+
+static sun4u_timer* sun4u_timer_create(const char* name, CPUState *env,
+                                       QEMUBHFunc *cb, uint32_t frequency,
+                                       int64_t period, uint64_t disabled_mask)
+{
+    sun4u_timer *timer;
+
+    timer = qemu_mallocz(sizeof (sun4u_timer));
+
+    timer->name = name;
+    timer->frequency = frequency;
+    timer->disabled = 1;
+    timer->disabled_mask = disabled_mask;
+
+    timer->qtimer = qemu_new_timer(vm_clock, cb, env);
+
+    return timer;
+}
+
+static void sun4u_timer_reset(sun4u_timer *timer)
+{
+    timer->disabled = 1;
+    qemu_del_timer(timer->qtimer);
+}
+
 static void main_cpu_reset(void *opaque)
 {
    ResetData *s = (ResetData *)opaque;
@@ -309,15 +350,11 @@ static void main_cpu_reset(void *opaque)
    static unsigned int nr_resets;

    cpu_reset(env);
-    env->tick_cmpr = TICK_INT_DIS | 0;
-    ptimer_set_limit(env->tick, TICK_MAX, 1);
-    ptimer_run(env->tick, 1);
-    env->stick_cmpr = TICK_INT_DIS | 0;
-    ptimer_set_limit(env->stick, TICK_MAX, 1);
-    ptimer_run(env->stick, 1);
-    env->hstick_cmpr = TICK_INT_DIS | 0;
-    ptimer_set_limit(env->hstick, TICK_MAX, 1);
-    ptimer_run(env->hstick, 1);
+
+    sun4u_timer_reset(env->tick);
+    sun4u_timer_reset(env->stick);
+    sun4u_timer_reset(env->hstick);
+
    env->gregs[1] = 0; // Memory start
    env->gregs[2] = ram_size; // Memory size
    env->gregs[3] = 0; // Machine description XXX
@@ -334,44 +371,125 @@ static void tick_irq(void *opaque)
 {
    CPUState *env = opaque;

-    if (!(env->tick_cmpr & TICK_INT_DIS)) {
-        env->softint |= SOFTINT_TIMER;
-        cpu_interrupt(env, CPU_INTERRUPT_TIMER);
+    sun4u_timer* timer = (sun4u_timer*) env->tick;
+
+    if (timer->disabled)
+    {
+        fprintf(logfile, "tick_irq: softint disabled\n");
+        return;
    }
+    else
+    {
+        fprintf(logfile, "tick: fire\n");
+    }
+
+    env->softint |= SOFTINT_TM;
+    cpu_kick_irq(env);
 }

 static void stick_irq(void *opaque)
 {
    CPUState *env = opaque;

-    if (!(env->stick_cmpr & TICK_INT_DIS)) {
-        env->softint |= SOFTINT_STIMER;
-        cpu_interrupt(env, CPU_INTERRUPT_TIMER);
+    sun4u_timer* timer = (sun4u_timer*) env->stick;
+
+    if (timer->disabled)
+    {
+        CPUIRQ_DPRINTF("stick_irq: softint disabled\n");
+        return;
    }
+    else
+    {
+        CPUIRQ_DPRINTF("stick: fire\n");
+    }
+
+    env->softint |= SOFTINT_SM;
+    cpu_kick_irq(env);
 }

 static void hstick_irq(void *opaque)
 {
    CPUState *env = opaque;

-    if (!(env->hstick_cmpr & TICK_INT_DIS)) {
-        cpu_interrupt(env, CPU_INTERRUPT_TIMER);
+    sun4u_timer* timer = (sun4u_timer*) env->hstick;
+
+    if (timer->disabled)
+    {
+        CPUIRQ_DPRINTF("hstick_irq: softint disabled\n");
+        return;
+    }
+    else
+    {
+        CPUIRQ_DPRINTF("hstick: fire\n");
    }
+
+    env->softint |= SOFTINT_SM;
+    cpu_kick_irq(env);
 }

 void cpu_tick_set_count(void *opaque, uint64_t count)
 {
-    ptimer_set_count(opaque, -count);
+    sun4u_timer *timer = opaque;
+
+    uint64_t real_count = count & ~timer->disabled_mask;
+    timer->disabled = (count & timer->disabled_mask) ? 1 : 0;
+
+    fprintf(logfile, "%s (ignored) set_count count=0x%016lx (%s) p=%p\n",
+           timer->name, real_count,
timer->disabled?"disabled":"enabled", opaque);
+
+    // TODO: save offset in our timer
 }

 uint64_t cpu_tick_get_count(void *opaque)
 {
-    return -ptimer_get_count(opaque);
+    sun4u_timer *timer = opaque;
+
+    uint64_t real_count = muldiv64(qemu_get_clock(vm_clock),
timer->frequency, get_ticks_per_sec());
+
+    fprintf(logfile, "%s get_count count=0x%016lx (%s) p=%p\n",
+           timer->name, real_count,
timer->disabled?"disabled":"enabled", opaque);
+
+    if (timer->disabled)
+        real_count |= timer->disabled_mask;
+
+    return real_count;
 }

 void cpu_tick_set_limit(void *opaque, uint64_t limit)
 {
-    ptimer_set_limit(opaque, -limit, 0);
+    sun4u_timer *timer = opaque;
+
+    int64_t now = qemu_get_clock(vm_clock);
+
+    int64_t real_limit = limit & ~timer->disabled_mask;
+    int64_t expires = muldiv64(now, timer->frequency,
get_ticks_per_sec()) & ~timer->disabled_mask;
+    int64_t current_tick = expires;
+    int64_t delta = real_limit - current_tick;
+    if (delta < 0)
+        delta = 1;
+
+    timer->disabled = (limit & timer->disabled_mask) ? 1 : 0;
+
+    fprintf(logfile, "%s set_limit limit=0x%016lx (%s) p=%p "
+            "called with limit=0x%016lx at 0x%016lx (delta=0x%016lx)\n",
+           timer->name, real_limit, timer->disabled?"disabled":"enabled",
+                           opaque, limit, current_tick, delta);
+
+    if (!real_limit)
+    {
+        fprintf(logfile, "%s set_limit limit=ZERO - not starting timer\n",
+                timer->name);
+        qemu_del_timer(timer->qtimer);
+    }
+    else if (timer->disabled)
+    {
+        qemu_del_timer(timer->qtimer);
+    }
+    else
+    {
+        qemu_mod_timer(timer->qtimer, now + muldiv64(delta,
get_ticks_per_sec(),
