Index: emu/all.h
===================================================================
--- emu/all.h	(revision 8e1b1633c6a334db78a3162f8ec73ec97dfe8c03)
+++ emu/all.h	(revision 3c30832518143c8c449bb24c646119cd91cc8d89)
@@ -72,5 +72,5 @@
 extern void fpu_init(void);
 extern void fpu_quit(void);
-extern void fpu_exec(void);
+extern bool fpu_exec(void);
 extern uint32_t fpu_read(uint32_t off, int32_t sz);
 extern void fpu_write(uint32_t off, int32_t sz, uint32_t val);
@@ -78,5 +78,5 @@
 extern void vid_init(void);
 extern void vid_quit(void);
-extern void vid_exec(void);
+extern bool vid_exec(void);
 extern uint32_t vid_read(uint32_t off, int32_t sz);
 extern void vid_write(uint32_t off, int32_t sz, uint32_t val);
@@ -84,5 +84,5 @@
 extern void tim_init(void);
 extern void tim_quit(void);
-extern void tim_exec(void);
+extern bool tim_exec(void);
 extern uint32_t tim_read(uint32_t off, int32_t sz);
 extern void tim_write(uint32_t off, int32_t sz, uint32_t val);
@@ -90,5 +90,5 @@
 extern void lcd_init(void);
 extern void lcd_quit(void);
-extern void lcd_exec(void);
+extern bool lcd_exec(void);
 extern uint32_t lcd_read(uint32_t off, int32_t sz);
 extern void lcd_write(uint32_t off, int32_t sz, uint32_t val);
@@ -96,5 +96,5 @@
 extern void ser_init(void);
 extern void ser_quit(void);
-extern void ser_exec(void);
+extern bool ser_exec(void);
 extern uint32_t ser_read(uint32_t off, int32_t sz);
 extern void ser_write(uint32_t off, int32_t sz, uint32_t val);
@@ -105,5 +105,5 @@
 extern void mid_init(void);
 extern void mid_quit(void);
-extern void mid_exec(void);
+extern bool mid_exec(void);
 extern uint32_t mid_read(uint32_t off, int32_t sz);
 extern void mid_write(uint32_t off, int32_t sz, uint32_t val);
@@ -111,5 +111,5 @@
 extern void fdd_init(void);
 extern void fdd_quit(void);
-extern void fdd_exec(void);
+extern bool fdd_exec(void);
 extern uint32_t fdd_read(uint32_t off, int32_t sz);
 extern void fdd_write(uint32_t off, int32_t sz, uint32_t val);
@@ -117,5 +117,5 @@
 extern void snd_init(void);
 extern void snd_quit(void);
-extern void snd_exec(void);
+extern bool snd_exec(void);
 extern uint32_t snd_read(uint32_t off, int32_t sz);
 extern void snd_write(uint32_t off, int32_t sz, uint32_t val);
@@ -123,5 +123,5 @@
 extern void led_init(void);
 extern void led_quit(void);
-extern void led_exec(void);
+extern bool led_exec(void);
 extern uint32_t led_read(uint32_t off, int32_t sz);
 extern void led_write(uint32_t off, int32_t sz, uint32_t val);
@@ -129,5 +129,5 @@
 extern void kbd_init(void);
 extern void kbd_quit(void);
-extern void kbd_exec(void);
+extern bool kbd_exec(void);
 extern uint32_t kbd_read(uint32_t off, int32_t sz);
 extern void kbd_write(uint32_t off, int32_t sz, uint32_t val);
Index: emu/cpu.c
===================================================================
--- emu/cpu.c	(revision 8e1b1633c6a334db78a3162f8ec73ec97dfe8c03)
+++ emu/cpu.c	(revision 3c30832518143c8c449bb24c646119cd91cc8d89)
@@ -37,5 +37,5 @@
 typedef void (*hw_init_t)(void);
 typedef void (*hw_quit_t)(void);
-typedef void (*hw_exec_t)(void);
+typedef bool (*hw_exec_t)(void);
 typedef uint32_t (*hw_read_t)(uint32_t off, int32_t sz);
 typedef void (*hw_write_t)(uint32_t off, int32_t sz, uint32_t val);
@@ -44,4 +44,5 @@
 	uint32_t addr_beg;
 	uint32_t addr_end;
+	uint32_t irq;
 	hw_init_t init;
 	hw_quit_t quit;
