changeset 792:724bbec47f86

Use a new fatal_error function instead of calling fprintf and exit for fatal errors. This new function more gracefully handles the case in which BlastEm was not started from a terminal or disconnected from ther terminal (Windows).
author Michael Pavone <pavone@retrodev.com>
date Sat, 25 Jul 2015 18:22:07 -0700
parents 60686f8d5e48
children 9aff36a172b2
files blastem.c config.c debug.c gdb_remote.c gen.c gen_arm.c gen_x86.c m68k_core.c m68k_core_x86.c render.h render_sdl.c romdb.c stateview.c tern.c util.c util.h vgmplay.c z80_to_x86.c
diffstat 18 files changed, 182 insertions(+), 187 deletions(-) [+]
line wrap: on
line diff
--- a/blastem.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/blastem.c	Sat Jul 25 18:22:07 2015 -0700
@@ -91,8 +91,7 @@
 		return 0;
 	}
 	if (sizeof(header) != fread(header, 1, sizeof(header), f)) {
-		fprintf(stderr, "Error reading from %s\n", filename);
-		exit(1);
+		fatal_error("Error reading from %s\n", filename);
 	}
 	fseek(f, 0, SEEK_END);
 	long filesize = ftell(f);
@@ -106,16 +105,14 @@
 		}
 		if (i == 8) {
 			if (header[2]) {
-				fprintf(stderr, "%s is a split SMD ROM which is not currently supported", filename);
-				exit(1);
+				fatal_error("%s is a split SMD ROM which is not currently supported", filename);
 			}
 			return load_smd_rom(filesize, f);
 		}
 	}
 	cart = malloc(nearest_pow2(filesize));
 	if (filesize != fread(cart, 1, filesize, f)) {
-		fprintf(stderr, "Error reading from %s\n", filename);
-		exit(1);
+		fatal_error("Error reading from %s\n", filename);
 	}
 	fclose(f);
 	return filesize;
@@ -289,8 +286,7 @@
 m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_t value)
 {
 	if (vdp_port & 0x2700E0) {
-		printf("machine freeze due to write to address %X\n", 0xC00000 | vdp_port);
-		exit(1);
+		fatal_error("machine freeze due to write to address %X\n", 0xC00000 | vdp_port);
 	}
 	vdp_port &= 0x1F;
 	//printf("vdp_port write: %X, value: %X, cycle: %d\n", vdp_port, value, context->current_cycle);
@@ -339,8 +335,7 @@
 				adjust_int_cycle(context, v_context);
 			}
 		} else {
-			printf("Illegal write to HV Counter port %X\n", vdp_port);
-			exit(1);
+			fatal_error("Illegal write to HV Counter port %X\n", vdp_port);
 		}
 		if (v_context->cycles != before_cycle) {
 			//printf("68K paused for %d (%d) cycles at cycle %d (%d) for write\n", v_context->cycles - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle);
@@ -369,8 +364,7 @@
 	genesis_context * gen = context->system;
 	vdp_port &= 0xFF;
 	if (vdp_port & 0xE0) {
-		printf("machine freeze due to write to Z80 address %X\n", 0x7F00 | vdp_port);
-		exit(1);
+		fatal_error("machine freeze due to write to Z80 address %X\n", 0x7F00 | vdp_port);
 	}
 	if (vdp_port < 0x10) {
 		//These probably won't currently interact well with the 68K accessing the VDP
@@ -380,8 +374,7 @@
 		} else if (vdp_port < 8) {
 			vdp_control_port_write(gen->vdp, value << 8 | value);
 		} else {
-			printf("Illegal write to HV Counter port %X\n", vdp_port);
-			exit(1);
+			fatal_error("Illegal write to HV Counter port %X\n", vdp_port);
 		}
 	} else if (vdp_port < 0x18) {
 		sync_sound(gen, context->current_cycle);
@@ -395,8 +388,7 @@
 uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context)
 {
 	if (vdp_port & 0x2700E0) {
-		printf("machine freeze due to read from address %X\n", 0xC00000 | vdp_port);
-		exit(1);
+		fatal_error("machine freeze due to read from address %X\n", 0xC00000 | vdp_port);
 	}
 	vdp_port &= 0x1F;
 	uint16_t value;
@@ -413,8 +405,7 @@
 			//printf("HV Counter: %X at cycle %d\n", value, v_context->cycles);
 		}
 	} else if (vdp_port < 0x18){
-		printf("Illegal read from PSG  port %X\n", vdp_port);
-		exit(1);
+		fatal_error("Illegal read from PSG  port %X\n", vdp_port);
 	} else {
 		value = vdp_test_port_read(v_context);
 	}
