Add good error handling
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8fc9b17..050b299 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,5 +21,6 @@
 	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)
+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})
diff --git a/common.c b/common.c
new file mode 100644
index 0000000..6772dad
--- /dev/null
+++ b/common.c
@@ -0,0 +1,41 @@
+#include "common.h"
+
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+catch_t g_catch[MAX_CATCH_LEN];
+unsigned g_catch_len;
+
+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);
+
+	for (int i = g_catch_len - 1; i >= 0; i--)
+	{
+		g_catch[i].fn(g_catch[i].arg);
+	}
+
+	exit(1);
+}
+
+void catch_(handle_t hdl, intptr_t arg)
+{
+	if (g_catch_len > MAX_CATCH_LEN)
+	{
+		THROW("Catch overflow");
+	}
+
+	g_catch[g_catch_len++] = (catch_t){ .fn = hdl, .arg = arg };
+}
+
+void catch_signal(int sig)
+{
+	if (sig == SIGSEGV)
+		throw_("Segmentation fault", "unknown", 0);
+}
+
+__attribute__((constructor)) void init_catch()
+{
+	signal(SIGSEGV, catch_signal);
+}
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..6b751ba
--- /dev/null
+++ b/common.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <stdint.h>
+#include <string.h>
+
+#define __FILENAME__ \
+	(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 \
+		: strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
+
+
+#define ASSERT(message, body) \
+	{ \
+		if (!body) \
+			THROW("Assert failed: " message " [" #body "]"); \
+	}
+
+#define MAX_CATCH_LEN 32
+
+typedef void (* handle_t)(intptr_t);
+
+typedef struct
+{
+	handle_t fn;
+	intptr_t arg;
+} catch_t;
+
+extern catch_t g_catch[MAX_CATCH_LEN];
+extern unsigned g_catch_len;
+
+void throw_(const char *msg, const char *file, unsigned int line);
+void catch_(handle_t hdl, intptr_t arg);
+
+#define THROW(msg) throw_(msg, __FILENAME__, __LINE__)
+#define CATCH(fn, arg) catch_((handle_t) fn, (intptr_t) arg)
diff --git a/cpu.c b/cpu.c
index 6e56134..d457868 100644
--- a/cpu.c
+++ b/cpu.c
@@ -1,4 +1,5 @@
 #include "cpu.h"
+#include "common.h"
 #include "instructions.h"
 #define SCREEN_ONLY_SDL
 #include "screen.h"
@@ -36,10 +37,7 @@
 	cpu.screen_dirty = true;
 	memset(cpu.mem, 0, 0xFFFF);
 
-	if (!cpu.mem)
-	{
-		die("Could not allocate memory for CPU");
-	}
+	ASSERT("Allocate memory for CPU", cpu.mem);
 
 	return cpu;
 }
@@ -415,7 +413,8 @@
 			break;
 
 		default:
-			die("Unsupported opcode: %x\n", op);
+			warn("Unsupported opcode: %x\n", op);
+			THROW("Unsupported opcode");
 	}
 	#undef REGS
 }
@@ -498,23 +497,23 @@
 
 		case AM_ZPX:
 			if (f & FETCH_NO_INDIRECTION)
-				return arg_ptr(cpu, f, cpu->mem[scr_dirty(cpu, cpu->pc++)]);
-			return arg_ptr(cpu, f, cpu->mem[scr_dirty(cpu, cpu->pc++)] + cpu->regs[X]);
+				return arg_ptr(cpu, f, cpu->mem[cpu->pc++]);
+			return arg_ptr(cpu, f, cpu->mem[cpu->pc++] + cpu->regs[X]);
 
 		case AM_ZPY:
 			if (f & FETCH_NO_INDIRECTION)
-				return arg_ptr(cpu, f, cpu->mem[scr_dirty(cpu, cpu->pc++)]);
-			return arg_ptr(cpu, f, cpu->mem[scr_dirty(cpu, cpu->pc++)] + cpu->regs[Y]);
+				return arg_ptr(cpu, f, cpu->mem[cpu->pc++]);
+			return arg_ptr(cpu, f, cpu->mem[cpu->pc++] + cpu->regs[Y]);
 
 		case AM_ZIX:
 		{
-			uint8_t zp = cpu->mem[scr_dirty(cpu, cpu->pc++)];
+			uint8_t zp = cpu->mem[cpu->pc++];
 
 			if (f & FETCH_NO_INDIRECTION)
 				return arg_imm(zp);
 
 			uint16_t addr = zp + cpu->regs[X];
-			uint16_t indirect = le_to_native(cpu->mem[scr_dirty(cpu, addr)], cpu->mem[scr_dirty(cpu, addr + 1)]);
+			uint16_t indirect = le_to_native(cpu->mem[addr], cpu->mem[addr + 1]);
 			return arg_ptr(cpu, f, indirect);
 		}
 
@@ -525,12 +524,13 @@
 			if (f & FETCH_NO_INDIRECTION)
 				return arg_imm(zp);
 
-			uint16_t base = le_to_native(cpu->mem[scr_dirty(cpu, zp)], cpu->mem[scr_dirty(cpu, zp + 1)]);
+			uint16_t base = le_to_native(cpu->mem[zp], cpu->mem[scr_dirty(cpu, zp + 1)]);
 			return arg_ptr(cpu, f, base + cpu->regs[Y]);
 		}
 
 		default:
-			die("Unknown address mode %x", am);
+			warn("Unknown address mode %x", am);
+			THROW("Unknowng address mode");
 			__builtin_unreachable();
 	}
 }
