Multithread debugger and emulator with message queues
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 050b299..cfb97fe 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,6 @@
 project(6502 VERSION 0.1.0 LANGUAGES C)
 
 option(GEN_INSTRUCTIONS_HEADER ON)
-option(NO_PTHREAD OFF)
 
 include_directories(nuklear)
 
@@ -15,12 +14,6 @@
 		WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
 endif()
 
-if (${NO_PTHREAD})
-	add_definitions(-DNO_PTHREAD)
-else()
-	set(THREAD pthread)
-endif()
-
 add_executable(6502 main.c cpu.c cpu.h dbg.c dbg.h 
 	instructions.h gui.h gui.c screen.h screen.c common.h common.c)
-target_link_libraries(6502 readline SDL2 GL GLU GLEW m ${THREAD})
+target_link_libraries(6502 readline SDL2 GL GLU GLEW m rt pthread)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..2a6c61e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,23 @@
+Copyright 2020 swissChili
+
+Redistribution and use in source and binary forms, with or without modification, are permitted 
+provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and 
+the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 
+and the following disclaimer in the documentation and/or other materials provided with the 
+distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
+or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/README.md b/README.md
index 2849e4a..924f97d 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,45 @@
 definitions of every 6502 opcode, its mnemonic and addressing mode.
 It is built automatically by cmake. 
 