@@ -444,8 +435,7 @@
 {
 	z80_context * context = vcontext;
 	if (vdp_port & 0xE0) {
-		printf("machine freeze due to read from Z80 address %X\n", 0x7F00 | vdp_port);
-		exit(1);
+		fatal_error("machine freeze due to read from Z80 address %X\n", 0x7F00 | vdp_port);
 	}
 	genesis_context * gen = context->system;
 	//VDP access goes over the 68K bus like a bank area access
@@ -467,8 +457,7 @@
 		} else if (vdp_port < 8) {
 			ret = vdp_control_port_read(gen->vdp);
 		} else {
-			printf("Illegal write to HV Counter port %X\n", vdp_port);
-			exit(1);
+			fatal_error("Illegal write to HV Counter port %X\n", vdp_port);
 		}
 	} else {
 		//TODO: Figure out the correct value today
@@ -507,8 +496,7 @@
 					gen->z80->mem_pointers[1] = NULL;
 				}
 			} else {
-				printf("68K write to unhandled Z80 address %X\n", location);
-				exit(1);
+				fatal_error("68K write to unhandled Z80 address %X\n", location);
 			}
 		}
 	} else {
@@ -847,8 +835,7 @@
 	if (statefile) {
 		uint32_t pc = load_gst(gen, statefile);
 		if (!pc) {
-			fprintf(stderr, "Failed to load save state %s\n", statefile);
-			exit(1);
+			fatal_error("Failed to load save state %s\n", statefile);
 		}
 		printf("Loaded %s\n", statefile);
 		if (debugger) {
@@ -907,10 +894,6 @@
 
 int main(int argc, char ** argv)
 {
-	if (argc < 2) {
-		fputs("Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n", stderr);
-		return 1;
-	}
 	set_exe_str(argv[0]);
 	config = load_config();
 	int width = -1;
@@ -931,8 +914,7 @@
 			case 'b':
 				i++;
 				if (i >= argc) {
-					fputs("-b must be followed by a frame count\n", stderr);
-					return 1;
+					fatal_error("-b must be followed by a frame count\n");
 				}
 				headless = 1;
 				exit_after = atoi(argv[i]);
@@ -954,7 +936,7 @@
 				address_log = fopen("address.log", "w");
 				break;
 			case 'v':
-				printf("blastem %s\n", BLASTEM_VERSION);
+				info_message("blastem %s\n", BLASTEM_VERSION);
 				return 0;
 				break;
 			case 'n':
@@ -963,20 +945,17 @@
 			case 'r':
 				i++;
 				if (i >= argc) {
-					fputs("-r must be followed by region (J, U or E)\n", stderr);
-					return 1;
+					fatal_error("-r must be followed by region (J, U or E)\n");
 				}
 				force_version = translate_region_char(toupper(argv[i][0]));
 				if (!force_version) {
-					fprintf(stderr, "'%c' is not a valid region character for the -r option\n", argv[i][0]);
-					return 1;
+					fatal_error("'%c' is not a valid region character for the -r option\n", argv[i][0]);
 				}
 				break;
 			case 's':
 				i++;
 				if (i >= argc) {
-					fputs("-s must be followed by a savestate filename\n", stderr);
-					return 1;
+					fatal_error("-s must be followed by a savestate filename\n");
 				}
 				statefile = argv[i];
 				break;
@@ -984,7 +963,7 @@
 				ym_log = 1;
 				break;
 			case 'h':
-				puts(
+				info_message(
 					"Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n"
 					"Options:\n"
 					"	-h          Print this help text\n"
@@ -1000,13 +979,11 @@
 				);
 				return 0;
 			default:
-				fprintf(stderr, "Unrecognized switch %s\n", argv[i]);
-				return 1;
+				fatal_error("Unrecognized switch %s\n", argv[i]);
 			}
 		} else if (!loaded) {
 			if (!(rom_size = load_rom(argv[i]))) {
-				fprintf(stderr, "Failed to open %s for reading\n", argv[i]);
-				return 1;
+				fatal_error("Failed to open %s for reading\n", argv[i]);
 			}
 			romfname = argv[i];
 			loaded = 1;
@@ -1017,8 +994,7 @@
 		}
 	}
 	if (!loaded) {
-		fputs("You must specify a ROM filename!\n", stderr);
-		return 1;
+		fatal_error("Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n");
 	}
 	tern_node *rom_db = load_rom_db();
 	rom_info info = configure_rom(rom_db, cart, rom_size, base_map, sizeof(base_map)/sizeof(base_map[0]));
--- a/config.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/config.c	Sat Jul 25 18:22:07 2015 -0700
@@ -53,8 +53,7 @@
 			if (started) {
 				return head;
 			}
-			fprintf(stderr, "unexpected } on line %d\n", *line);
-			exit(1);
+			fatal_error("unexpected } on line %d\n", *line);
 		}
 		
 		char * end = curline + len - 1;
@@ -133,7 +132,8 @@
 		return ret;
 	}
 no_config:
-	fputs("Failed to find a config file in ~/.config/blastem/blastem.cfg or in the blastem executable directory\n", stderr);
-	exit(1);
+	fatal_error("Failed to find a config file in ~/.config/blastem/blastem.cfg or in the blastem executable directory\n");
+	//this will never get reached, but the compiler doesn't know that. Let's make it happy
+	return NULL;
 }
 
--- a/debug.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/debug.c	Sat Jul 25 18:22:07 2015 -0700
@@ -7,6 +7,7 @@
 #include <sys/select.h>
 #endif
 #include "render.h"
+#include "util.h"
 
 static bp_def * breakpoints = NULL;
 static bp_def * zbreakpoints = NULL;
@@ -298,15 +299,12 @@
 		pc = z80_ram + (address & 0x1FFF);
 	} else if (address >= 0x8000) {
 		if (context->bank_reg < (0x400000 >> 15)) {
-			fprintf(stderr, "Entered Z80 debugger in banked memory address %X, which is not yet supported\n", address);
-			exit(1);
+			fatal_error("Entered Z80 debugger in banked memory address %X, which is not yet supported\n", address);
 		} else {
-			fprintf(stderr, "Entered Z80 debugger in banked memory address %X, but the bank is not pointed to a cartridge address\n", address);
-			exit(1);
+			fatal_error("Entered Z80 debugger in banked memory address %X, but the bank is not pointed to a cartridge address\n", address);
 		}
 	} else {
-		fprintf(stderr, "Entered Z80 debugger at address %X\n", address);
-		exit(1);
+		fatal_error("Entered Z80 debugger at address %X\n", address);
 	}
 	for (disp_def * cur = zdisplays; cur; cur = cur->next) {
 		zdebugger_print(context, cur->format_char, cur->param);
@@ -504,8 +502,7 @@
 	} else if(address > 0xE00000) {
 		pc = ram + (address & 0xFFFF)/2;
 	} else {
-		fprintf(stderr, "Entered 68K debugger at address %X\n", address);
-		exit(1);
+		fatal_error("Entered 68K debugger at address %X\n", address);
 	}
 	uint16_t * after_pc = m68k_decode(pc, &inst, address);
 	m68k_disasm(&inst, input_buf);