@@ -540,7 +540,9 @@
 	static int steps;
 	steps++;
 	cpu->screen_dirty = false;
-	switch (cpu->mem[scr_dirty(cpu, cpu->pc++)])
+	uint8_t pc = cpu->pc;
+	uint8_t op = cpu->mem[cpu->pc++];
+	switch (op)
 	{
 #define INST(mn, am, op) \
 		case op: \
@@ -552,7 +554,8 @@
 #undef INST
 
 		default:
-			die("Undefined opcode");
+			warn("Undefined opcode %x near %x [%x]", op, pc, cpu->mem[pc]);
+			THROW("Undefined opcode");
 	}
 
 	if (steps % 100 == 0)
diff --git a/main.c b/main.c
index e91e699..3b1da46 100644
--- a/main.c
+++ b/main.c
@@ -1,3 +1,4 @@
+#include "common.h"
 #include "cpu.h"
 #include "dbg.h"
 #include "gui.h"
@@ -11,6 +12,18 @@
 
 extern sdl_screen_t *g_scr;
 
+#ifndef NO_PTHREAD
+#include <pthread.h>
+
+
+void cleanup_screen_thread(pthread_t thread)
+{
+	g_screen_thread_halt = true;
+	puts("Cleaning up screen...");
+	pthread_join(thread, NULL);
+}
+#endif
+
 int main(int argc, char **argv)
 {
 	bool disflag = false,
@@ -19,7 +32,8 @@
 		debugflag = false,
 		should_read = false,
 		guiflag = false,
-		scrflag = false;
+		scrflag = false,
+		nohaltflag = false;
 
 	int disasm_len = 0;
 
@@ -27,10 +41,13 @@
 
 	char c;
 
-	while ((c = getopt(argc, argv, "Dsdrhgi:n:")) != -1)
+	while ((c = getopt(argc, argv, "HDsdrhgi:n:")) != -1)
 	{
 		switch (c)
 		{
+		case 'H':
+			nohaltflag = true;
+			break;
 		case 'd':
 			disflag = true;
 			should_read = true;
@@ -68,6 +85,8 @@
 		printf("6502 emulator, disassembler and debugger\n"
 			"Usage:\n"
 			"	-g use GUI\n"
+			"	-s use SDL screen (faster than GUI debugger)\n"
+			"	-H keep running after CPU halts (useful on windows and to look at screen)\n"
 			"	-d disassemble input\n"
 			"	-r run input\n"
 			"	-D open CLI debug prompt (like gdb)\n"
@@ -93,13 +112,18 @@
 	if (scrflag)
 	{
 #ifndef NO_PTHREAD
-		start_screen_thread(cpu.mem + CPU_FB_ADDR);
+		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)
+	{
+		THROW("-g and -s cannot be used together");
+	}
+
 	if (guiflag)
 	{
 		gui(&cpu);
@@ -111,14 +135,21 @@
 	else if (runflag)
 	{
 		run(&cpu);
+		if (nohaltflag)
+		{
+			puts("Press any key to exit");
+			getchar();
+		}
 	}
 	else if (debugflag)
 	{
 		debug(&cpu);
 	}
 	
+#ifdef NO_PTHREAD
 	if (scrflag)
 		free_sdl_screen(g_scr);
+#endif
 
 	if (should_read)
 		free_cpu(&cpu);
diff --git a/screen.c b/screen.c
index e2bf5e0..e19baa2 100644
--- a/screen.c
+++ b/screen.c
@@ -1,5 +1,6 @@
 #include "screen.h"
 #include "cpu.h"
+#include "common.h"
 
 #include <SDL2/SDL.h>
 
@@ -52,10 +53,13 @@
 		size * 32,
 		size * 32,
 		0);
+	ASSERT("Create Screen SDL_Window", scr.win);
 	scr.size = size;
 	scr.r = SDL_CreateRenderer(scr.win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
+	ASSERT("Create SDL_Renderer", scr.r);
 	scr.tex = SDL_CreateTexture(scr.r, SDL_PIXELFORMAT_RGB332, SDL_TEXTUREACCESS_STREAMING,
 		CPU_FB_W, CPU_FB_H);
+	ASSERT("Create SDL_Texture", scr.tex);
 
 	return scr;
 }
@@ -100,12 +104,15 @@
 
 #ifndef NO_PTHREAD
 
+bool g_screen_thread_halt = false;
+
+
 void *screen_thread(uint8_t *mem)
 {
 	sdl_screen_t scr = new_sdl_screen(8);
 	while (true)
 	{
-		if (sdl_screen(&scr, mem, true))
+		if (sdl_screen(&scr, mem, true) || g_screen_thread_halt)
 			break;
 	}
 	free_sdl_screen(&scr);
@@ -115,10 +122,11 @@
 	return NULL;
 }
 
-void start_screen_thread(uint8_t *mem)
+pthread_t start_screen_thread(uint8_t *mem)
 {
 	pthread_t thread;
 	pthread_create(&thread, NULL, (void *(*)(void *))&screen_thread, mem);
+	return thread;
 }
 
 #endif
diff --git a/screen.h b/screen.h
index 75dc8cf..9eeddc8 100644
--- a/screen.h
+++ b/screen.h
@@ -36,6 +36,12 @@
 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);
-void start_screen_thread(uint8_t *mem);
+pthread_t start_screen_thread(uint8_t *mem);
+
 #endif
diff --git a/test.dat b/test.dat
index b004f7a..d5569e0 100644
--- a/test.dat
+++ b/test.dat
Binary files differ