@@ -67,14 +68,14 @@
 
 static hw_t hw_map[] = {
-	{ 0x180000, 0x200000, fpu_init, fpu_quit, fpu_exec, fpu_read, fpu_write },
-	{ 0x200000, 0x280000, vid_init, vid_quit, vid_exec, vid_read, vid_write },
-	{ 0x3a0001, 0x3a4001, tim_init, tim_quit, tim_exec, tim_read, tim_write },
-	{ 0x3a4001, 0x3a8001, lcd_init, lcd_quit, lcd_exec, lcd_read, lcd_write },
-	{ 0x3a8001, 0x3ac001, ser_init, ser_quit, ser_exec, ser_read, ser_write },
-	{ 0x3ac001, 0x3b0001, mid_init, mid_quit, mid_exec, mid_read, mid_write },
-	{ 0x3b0001, 0x3b4001, fdd_init, fdd_quit, fdd_exec, fdd_read, fdd_write },
-	{ 0x3b4001, 0x3b8001, snd_init, snd_quit, snd_exec, snd_read, snd_write },
-	{ 0x3b8001, 0x3bc001, led_init, led_quit, led_exec, led_read, led_write },
-	{ 0x3bc001, 0x3c0001, kbd_init, kbd_quit, kbd_exec, kbd_read, kbd_write }
+	{ 0x180000, 0x200000, 0, fpu_init, fpu_quit, fpu_exec, fpu_read, fpu_write },
+	{ 0x200000, 0x280000, 0, vid_init, vid_quit, vid_exec, vid_read, vid_write },
+	{ 0x3a0001, 0x3a4001, 0, 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 },
+	{ 0x3ac001, 0x3b0001, 0, mid_init, mid_quit, mid_exec, mid_read, mid_write },
+	{ 0x3b0001, 0x3b4001, 0, fdd_init, fdd_quit, fdd_exec, fdd_read, fdd_write },
+	{ 0x3b4001, 0x3b8001, 0, snd_init, snd_quit, snd_exec, snd_read, snd_write },
+	{ 0x3b8001, 0x3bc001, 0, led_init, led_quit, led_exec, led_read, led_write },
+	{ 0x3bc001, 0x3c0001, 0, kbd_init, kbd_quit, kbd_exec, kbd_read, kbd_write }
 };
 
@@ -108,9 +109,15 @@
 }
 
-static void hw_exec(void)
-{
+static uint32_t hw_exec(void)
+{
+	uint32_t irq = 0;
+
 	for (int32_t i = 0; i < ARRAY_COUNT(hw_map); ++i) {
-		hw_map[i].exec();
-	}
+		if (hw_map[i].exec() && hw_map[i].irq > irq) {
+			irq = hw_map[i].irq;
+		}
+	}
+
+	return irq;
 }
 
@@ -629,5 +636,11 @@
 
 		m68k_execute(CPU_FREQ / PER_SEC);
-		hw_exec();
+		uint32_t irq = hw_exec();
+
+		if (irq > 0) {
+			ver2("irq %u", irq);
+		}
+
+		m68k_set_irq(irq);
 
 		SDL_Event ev;
Index: emu/fdd.c
===================================================================
--- emu/fdd.c	(revision 8e1b1633c6a334db78a3162f8ec73ec97dfe8c03)
+++ emu/fdd.c	(revision 3c30832518143c8c449bb24c646119cd91cc8d89)
@@ -34,7 +34,8 @@
 }
 
-void fdd_exec(void)
+bool fdd_exec(void)
 {
 	ver3("fdd exec");
+	return false;
 }
 
Index: emu/fpu.c
===================================================================
--- emu/fpu.c	(revision 8e1b1633c6a334db78a3162f8ec73ec97dfe8c03)
+++ emu/fpu.c	(revision 3c30832518143c8c449bb24c646119cd91cc8d89)
@@ -34,7 +34,8 @@
 }
 
-void fpu_exec(void)
+bool fpu_exec(void)
 {
 	ver3("fpu exec");
+	return false;
 }
 
Index: emu/kbd.c
===================================================================
--- emu/kbd.c	(revision 8e1b1633c6a334db78a3162f8ec73ec97dfe8c03)
+++ emu/kbd.c	(revision 3c30832518143c8c449bb24c646119cd91cc8d89)
@@ -34,7 +34,8 @@
 }
 
-void kbd_exec(void)
+bool kbd_exec(void)
 {
 	ver3("kbd exec");
+	return false;
 }
 
Index: emu/lcd.c
===================================================================
--- emu/lcd.c	(revision 8e1b1633c6a334db78a3162f8ec73ec97dfe8c03)
+++ emu/lcd.c	(revision 3c30832518143c8c449bb24c646119cd91cc8d89)
@@ -34,7 +34,8 @@
 }
 
-void lcd_exec(void)
+bool lcd_exec(void)
 {
 	ver3("lcd exec");
+	return false;
 }
 
Index: emu/led.c
===================================================================
--- emu/led.c	(revision 8e1b1633c6a334db78a3162f8ec73ec97dfe8c03)
+++ emu/led.c	(revision 3c30832518143c8c449bb24c646119cd91cc8d89)
@@ -34,7 +34,8 @@
 }
 
-void led_exec(void)
+bool led_exec(void)
 {
 	ver3("led exec");
+	return false;
 }
 
Index: emu/mid.c
===================================================================
--- emu/mid.c	(revision 8e1b1633c6a334db78a3162f8ec73ec97dfe8c03)
+++ emu/mid.c	(revision 3c30832518143c8c449bb24c646119cd91cc8d89)
@@ -34,7 +34,8 @@
 }
 
-void mid_exec(void)
+bool mid_exec(void)
 {
 	ver3("mid exec");
+	return false;
 }
 