--- a/gdb_remote.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/gdb_remote.c	Sat Jul 25 18:22:07 2015 -0700
@@ -6,6 +6,7 @@
 #include "gdb_remote.h"
 #include "68kinst.h"
 #include "debug.h"
+#include "util.h"
 #include <unistd.h>
 #include <fcntl.h>
 #include <stddef.h>
@@ -76,8 +77,7 @@
 void write_or_die(int fd, const void *buf, size_t count)
 {
 	if (write(fd, buf, count) < count) {
-		fputs("Error writing to stdout\n", stderr);
-		exit(1);
+		fatal_error("Error writing to stdout\n");
 	}
 }
 
@@ -186,8 +186,7 @@
 		} else if(pc > 0xE00000) {
 			pc_ptr = ram + (pc & 0xFFFF)/2;
 		} else {
-			fprintf(stderr, "Entered gdb remote debugger stub at address %X\n", pc);
-			exit(1);
+			fatal_error("Entered gdb remote debugger stub at address %X\n", pc);
 		}
 		uint16_t * after_pc = m68k_decode(pc_ptr, &inst, pc & 0xFFFFFF);
 		uint32_t after = pc + (after_pc-pc_ptr)*2;
@@ -407,8 +406,7 @@
 				} else if(pc > 0xE00000) {
 					pc_ptr = ram + (pc & 0xFFFF)/2;
 				} else {
-					fprintf(stderr, "Entered gdb remote debugger stub at address %X\n", pc);
-					exit(1);
+					fatal_error("Entered gdb remote debugger stub at address %X\n", pc);
 				}
 				uint16_t * after_pc = m68k_decode(pc_ptr, &inst, pc & 0xFFFFFF);
 				uint32_t after = pc + (after_pc-pc_ptr)*2;
@@ -452,8 +450,7 @@
 	}
 	return;
 not_impl:
-	fprintf(stderr, "Command %s is not implemented, exiting...\n", command);
-	exit(1);
+	fatal_error("Command %s is not implemented, exiting...\n", command);
 }
 
 m68k_context *  gdb_debug_enter(m68k_context * context, uint32_t pc)
@@ -516,8 +513,7 @@
 						*curbuf = 0;
 						//send acknowledgement
 						if (write(STDOUT_FILENO, "+", 1) < 1) {
-							fputs("Error writing to stdout\n", stderr);
-							exit(1);
+							fatal_error("Error writing to stdout\n");
 						}
 						gdb_run_command(context, pc, start);
 						curbuf += 2;
--- a/gen.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/gen.c	Sat Jul 25 18:22:07 2015 -0700
@@ -2,14 +2,14 @@
 #include <stdlib.h>
 #include "gen.h"
 #include "mem.h"
+#include "util.h"
 
 void init_code_info(code_info *code)
 {
 	size_t size = CODE_ALLOC_SIZE;
 	code->cur = alloc_code(&size);
 	if (!code->cur) {
-		fputs("Failed to allocate memory for generated code\n", stderr);
-		exit(1);
+		fatal_error("Failed to allocate memory for generated code\n");
 	}
 	code->last = code->cur + size/sizeof(code_word) - RESERVE_WORDS;
 }
--- a/gen_arm.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/gen_arm.c	Sat Jul 25 18:22:07 2015 -0700
@@ -76,8 +76,7 @@
 		size_t size = CODE_ALLOC_SIZE;
 		uint32_t *next_code = alloc_code(&size);
 		if (!next_code) {
-			fputs("Failed to allocate memory for generated code\n", stderr);
-			exit(1);
+			fatal_error("Failed to allocate memory for generated code\n");
 		}
 		if (next_code = code->last + RESERVE_WORDS) {
 			//new chunk is contiguous with the current one
--- a/gen_x86.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/gen_x86.c	Sat Jul 25 18:22:07 2015 -0700
@@ -5,6 +5,7 @@
 */
 #include "gen_x86.h"
 #include "mem.h"
+#include "util.h"
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -187,8 +188,7 @@
 			disp >>= 8;
 			*(out++) = disp;
 		} else {
-			fprintf(stderr, "jmp: %p - %p = %lX\n", dest, out + 6, (long)disp);
-			exit(1);
+			fatal_error("jmp: %p - %p = %l which is out of range of a 32-bit displacementX\n", dest, out + 6, (long)disp);
 		}
 	}
 	code->cur = out;
