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