Index: emu/all.h
===================================================================
--- emu/all.h	(revision 7cc6ac095e55080aed20ab2b4666738b9d5f9449)
+++ emu/all.h	(revision 5fa53696c42d276435b389efd40733f2a9cfa4c2)
@@ -81,4 +81,7 @@
 extern void cpu_loop(void);
 
+extern uint8_t cpu_peek(int32_t addr);
+extern void cpu_poke(int32_t addr, uint8_t val);
+
 extern void fpu_init(void);
 extern void fpu_quit(void);
Index: emu/cpu.c
===================================================================
--- emu/cpu.c	(revision 7cc6ac095e55080aed20ab2b4666738b9d5f9449)
+++ emu/cpu.c	(revision 5fa53696c42d276435b389efd40733f2a9cfa4c2)
@@ -468,4 +468,28 @@
 }
 
+uint8_t cpu_peek(int32_t addr)
+{
+	if (addr >= RAM_START && addr <= RAM_START + RAM_SIZE - 1) {
+		return ram_data[addr - RAM_START];
+	}
+
+	if (addr >= ROM_START && addr <= ROM_START + ROM_SIZE - 1) {
+		return rom_data[addr - ROM_START];
+	}
+
+	return 0;
+}
+
+void cpu_poke(int32_t addr, uint8_t val)
+{
+	if (addr >= RAM_START && addr <= RAM_START + RAM_SIZE - 1) {
+		ram_data[addr - RAM_START] = val;
+	}
+
+	if (addr >= ROM_START && addr <= ROM_START + ROM_SIZE - 1) {
+		rom_data[addr - ROM_START] = val;
+	}
+}
+
 static void inst_cb(void)
 {
Index: emu/gdb.c
===================================================================
--- emu/gdb.c	(revision 7cc6ac095e55080aed20ab2b4666738b9d5f9449)
+++ emu/gdb.c	(revision 5fa53696c42d276435b389efd40733f2a9cfa4c2)
@@ -117,5 +117,5 @@
 	}
 
-	ver2("<- lock none");
+	ver2("<- lock none / req");
 
 	if (SDL_LockMutex(cpu_mutex) < 0) {
@@ -124,10 +124,10 @@
 }
 
-static void lock_cpu(void)
+static void stop_cpu(void)
 {
 	ver2("-> lock req");
 	SDL_AtomicSet(&lock, LOCK_REQ);
 
-	while (SDL_AtomicGet(&lock) == LOCK_REQ) {
+	while (SDL_AtomicGet(&lock) != LOCK_ACK) {
 		SDL_Delay(100);
 	}
@@ -136,5 +136,5 @@
 }
 
-static void free_cpu(void)
+static void cont_cpu(void)
 {
 	ver2("-> lock none");
@@ -142,17 +142,94 @@
 }
 
+static void step_cpu(void)
+{
+	ver2("-> lock req");
+	SDL_AtomicSet(&lock, LOCK_REQ);
+}
+
+static void wait_cpu(void)
+{
+	while (SDL_AtomicGet(&lock) != LOCK_ACK) {
+		SDL_Delay(100);
+	}
+
+	ver2("<- lock ack");
+}
+
+static int32_t hex_digit(char c)
+{
+	if (c >= '0' && c <= '9') {
+		return c - '0';
+	}
+
+	if (c >= 'a' && c <= 'f') {
+		return 10 + c - 'a';
+	}
+
+	if (c >= 'A' && c <= 'F') {
+		return 10 + c - 'A';
+	}
+
+	return -1;
+}
+
+static int32_t hex_num(const char **pp)
+{
+	int32_t res = 0;
+	int32_t dig;
+
+	while ((dig = hex_digit(**pp)) >= 0) {
+		res = (res << 4) | dig;
+		++*pp;
+	}
+
+	return res;
+}
+
 static result_t com_reason(void)
 {
-	return RES_DAT("S0a", 4);
-}
-
-static result_t com_cont(char *req)
-{
-	return RES_DAT("", 0);
-}
-
-static result_t com_step(char *req)
-{
-	return RES_DAT("", 0);
+	// 0x05 = SIGTRAP
+	return RES_DAT("S05", 3);
+}
+
+static void set_pc(const char **req)
+{
+	int32_t addr = hex_num(req);
+
+	if (SDL_LockMutex(cpu_mutex) < 0) {
+		fail("SDL_LockMutex() failed: %s", SDL_GetError());
+	}
+
+	m68k_set_reg(M68K_REG_PC, (uint32_t)addr);
+
+	if (SDL_UnlockMutex(cpu_mutex) < 0) {
+		fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
+	}
+}
+
+static result_t com_cont(const char *req)
+{
+	if (req[0] != 0) {
+		set_pc(&req);
+	}
+
+	cont_cpu();
+	wait_cpu();
+
+	// 0x05 = SIGTRAP
+	return RES_DAT("S05", 3);
+}
+
+static result_t com_step(const char *req)
+{
+	if (req[0] != 0) {
+		set_pc(&req);
+	}
+
+	step_cpu();
+	wait_cpu();
+
+	// 0x05 = SIGTRAP
+	return RES_DAT("S05", 3);
 }
 
@@ -201,5 +278,5 @@
 }
 