@@ -200,8 +200,7 @@
 		size_t size = CODE_ALLOC_SIZE;
 		code_ptr next_code = alloc_code(&size);
 		if (!next_code) {
-			fputs("Failed to allocate memory for generated code\n", stderr);
-			exit(1);
+			fatal_error("Failed to allocate memory for generated code\n");
 		}
 		if (next_code != code->last + RESERVE_WORDS) {
 			//new chunk is not contiguous with the current one
@@ -231,8 +230,7 @@
 #ifdef X86_64
 		*out = PRE_REX;
 		if (src >= AH && src <= BH || dst >= AH && dst <= BH) {
-			fprintf(stderr, "attempt to use *H reg in an instruction requiring REX prefix. opcode = %X\n", opcode);
-			exit(1);
+			fatal_error("attempt to use *H reg in an instruction requiring REX prefix. opcode = %X\n", opcode);
 		}
 		if (size == SZ_Q) {
 			*out |= REX_QUAD;
@@ -247,8 +245,7 @@
 		}
 		out++;
 #else
-		fprintf(stderr, "Instruction requires REX prefix but this is a 32-bit build | opcode: %X, src: %s, dst: %s, size: %s\n", opcode, x86_reg_names[src], x86_reg_names[dst], x86_sizes[size]);
-		exit(1);
+		fatal_error("Instruction requires REX prefix but this is a 32-bit build | opcode: %X, src: %s, dst: %s, size: %s\n", opcode, x86_reg_names[src], x86_reg_names[dst], x86_sizes[size]);
 #endif
 	}
 	if (size == SZ_B) {
@@ -284,8 +281,7 @@
 #ifdef X86_64
 		*out = PRE_REX;
 		if (reg >= AH && reg <= BH) {
-			fprintf(stderr, "attempt to use *H reg in an instruction requiring REX prefix. opcode = %X\n", opcode);
-			exit(1);
+			fatal_error("attempt to use *H reg in an instruction requiring REX prefix. opcode = %X\n", opcode);
 		}
 		if (size == SZ_Q) {
 			*out |= REX_QUAD;
@@ -300,8 +296,7 @@
 		}
 		out++;
 #else
-		fprintf(stderr, "Instruction requires REX prefix but this is a 32-bit build | opcode: %X, reg: %s, base: %s, size: %s\n", opcode, x86_reg_names[reg], x86_reg_names[base], x86_sizes[size]);
-		exit(1);
+		fatal_error("Instruction requires REX prefix but this is a 32-bit build | opcode: %X, reg: %s, base: %s, size: %s\n", opcode, x86_reg_names[reg], x86_reg_names[base], x86_sizes[size]);
 #endif
 	}
 	if (size == SZ_B) {
@@ -349,8 +344,7 @@
 #ifdef X86_64
 		*out = PRE_REX;
 		if (reg >= AH && reg <= BH) {
-			fprintf(stderr, "attempt to use *H reg in an instruction requiring REX prefix. opcode = %X\n", opcode);
-			exit(1);
+			fatal_error("attempt to use *H reg in an instruction requiring REX prefix. opcode = %X\n", opcode);
 		}
 		if (size == SZ_Q) {
 			*out |= REX_QUAD;
@@ -365,8 +359,7 @@
 		}
 		out++;
 #else
-		fprintf(stderr, "Instruction requires REX prefix but this is a 32-bit build | opcode: %X, reg: %s, base: %s, size: %s\n", opcode, x86_reg_names[reg], x86_reg_names[base], x86_sizes[size]);
-		exit(1);
+		fatal_error("Instruction requires REX prefix but this is a 32-bit build | opcode: %X, reg: %s, base: %s, size: %s\n", opcode, x86_reg_names[reg], x86_reg_names[base], x86_sizes[size]);
 #endif
 	}
 	if (size == SZ_B) {
@@ -405,8 +398,7 @@
 #ifdef X86_64
 		*out = PRE_REX;
 		if (reg >= AH && reg <= BH) {
-			fprintf(stderr, "attempt to use *H reg in an instruction requiring REX prefix. opcode = %X\n", opcode);
-			exit(1);
+			fatal_error("attempt to use *H reg in an instruction requiring REX prefix. opcode = %X\n", opcode);
 		}
 		if (size == SZ_Q) {
 			*out |= REX_QUAD;
@@ -425,8 +417,7 @@
 		}
 		out++;
 #else
-		fprintf(stderr, "Instruction requires REX prefix but this is a 32-bit build | opcode: %X, reg: %s, base: %s, size: %s\n", opcode, x86_reg_names[reg], x86_reg_names[base], x86_sizes[size]);
-		exit(1);
+		fatal_error("Instruction requires REX prefix but this is a 32-bit build | opcode: %X, reg: %s, base: %s, size: %s\n", opcode, x86_reg_names[reg], x86_reg_names[base], x86_sizes[size]);
 #endif
 	}
 	if (size == SZ_B) {
@@ -461,8 +452,7 @@
 #ifdef X86_64
 		*out = PRE_REX;
 		if (dst >= AH && dst <= BH) {
-			fprintf(stderr, "attempt to use *H reg in an instruction requiring REX prefix. opcode = %X\n", opcode);
-			exit(1);
+			fatal_error("attempt to use *H reg in an instruction requiring REX prefix. opcode = %X\n", opcode);
 		}
 		if (size == SZ_Q) {
 			*out |= REX_QUAD;
@@ -473,8 +463,7 @@
 		}
 		out++;
 #else
-		fprintf(stderr, "Instruction requires REX prefix but this is a 32-bit build | opcode: %X:%X, reg: %s, size: %s\n", opcode, opex, x86_reg_names[dst], x86_sizes[size]);
-		exit(1);
+		fatal_error("Instruction requires REX prefix but this is a 32-bit build | opcode: %X:%X, reg: %s, size: %s\n", opcode, opex, x86_reg_names[dst], x86_sizes[size]);
 #endif
 	}
 	if (size == SZ_B) {
@@ -509,8 +498,7 @@
 		}
 		out++;
 #else
-		fprintf(stderr, "Instruction requires REX prefix but this is a 32-bit build | opcode: %X:%X, reg: %s, size: %s\n", opcode, opex, x86_reg_names[dst], x86_sizes[size]);
-		exit(1);
+		fatal_error("Instruction requires REX prefix but this is a 32-bit build | opcode: %X:%X, reg: %s, size: %s\n", opcode, opex, x86_reg_names[dst], x86_sizes[size]);
 #endif
 	}
 	if (size != SZ_B) {
@@ -549,8 +537,7 @@
 #ifdef X86_64
 				*out = PRE_REX | REX_QUAD;
 #else
-		fprintf(stderr, "Instruction requires REX prefix but this is a 32-bit build | opcode: %X, reg: %s, size: %s\n", al_opcode, x86_reg_names[dst], x86_sizes[size]);
-		exit(1);
+		fatal_error("Instruction requires REX prefix but this is a 32-bit build | opcode: %X, reg: %s, size: %s\n", al_opcode, x86_reg_names[dst], x86_sizes[size]);
 #endif
 			}
 		}
@@ -568,8 +555,7 @@
 			}
 			out++;
 #else
