Index: emu/cpu.c
===================================================================
--- emu/cpu.c	(revision 05e6dbe8b288dd07888f2ffc2ab329f6b96cc97d)
+++ emu/cpu.c	(revision 7b50125d0c8b6074abd7478c3e565703e41dda81)
@@ -75,5 +75,5 @@
 	{ 0x180000, 0x200000, 0, fpu_init, fpu_quit, fpu_exec, fpu_read, fpu_write },
 	{ 0x200000, 0x280002, 0, vid_init, vid_quit, vid_exec, vid_read, vid_write },
-	{ 0x3a0001, 0x3a4001, 0, tim_init, tim_quit, tim_exec, tim_read, tim_write },
+	{ 0x3a0001, 0x3a4001, 4, tim_init, tim_quit, tim_exec, tim_read, tim_write },
 	{ 0x3a4001, 0x3a8001, 0, lcd_init, lcd_quit, lcd_exec, lcd_read, lcd_write },
 	{ 0x3a8001, 0x3ac001, 5, ser_init, ser_quit, ser_exec, ser_read, ser_write },
Index: emu/tim.c
===================================================================
--- emu/tim.c	(revision 05e6dbe8b288dd07888f2ffc2ab329f6b96cc97d)
+++ emu/tim.c	(revision 7b50125d0c8b6074abd7478c3e565703e41dda81)
@@ -24,4 +24,33 @@
 int32_t tim_verbose = 0;
 
+#define REG_CRX 0
+#define REG_CR2_SR 1
+#define REG_T1H	2
+#define REG_T1L 3
+#define REG_T2H 4
+#define REG_T2L 5
+#define REG_T3H 6
+#define REG_T3L 7
+
+#define COUNT_1 32
+#define COUNT_2 3200
+#define COUNT_3 801
+
+typedef struct {
+	bool irq_e;
+	bool irq;
+	int32_t latch;
+	int32_t count;
+} state_timer;
+
+static state_timer timers[] = {
+		{ .irq_e = false, .irq = false, .latch = COUNT_1, .count = COUNT_1 },
+		{ .irq_e = false, .irq = false, .latch = COUNT_2, .count = COUNT_2 },
+		{ .irq_e = false, .irq = false, .latch = COUNT_3, .count = COUNT_3 }
+};
+
+static bool wr_cr1 = false;
+static bool oper = false;
+
 void tim_init(void)
 {
@@ -36,6 +65,21 @@
 bool tim_exec(void)
 {
-	ver3("tim exec");
-	return false;
+	if (oper) {
+		for (int32_t i = 0; i < ARRAY_COUNT(timers); ++i) {
+			--timers[i].count;
+
+			if (timers[i].count == 0) {
+				ver2("tim %d zero", i + 1);
+				timers[i].count = timers[i].latch;
+
+				if (timers[i].irq_e) {
+					ver2("tim %d irq", i + 1);
+					timers[i].irq = true;
+				}
+			}
+		}
+	}
+
+	return timers[0].irq || timers[1].irq || timers[2].irq;
 }
 
@@ -43,5 +87,50 @@
 {
 	ver2("tim rd %u:%d", off, sz * 8);
-	return 0;
+
+	if (sz != 1 || off > 7) {
+		fail("invalid tim rd %u:%d", off, sz * 8);
+	}
+
+	uint32_t rv = 0x00;
+
+	switch (off) {
+	case REG_CR2_SR:
+		rv |= (uint32_t)timers[0].irq << 0;
+		rv |= (uint32_t)timers[1].irq << 1;
+		rv |= (uint32_t)timers[2].irq << 2;
+
+		ver2("tim irqs %u %u %u",
+				(uint32_t)timers[0].irq, (uint32_t)timers[1].irq, (uint32_t)timers[2].irq);
+		break;
+
+	case REG_T1L:
+		if (timers[0].irq) {
+			ver2("tim 1 !irq");
+			timers[0].irq = false;
+		}
+
+		break;
+
+	case REG_T2L:
+		if (timers[1].irq) {
+			ver2("tim 2 !irq");
+			timers[1].irq = false;
+		}
+
+		break;
+
+	case REG_T3L:
+		if (timers[2].irq) {
+			ver2("tim 3 !irq");
+			timers[2].irq = false;
+		}
+
+		break;
+
+	default:
+		break;
+	}
+
+	return rv;
 }
 
@@ -49,3 +138,32 @@
 {
 	ver2("tim wr %u:%d 0x%0*x", off, sz * 8, sz * 2, val);
+
+	if (sz != 1 || off > 7) {
+		fail("invalid tim wr %u:%d", off, sz * 8);
+	}
+
+	switch (off) {
+	case REG_CRX:
+		if (wr_cr1) {
+			if ((val & 0x01) == 0) {
+				ver2("tim start");
+				oper = true;
+			}
+
+			timers[0].irq_e = (val & 0x40) != 0;
+		}
+		else {
+			timers[2].irq_e = (val & 0x40) != 0;
+		}
+
+		break;
+
+	case REG_CR2_SR:
+		wr_cr1 = (val & 0x01) != 0;
+		timers[1].irq_e = (val & 0x40) != 0;
+		break;
+
+	default:
+		break;
+	}
 }