Index: emu/ser.c
===================================================================
--- emu/ser.c	(revision 8e1b1633c6a334db78a3162f8ec73ec97dfe8c03)
+++ emu/ser.c	(revision 3c30832518143c8c449bb24c646119cd91cc8d89)
@@ -31,5 +31,20 @@
 #define CON_FONT "ttf/vera-sans-mono.ttf"
 
+#define REG_IER_ISR 0
+#define REG_CFR_SR  1
+#define REG_CDR_TBR 2
+#define REG_TDR_RDR 3
+
+typedef struct {
+	bool irq;
+	uint8_t rdr;
+} state_t;
+
 int32_t ser_verbose = 0;
+
+static state_t state[] = {
+	{ .irq = false, .rdr = 0x00 },
+	{ .irq = false, .rdr = 0x00 }
+};
 
 static uint8_t mem[CON_H][CON_W + 1];
@@ -42,4 +57,10 @@
 
 static int32_t cur_x = 0, cur_y = 0;
+
+static void out(int32_t un, uint8_t c)
+{
+	state[un].irq = true;
+	state[un].rdr = c;
+}
 
 static void update(void)
@@ -173,10 +194,9 @@
 	switch (ev->keysym.sym) {
 	case SDLK_BACKSPACE:
-		echo('\b');
+		out(1, '\b');
 		break;
 
 	case SDLK_RETURN:
-		echo('\r');
-		echo('\n');
+		out(1, '\r');
 		break;
 
@@ -184,5 +204,5 @@
 		if ((ev->keysym.mod & KMOD_CTRL) != 0 &&
 				ev->keysym.sym >= SDLK_a && ev->keysym.sym <= SDLK_z) {
-			echo((uint8_t)(ev->keysym.sym - SDLK_a + 1));
+			out(1, (uint8_t)(ev->keysym.sym - SDLK_a + 1));
 		}
 
@@ -194,5 +214,5 @@
 {
 	for (int32_t i = 0; ev->text[i] != 0; ++i) {
-		echo((uint8_t)ev->text[i]);
+		out(1, (uint8_t)ev->text[i]);
 	}
 }
@@ -248,7 +268,8 @@
 }
 
-void ser_exec(void)
+bool ser_exec(void)
 {
 	ver3("ser exec");
+	return state[0].irq || state[1].irq;
 }
 
@@ -256,5 +277,32 @@
 {
 	ver2("ser rd %u:%d", off, sz * 8);
-	return 0;
+
+	if (sz != 1 || off > 7) {
+		fail("invalid ser rd %u:%d", off, sz * 8);
+	}
+
+	int32_t rg = (int32_t)(off % 4);
+	int32_t un = (int32_t)(off / 4);
+
+	uint32_t rv;
+
+	switch (rg) {
+	case REG_IER_ISR:
+		rv = (uint32_t)(0xc0 | (state[un].irq ? 0x01 : 0x00));
+		ver2("ISR[%d] 0x%02x", un, rv);
+		break;
+
+	case REG_TDR_RDR:
+		rv = state[un].rdr;
+		state[un].irq = false;
+		ver2("RDR[%d] 0x%02x", un, rv);
+		break;
+
+	default:
+		rv = 0x00;
+		break;
+	}
+
+	return rv;
 }
 
@@ -262,3 +310,20 @@
 {
 	ver2("ser wr %u:%d 0x%0*x", off, sz * 8, sz * 2, val);
-}
+
+	if (sz != 1 || off > 7) {
+		fail("invalid ser wr %u:%d", off, sz * 8);
+	}
+
+	int32_t rg = (int32_t)(off % 4);
+	int32_t un = (int32_t)(off / 4);
+
+	switch (rg) {
+	case REG_TDR_RDR:
+		ver2("TDR[%d] 0x%02x", un, val);
+		echo((uint8_t)val);
+		break;
+
+	default:
+		break;
+	}
+}
Index: emu/snd.c
===================================================================
--- emu/snd.c	(revision 8e1b1633c6a334db78a3162f8ec73ec97dfe8c03)
+++ emu/snd.c	(revision 3c30832518143c8c449bb24c646119cd91cc8d89)
@@ -34,7 +34,8 @@
 }
 
-void snd_exec(void)
+bool snd_exec(void)
 {
 	ver3("snd exec");
+	return false;
 }
 
Index: emu/tim.c
===================================================================
--- emu/tim.c	(revision 8e1b1633c6a334db78a3162f8ec73ec97dfe8c03)
+++ emu/tim.c	(revision 3c30832518143c8c449bb24c646119cd91cc8d89)
@@ -34,7 +34,8 @@
 }
 
-void tim_exec(void)
+bool tim_exec(void)
 {
 	ver3("tim exec");
+	return false;
 }
 
Index: emu/vid.c
===================================================================
--- emu/vid.c	(revision 8e1b1633c6a334db78a3162f8ec73ec97dfe8c03)
+++ emu/vid.c	(revision 3c30832518143c8c449bb24c646119cd91cc8d89)
@@ -34,7 +34,8 @@
 }
 
-void vid_exec(void)
+bool vid_exec(void)
 {
 	ver3("vid exec");
+	return false;
 }
 