-		fprintf(stderr, "Instruction requires REX prefix but this is a 32-bit build | opcode: %X:%X, reg: %s, size: %s\n", opcode, op_ex, x86_reg_names[dst], x86_sizes[size]);
-		exit(1);
+		fatal_error("Instruction requires REX prefix but this is a 32-bit build | opcode: %X:%X, reg: %s, size: %s\n", opcode, op_ex, x86_reg_names[dst], x86_sizes[size]);
 #endif
 		}
 		if (dst >= AH && dst <= BH) {
@@ -620,8 +606,7 @@
 		}
 		out++;
 #else
-		fprintf(stderr, "Instruction requires REX prefix but this is a 32-bit build | opcode: %X:%X, reg: %s, size: %s\n", opcode, op_ex, x86_reg_names[dst], x86_sizes[size]);
-		exit(1);
+		fatal_error("Instruction requires REX prefix but this is a 32-bit build | opcode: %X:%X, reg: %s, size: %s\n", opcode, op_ex, x86_reg_names[dst], x86_sizes[size]);
 #endif
 	}
 	if (size != SZ_B) {
@@ -1921,8 +1906,7 @@
 			disp >>= 8;
 			*(out++) = disp;
 		} else {
-			fprintf(stderr, "jcc: %p - %p = %lX\n", dest, out + 6, (long)disp);
-			exit(1);
+			fatal_error("jcc: %p - %p = %lX which is out of range for a 32-bit displacement\n", dest, out + 6, (long)disp);
 		}
 	}
 	code->cur = out;
@@ -1948,8 +1932,7 @@
 			disp >>= 8;
 			*(out++) = disp;
 		} else {
-			fprintf(stderr, "jmp: %p - %p = %lX\n", dest, out + 6, (long)disp);
-			exit(1);
+			fatal_error("jmp: %p - %p = %lX which is out of range for a 32-bit displacement\n", dest, out + 6, (long)disp);
 		}
 	}
 	code->cur = out;
@@ -1997,8 +1980,7 @@
 		*(out++) = disp;
 	} else {
 		//TODO: Implement far call???
-		fprintf(stderr, "%p - %p = %lX\n", fun, out + 5, (long)disp);
-		exit(1);
+		fatal_error("call: %p - %p = %lX which is out of range for a 32-bit displacement\n", fun, out + 5, (long)disp);
 	}
 	code->cur = out;
 }
--- a/m68k_core.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/m68k_core.c	Sat Jul 25 18:22:07 2015 -0700
@@ -8,6 +8,7 @@
 #include "68kinst.h"
 #include "backend.h"
 #include "gen.h"
+#include "util.h"
 #include <stdio.h>
 #include <stddef.h>
 #include <stdlib.h>
@@ -174,8 +175,7 @@
 		break;
 	default:
 		m68k_disasm(inst, disasm_buf);
-		printf("%X: %s\naddress mode %d not implemented (lea src)\n", inst->address, disasm_buf, inst->src.addr_mode);
-		exit(1);
+		fatal_error("%X: %s\naddress mode %d not implemented (lea src)\n", inst->address, disasm_buf, inst->src.addr_mode);
 	}
 	if (inst->op == M68K_PEA) {
 		subi_areg(opts, 4, 7);
@@ -283,8 +283,7 @@
 		break;
 	default:
 		m68k_disasm(inst, disasm_buf);
-		printf("%s\naddress mode %d not yet supported (%s)\n", disasm_buf, inst->src.addr_mode, is_jsr ? "jsr" : "jmp");
-		exit(1);
+		fatal_error("%s\naddress mode %d not yet supported (%s)\n", disasm_buf, inst->src.addr_mode, is_jsr ? "jsr" : "jmp");
 	}
 }
 
@@ -410,8 +409,7 @@
 			break;
 		default:
 			m68k_disasm(inst, disasm_buf);
-			printf("%X: %s\naddress mode %d not implemented (movem dst)\n", inst->address, disasm_buf, inst->dst.addr_mode);
-			exit(1);
+			fatal_error("%X: %s\naddress mode %d not implemented (movem dst)\n", inst->address, disasm_buf, inst->dst.addr_mode);
 		}
 		if (inst->dst.addr_mode == MODE_AREG_PREDEC) {
 			reg = 15;
@@ -481,8 +479,7 @@
 			break;
 		default:
 			m68k_disasm(inst, disasm_buf);
-			printf("%X: %s\naddress mode %d not implemented (movem src)\n", inst->address, disasm_buf, inst->src.addr_mode);
-			exit(1);
+			fatal_error("%X: %s\naddress mode %d not implemented (movem src)\n", inst->address, disasm_buf, inst->src.addr_mode);
 		}
 		cycles(&opts->gen, early_cycles);
 		for(reg = 0; reg < 16; reg ++) {
@@ -830,8 +827,7 @@
 		translate_m68k_unary(opts, inst, info->impl.flag_mask, inst->dst.addr_mode != MODE_UNUSED ? &dst_op : &src_op);
 	} else {
 		m68k_disasm(inst, disasm_buf);
-		printf("%X: %s\ninstruction %d not yet implemented\n", inst->address, disasm_buf, inst->op);
-		exit(1);
+		fatal_error("%X: %s\ninstruction %d not yet implemented\n", inst->address, disasm_buf, inst->op);
 	}
 }
 
--- a/m68k_core_x86.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/m68k_core_x86.c	Sat Jul 25 18:22:07 2015 -0700
@@ -9,6 +9,7 @@
 #include "68kinst.h"
 #include "mem.h"
 #include "backend.h"
+#include "util.h"
 #include <stdio.h>
 #include <stddef.h>
 #include <stdlib.h>
@@ -507,8 +508,7 @@
 		return;
 	default:
 		m68k_disasm(inst, disasm_buf);