-static result_t com_wr_reg(char *req)
+static result_t com_wr_reg(const char *req)
 {
 	uint32_t d0, d1, d2, d3, d4, d5, d6, d7;
@@ -207,5 +284,5 @@
 	uint32_t ps, pc;
 
-	if (sscanf(req + 1,
+	if (sscanf(req,
 			"%08x%08x%08x%08x%08x%08x%08x%08x"
 			"%08x%08x%08x%08x%08x%08x%08x%08x"
@@ -249,15 +326,84 @@
 }
 
-static result_t com_rd_mem(char *req)
-{
-	return RES_DAT("", 0);
-}
-
-static result_t com_wr_mem(char *req)
-{
-	return RES_DAT("", 0);
-}
-
-static result_t handle(char *req)
+static result_t com_rd_mem(const char *req)
+{
+	int32_t addr = hex_num(&req);
+
+	if (*req++ != ',') {
+		return RES_DAT("E01", 3);
+	}
+
+	int32_t len = hex_num(&req);
+
+	if (*req != 0 || len == 0) {
+		return RES_DAT("E01", 3);
+	}
+
+	if (len > 10000) {
+		len = 10000;
+	}
+
+	static char buf[10000 * 2 + 1];
+
+	if (SDL_LockMutex(cpu_mutex) < 0) {
+		fail("SDL_LockMutex() failed: %s", SDL_GetError());
+	}
+
+	for (int32_t i = 0; i < len; ++i) {
+		uint8_t byte = cpu_peek(addr + i);
+		sprintf(buf + i * 2, "%02x", byte);
+	}
+
+	if (SDL_UnlockMutex(cpu_mutex) < 0) {
+		fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
+	}
+
+	return RES_DAT(buf, len * 2);
+}
+
+static result_t com_wr_mem(const char *req)
+{
+	int32_t addr = hex_num(&req);
+
+	if (*req++ != ',') {
+		return RES_DAT("E01", 3);
+	}
+
+	int32_t len = hex_num(&req);
+
+	if (*req++ != ':') {
+		return RES_DAT("E01", 3);
+	}
+
+	if (SDL_LockMutex(cpu_mutex) < 0) {
+		fail("SDL_LockMutex() failed: %s", SDL_GetError());
+	}
+
+	int32_t i;
+
+	for (i = 0; i < len; ++i) {
+		int32_t hi = hex_digit(*req++);
+
+		if (hi < 0) {
+			break;
+		}
+
+		int32_t lo = hex_digit(*req++);
+
+		if (lo < 0) {
+			break;
+		}
+
+		cpu_poke(addr + i, (uint8_t)((hi << 4) | lo));
+	}
+
+	if (SDL_UnlockMutex(cpu_mutex) < 0) {
+		fail("SDL_UnlockMutex() failed: %s", SDL_GetError());
+	}
+
+	return RES_DAT("OK", 2);
+}
+
+static result_t handle(const char *req)
 {
 	result_t res;
@@ -269,9 +415,17 @@
 
 	case 'c':
-		res = com_cont(req);
+		res = com_cont(req + 1);
+		break;
+
+	case 'C':
+		res = com_cont(req + 4);
 		break;
 
 	case 's':
-		res = com_step(req);
+		res = com_step(req + 1);
+		break;
+
+	case 'S':
+		res = com_step(req + 4);
 		break;
 
@@ -281,13 +435,13 @@
 
 	case 'G':
-		res = com_wr_reg(req);
+		res = com_wr_reg(req + 1);
 		break;
 
 	case 'm':
-		res = com_rd_mem(req);
+		res = com_rd_mem(req + 1);
 		break;
 
 	case 'M':
-		res = com_wr_mem(req);
+		res = com_wr_mem(req + 1);
 		break;
 
@@ -320,21 +474,4 @@
 	ver2("resp %s", (char *)buf);
 	return RES_DAT(buf, res.n_out + 4);
-}
-
-static int32_t hex_digit(uint8_t byte)
-{
-	if (byte >= '0' && byte <= '9') {
-		return byte - '0';
-	}
-
-	if (byte >= 'a' && byte <= 'f') {
-		return 10 + byte - 'a';
-	}
-
-	if (byte >= 'A' && byte <= 'F') {
-		return 10 + byte - 'A';
-	}
-
-	return -1;
 }
 
@@ -380,5 +517,5 @@
 
 	case STATE_CHECK_1:
-		hex = hex_digit(byte);
+		hex = hex_digit((char)byte);
 
 		if (hex < 0) {
@@ -392,5 +529,5 @@
 
 	case STATE_CHECK_2:
-		hex = hex_digit(byte);
+		hex = hex_digit((char)byte);
 
 		if (hex < 0) {
@@ -442,5 +579,5 @@
 
 	SDLNet_TCP_Close(con);
-	free_cpu();
+	cont_cpu();
 }
 
@@ -480,5 +617,5 @@
 			}
 
-			lock_cpu();
+			stop_cpu();
 			state = STATE_ACK;
 			continue;
