Index: emu/cpu.c
===================================================================
--- emu/cpu.c	(revision f0af1685f7142c805b097dfbaf07ac8813c78551)
+++ emu/cpu.c	(revision 56746cf7ede4e619d103f5532f893d14cf9ccf8d)
@@ -91,5 +91,5 @@
 	{ 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 },
+	{ 0x3ac001, 0x3b0001, 5, 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 },
Index: emu/mid.c
===================================================================
--- emu/mid.c	(revision f0af1685f7142c805b097dfbaf07ac8813c78551)
+++ emu/mid.c	(revision 56746cf7ede4e619d103f5532f893d14cf9ccf8d)
@@ -17,10 +17,9 @@
 
 #include <all.h>
+#include <rtmidi/rtmidi_c.h>
 
 #define ver(...) _ver(mid_verbose, 0, __VA_ARGS__)
 #define ver2(...) _ver(mid_verbose, 1, __VA_ARGS__)
 #define ver3(...) _ver(mid_verbose, 2, __VA_ARGS__)
-
-int32_t mid_verbose = 0;
 
 #define REG_IER_ISR 0
@@ -29,11 +28,128 @@
 #define REG_TDR_RDR 3
 
+#define BUF_SZ 128
+
+typedef struct {
+	int32_t buf_hd;
+	int32_t buf_tl;
+	uint8_t buf[BUF_SZ];
+	bool irq_r;
+	bool irq_t;
+	bool rdr_ok;
+	uint8_t rdr;
+} state_t;
+
+static state_t state[] = {
+	{ .buf_hd = 0, .buf_tl = 0, .irq_r = false, .irq_t = false, .rdr_ok = false, .rdr = 0x00 },
+	{ .buf_hd = 0, .buf_tl = 0, .irq_r = false, .irq_t = false, .rdr_ok = false, .rdr = 0x00 }
+};
+
+int32_t mid_verbose = 0;
+
+struct RtMidiWrapper* midiin;
+
+static void xmit(int32_t un)
+{
+	int32_t i = state[un].buf_tl;
+	ver2("mid xmit %d %d", i, state[un].buf_hd);
+
+	if (i >= state[un].buf_hd) {
+		return;
+	}
+
+	uint8_t byte = state[un].buf[i % BUF_SZ];
+	ver2("mid xmit 0x%02x", byte);
+
+	state[un].rdr = byte;
+	state[un].rdr_ok = true;
+	state[un].irq_r = true;
+
+	state[un].buf_tl = i + 1;
+
+	if (state[un].buf_tl >= BUF_SZ) {
+		state[un].buf_hd -= BUF_SZ;
+		state[un].buf_tl -= BUF_SZ;
+		ver2("mid adj %d %d", state[un].buf_tl, state[un].buf_hd);
+	}
+}
+
+static void out_lk(int32_t un, uint8_t c)
+{
+	int32_t i = state[un].buf_hd;
+	ver2("mid out %d %d 0x%02x", state[un].buf_tl, i, c);
+
+	if (i >= state[un].buf_tl + BUF_SZ) {
+		err("midi port %d losing data", un);
+		return;
+	}
+
+	state[un].buf[i % BUF_SZ] = c;
+	state[un].buf_hd = i + 1;
+
+	if (!state[un].irq_r && !state[un].rdr_ok) {
+		xmit(un);
+	}
+}
+
+static void out(int32_t un, uint8_t c)
+{
+	if (SDL_LockMutex(cpu_mutex) < 0) {
+		fail("SDL_LockMutex() failed: %s", SDL_GetError());
+	}
+
+	out_lk(un, c);
+
+	if (SDL_UnlockMutex(cpu_mutex) < 0) {
+		fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
+	}
+}
+
+static void midi_callback(double timeStamp, const unsigned char* message, void *userData) {
+	ver2("Timestamp %f\n", timeStamp);
+
+	for (uint8_t i = 0; i < sizeof(message); i++) {
+		ver2("Message %i %u", i, (uint8_t) message[i]);
+		out(0, message[i]);
+	}
+}
+
 void mid_init(void)
 {
 	ver("mid init");
+
+	midiin = rtmidi_in_create_default();
+	ver2("%p", midiin->ptr);
+	// check if null
+	uint32_t portcount = rtmidi_get_port_count(midiin);
+	ver2("%d", midiin->ok);
+
+	if (portcount == 0) {
+		midiin = NULL;
+		ver2("no midi ports\n");
+		return;
+	}
+
+	for (uint32_t i = 0; i < portcount; i++) {
+		ver2("Port %d: %s", i, rtmidi_get_port_name(midiin, i));
+	}
+
+	rtmidi_open_port(midiin, 0, rtmidi_get_port_name(midiin, 0));
+	if(!midiin->ok) {
+		fail("Failed to open Midi port");
+	}
+
+	rtmidi_in_set_callback(midiin, midi_callback, midiin->data);
+	if(!midiin->ok) {
+		fail("Failed to set Midi Callback");
+	}
 }
 
 void mid_quit(void)
 {
+	if(midiin) {
+		rtmidi_close_port(midiin);
+		rtmidi_in_free(midiin);
+	}
+
 	ver("mid quit");
 }
@@ -42,5 +158,5 @@
 {
 	ver3("mid exec");
-	return false;
+	return state[0].irq_r || state[0].irq_t || state[1].irq_r || state[1].irq_t;
 }
 
@@ -48,5 +164,38 @@
 {
 	ver2("mid rd %u:%d", off, sz * 8);
-	return 0;
+
+	if (sz != 1 || off > 7) {
+		fail("invalid mid 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].rdr_ok ? 0x01 : 0x00));
+		state[un].irq_r = false;
+		state[un].irq_t = false;
+		ver2("ISR[%d] 0x%02x", un, rv);
+		break;
+
+	case REG_TDR_RDR:
+		rv = state[un].rdr;
+		state[un].rdr_ok = false;
+		ver2("RDR[%d] 0x%02x", un, rv);
+		break;
+
+	default:
+		rv = 0x00;
+		break;
+	}
+
+	if (!state[un].irq_r && !state[un].rdr_ok) {
+		xmit(un);
+	}
+
+	return rv;
 }
 