-		printf("%X: %s\naddress mode %d not implemented (%s)\n", inst->address, disasm_buf, op->addr_mode, dst ? "dst" : "src");
-		exit(1);
+		fatal_error("%X: %s\naddress mode %d not implemented (%s)\n", inst->address, disasm_buf, op->addr_mode, dst ? "dst" : "src");
 	}
 	if (!dst && inst->dst.addr_mode == MODE_AREG && inst->extra.size == OPSIZE_WORD) {
 		if (ea->mode == MODE_REG_DIRECT) {
@@ -684,8 +684,7 @@
 		break;
 	default:
 		m68k_disasm(inst, disasm_buf);
-		printf("%X: %s\naddress mode %d not implemented (move dst)\n", inst->address, disasm_buf, inst->dst.addr_mode);
-		exit(1);
+		fatal_error("%X: %s\naddress mode %d not implemented (move dst)\n", inst->address, disasm_buf, inst->dst.addr_mode);
 	}
 
 	if (inst->dst.addr_mode != MODE_AREG) {
--- a/render.h	Sat Jul 25 18:19:00 2015 -0700
+++ b/render.h	Sat Jul 25 18:22:07 2015 -0700
@@ -50,7 +50,9 @@
 int render_joystick_num_hats(int joystick);
 int render_num_joysticks();
 void process_events();
-
+void render_errorbox(char *title, char *message);
+void render_warnbox(char *title, char *message);
+void render_infobox(char *title, char *message);
 
 
 #endif //RENDER_H_
--- a/render_sdl.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/render_sdl.c	Sat Jul 25 18:22:07 2015 -0700
@@ -184,8 +184,7 @@
 	GLint link_status;
 	glGetProgramiv(program, GL_LINK_STATUS, &link_status);
 	if (!link_status) {
-		fputs("Failed to link shader program\n", stderr);
-		exit(1);
+		fatal_error("Failed to link shader program\n");
 	}
 	un_textures[0] = glGetUniformLocation(program, "textures[0]");
 	un_textures[1] = glGetUniformLocation(program, "textures[1]");
@@ -198,9 +197,9 @@
 void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen)
 {
 	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) {
-		fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
-		exit(1);
+		fatal_error("Unable to init SDL: %s\n", SDL_GetError());
 	}
+	atexit(SDL_Quit);
 	printf("width: %d, height: %d\n", width, height);
 	uint32_t flags = SDL_WINDOW_OPENGL;
 
@@ -222,23 +221,17 @@
 	}
 	main_window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, flags);
 	if (!main_window) {
-		fprintf(stderr, "Unable to create SDL window: %s\n", SDL_GetError());
-		SDL_Quit();
-		exit(1);
+		fatal_error("Unable to create SDL window: %s\n", SDL_GetError());
 	}
 	SDL_GetWindowSize(main_window, &width, &height);
 	printf("Window created with size: %d x %d\n", width, height);
 	main_context = SDL_GL_CreateContext(main_window);
 	GLenum res = glewInit();
 	if (res != GLEW_OK) {
-		fprintf(stderr, "Initialization of GLEW failed with code %d\n", res);
-		SDL_Quit();
-		exit(1);
+		fatal_error("Initialization of GLEW failed with code %d\n", res);
 	}
 	if (!GLEW_VERSION_2_0) {
-		fputs("BlastEm requires at least OpenGL 2.0, but it is unavailable\n", stderr);
-		SDL_Quit();
-		exit(1);
+		fatal_error("BlastEm requires at least OpenGL 2.0, but it is unavailable\n");
 	}
 	float aspect = (float)width / height;
 	tern_val def = {.ptrval = "normal"};
@@ -294,9 +287,7 @@
 	desired.userdata = NULL;
 
 	if (SDL_OpenAudio(&desired, &actual) < 0) {
-		fprintf(stderr, "Unable to open SDL audio: %s\n", SDL_GetError());
-		SDL_Quit();
-		exit(1);
+		fatal_error("Unable to open SDL audio: %s\n", SDL_GetError());
 	}
 	buffer_samples = actual.samples;
 	sample_rate = actual.freq;
@@ -315,7 +306,6 @@
 	}
 	SDL_JoystickEventState(SDL_ENABLE);
 	
-	atexit(SDL_Quit);
 	atexit(render_close_audio);
 }
 
@@ -530,4 +520,18 @@
 	return sample_rate;
 }
 
+void render_errorbox(char *title, char *message)
+{
+	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, message, NULL);
+}
 