+## Dependencies
+
+- POSIX Threads
+- POSIX Messsage Queues
+- SDL2
+- OpenGL 3
+- GLEW
+- GNU Readline
+- Nuklear (included)
+
+## License
+
+```
+Copyright 2020 swissChili
+
+Redistribution and use in source and binary forms, with or without modification, are permitted 
+provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and 
+the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 
+and the following disclaimer in the documentation and/or other materials provided with the 
+distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
+or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+```
+
+---
 
 ```
  ____________________________________
diff --git a/common.c b/common.c
index 6772dad..7f1875f 100644
--- a/common.c
+++ b/common.c
@@ -9,7 +9,7 @@
 
 void throw_(const char *msg, const char *file, unsigned int line)
 {
-	printf("\033[31mException thrown:\033[33m %s:%d\033[0m %s\n", file, line, msg);
+	fprintf(stderr, "\033[31mException thrown:\033[33m %s:%d\033[0m %s\n", file, line, msg);
 
 	for (int i = g_catch_len - 1; i >= 0; i--)
 	{
diff --git a/common.h b/common.h
index 6b751ba..e1e297f 100644
--- a/common.h
+++ b/common.h
@@ -10,7 +10,7 @@
 
 #define ASSERT(message, body) \
 	{ \
-		if (!body) \
+		if (!(body)) \
 			THROW("Assert failed: " message " [" #body "]"); \
 	}
 
diff --git a/cpu.c b/cpu.c
index 9d85261..8b5eda8 100644
--- a/cpu.c
+++ b/cpu.c
@@ -4,6 +4,8 @@
 #define SCREEN_ONLY_SDL
 #include "screen.h"
 
+#include "dbg.h"
+#include <errno.h>
 #include <endian.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -539,8 +541,6 @@
 
 void step(cpu_t *cpu)
 {
-	static int steps;
-	steps++;
 	cpu->screen_dirty = false;
 	uint8_t pc = cpu->pc;
 	uint8_t op = cpu->mem[cpu->pc++];
@@ -559,17 +559,6 @@
 			warn("Undefined opcode %x near %x [%x]", op, pc, cpu->mem[pc]);
 			THROW("Undefined opcode");
 	}
-
-	if (steps % 100 == 0)
-		printf("%d\n", steps);
-
-// If can't run screen in seperate thread, just run it here (bad)
-#ifdef NO_PTHREAD
-	if (g_scr)
-	{
-		sdl_screen(g_scr, cpu->mem + CPU_FB_ADDR, cpu->screen_dirty);
-	}
-#endif
 }
 
 int dump_inst(cpu_t *cpu, char *buf, const char *mn, uint16_t addr, uint8_t am)
@@ -677,3 +666,34 @@
 
 	printf("CPU Halted\n");
 }
+
+void run_mq(cpu_t *cpu, mqd_t mq)
+{
+	char buf[MQ_BUF_LEN];
+	bool running;
+
+	while (true)
+	{
+		if (running)
+		{
+			if (cpu->running)
+				step(cpu);
+			else
+				running = false;
+		}
+
+		ssize_t recvd = mq_receive(mq, buf, MQ_BUF_LEN * 2, NULL);
+
+		if (recvd == -1 && errno != EAGAIN)
+		{
+			printf("errno = %d\n", errno);
+			THROW("mq_receive returned -1");
+		}
+
+		if (recvd > 0)
+		{
+			if (debug_stmt(cpu, buf, &running))
+				break;
+		}
+	}
+}
diff --git a/cpu.h b/cpu.h
index 5b91ed4..3c82c6c 100644
--- a/cpu.h
+++ b/cpu.h
@@ -2,12 +2,16 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+#include <mqueue.h>
 
 #define REGISTERS R(A) R(X) R(Y) R(SP)
 #define CPU_FB_ADDR 0x200
 #define CPU_FB_W 32
 #define CPU_FB_H 32
 
+#define MQ_BUF_LEN 512
+#define MQ_NAME "/6502-to-cpu"
+
 enum // Registers
 {
 	A, X, Y, SP
@@ -148,3 +152,4 @@
 // Buffer must be freed by user
 char *disas_step(cpu_t *cpu);
 void run(cpu_t *cpu);
+void run_mq(cpu_t *cpu, mqd_t mq);
diff --git a/dbg.c b/dbg.c
index f5ca437..577feee 100644
--- a/dbg.c
+++ b/dbg.c
@@ -4,64 +4,103 @@
 #include <readline/readline.h>
 #include <readline/history.h>
 #include <string.h>
+#include <pthread.h>
 #include <stdlib.h>
 
-void debug(cpu_t *cpu)
+bool debug_stmt(cpu_t *cpu, char *input, bool *running)
 {
-	while (true)
+	char *tok = strtok(input, " \t\r\n\v");
+
+	if (!tok || !*tok)
+		return false;
+	
+	if (!strcmp(tok, "step") || !strcmp(tok, "s"))
+	{
+		step(cpu);
+	}
+	else if (!strcmp(tok, "show") || !strcmp(tok, "print"))
+	{
+		if ((tok = strtok(NULL, " ")))
+		{
+			char *ok = 0;
+			if (tok[0] == '$')
+			{
+				uint16_t addr = strtol(tok + 1, &ok, 16);
+
+				printf("Memory:\n");
+				printf("\t$%x	%x\n", addr, cpu->mem[addr]);
+				if (addr < 0xFFFF)
+					printf("\t$%x	%x\n", addr + 1, cpu->mem[addr + 1]);
+			}
+			else
+			{
+				printf("Expected an address as an argument in the form "
+					"$1234, not %s\n", tok);
+			}
+		}
+		else
+		{
+			printf("Registers:\n");
+
+			printf("\tPC:\t$%x\n", cpu->pc);
+			#define R(r) printf("\t" #r ":\t$%x\n", cpu->regs[r]);
+				REGISTERS
+			#undef R
+		}
+	}
+	else if (!strcmp(tok, "run"))
+	{
+		*running = true;
+	}
+	else if (!strcmp(tok, "quit") || !strcmp(tok, "exit"))
+	{
+		printf("Bye\n");
+		return true;
+	}
+	else
+	{
+		printf("Unknown command %s\n", tok);
+	}
+	
+	return false;
+}
+
+typedef struct
+{
+	mqd_t mq;
+	cpu_t *cpu;
+} debug_prompt_arg_t;
+
+void debug_prompt(debug_prompt_arg_t *arg)
+{
+	mqd_t mq = arg->mq;
+	cpu_t *cpu = arg->cpu;
+	free(arg);
+
+	bool running = true;
+	while (running)
 	{
 		char *input = readline("\033[33m> \033[0m");
 		if (!input || !*input)
 			continue;
 
-		char *tok = strtok(input, " \t\r\n\v");
+		if (!strcmp(input, "quit") || !strcmp(input, "exit"))
+			running = false;
 
-		if (!tok || !*tok)
-			continue;
-		
-		if (!strcmp(tok, "step") || !strcmp(tok, "s"))
-		{
-			step(cpu);
-		}
-		else if (!strcmp(tok, "show") || !strcmp(tok, "print"))
-		{
-			if ((tok = strtok(NULL, " ")))
-			{
-				char *ok = 0;
-				if (tok[0] == '$')
-				{
-					uint16_t addr = strtol(tok + 1, &ok, 16);
-
-					printf("Memory:\n");
-					printf("\t$%x	%x\n", addr, cpu->mem[addr]);
-					if (addr < 0xFFFF)
-						printf("\t$%x	%x\n", addr + 1, cpu->mem[addr + 1]);
-				}
-				else
-				{
-					printf("Expected an address as an argument in the form "
-						"$1234, not %s\n", tok);
-				}
-			}
-			else
-			{
-				printf("Registers:\n");
-
-				#define R(r) printf("\t" #r ":\t%x\n", cpu->regs[r]);
-					REGISTERS
-				#undef R
-			}
-		}
-		else if (!strcmp(tok, "quit") || !strcmp(tok, "exit"))
-		{
-			printf("Bye\n");
-			return;
-		}
-		else
-		{
-			printf("Unknown command %s\n", tok);
-		}
+		mq_send(mq, input, strlen(input) + 1, 2);
 
 		add_history(input);
+		free(input);
 	}
 }
+
+pthread_t start_debug_prompt(mqd_t mq, cpu_t *cpu)
+{
+	debug_prompt_arg_t *arg = malloc(sizeof(debug_prompt_arg_t));
+	arg->mq = mq;
+	arg->cpu = cpu;
+
+	pthread_t thread;
+	pthread_create(&thread, NULL, (void *(*)(void *))&debug_prompt, arg);
+	return thread;
+}
diff --git a/dbg.h b/dbg.h
index c31c1c3..67f082d 100644
--- a/dbg.h
+++ b/dbg.h
@@ -2,4 +2,9 @@
 
 #include "cpu.h"
 
-void debug(cpu_t *cpu);
+#include <stdbool.h>
+#include <mqueue.h>
+
+bool debug_stmt(cpu_t *cpu, char *input, bool *running);
+// void debug_prompt(mqd_t mq, cpu_t *cpu);
+pthread_t start_debug_prompt(mqd_t mq, cpu_t *cpu);
\ No newline at end of file
diff --git a/gui.c b/gui.c
index 2b5799a..7a9841f 100644
--- a/gui.c
+++ b/gui.c
@@ -23,8 +23,19 @@
 #define MAX_VERTEX_MEMORY 512 * 1024
 #define MAX_ELEMENT_MEMORY 128 * 1024
 
-void gui(cpu_t *cpu)
+typedef struct
 {
+	cpu_t *cpu;
+	mqd_t mq;
+} gui_arg_t;
+
+void gui(gui_arg_t *arg)
+{
+	cpu_t *cpu = arg->cpu;
+	mqd_t mq = arg->mq;
+
+	free(arg);
+
 	SDL_Window *win;
 	SDL_GLContext glContext;
 	int win_width, win_height;
@@ -223,3 +234,13 @@
 	SDL_DestroyWindow(win);
 	SDL_Quit();
 }
+
+
+void start_gui(mqd_t mq, cpu_t *cpu)
+{
+	pthread_t gui_thread;
+	gui_arg_t *arg = malloc(sizeof(gui_arg_t));
+	arg->cpu = cpu;
+	arg->mq = mq;
+	pthread_create(&gui_thread, NULL, (void *(*)(void *))&gui, arg);
+}
diff --git a/gui.h b/gui.h
index 730db11..cba3baa 100644
--- a/gui.h
+++ b/gui.h
@@ -2,4 +2,5 @@
 
 #include "cpu.h"
 
-void gui(cpu_t *cpu);
+// void gui(cpu_t *cpu);
+void start_gui(mqd_t mq_to_cpu, cpu_t *cpu);
diff --git a/main.c b/main.c
index 3b1da46..779096c 100644
--- a/main.c
+++ b/main.c
@@ -6,23 +6,26 @@
 
 #include <bits/getopt_core.h>
 #include <ctype.h>
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
-
-extern sdl_screen_t *g_scr;
-
-#ifndef NO_PTHREAD
 #include <pthread.h>
+#include <mqueue.h>
+#include <sys/stat.h>
 
 
 void cleanup_screen_thread(pthread_t thread)
 {
-	g_screen_thread_halt = true;
 	puts("Cleaning up screen...");
-	pthread_join(thread, NULL);
+	pthread_cancel(thread);
 }
-#endif
+
+void cleanup_debug_prompt_thread(pthread_t thread)
+{
+	puts("Cleaning up debug prompt...");
+	pthread_cancel(thread);
+}
 
 int main(int argc, char **argv)
 {
@@ -97,11 +100,28 @@
 	}
 
 	cpu_t cpu;
+	mqd_t mq_to_cpu;
+
+	struct mq_attr attrs;
+	attrs.mq_maxmsg = 10;
+	attrs.mq_msgsize = MQ_BUF_LEN;
 
 	if (should_read)
 	{
 		cpu = new_cpu();
 		fread(cpu.mem + 0x600, 0xFFFF - 0x600, 1, input);
+		
+		int unlink = mq_unlink(MQ_NAME);
+		if (unlink < 0 && errno != ENOENT)
+		{
+			printf("Warning: mq_unlink() error: %d %s\n", errno, strerror(errno));
+		}
+		
+		mq_to_cpu = mq_open(MQ_NAME, O_RDWR | O_CREAT | O_NONBLOCK, S_IWUSR|S_IRUSR, &attrs);
+		printf("error after mq_open (%ld) = %d %s\n", attrs.mq_msgsize, errno, strerror(errno));
+		ASSERT("Open message queue for emulator", mq_to_cpu > 0)
+
+		mq_send(mq_to_cpu, "init", 5, 2);
 	}
 	else
 	{
@@ -111,12 +131,7 @@
 
 	if (scrflag)
 	{
-#ifndef NO_PTHREAD
 		CATCH(&cleanup_screen_thread, start_screen_thread(cpu.mem + CPU_FB_ADDR));
-#else
-		sdl_screen_t scr = new_sdl_screen(8);
-		g_scr = &scr;
-#endif
 	}
 
 	if (guiflag && scrflag)
@@ -126,11 +141,12 @@
 
 	if (guiflag)
 	{
-		gui(&cpu);
+		start_gui(mq_to_cpu, &cpu);
+		run_mq(&cpu, mq_to_cpu);
 	}
 	else if (disflag)
 	{
-		disas_num(&cpu, 12);
+		disas_num(&cpu, 64);
 	}
 	else if (runflag)
 	{
@@ -143,14 +159,13 @@
 	}
 	else if (debugflag)
 	{
-		debug(&cpu);
+		CATCH(&cleanup_debug_prompt_thread, start_debug_prompt(mq_to_cpu, &cpu));
+		run_mq(&cpu, mq_to_cpu);
 	}
-	
-#ifdef NO_PTHREAD
-	if (scrflag)
-		free_sdl_screen(g_scr);
-#endif
 
 	if (should_read)
+	{
 		free_cpu(&cpu);
+		mq_close(mq_to_cpu);
+	}
 }
diff --git a/screen.c b/screen.c
index e19baa2..4b01adc 100644
--- a/screen.c
+++ b/screen.c
@@ -3,10 +3,8 @@
 #include "common.h"
 
 #include <SDL2/SDL.h>
-
-#ifndef NO_PTHREAD
 #include <pthread.h>
-#endif
+
 
 struct nk_color byte_to_color(uint8_t b)
 {
@@ -101,25 +99,23 @@
 	return false;
 }
 
-
-#ifndef NO_PTHREAD
-
 bool g_screen_thread_halt = false;
 
-
 void *screen_thread(uint8_t *mem)
 {
 	sdl_screen_t scr = new_sdl_screen(8);
+	
+	pthread_cleanup_push(((void (*)(void *))&free_sdl_screen), ((void *)&scr));
+
 	while (true)
 	{
 		if (sdl_screen(&scr, mem, true) || g_screen_thread_halt)
 			break;
 	}
-	free_sdl_screen(&scr);
 
-	exit(0);
+	pthread_cleanup_pop(true);
 
-	return NULL;
+	pthread_exit(NULL);
 }
 
 pthread_t start_screen_thread(uint8_t *mem)
@@ -128,5 +124,3 @@
 	pthread_create(&thread, NULL, (void *(*)(void *))&screen_thread, mem);
 	return thread;
 }
-
-#endif
diff --git a/screen.h b/screen.h
index 9eeddc8..da3dd44 100644
--- a/screen.h
+++ b/screen.h
@@ -2,6 +2,7 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+#include <pthread.h>
 
 #ifndef SCREEN_ONLY_SDL
 
@@ -35,13 +36,7 @@
 void free_sdl_screen(sdl_screen_t *scr);
 bool sdl_screen(sdl_screen_t *scr, uint8_t *mem, bool dirty);
 
-#ifndef NO_PTHREAD
-
-#include <pthread.h>
-
 extern bool g_screen_thread_halt;
 
 void *screen_thread(uint8_t *mem);
 pthread_t start_screen_thread(uint8_t *mem);
-
-#endif