+void render_warnbox(char *title, char *message)
+{
+	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, title, message, NULL);
+}
+
+void render_infobox(char *title, char *message)
+{
+	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title, message, NULL);
+}
+
--- a/romdb.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/romdb.c	Sat Jul 25 18:22:07 2015 -0700
@@ -317,8 +317,7 @@
 	genesis_context *gen = ((m68k_context *)context)->system;
 	eeprom_map *map = find_eeprom_map(address, gen);
 	if (!map) {
-		fprintf(stderr, "Could not find EEPROM map for address %X\n", address);
-		exit(1);
+		fatal_error("Could not find EEPROM map for address %X\n", address);
 	}
 	if (map->scl_mask) {
 		set_scl(&gen->eeprom, (value & map->scl_mask) != 0);
@@ -334,8 +333,7 @@
 	genesis_context *gen = ((m68k_context *)context)->system;
 	eeprom_map *map = find_eeprom_map(address, gen);
 	if (!map) {
-		fprintf(stderr, "Could not find EEPROM map for address %X\n", address);
-		exit(1);
+		fatal_error("Could not find EEPROM map for address %X\n", address);
 	}
 	
 	uint16_t expanded, mask;
@@ -360,8 +358,7 @@
 	genesis_context *gen = ((m68k_context *)context)->system;
 	eeprom_map *map = find_eeprom_map(address, gen);
 	if (!map) {
-		fprintf(stderr, "Could not find EEPROM map for address %X\n", address);
-		exit(1);
+		fatal_error("Could not find EEPROM map for address %X\n", address);
 	}
 	uint16_t ret = 0;
 	if (map->sda_read_bit < 16) {
@@ -375,8 +372,7 @@
 	genesis_context *gen = ((m68k_context *)context)->system;
 	eeprom_map *map = find_eeprom_map(address, gen);
 	if (!map) {
-		fprintf(stderr, "Could not find EEPROM map for address %X\n", address);
-		exit(1);
+		fatal_error("Could not find EEPROM map for address %X\n", address);
 	}
 	uint8_t bit = address & 1 ? map->sda_read_bit : map->sda_read_bit - 8;
 	uint8_t ret = 0;
@@ -390,14 +386,13 @@
 {
 	char *exe_dir = get_exe_dir();
 	if (!exe_dir) {
-		fputs("Failed to find executable path\n", stderr);
-		exit(1);
+		fatal_error("Failed to find executable path\n");
 	}
 	char *path = alloc_concat(exe_dir, "/rom.db");
 	tern_node *db = parse_config_file(path);
 	free(path);
 	if (!db) {
-		fputs("Failed to load ROM DB\n", stderr);
+		fatal_error("Failed to load ROM DB\n");
 	}
 	return db;
 }
@@ -615,13 +610,11 @@
 	if (!state->info->save_size) {
 		char * size = tern_find_path(state->root, "SRAM\0size\0").ptrval;
 		if (!size) {
-			fprintf(stderr, "ROM DB map entry %d with address %s has device type SRAM, but the SRAM size is not defined\n", state->index, key);
-			exit(1);
+			fatal_error("ROM DB map entry %d with address %s has device type SRAM, but the SRAM size is not defined\n", state->index, key);
 		}
 		state->info->save_size = atoi(size);
 		if (!state->info->save_size) {
-			fprintf(stderr, "SRAM size %s is invalid\n", size);
-			exit(1);
+			fatal_error("SRAM size %s is invalid\n", size);
 		}
 		state->info->save_mask = nearest_pow2(state->info->save_size)-1;
 		state->info->save_buffer = malloc(state->info->save_size);
@@ -642,13 +635,11 @@
 	if (!state->info->save_size) {
 		char * size = tern_find_path(state->root, "EEPROM\0size\0").ptrval;
 		if (!size) {
-			fprintf(stderr, "ROM DB map entry %d with address %s has device type EEPROM, but the EEPROM size is not defined\n", state->index, key);
-			exit(1);
+			fatal_error("ROM DB map entry %d with address %s has device type EEPROM, but the EEPROM size is not defined\n", state->index, key);
 		}
 		state->info->save_size = atoi(size);
 		if (!state->info->save_size) {
-			fprintf(stderr, "EEPROM size %s is invalid\n", size);
-			exit(1);
+			fatal_error("EEPROM size %s is invalid\n", size);
 		}
 		char *etype = tern_find_path(state->root, "EEPROM\0type\0").ptrval;
 		if (!etype) {
@@ -657,8 +648,7 @@
 		if (!strcmp(etype, "i2c")) {
 			state->info->save_type = SAVE_I2C;
 		} else {
-			fprintf(stderr, "EEPROM type %s is invalid\n", etype);
-			exit(1);
+			fatal_error("EEPROM type %s is invalid\n", etype);
 		}
 		state->info->save_buffer = malloc(state->info->save_size);
 		memset(state->info->save_buffer, 0xFF, state->info->save_size);
@@ -690,14 +680,12 @@
 	map_iter_state *state = data;
 	tern_node *node = tern_get_node(val);
 	if (!node) {
-		fprintf(stderr, "ROM DB map entry %d with address %s is not a node\n", state->index, key);
-		exit(1);
+		fatal_error("ROM DB map entry %d with address %s is not a node\n", state->index, key);
 	}
 	uint32_t start = strtol(key, NULL, 16);
 	uint32_t end = strtol(tern_find_ptr_default(node, "last", "0"), NULL, 16);
 	if (!end || end < start) {
-		fprintf(stderr, "'last' value is missing or invalid for ROM DB map entry %d with address %s\n", state->index, key);
-		exit(1);
+		fatal_error("'last' value is missing or invalid for ROM DB map entry %d with address %s\n", state->index, key);
 	}
 	char * dtype = tern_find_ptr_default(node, "device", "ROM");
 	uint32_t offset = strtol(tern_find_ptr_default(node, "offset", "0"), NULL, 16);
@@ -769,8 +757,7 @@
 		map->write_16 = (write_16_fun)write_bank_reg_w;
 		map->write_8 = (write_8_fun)write_bank_reg_b;
 	} else {
-		fprintf(stderr, "Invalid device type for ROM DB map entry %d with address %s\n", state->index, key);
-		exit(1);
+		fatal_error("Invalid device type for ROM DB map entry %d with address %s\n", state->index, key);
 	}
 	state->index++;
 }
--- a/stateview.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/stateview.c	Sat Jul 25 18:22:07 2015 -0700
@@ -61,13 +61,11 @@
 int main(int argc, char ** argv)
 {
 	if (argc < 2) {
-		fprintf(stderr, "Usage: stateview FILENAME\n");
-		exit(1);
+		fatal_error("Usage: stateview FILENAME\n");
 	}
 	FILE * state_file = fopen(argv[1], "rb");
 	if (!state_file) {
-		fprintf(stderr, "Failed to open %s\n", argv[1]);
-		exit(1);
+		fatal_error("Failed to open %s\n", argv[1]);
 	}
 	config = load_config(argv[0]);
 	int width = -1;
--- a/tern.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/tern.c	Sat Jul 25 18:22:07 2015 -0700
@@ -8,6 +8,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
+#include "util.h"
 
 tern_node * tern_insert(tern_node * head, char * key, tern_val value)
 {
@@ -190,8 +191,7 @@
 	}
 	if (head->el) {
 		if (pos == MAX_ITER_KEY) {
-			fputs("exceeded maximum key size", stderr);
-			exit(1);
+			fatal_error("tern_foreach_int: exceeded maximum key size");
 		}
 		keybuf[pos] = head->el;
 		tern_foreach_int(head->straight.next, fun, data, keybuf, pos+1);
--- a/util.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/util.c	Sat Jul 25 18:22:07 2015 -0700
@@ -8,6 +8,9 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include "blastem.h" //for headless global
+#include "render.h" //for render_errorbox
+
 char * alloc_concat(char * first, char * second)
 {
 	int flen = strlen(first);
@@ -86,6 +89,62 @@
 	exe_str = str;
 }
 
+void fatal_error(char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+	if (!headless) {
+		//take a guess at the final size
+		size_t size = strlen(format) * 2;
+		char *buf = malloc(size);
+		size_t actual = vsnprintf(buf, size, format, args);
+		if (actual >= size) {
+			actual++;
+			free(buf);
+			buf = malloc(actual);
+			va_end(args);
+			va_start(args, format);
+			vsnprintf(buf, actual, format, args);
+		}
+		fputs(buf, stderr);
+		render_errorbox("Fatal Error", buf);
+		free(buf);
+	} else {
+		vfprintf(stderr, format, args);
+	}
+	va_end(args);
+	exit(1);
+}
+
+void info_message(char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+#ifndef _WIN32
+	if (headless || (isatty(STDOUT_FILENO) && isatty(STDIN_FILENO))) {
+		vprintf(format, args);
+	} else {
+#endif
+		size_t size = strlen(format) * 2;
+		char *buf = malloc(size);
+		size_t actual = vsnprintf(buf, size, format, args);
+		if (actual >= size) {
+			actual++;
+			free(buf);
+			buf = malloc(actual);
+			va_end(args);
+			va_start(args, format);
+			vsnprintf(buf, actual, format, args);
+		}
+		fputs(buf, stdout);
+		render_infobox("BlastEm Info", buf);
+		free(buf);
+#ifndef _WIN32
+	}
+#endif
+	va_end(args);
+}
+
 #ifdef _WIN32
 #include "Shlobj.h"
 #include "Windows.h"
--- a/util.h	Sat Jul 25 18:19:00 2015 -0700
+++ b/util.h	Sat Jul 25 18:22:07 2015 -0700
@@ -25,5 +25,9 @@
 char * get_home_dir();
 //Returns the contents of a symlink in a newly allocated string
 char * readlink_alloc(char * path);
+//Prints an error message to stderr and to a message box if not in headless mode and then exits
+void fatal_error(char *format, ...);
+//Prints an information message to stdout and to a message box if not in headless mode and not attached to a console
+void info_message(char *format, ...);
 
 #endif //UTIL_H_
--- a/vgmplay.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/vgmplay.c	Sat Jul 25 18:22:07 2015 -0700
@@ -282,8 +282,7 @@
 					wait(&y_context, &p_context, &current_cycle, wait_time);
 				}
 			} else {
-				printf("unimplemented command: %X at offset %X\n", cmd, (unsigned int)(cur - data - 1));
-				exit(1);
+				fatal_error("unimplemented command: %X at offset %X\n", cmd, (unsigned int)(cur - data - 1));
 			}
 		}
 	}
--- a/z80_to_x86.c	Sat Jul 25 18:19:00 2015 -0700
+++ b/z80_to_x86.c	Sat Jul 25 18:22:07 2015 -0700
@@ -7,6 +7,7 @@
 #include "z80_to_x86.h"
 #include "gen_x86.h"
 #include "mem.h"
+#include "util.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -256,8 +257,7 @@
 		ea->mode = MODE_UNUSED;
 		break;
 	default:
-		fprintf(stderr, "Unrecognized Z80 addressing mode %d\n", inst->addr_mode & 0x1F);
-		exit(1);
+		fatal_error("Unrecognized Z80 addressing mode %d\n", inst->addr_mode & 0x1F);
 	}
 }
 
@@ -1939,11 +1939,10 @@
 	default: {
 		char disbuf[80];
 		z80_disasm(inst, disbuf, address);
-		fprintf(stderr, "unimplemented instruction: %s at %X\n", disbuf, address);
 		FILE * f = fopen("zram.bin", "wb");
 		fwrite(context->mem_pointers[0], 1, 8 * 1024, f);
 		fclose(f);
-		exit(1);
+		fatal_error("unimplemented Z80 instruction: %s at %X\nZ80 RAM has been saved to zram.bin for debugging", disbuf, address);
 	}
 	}
 }
@@ -1952,8 +1951,7 @@
 {
 	if (!context->interp_code[opcode]) {
 		if (opcode == 0xCB || (opcode >= 0xDD && (opcode & 0xF) == 0xD)) {
-			fprintf(stderr, "Encountered prefix byte %X at address %X. Z80 interpeter doesn't support those yet.", opcode, context->pc);
-			exit(1);
+			fatal_error("Encountered prefix byte %X at address %X. Z80 interpeter doesn't support those yet.", opcode, context->pc);
 		}
 		uint8_t codebuf[8];
 		memset(codebuf, 0, sizeof(codebuf));
@@ -1961,8 +1959,7 @@
 		z80inst inst;
 		uint8_t * after = z80_decode(codebuf, &inst);
 		if (after - codebuf > 1) {
-			fprintf(stderr, "Encountered multi-byte Z80 instruction at %X. Z80 interpeter doesn't support those yet.", context->pc);
-			exit(1);
+			fatal_error("Encountered multi-byte Z80 instruction at %X. Z80 interpeter doesn't support those yet.", context->pc);
 		}
 
 		z80_options * opts = context->options;
@@ -2243,7 +2240,7 @@
 		if (opts->gen.deferred) {
 			address = opts->gen.deferred->address;
 			dprintf("defferred address: %X\n", address);
-			}
+		}
 	} while (opts->gen.deferred);
 }
 
@@ -2749,6 +2746,6 @@
 		opts->gen.code.last = native + 16;
 		check_cycles_int(&opts->gen, address);
 		opts->gen.code = tmp_code;
-}
+	}
 }