shithub: rgbds

Download patch

ref: a1286e6f0e3db80acf482f6904632fe87985a799
parent: c0808246e5fbd6e0f1ac175606be2e2d51b12b55
author: ISSOtm <[email protected]>
date: Sun Sep 27 06:54:06 EDT 2020

Make newlines explicit in error messages

In preparation for a change a PR is about to make

--- a/include/asm/warning.h
+++ b/include/asm/warning.h
@@ -62,6 +62,6 @@
  * get a list of all errors at the end, making it easier to fix all of them at
  * once.
  */
-void yyerror(const char *fmt, ...);
+void error(const char *fmt, ...);
 
 #endif
--- a/src/asm/asmy.y
+++ b/src/asm/asmy.y
@@ -51,11 +51,11 @@
 		size_t i;
 
 		if (mode)
-			yyerror("Print types are only allowed for numbers");
+			error("Print types are only allowed for numbers\n");
 
 		for (i = 0; src[i] != 0; i++) {
 			if (i >= maxLength)
-				fatalerror("Symbol value too long to fit buffer");
+				fatalerror("Symbol value too long to fit buffer\n");
 
 			dest[i] = src[i];
 		}
@@ -85,11 +85,11 @@
 		}
 
 		if (fullLength < 0) {
-			fatalerror("snprintf encoding error");
+			fatalerror("snprintf encoding error\n");
 		} else {
 			length = (size_t)fullLength;
 			if (length > maxLength)
-				fatalerror("Symbol value too long to fit buffer");
+				fatalerror("Symbol value too long to fit buffer\n");
 		}
 	}
 
@@ -161,7 +161,7 @@
 	}
 
 	if (level != 0)
-		fatalerror("Unterminated REPT block");
+		fatalerror("Unterminated REPT block\n");
 
 	len = src - pCurrentBuffer->pBuffer - 4;
 
@@ -171,7 +171,7 @@
 	tzNewMacro = malloc(ulNewMacroSize + 1);
 
 	if (tzNewMacro == NULL)
-		fatalerror("Not enough memory for REPT block.");
+		fatalerror("Not enough memory for REPT block.\n");
 
 	uint32_t i;
 
@@ -231,7 +231,7 @@
 	}
 
 	if (level != 0)
-		fatalerror("Unterminated MACRO definition.");
+		fatalerror("Unterminated MACRO definition.\n");
 
 	len = src - pCurrentBuffer->pBuffer - 4;
 
@@ -240,7 +240,7 @@
 
 	tzNewMacro = (char *)malloc(ulNewMacroSize + 1);
 	if (tzNewMacro == NULL)
-		fatalerror("Not enough memory for MACRO definition.");
+		fatalerror("Not enough memory for MACRO definition.\n");
 
 	uint32_t i;
 
@@ -329,7 +329,7 @@
 	}
 
 	if (level != 0)
-		fatalerror("Unterminated IF construct");
+		fatalerror("Unterminated IF construct\n");
 
 	int32_t len = src - pCurrentBuffer->pBuffer;
 
@@ -374,7 +374,7 @@
 	}
 
 	if (level != 0)
-		fatalerror("Unterminated IF construct");
+		fatalerror("Unterminated IF construct\n");
 
 	int32_t len = src - pCurrentBuffer->pBuffer;
 
@@ -392,7 +392,7 @@
 	while (*s) {
 		switch (decode(&state, &codep, (uint8_t)*s)) {
 		case 1:
-			fatalerror("STRLEN: Invalid UTF-8 character");
+			fatalerror("STRLEN: Invalid UTF-8 character\n");
 			break;
 		case 0:
 			len++;
@@ -403,7 +403,7 @@
 
 	/* Check for partial code point. */
 	if (state != 0)
-		fatalerror("STRLEN: Invalid UTF-8 character");
+		fatalerror("STRLEN: Invalid UTF-8 character\n");
 
 	return len;
 }
@@ -418,7 +418,7 @@
 	uint32_t curLen = 0;
 
 	if (pos < 1) {
-		warning(WARNING_BUILTIN_ARG, "STRSUB: Position starts at 1");
+		warning(WARNING_BUILTIN_ARG, "STRSUB: Position starts at 1\n");
 		pos = 1;
 	}
 
@@ -426,7 +426,7 @@
 	while (src[srcIndex] && curPos < pos) {
 		switch (decode(&state, &codep, (uint8_t)src[srcIndex])) {
 		case 1:
-			fatalerror("STRSUB: Invalid UTF-8 character");
+			fatalerror("STRSUB: Invalid UTF-8 character\n");
 			break;
 		case 0:
 			curPos++;
@@ -436,7 +436,8 @@
 	}
 
 	if (!src[srcIndex] && len)
-		warning(WARNING_BUILTIN_ARG, "STRSUB: Position %lu is past the end of the string",
+		warning(WARNING_BUILTIN_ARG,
+			"STRSUB: Position %lu is past the end of the string\n",
 			(unsigned long)pos);
 
 	/* Copy from source to destination. */
@@ -443,7 +444,7 @@
 	while (src[srcIndex] && destIndex < MAXSTRLEN && curLen < len) {
 		switch (decode(&state, &codep, (uint8_t)src[srcIndex])) {
 		case 1:
-			fatalerror("STRSUB: Invalid UTF-8 character");
+			fatalerror("STRSUB: Invalid UTF-8 character\n");
 			break;
 		case 0:
 			curLen++;
@@ -453,11 +454,11 @@
 	}
 
 	if (curLen < len)
-		warning(WARNING_BUILTIN_ARG, "STRSUB: Length too big: %lu", (unsigned long)len);
+		warning(WARNING_BUILTIN_ARG, "STRSUB: Length too big: %lu\n", (unsigned long)len);
 
 	/* Check for partial code point. */
 	if (state != 0)
-		fatalerror("STRSUB: Invalid UTF-8 character");
+		fatalerror("STRSUB: Invalid UTF-8 character\n");
 
 	dest[destIndex] = 0;
 }
@@ -466,13 +467,12 @@
 {
 	switch (type) {
 		case ASSERT_FATAL:
-			fatalerror("Assertion failed");
+			fatalerror("Assertion failed\n");
 		case ASSERT_ERROR:
-			yyerror("Assertion failed");
+			error("Assertion failed\n");
 			break;
 		case ASSERT_WARN:
-			warning(WARNING_ASSERT,
-				"Assertion failed");
+			warning(WARNING_ASSERT, "Assertion failed\n");
 			break;
 	}
 }
@@ -481,17 +481,18 @@
 {
 	switch (type) {
 		case ASSERT_FATAL:
-			fatalerror("Assertion failed: %s", msg);
+			fatalerror("Assertion failed: %s\n", msg);
 		case ASSERT_ERROR:
-			yyerror("Assertion failed: %s", msg);
+			error("Assertion failed: %s\n", msg);
 			break;
 		case ASSERT_WARN:
-			warning(WARNING_ASSERT,
-				"Assertion failed: %s", msg);
+			warning(WARNING_ASSERT, "Assertion failed: %s\n", msg);
 			break;
 	}
 }
 
+#define yyerror(str) error(str "\n")
+
 %}
 
 %union
@@ -674,7 +675,7 @@
 ;
 
 scoped_label_bare : T_LABEL {
-			warning(WARNING_OBSOLETE, "Non-local labels without a colon are deprecated");
+			warning(WARNING_OBSOLETE, "Non-local labels without a colon are deprecated\n");
 			strcpy($$, $1);
 		}
 		| T_LOCAL_ID {
@@ -784,17 +785,15 @@
 
 align		: T_OP_ALIGN uconst {
 			if ($2 > 16)
-				yyerror("Alignment must be between 0 and 16, not %u",
-					$2);
+				error("Alignment must be between 0 and 16, not %u\n", $2);
 			else
 				sect_AlignPC($2, 0);
 		}
 		| T_OP_ALIGN uconst ',' uconst {
 			if ($2 > 16)
-				yyerror("Alignment must be between 0 and 16, not %u",
-					$2);
+				error("Alignment must be between 0 and 16, not %u\n", $2);
 			else if ($4 >= 1 << $2)
-				yyerror("Offset must be between 0 and %u, not %u",
+				error("Offset must be between 0 and %u, not %u\n",
 					(1 << $2) - 1, $4);
 			else
 				sect_AlignPC($2, $4);
@@ -827,10 +826,10 @@
 pushs		: T_POP_PUSHS		{ out_PushSection(); }
 ;
 
-fail		: T_POP_FAIL string	{ fatalerror("%s", $2); }
+fail		: T_POP_FAIL string	{ fatalerror("%s\n", $2); }
 ;
 
-warn		: T_POP_WARN string	{ warning(WARNING_USER, "%s", $2); }
+warn		: T_POP_WARN string	{ warning(WARNING_USER, "%s\n", $2); }
 ;
 
 assert_type	: /* empty */		{ $$ = ASSERT_ERROR; }
@@ -844,7 +843,7 @@
 			if (!rpn_isKnown(&$3)) {
 				if (!out_CreateAssert($2, &$3, "",
 						      sect_GetOutputOffset()))
-					yyerror("Assertion creation failed: %s",
+					error("Assertion creation failed: %s\n",
 						strerror(errno));
 			} else if ($3.nVal == 0) {
 				failAssert($2);
@@ -856,7 +855,7 @@
 			if (!rpn_isKnown(&$3)) {
 				if (!out_CreateAssert($2, &$3, $5,
 						      sect_GetOutputOffset()))
-					yyerror("Assertion creation failed: %s",
+					error("Assertion creation failed: %s\n",
 						strerror(errno));
 			} else if ($3.nVal == 0) {
 				failAssertMsg($2, $5);
@@ -949,7 +948,8 @@
 /* Authorize empty entries if there is only one */
 db		: T_POP_DB constlist_8bit_entry ',' constlist_8bit {
 			if (nListCountEmpty > 0)
-				warning(WARNING_EMPTY_ENTRY, "Empty entry in list of 8-bit elements (treated as padding).");
+				warning(WARNING_EMPTY_ENTRY,
+					"Empty entry in list of 8-bit elements (treated as padding).\n");
 		}
 		| T_POP_DB constlist_8bit_entry
 ;
@@ -956,7 +956,8 @@
 
 dw		: T_POP_DW constlist_16bit_entry ',' constlist_16bit {
 			if (nListCountEmpty > 0)
-				warning(WARNING_EMPTY_ENTRY, "Empty entry in list of 16-bit elements (treated as padding).");
+				warning(WARNING_EMPTY_ENTRY,
+					"Empty entry in list of 16-bit elements (treated as padding).\n");
 		}
 		| T_POP_DW constlist_16bit_entry
 ;
@@ -963,7 +964,8 @@
 
 dl		: T_POP_DL constlist_32bit_entry ',' constlist_32bit {
 			if (nListCountEmpty > 0)
-				warning(WARNING_EMPTY_ENTRY, "Empty entry in list of 32-bit elements (treated as padding).");
+				warning(WARNING_EMPTY_ENTRY,
+					"Empty entry in list of 32-bit elements (treated as padding).\n");
 		}
 		| T_POP_DL constlist_32bit_entry
 ;
@@ -987,10 +989,11 @@
 
 export_token	: T_POP_EXPORT
 		| T_POP_GLOBAL {
-			warning(WARNING_OBSOLETE, "`GLOBAL` is a deprecated synonym for `EXPORT`");
+			warning(WARNING_OBSOLETE,
+				"`GLOBAL` is a deprecated synonym for `EXPORT`\n");
 		}
 		| T_POP_XDEF {
-			warning(WARNING_OBSOLETE, "`XDEF` is a deprecated synonym for `EXPORT`");
+			warning(WARNING_OBSOLETE, "`XDEF` is a deprecated synonym for `EXPORT`\n");
 		}
 ;
 
@@ -1034,10 +1037,10 @@
 
 charmap		: T_POP_CHARMAP string ',' const {
 			if ($4 < INT8_MIN || $4 > UINT8_MAX)
-				warning(WARNING_TRUNCATION, "Expression must be 8-bit");
+				warning(WARNING_TRUNCATION, "Expression must be 8-bit\n");
 
 			if (charmap_Add($2, (uint8_t)$4) == -1)
-				yyerror("Error adding new charmap mapping: %s\n", strerror(errno));
+				error("Error adding new charmap mapping: %s\n", strerror(errno));
 		}
 ;
 
@@ -1075,7 +1078,7 @@
 
 elif		: T_POP_ELIF const {
 			if (nIFDepth <= 0)
-				fatalerror("Found ELIF outside an IF construct");
+				fatalerror("Found ELIF outside an IF construct\n");
 
 			if (skipElif) {
 				/*
@@ -1107,7 +1110,7 @@
 
 else		: T_POP_ELSE {
 			if (nIFDepth <= 0)
-				fatalerror("Found ELSE outside an IF construct");
+				fatalerror("Found ELSE outside an IF construct\n");
 
 			/* Continue parsing at ENDC keyword */
 			if_skip_to_endc();
@@ -1116,7 +1119,7 @@
 
 endc		: T_POP_ENDC {
 			if (nIFDepth <= 0)
-				fatalerror("Found ENDC outside an IF construct");
+				fatalerror("Found ENDC outside an IF construct\n");
 
 			nIFDepth--;
 		}
@@ -1126,7 +1129,7 @@
 			int32_t value = $1;
 
 			if ((value < 0) || (value > 7)) {
-				yyerror("Immediate value must be 3-bit");
+				error("Immediate value must be 3-bit\n");
 				$$ = 0;
 			} else {
 				$$ = value & 0x7;
@@ -1177,7 +1180,7 @@
 reloc_8bit	: relocexpr {
 			if(rpn_isKnown(&$1)
 			 && ($1.nVal < -128 || $1.nVal > 255))
-				warning(WARNING_TRUNCATION, "Expression must be 8-bit");
+				warning(WARNING_TRUNCATION, "Expression must be 8-bit\n");
 			$$ = $1;
 		}
 ;
@@ -1185,7 +1188,7 @@
 reloc_8bit_no_str : relocexpr_no_str {
 			if(rpn_isKnown(&$1)
 			 && ($1.nVal < -128 || $1.nVal > 255))
-				warning(WARNING_TRUNCATION, "Expression must be 8-bit");
+				warning(WARNING_TRUNCATION, "Expression must be 8-bit\n");
 			$$ = $1;
 		}
 ;
@@ -1193,7 +1196,7 @@
 reloc_16bit	: relocexpr {
 			if (rpn_isKnown(&$1)
 			 && ($1.nVal < -32768 || $1.nVal > 65535))
-				warning(WARNING_TRUNCATION, "Expression must be 16-bit");
+				warning(WARNING_TRUNCATION, "Expression must be 16-bit\n");
 			$$ = $1;
 		}
 ;
@@ -1342,7 +1345,7 @@
 uconst		: const {
 			$$ = $1;
 			if ($$ < 0)
-				fatalerror("Constant mustn't be negative: %d",
+				fatalerror("Constant mustn't be negative: %d\n",
 					   $1);
 		}
 ;
@@ -1349,7 +1352,7 @@
 
 const		: relocexpr {
 			if (!rpn_isKnown(&$1)) {
-				yyerror("Expected constant expression: %s",
+				error("Expected constant expression: %s\n",
 					$1.reason);
 				$$ = 0;
 			} else {
@@ -1360,8 +1363,7 @@
 
 string		: T_STRING {
 			if (snprintf($$, MAXSTRLEN + 1, "%s", $1) > MAXSTRLEN)
-				warning(WARNING_LONG_STR, "String is too long '%s'",
-					$1);
+				warning(WARNING_LONG_STR, "String is too long '%s'\n", $1);
 		}
 		| T_OP_STRSUB '(' string ',' uconst ',' uconst ')' {
 			strsubUTF8($$, $3, $5, $7);
@@ -1368,20 +1370,18 @@
 		}
 		| T_OP_STRCAT '(' string ',' string ')' {
 			if (snprintf($$, MAXSTRLEN + 1, "%s%s", $3, $5) > MAXSTRLEN)
-				warning(WARNING_LONG_STR, "STRCAT: String too long '%s%s'",
+				warning(WARNING_LONG_STR, "STRCAT: String too long '%s%s'\n",
 					$3, $5);
 		}
 		| T_OP_STRUPR '(' string ')' {
 			if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN)
-				warning(WARNING_LONG_STR, "STRUPR: String too long '%s'",
-					$3);
+				warning(WARNING_LONG_STR, "STRUPR: String too long '%s'\n", $3);
 
 			upperstring($$);
 		}
 		| T_OP_STRLWR '(' string ')' {
 			if (snprintf($$, MAXSTRLEN + 1, "%s", $3) > MAXSTRLEN)
-				warning(WARNING_LONG_STR, "STRUPR: String too long '%s'",
-					$3);
+				warning(WARNING_LONG_STR, "STRUPR: String too long '%s'\n", $3);
 
 			lowerstring($$);
 		}
@@ -1410,7 +1410,7 @@
 sectorg		: /* empty */ { $$ = -1; }
 		| '[' uconst ']' {
 			if ($2 < 0 || $2 >= 0x10000) {
-				yyerror("Address $%x is not 16-bit", $2);
+				error("Address $%x is not 16-bit\n", $2);
 				$$ = -1;
 			} else {
 				$$ = $2;
@@ -1425,19 +1425,17 @@
 		}
 		| sectattrs ',' T_OP_ALIGN '[' uconst ']' {
 			if ($5 > 16)
-				yyerror("Alignment must be between 0 and 16, not %u",
-					$5);
+				error("Alignment must be between 0 and 16, not %u\n", $5);
 			else
 				$$.alignment = $5;
 		}
 		| sectattrs ',' T_OP_ALIGN '[' uconst ',' uconst ']' {
 			if ($5 > 16) {
-				yyerror("Alignment must be between 0 and 16, not %u",
-					$5);
+				error("Alignment must be between 0 and 16, not %u\n", $5);
 			} else {
 				$$.alignment = $5;
 				if ($7 >= 1 << $$.alignment)
-					yyerror("Alignment offset must not be greater than alignment (%u < %u)",
+					error("Alignment offset must not be greater than alignment (%u < %u)\n",
 						$7, 1 << $$.alignment);
 				else
 					$$.alignOfs = $7;
@@ -1701,7 +1699,7 @@
 		}
 		| T_Z80_LD reg_r ',' reg_r {
 			if (($2 == REG_HL_IND) && ($4 == REG_HL_IND))
-				yyerror("LD [HL],[HL] not a valid instruction");
+				error("LD [HL],[HL] not a valid instruction\n");
 			else
 				out_AbsByte(0x40 | ($2 << 3) | $4);
 		}
@@ -1711,13 +1709,13 @@
 			if ($2 == REG_A)
 				out_AbsByte(0xF2);
 			else
-				yyerror("Destination operand must be A");
+				error("Destination operand must be A\n");
 		}
 		| T_Z80_LD reg_r ',' reg_rr {
 			if ($2 == REG_A)
 				out_AbsByte(0x0A | ($4 << 4));
 			else
-				yyerror("Destination operand must be A");
+				error("Destination operand must be A\n");
 		}
 		| T_Z80_LD reg_r ',' op_mem_ind {
 			if ($2 == REG_A) {
@@ -1731,7 +1729,7 @@
 					out_RelWord(&$4);
 				}
 			} else {
-				yyerror("Destination operand must be A");
+				error("Destination operand must be A\n");
 				rpn_Free(&$4);
 			}
 		}
--- a/src/asm/charmap.c
+++ b/src/asm/charmap.c
@@ -61,19 +61,19 @@
 		base = charmap_Get(baseName);
 
 		if (base == NULL)
-			yyerror("Base charmap '%s' doesn't exist", baseName);
+			error("Base charmap '%s' doesn't exist\n", baseName);
 	}
 
 	struct Charmap *charmap = charmap_Get(name);
 
 	if (charmap != NULL) {
-		yyerror("Charmap '%s' already exists", name);
+		error("Charmap '%s' already exists\n", name);
 		return NULL;
 	}
 
 	charmap = malloc(sizeof(*charmap));
 	if (charmap == NULL)
-		fatalerror("Failed to create charmap: %s", strerror(errno));
+		fatalerror("Failed to create charmap: %s\n", strerror(errno));
 
 	/* Init the new charmap's fields */
 	snprintf(charmap->name, sizeof(charmap->name), "%s", name);
@@ -100,7 +100,7 @@
 	struct Charmap *charmap = charmap_Get(name);
 
 	if (charmap == NULL)
-		yyerror("Charmap '%s' doesn't exist", name);
+		error("Charmap '%s' doesn't exist\n", name);
 	else
 		currentCharmap = charmap;
 }
@@ -111,7 +111,7 @@
 
 	stackEntry = malloc(sizeof(struct CharmapStackEntry));
 	if (stackEntry == NULL)
-		fatalerror("No memory for charmap stack");
+		fatalerror("No memory for charmap stack\n");
 
 	stackEntry->charmap = currentCharmap;
 	stackEntry->next = charmapStack;
@@ -122,7 +122,7 @@
 void charmap_Pop(void)
 {
 	if (charmapStack == NULL)
-		fatalerror("No entries in the charmap stack");
+		fatalerror("No entries in the charmap stack\n");
 
 	struct CharmapStackEntry *top = charmapStack;
 
@@ -182,7 +182,8 @@
 
 	output = malloc(strlen(*input));
 	if (output == NULL)
-		fatalerror("Not enough memory for buffer");
+		fatalerror("Not enough memory for charmap conversion buffer: %s\n",
+			   strerror(errno));
 
 	length = 0;
 
--- a/src/asm/fstack.c
+++ b/src/asm/fstack.c
@@ -70,7 +70,7 @@
 	struct sContext **ppFileStack;
 
 	if (++nFileStackDepth > nMaxRecursionDepth)
-		fatalerror("Recursion limit (%u) exceeded", nMaxRecursionDepth);
+		fatalerror("Recursion limit (%u) exceeded\n", nMaxRecursionDepth);
 
 	ppFileStack = &pFileStack;
 	while (*ppFileStack)
@@ -79,7 +79,7 @@
 	*ppFileStack = malloc(sizeof(struct sContext));
 
 	if (*ppFileStack == NULL)
-		fatalerror("No memory for context");
+		fatalerror("No memory for context\n");
 
 	(*ppFileStack)->FlexHandle = CurrentFlexHandle;
 	(*ppFileStack)->next = NULL;
@@ -104,7 +104,7 @@
 		(*ppFileStack)->nREPTBodyLastLine = nCurrentREPTBodyLastLine;
 		break;
 	default:
-		fatalerror("%s: Internal error.", __func__);
+		fatalerror("%s: Internal error.\n", __func__);
 	}
 	(*ppFileStack)->uniqueID = macro_GetUniqueID();
 
@@ -147,7 +147,7 @@
 				 */
 				sprintf(pREPTIterationWritePtr, "%lu",
 					nREPTIterationNo);
-				fatalerror("Cannot write REPT count to file path");
+				fatalerror("Cannot write REPT count to file path\n");
 			}
 
 			nLineNo = nCurrentREPTBodyFirstLine;
@@ -208,7 +208,7 @@
 		nCurrentREPTBodyFirstLine = pLastFile->nREPTBodyFirstLine;
 		break;
 	default:
-		fatalerror("%s: Internal error.", __func__);
+		fatalerror("%s: Internal error.\n", __func__);
 	}
 	macro_SetUniqueID(pLastFile->uniqueID);
 
@@ -237,7 +237,7 @@
 	case STAT_isREPTBlock:
 		break; /* Peek top file of the stack */
 	default:
-		fatalerror("%s: Internal error.", __func__);
+		fatalerror("%s: Internal error.\n", __func__);
 	}
 
 	pLastFile = pFileStack;
@@ -254,7 +254,7 @@
 	 * This is only reached if the lexer is in REPT or MACRO mode but there
 	 * are no saved contexts with the origin of said REPT or MACRO.
 	 */
-	fatalerror("%s: Internal error.", __func__);
+	fatalerror("%s: Internal error.\n", __func__);
 }
 
 int yywrap(void)
@@ -290,8 +290,7 @@
 		retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ") -> ",
 				   pLastFile->tzFileName, pLastFile->nLine);
 		if (retcode < 0)
-			fatalerror("Failed to dump file stack to string: %s",
-				   strerror(errno));
+			fatalerror("Failed to dump file stack to string: %s\n", strerror(errno));
 		else if (retcode >= len)
 			len = 0;
 		else
@@ -302,8 +301,7 @@
 	retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ")",
 			   tzCurrentFileName, nLineNo);
 	if (retcode < 0)
-		fatalerror("Failed to dump file stack to string: %s",
-			   strerror(errno));
+		fatalerror("Failed to dump file stack to string: %s\n", strerror(errno));
 	else if (retcode >= len)
 		len = 0;
 	else
@@ -310,7 +308,7 @@
 		len -= retcode;
 
 	if (!len)
-		warning(WARNING_LONG_STR, "File stack dump too long, got truncated");
+		warning(WARNING_LONG_STR, "File stack dump too long, got truncated\n");
 }
 
 /*
@@ -333,7 +331,7 @@
 void fstk_AddIncludePath(char *s)
 {
 	if (NextIncPath == MAXINCPATHS)
-		fatalerror("Too many include directories passed from command line");
+		fatalerror("Too many include directories passed from command line\n");
 
 	// Find last occurrence of slash; is it at the end of the string?
 	char const *lastSlash = strrchr(s, '/');
@@ -341,7 +339,7 @@
 
 	if (snprintf(IncludePaths[NextIncPath++], _MAX_PATH, pattern,
 		     s) >= _MAX_PATH)
-		fatalerror("Include path too long '%s'", s);
+		fatalerror("Include path too long '%s'\n", s);
 }
 
 static void printdep(const char *fileName)
@@ -425,8 +423,7 @@
 			oFailedOnMissingInclude = true;
 			return;
 		}
-		yyerror("Unable to open included file '%s': %s", tzFileName,
-			strerror(errno));
+		error("Unable to open included file '%s': %s\n", tzFileName, strerror(errno));
 		return;
 	}
 
@@ -456,11 +453,11 @@
 	int nPrintedChars;
 
 	if (sym == NULL) {
-		yyerror("Macro \"%s\" not defined", s);
+		error("Macro \"%s\" not defined\n", s);
 		return;
 	}
 	if (sym->type != SYM_MACRO) {
-		yyerror("\"%s\" is not a macro", s);
+		error("\"%s\" is not a macro\n", s);
 		return;
 	}
 
@@ -474,7 +471,7 @@
 				 "%s::%s", sym->fileName, s);
 	if (nPrintedChars > _MAX_PATH) {
 		popcontext();
-		fatalerror("File name + macro name is too large to fit into buffer");
+		fatalerror("File name + macro name is too large to fit into buffer\n");
 	}
 
 	pCurrentMacro = sym;
@@ -505,8 +502,7 @@
 		nLineNo = nReptLineNo;
 
 		if (strlen(tzCurrentFileName) + strlen(tzReptStr) > _MAX_PATH)
-			fatalerror("Cannot append \"%s\" to file path",
-				   tzReptStr);
+			fatalerror("Cannot append \"%s\" to file path\n", tzReptStr);
 		strcat(tzCurrentFileName, tzReptStr);
 
 		CurrentFlexHandle =
@@ -530,7 +526,7 @@
 	// minus 2 to account for trailing "\"\0"
 	// minus 1 to avoid a buffer overflow in extreme cases
 	while (*c && fileNameIndex < sizeof(tzSymFileName) - 2 - 1) {
-		
+
 		if (*c == '"') {
 			tzSymFileName[fileNameIndex++] = '\\';
 		}
@@ -550,8 +546,7 @@
 	} else {
 		pCurrentFile = fopen(pFileName, "rb");
 		if (pCurrentFile == NULL)
-			fatalerror("Unable to open file '%s': %s", pFileName,
-				   strerror(errno));
+			fatalerror("Unable to open file '%s': %s\n", pFileName, strerror(errno));
 	}
 	nFileStackDepth = 0;
 
--- a/src/asm/globlex.c
+++ b/src/asm/globlex.c
@@ -110,7 +110,7 @@
 		 * There are no digits after the radix prefix
 		 * (or the string is empty, which shouldn't happen).
 		 */
-		yyerror("Invalid integer constant");
+		error("Invalid integer constant\n");
 	} else if (radix == 4) {
 		int32_t size = 0;
 		int32_t c;
@@ -126,7 +126,7 @@
 		 * the Game Boy tile width, produces a nonsensical result.
 		 */
 		if (size > 8) {
-			warning(WARNING_LARGE_CONSTANT, "Graphics constant '%s' is too long",
+			warning(WARNING_LARGE_CONSTANT, "Graphics constant '%s' is too long\n",
 				start);
 		}
 	} else {
@@ -143,7 +143,7 @@
 		}
 
 		if (overflow)
-			warning(WARNING_LARGE_CONSTANT, "Integer constant '%s' is too large",
+			warning(WARNING_LARGE_CONSTANT, "Integer constant '%s' is too large\n",
 				start);
 	}
 
@@ -176,7 +176,7 @@
 	char dest[256];
 
 	if (size > 255)
-		fatalerror("Number token too long");
+		fatalerror("Number token too long\n");
 
 	strncpy(dest, s, size);
 	dest[size] = 0;
@@ -201,10 +201,10 @@
 	else if (whichArg >= '1' && whichArg <= '9')
 		marg = macro_GetArg(whichArg - '0');
 	else
-		fatalerror("Invalid macro argument '\\%c' in symbol", whichArg);
+		fatalerror("Invalid macro argument '\\%c' in symbol\n", whichArg);
 
 	if (!marg)
-		fatalerror("Macro argument '\\%c' not defined", whichArg);
+		fatalerror("Macro argument '\\%c' not defined\n", whichArg);
 
 	char ch;
 
@@ -217,7 +217,7 @@
 		 || ch == '#'
 		 || ch == '.') {
 			if (*destIndex >= MAXSYMLEN)
-				fatalerror("Symbol too long");
+				fatalerror("Symbol too long\n");
 
 			dest[*destIndex] = ch;
 			(*destIndex)++;
@@ -256,7 +256,7 @@
 				break;
 		} else {
 			if (destIndex >= MAXSYMLEN)
-				fatalerror("Symbol too long");
+				fatalerror("Symbol too long\n");
 			dest[destIndex++] = ch;
 		}
 	}
@@ -311,9 +311,9 @@
 		if (s != NULL)
 			yyunputstr(s);
 		else
-			yyerror("Macro argument '\\%c' not defined", src[1]);
+			error("Macro argument '\\%c' not defined\n", src[1]);
 	} else {
-		yyerror("Invalid macro argument '\\%c'", src[1]);
+		error("Invalid macro argument '\\%c'\n", src[1]);
 	}
 	return 0;
 }
@@ -330,7 +330,7 @@
 	if (s != NULL)
 		yyunputstr(s);
 	else
-		yyerror("Macro unique label string not defined");
+		error("Macro unique label string not defined\n");
 
 	return 0;
 }
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -89,7 +89,7 @@
 void yyunput(char c)
 {
 	if (pLexBuffer <= pLexBufferRealStart)
-		fatalerror("Buffer safety margin exceeded");
+		fatalerror("Buffer safety margin exceeded\n");
 
 	*(--pLexBuffer) = c;
 }
@@ -107,7 +107,7 @@
 	 * Refer to https://github.com/rednex/rgbds/pull/411#discussion_r319779797
 	 */
 	if (pLexBuffer - pLexBufferRealStart < len)
-		fatalerror("Buffer safety margin exceeded");
+		fatalerror("Buffer safety margin exceeded\n");
 
 	pLexBuffer -= len;
 
@@ -121,7 +121,7 @@
 void lex_BeginStringExpansion(const char *tzName)
 {
 	if (++nNbStringExpansions > nMaxRecursionDepth)
-		fatalerror("Recursion limit (%u) exceeded", nMaxRecursionDepth);
+		fatalerror("Recursion limit (%u) exceeded\n", nMaxRecursionDepth);
 
 	struct sStringExpansionPos *pNewStringExpansion =
 		malloc(sizeof(*pNewStringExpansion));
@@ -128,8 +128,7 @@
 	char *tzNewExpansionName = strdup(tzName);
 
 	if (!pNewStringExpansion || !tzNewExpansionName)
-		fatalerror("Could not allocate memory to expand '%s'",
-			   tzName);
+		fatalerror("Could not allocate memory to expand '%s'\n", tzName);
 
 	pNewStringExpansion->tzName = tzNewExpansionName;
 	pNewStringExpansion->pBuffer = pLexBufferRealStart;
@@ -196,7 +195,7 @@
 	YY_BUFFER_STATE pBuffer = malloc(sizeof(struct yy_buffer_state));
 
 	if (pBuffer == NULL)
-		fatalerror("%s: Out of memory!", __func__);
+		fatalerror("%s: Out of memory!\n", __func__);
 
 	size_t capacity = size + 3; /* space for 2 newlines and terminator */
 
@@ -203,7 +202,7 @@
 	pBuffer->pBufferRealStart = malloc(capacity + SAFETYMARGIN);
 
 	if (pBuffer->pBufferRealStart == NULL)
-		fatalerror("%s: Out of memory for buffer!", __func__);
+		fatalerror("%s: Out of memory for buffer!\n", __func__);
 
 	pBuffer->pBufferStart = pBuffer->pBufferRealStart + SAFETYMARGIN;
 	pBuffer->pBuffer = pBuffer->pBufferRealStart + SAFETYMARGIN;
@@ -221,7 +220,7 @@
 	YY_BUFFER_STATE pBuffer = malloc(sizeof(struct yy_buffer_state));
 
 	if (pBuffer == NULL)
-		fatalerror("%s: Out of memory!", __func__);
+		fatalerror("%s: Out of memory!\n", __func__);
 
 	size_t size = 0, capacity = -1;
 	char *buf = NULL;
@@ -263,7 +262,7 @@
 			buf = realloc(buf, capacity + SAFETYMARGIN + 3);
 
 			if (buf == NULL)
-				fatalerror("%s: Out of memory for buffer!",
+				fatalerror("%s: Out of memory for buffer!\n",
 					   __func__);
 		}
 
@@ -271,7 +270,7 @@
 		size_t read_count = fread(bufpos, 1, capacity - size, f);
 
 		if (read_count == 0 && !feof(f))
-			fatalerror("%s: fread error", __func__);
+			fatalerror("%s: fread error\n", __func__);
 
 		size += read_count;
 	} while (!feof(f));
@@ -320,7 +319,7 @@
 
 	if (mem != pBuffer->pBuffer + size) {
 		nLineNo = lineCount + 1;
-		fatalerror("Found null character");
+		fatalerror("Found null character\n");
 	}
 
 	/* Remove comments */
@@ -343,7 +342,8 @@
 					*mem++ = ' ';
 			/* Comments that start with * at the start of a line */
 			} else if ((mem[0] == '\n') && (mem[1] == '*')) {
-				warning(WARNING_OBSOLETE, "'*' is deprecated for comments, please use ';' instead");
+				warning(WARNING_OBSOLETE,
+					"'*' is deprecated for comments, please use ';' instead\n");
 				mem++;
 				while (!((*mem == '\n') || (*mem == '\0')))
 					*mem++ = ' ';
@@ -372,7 +372,7 @@
 bool lex_CheckCharacterRange(uint16_t start, uint16_t end)
 {
 	if (start > end || start < 1 || end > 127) {
-		yyerror("Invalid character range (start: %" PRIu16 ", end: %" PRIu16 ")",
+		error("Invalid character range (start: %" PRIu16 ", end: %" PRIu16 ")\n",
 			start, end);
 		return false;
 	}
@@ -442,7 +442,7 @@
 static struct sLexFloat *lexgetfloat(uint32_t nFloatMask)
 {
 	if (nFloatMask == 0)
-		fatalerror("Internal error in %s", __func__);
+		fatalerror("Internal error in %s\n", __func__);
 
 	int32_t i = 0;
 
@@ -496,11 +496,11 @@
 
 		*ppHash = malloc(sizeof(struct sLexString));
 		if (*ppHash == NULL)
-			fatalerror("Out of memory!");
+			fatalerror("Out of memory!\n");
 
 		(*ppHash)->tzName = (char *)strdup(lex->tzName);
 		if ((*ppHash)->tzName == NULL)
-			fatalerror("Out of memory!");
+			fatalerror("Out of memory!\n");
 
 		(*ppHash)->nNameLength = strlen(lex->tzName);
 		(*ppHash)->nToken = lex->nToken;
@@ -595,12 +595,12 @@
 		return 0;
 
 	if (s == NULL)
-		fatalerror("Macro argument '\\%c' not defined", c);
+		fatalerror("Macro argument '\\%c' not defined\n", c);
 
 	// TODO: `strncpy`, nay?
 	for (i = 0; s[i] != 0; i++) {
 		if (i >= maxLength)
-			fatalerror("Macro argument too long to fit buffer");
+			fatalerror("Macro argument too long to fit buffer\n");
 
 		dest[i] = s[i];
 	}
@@ -611,7 +611,7 @@
 static inline void yylex_StringWriteChar(char *s, size_t index, char c)
 {
 	if (index >= MAXSTRLEN)
-		fatalerror("String too long");
+		fatalerror("String too long\n");
 
 	s[index] = c;
 }
@@ -619,7 +619,7 @@
 static inline void yylex_SymbolWriteChar(char *s, size_t index, char c)
 {
 	if (index >= MAXSYMLEN)
-		fatalerror("Symbol too long");
+		fatalerror("Symbol too long\n");
 
 	s[index] = c;
 }
@@ -657,7 +657,7 @@
 			if (length != 0)
 				i += length;
 			else
-				fatalerror("Illegal character escape '%c'", ch);
+				fatalerror("Illegal character escape '%c'\n", ch);
 		} else if (ch == '{') {
 			/* Handle nested symbols */
 			++pLexBuffer;
@@ -679,12 +679,11 @@
 			const char *designatedMode;
 
 			if (i != 1)
-				fatalerror("Print types are exactly 1 character long");
+				fatalerror("Print types are exactly 1 character long\n");
 
 			designatedMode = strchr(acceptedModes, sym[i - 1]);
 			if (!designatedMode)
-				fatalerror("Illegal print type '%c'",
-					   sym[i - 1]);
+				fatalerror("Illegal print type '%c'\n", sym[i - 1]);
 			mode = formatSpecifiers[designatedMode - acceptedModes];
 			/* Begin writing the symbol again */
 			i = 0;
@@ -703,7 +702,7 @@
 	if (*pLexBuffer == '}')
 		pLexBuffer++;
 	else
-		fatalerror("Missing }");
+		fatalerror("Missing }\n");
 
 	return length;
 }
@@ -752,8 +751,7 @@
 				if (length != 0)
 					index += length;
 				else
-					fatalerror("Illegal character escape '%c'",
-						   ch);
+					fatalerror("Illegal character escape '%c'\n", ch);
 
 				ch = 0;
 				break;
@@ -774,7 +772,7 @@
 	if (*pLexBuffer == '"')
 		pLexBuffer++;
 	else
-		fatalerror("Unterminated string");
+		fatalerror("Unterminated string\n");
 }
 
 static uint32_t yylex_NORMAL(void)
@@ -818,7 +816,7 @@
 					nLineNo++;
 					goto scanagain;
 				} else {
-					yyerror("Expected a new line after the continuation character.");
+					error("Expected a new line after the continuation character.\n");
 					pLexBuffer++;
 				}
 			}
@@ -883,7 +881,7 @@
 		if (ch != 0
 		 && ch != '\n'
 		 && !(ch >= 0x20 && ch <= 0x7E))
-			fatalerror("Found garbage character: 0x%02X", ch);
+			fatalerror("Found garbage character: 0x%02X\n", ch);
 
 		return ch;
 	}
@@ -973,7 +971,7 @@
 						ch = 0;
 						break;
 					} else {
-						yyerror("Expected a new line after the continuation character.");
+						error("Expected a new line after the continuation character.\n");
 					}
 				}
 				break;
@@ -990,8 +988,7 @@
 				if (length != 0)
 					index += length;
 				else
-					fatalerror("Illegal character escape '%c'",
-						   ch);
+					fatalerror("Illegal character escape '%c'\n", ch);
 
 				ch = 0;
 				break;
@@ -1022,7 +1019,7 @@
 		return ',';
 	}
 
-	fatalerror("Internal error in %s", __func__);
+	fatalerror("Internal error in %s\n", __func__);
 }
 
 int yylex(void)
@@ -1037,7 +1034,7 @@
 		returnedChar = yylex_MACROARGS();
 		break;
 	default:
-		fatalerror("%s: Internal error.", __func__);
+		fatalerror("%s: Internal error.\n", __func__);
 	}
 
 	/* Check if string expansions were fully read */
--- a/src/asm/macro.c
+++ b/src/asm/macro.c
@@ -48,7 +48,7 @@
 	struct MacroArgs *args = malloc(SIZEOF_ARGS(INITIAL_ARG_SIZE));
 
 	if (!args)
-		fatalerror("Unable to register macro arguments: %s", strerror(errno));
+		fatalerror("Unable to register macro arguments: %s\n", strerror(errno));
 
 	args->nbArgs = 0;
 	args->shift = 0;
@@ -60,16 +60,16 @@
 {
 #define macArgs (*argPtr)
 	if (macArgs->nbArgs == MAXMACROARGS)
-		yyerror("A maximum of " EXPAND_AND_STR(MAXMACROARGS)
-			" arguments is allowed");
+		error("A maximum of " EXPAND_AND_STR(MAXMACROARGS)
+			" arguments is allowed\n");
 	if (macArgs->nbArgs >= macArgs->capacity) {
 		macArgs->capacity *= 2;
 		/* Check that overflow didn't roll us back */
 		if (macArgs->capacity <= macArgs->nbArgs)
-			fatalerror("Failed to add new macro argument: possible capacity overflow");
+			fatalerror("Failed to add new macro argument: possible capacity overflow\n");
 		macArgs = realloc(macArgs, SIZEOF_ARGS(macArgs->capacity));
 		if (!macArgs)
-			fatalerror("Error adding new macro argument: %s", strerror(errno));
+			fatalerror("Error adding new macro argument: %s\n", strerror(errno));
 	}
 	macArgs->args[macArgs->nbArgs++] = s;
 #undef macArgs
--- a/src/asm/main.c
+++ b/src/asm/main.c
@@ -150,7 +150,7 @@
 			newopt.gbgfx[2] = s[3];
 			newopt.gbgfx[3] = s[4];
 		} else {
-			yyerror("Must specify exactly 4 characters for option 'g'");
+			error("Must specify exactly 4 characters for option 'g'\n");
 		}
 		break;
 	case 'b':
@@ -158,11 +158,11 @@
 			newopt.binary[0] = s[1];
 			newopt.binary[1] = s[2];
 		} else {
-			yyerror("Must specify exactly 2 characters for option 'b'");
+			error("Must specify exactly 2 characters for option 'b'\n");
 		}
 		break;
 	case 'z':
-		warning(WARNING_OBSOLETE, "Option 'z' is a deprecated alias for 'p'");
+		warning(WARNING_OBSOLETE, "Option 'z' is a deprecated alias for 'p'\n");
 		/* fallthrough */
 	case 'p':
 		if (strlen(&s[1]) <= 2) {
@@ -171,15 +171,15 @@
 
 			result = sscanf(&s[1], "%x", &fillchar);
 			if (result != EOF && result != 1)
-				yyerror("Invalid argument for option 'z'");
+				error("Invalid argument for option 'z'\n");
 			else
 				newopt.fillchar = fillchar;
 		} else {
-			yyerror("Invalid argument for option 'z'");
+			error("Invalid argument for option 'z'\n");
 		}
 		break;
 	default:
-		yyerror("Unknown option");
+		error("Unknown option\n");
 		break;
 	}
 
@@ -193,7 +193,7 @@
 	pOpt = malloc(sizeof(struct sOptionStackEntry));
 
 	if (pOpt == NULL)
-		fatalerror("No memory for option stack");
+		fatalerror("No memory for option stack\n");
 
 	pOpt->Options = CurrentOptions;
 	pOpt->next = pOptionStack;
@@ -203,7 +203,7 @@
 void opt_Pop(void)
 {
 	if (pOptionStack == NULL)
-		fatalerror("No entries in the option stack");
+		fatalerror("No entries in the option stack\n");
 
 	struct sOptionStackEntry *pOpt;
 
@@ -220,10 +220,10 @@
 	if (cldefines_index >= cldefines_numindices) {
 		/* Check for overflows */
 		if ((cldefines_numindices * 2) < cldefines_numindices)
-			fatalerror("No memory for command line defines");
+			fatalerror("No memory for command line defines\n");
 
 		if ((cldefines_bufsize * 2) < cldefines_bufsize)
-			fatalerror("No memory for command line defines");
+			fatalerror("No memory for command line defines\n");
 
 		cldefines_numindices *= 2;
 		cldefines_bufsize *= 2;
@@ -230,7 +230,7 @@
 
 		cldefines = realloc(cldefines, cldefines_bufsize);
 		if (!cldefines)
-			fatalerror("No memory for command line defines");
+			fatalerror("No memory for command line defines\n");
 	}
 	equals = strchr(s, '=');
 	if (equals) {
@@ -344,7 +344,7 @@
 	cldefines_bufsize = cldefines_numindices * cldefine_entrysize;
 	cldefines = malloc(cldefines_bufsize);
 	if (!cldefines)
-		fatalerror("No memory for command line defines");
+		fatalerror("No memory for command line defines\n");
 
 #if defined(YYDEBUG) && YYDEBUG
 	yydebug = 1;
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -146,7 +146,7 @@
 		sec = sec->next;
 	}
 
-	fatalerror("Unknown section '%s'", sect->name);
+	fatalerror("Unknown section '%s'\n", sect->name);
 }
 
 static uint32_t getSectIDIfAny(struct Section const *sect)
@@ -311,12 +311,11 @@
 	uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize;
 
 	if (!patch)
-		fatalerror("No memory for patch: %s", strerror(errno));
+		fatalerror("No memory for patch: %s\n", strerror(errno));
 	patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
 
 	if (!patch->pRPN)
-		fatalerror("No memory for patch's RPN expression: %s",
-			   strerror(errno));
+		fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
 
 	patch->type = type;
 	fstk_DumpToStr(patch->tzFilename, sizeof(patch->tzFilename));
--- a/src/asm/rpn.c
+++ b/src/asm/rpn.c
@@ -31,7 +31,7 @@
 	/* If we had `asprintf` this would be great, but alas. */ \
 	_expr->reason = malloc(128); /* Use an initial reasonable size */ \
 	if (!_expr->reason) \
-		fatalerror("Can't allocate err string: %s", strerror(errno)); \
+		fatalerror("Can't allocate err string: %s\n", strerror(errno)); \
 	int size = snprintf(_expr->reason, 128, __VA_ARGS__); \
 	if (size >= 128) { /* If this wasn't enough, try again */ \
 		_expr->reason = realloc(_expr->reason, size + 1); \
@@ -51,8 +51,8 @@
 			 * To avoid generating humongous object files, cap the
 			 * size of RPN expressions
 			 */
-			fatalerror("RPN expression cannot grow larger than %lu bytes",
-				   (unsigned long)MAXRPNLEN);
+			fatalerror("RPN expression cannot grow larger than "
+				   EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
 		else if (expr->nRPNCapacity > MAXRPNLEN / 2)
 			expr->nRPNCapacity = MAXRPNLEN;
 		else
@@ -60,8 +60,7 @@
 		expr->tRPN = realloc(expr->tRPN, expr->nRPNCapacity);
 
 		if (!expr->tRPN)
-			fatalerror("Failed to grow RPN expression: %s",
-				   strerror(errno));
+			fatalerror("Failed to grow RPN expression: %s\n", strerror(errno));
 	}
 
 	uint8_t *ptr = expr->tRPN + expr->nRPNLength;
@@ -108,7 +107,7 @@
 	struct Symbol *sym = sym_FindSymbol(tzSym);
 
 	if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
-		yyerror("PC has no value outside a section");
+		error("PC has no value outside a section\n");
 		rpn_Number(expr, 0);
 	} else if (!sym || !sym_IsConstant(sym)) {
 		rpn_Init(expr);
@@ -115,10 +114,8 @@
 		expr->isSymbol = true;
 
 		sym_Ref(tzSym);
-		makeUnknown(expr, sym_IsPC(sym)
-				      ? "PC is not constant at assembly time"
-				      : "'%s' is not constant at assembly time",
-			    tzSym);
+		makeUnknown(expr, sym_IsPC(sym) ? "PC is not constant at assembly time"
+						: "'%s' is not constant at assembly time", tzSym);
 		expr->nRPNPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
 
 		size_t nameLen = strlen(tzSym) + 1; /* Don't forget NUL! */
@@ -145,7 +142,7 @@
 	rpn_Init(expr);
 
 	if (!pCurrentSection) {
-		yyerror("PC has no bank outside a section");
+		error("PC has no bank outside a section\n");
 		expr->nVal = 1;
 	} else if (pCurrentSection->bank == -1) {
 		makeUnknown(expr, "Current section's bank is not known");
@@ -168,7 +165,7 @@
 
 	rpn_Init(expr);
 	if (sym && !sym_IsLabel(sym)) {
-		yyerror("BANK argument must be a label");
+		error("BANK argument must be a label\n");
 	} else {
 		sym_Ref(tzSym);
 		if (!sym)
@@ -223,8 +220,7 @@
 		/* That range is valid, but only keep the lower byte */
 		expr->nVal &= 0xFF;
 	} else if (expr->nVal < 0 || expr->nVal > 0xFF) {
-		yyerror("Source address $%" PRIx32 " not between $FF00 to $FFFF",
-			expr->nVal);
+		error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", expr->nVal);
 	}
 }
 
@@ -235,8 +231,7 @@
 	if (rpn_isKnown(expr)) {
 		/* A valid RST address must be masked with 0x38 */
 		if (expr->nVal & ~0x38)
-			yyerror("Invalid address $%" PRIx32 " for RST",
-				expr->nVal);
+			error("Invalid address $%" PRIx32 " for RST\n", expr->nVal);
 		/* The target is in the "0x38" bits, all other bits are set */
 		expr->nVal |= 0xC7;
 	} else {
@@ -263,7 +258,7 @@
 	if (amount >= 0) {
 		// Left shift
 		if (amount >= 32) {
-			warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32,
+			warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32 "\n",
 				amount);
 			return 0;
 
@@ -279,8 +274,8 @@
 		// Right shift
 		amount = -amount;
 		if (amount >= 32) {
-			warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32,
-				amount);
+			warning(WARNING_SHIFT_AMOUNT,
+				"Shifting right by large amount %" PRId32 "\n", amount);
 			return shiftee < 0 ? -1 : 0;
 
 		} else if (shiftee >= 0) {
@@ -376,7 +371,8 @@
 			break;
 		case RPN_SHL:
 			if (src2->nVal < 0)
-				warning(WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32,
+				warning(WARNING_SHIFT_AMOUNT,
+					"Shifting left by negative amount %" PRId32 "\n",
 					src2->nVal);
 
 			expr->nVal = shift(src1->nVal, src2->nVal);
@@ -383,11 +379,12 @@
 			break;
 		case RPN_SHR:
 			if (src1->nVal < 0)
-				warning(WARNING_SHIFT, "Shifting negative value %" PRId32,
+				warning(WARNING_SHIFT, "Shifting negative value %" PRId32 "\n",
 					src1->nVal);
 
 			if (src2->nVal < 0)
-				warning(WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32,
+				warning(WARNING_SHIFT_AMOUNT,
+					"Shifting right by negative amount %" PRId32 "\n",
 					src2->nVal);
 
 			expr->nVal = shift(src1->nVal, -src2->nVal);
@@ -397,11 +394,11 @@
 			break;
 		case RPN_DIV:
 			if (src2->nVal == 0)
-				fatalerror("Division by zero");
+				fatalerror("Division by zero\n");
 
-			if (src1->nVal == INT32_MIN
-			 && src2->nVal == -1) {
-				warning(WARNING_DIV, "Division of min value by -1");
+			if (src1->nVal == INT32_MIN && src2->nVal == -1) {
+				warning(WARNING_DIV, "Division of %" PRId32 " by -1 yields %"
+					PRId32 "\n", INT32_MIN, INT32_MIN);
 				expr->nVal = INT32_MIN;
 			} else {
 				expr->nVal = src1->nVal / src2->nVal;
@@ -409,7 +406,7 @@
 			break;
 		case RPN_MOD:
 			if (src2->nVal == 0)
-				fatalerror("Division by zero");
+				fatalerror("Division by zero\n");
 
 			if (src1->nVal == INT32_MIN && src2->nVal == -1)
 				expr->nVal = 0;
@@ -427,7 +424,7 @@
 		case RPN_RST:
 		case RPN_CONST:
 		case RPN_SYM:
-			fatalerror("%d is not a binary operator", op);
+			fatalerror("%d is not a binary operator\n", op);
 		}
 
 	} else if (op == RPN_SUB && isDiffConstant(src1, src2)) {
--- a/src/asm/section.c
+++ b/src/asm/section.c
@@ -40,7 +40,7 @@
 static inline void checksection(void)
 {
 	if (pCurrentSection == NULL)
-		fatalerror("Code generation before SECTION directive");
+		fatalerror("Code generation before SECTION directive\n");
 }
 
 /*
@@ -52,7 +52,7 @@
 	checksection();
 
 	if (!sect_HasData(pCurrentSection->type))
-		fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)",
+		fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
 			   pCurrentSection->name);
 }
 
@@ -61,8 +61,8 @@
 	uint32_t maxSize = maxsize[sect->type];
 
 	if (size > maxSize)
-		fatalerror("Section '%s' grew too big (max size = 0x%" PRIX32 " bytes, reached 0x%" PRIX32 ").",
-			   sect->name, maxSize, size);
+		fatalerror("Section '%s' grew too big (max size = 0x%" PRIX32
+			   " bytes, reached 0x%" PRIX32 ").\n", sect->name, maxSize, size);
 }
 
 /*
@@ -110,16 +110,16 @@
 	if (bank != -1) {
 		if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM
 		 && type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX)
-			yyerror("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections");
+			error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n");
 		else if (bank < bankranges[type][0]
 		      || bank > bankranges[type][1])
-			yyerror("%s bank value $%" PRIx32 " out of range ($%" PRIx32 " to $%" PRIx32 ")",
-				typeNames[type], bank,
+			error("%s bank value $%" PRIx32 " out of range ($%" PRIx32 " to $%"
+				PRIx32 ")\n", typeNames[type], bank,
 				bankranges[type][0], bankranges[type][1]);
 	}
 
 	if (alignOffset >= 1 << alignment) {
-		yyerror("Alignment offset must not be greater than alignment (%" PRIu16 " < %u)",
+		error("Alignment offset must not be greater than alignment (%" PRIu16 " < %u)\n",
 			alignOffset, 1U << alignment);
 		alignOffset = 0;
 	}
@@ -130,11 +130,11 @@
 
 		if (org != -1) {
 			if ((org - alignOffset) & mask)
-				yyerror("Section \"%s\"'s fixed address doesn't match its alignment",
+				error("Section \"%s\"'s fixed address doesn't match its alignment\n",
 					name);
 			alignment = 0; /* Ignore it if it's satisfied */
 		} else if (startaddr[type] & mask) {
-			yyerror("Section \"%s\"'s alignment cannot be attained in %s",
+			error("Section \"%s\"'s alignment cannot be attained in %s\n",
 				name, typeNames[type]);
 		}
 	}
@@ -141,7 +141,8 @@
 
 	if (org != -1) {
 		if (org < startaddr[type] || org > endaddr(type))
-			yyerror("Section \"%s\"'s fixed address %#" PRIx32 " is outside of range [%#" PRIx16 "; %#" PRIx16 "]",
+			error("Section \"%s\"'s fixed address %#" PRIx32
+				" is outside of range [%#" PRIx16 "; %#" PRIx16 "]\n",
 				name, org, startaddr[type], endaddr(type));
 	}
 
@@ -154,16 +155,16 @@
 		unsigned int nbSectErrors = 0;
 #define fail(...) \
 	do { \
-		yyerror(__VA_ARGS__); \
+		error(__VA_ARGS__); \
 		nbSectErrors++; \
 	} while (0)
 
 		if (type != sect->type)
-			fail("Section \"%s\" already exists but with type %s",
+			fail("Section \"%s\" already exists but with type %s\n",
 			     sect->name, typeNames[sect->type]);
 
 		if (sect->modifier != mod)
-			fail("Section \"%s\" already declared as %s section",
+			fail("Section \"%s\" already declared as %s section\n",
 			     sect->name, sectionModNames[sect->modifier]);
 		/*
 		 * Normal sections need to have exactly identical constraints;
@@ -176,18 +177,18 @@
 			 * `EndLoadSection` if modifying the following check!
 			 */
 			if (sect_HasData(type))
-				fail("Cannot declare ROM sections as UNION");
+				fail("Cannot declare ROM sections as UNION\n");
 			if (org != -1) {
 				/* If both are fixed, they must be the same */
 				if (sect->org != -1 && sect->org != org)
-					fail("Section \"%s\" already declared as fixed at different address $%" PRIx32,
+					fail("Section \"%s\" already declared as fixed at different address $%"
+					     PRIx32 "\n",
 					     sect->name, sect->org);
 				else if (sect->align != 0
 				      && (mask(sect->align)
 						& (org - sect->alignOfs)))
-					fail("Section \"%s\" already declared as aligned to %u bytes (offset %" PRIu16 ")",
-					     sect->name, 1U << sect->align,
-					     sect->alignOfs);
+					fail("Section \"%s\" already declared as aligned to %u bytes (offset %"
+					     PRIu16 ")\n", sect->name, 1U << sect->align, sect->alignOfs);
 				else
 					/* Otherwise, just override */
 					sect->org = org;
@@ -196,16 +197,15 @@
 				if (sect->org != -1) {
 					if ((sect->org - alignOffset)
 							& mask(alignment))
-						fail("Section \"%s\" already declared as fixed at incompatible address $%" PRIx32,
-						     sect->name,
-						     sect->org);
+						fail("Section \"%s\" already declared as fixed at incompatible address $%"
+						     PRIx32 "\n", sect->name, sect->org);
 				/* Check if alignment offsets are compatible */
 				} else if ((alignOffset & mask(sect->align))
 					!= (sect->alignOfs
 							& mask(alignment))) {
-					fail("Section \"%s\" already declared with incompatible %" PRIu8 "-byte alignment (offset %" PRIu16 ")",
-					     sect->name, sect->align,
-					     sect->alignOfs);
+					fail("Section \"%s\" already declared with incompatible %"
+					     PRIu8 "-byte alignment (offset %" PRIu16 ")\n",
+					     sect->name, sect->align, sect->alignOfs);
 				} else if (alignment > sect->align) {
 					/*
 					 * If the section is not fixed,
@@ -220,42 +220,42 @@
 				sect->bank = bank;
 			/* If both specify a bank, it must be the same one */
 			else if (bank != -1 && sect->bank != bank)
-				fail("Section \"%s\" already declared with different bank %" PRIu32,
-				     sect->name, sect->bank);
+				fail("Section \"%s\" already declared with different bank %"
+				     PRIu32 "\n", sect->name, sect->bank);
 		} else { /* Section fragments are handled identically in RGBASM */
 			/* However, concaternating non-fragments will be made an error */
 			if (sect->modifier != SECTION_FRAGMENT || mod != SECTION_FRAGMENT)
-				warning(WARNING_OBSOLETE, "Concatenation of non-fragment sections is deprecated");
+				warning(WARNING_OBSOLETE,
+					"Concatenation of non-fragment sections is deprecated\n");
 
 			if (org != sect->org) {
 				if (sect->org == -1)
-					fail("Section \"%s\" already declared as floating",
+					fail("Section \"%s\" already declared as floating\n",
 					     sect->name);
 				else
-					fail("Section \"%s\" already declared as fixed at $%" PRIx32,
-					     sect->name, sect->org);
+					fail("Section \"%s\" already declared as fixed at $%"
+					     PRIx32 "\n", sect->name, sect->org);
 			}
 			if (bank != sect->bank) {
 				if (sect->bank == -1)
-					fail("Section \"%s\" already declared as floating bank",
+					fail("Section \"%s\" already declared as floating bank\n",
 					     sect->name);
 				else
-					fail("Section \"%s\" already declared as fixed at bank %" PRIu32,
-					     sect->name, sect->bank);
+					fail("Section \"%s\" already declared as fixed at bank %"
+					     PRIu32 "\n", sect->name, sect->bank);
 			}
 			if (alignment != sect->align) {
 				if (sect->align == 0)
-					fail("Section \"%s\" already declared as unaligned",
+					fail("Section \"%s\" already declared as unaligned\n",
 					     sect->name);
 				else
-					fail("Section \"%s\" already declared as aligned to %u bytes",
-					     sect->name,
-					     1U << sect->align);
+					fail("Section \"%s\" already declared as aligned to %u bytes\n",
+					     sect->name, 1U << sect->align);
 			}
 		}
 
 		if (nbSectErrors)
-			fatalerror("Cannot create section \"%s\" (%u errors)",
+			fatalerror("Cannot create section \"%s\" (%u errors)\n",
 				   sect->name, nbSectErrors);
 #undef fail
 		return sect;
@@ -263,11 +263,11 @@
 
 	sect = malloc(sizeof(*sect));
 	if (sect == NULL)
-		fatalerror("Not enough memory for section");
+		fatalerror("Not enough memory for section: %s\n", strerror(errno));
 
 	sect->name = strdup(name);
 	if (sect->name == NULL)
-		fatalerror("Not enough memory for sectionname");
+		fatalerror("Not enough memory for section name: %s\n", strerror(errno));
 
 	sect->type = type;
 	sect->modifier = mod;
@@ -286,7 +286,7 @@
 		sectsize = maxsize[type];
 		sect->data = malloc(sectsize);
 		if (sect->data == NULL)
-			fatalerror("Not enough memory for section");
+			fatalerror("Not enough memory for section: %s\n", strerror(errno));
 	} else {
 		sect->data = NULL;
 	}
@@ -307,7 +307,7 @@
 static void changeSection(void)
 {
 	if (unionStack)
-		fatalerror("Cannot change the section within a UNION");
+		fatalerror("Cannot change the section within a UNION\n");
 
 	sym_SetCurrentSymbolScope(NULL);
 }
@@ -319,7 +319,7 @@
 		    struct SectionSpec const *attribs, enum SectionModifier mod)
 {
 	if (currentLoadSection)
-		fatalerror("Cannot change the section within a `LOAD` block");
+		fatalerror("Cannot change the section within a `LOAD` block\n");
 
 	struct Section *sect = getSection(name, type, org, attribs, mod);
 
@@ -337,7 +337,7 @@
 	checkcodesection();
 
 	if (currentLoadSection)
-		fatalerror("`LOAD` blocks cannot be nested");
+		fatalerror("`LOAD` blocks cannot be nested\n");
 
 	struct Section *sect = getSection(name, type, org, attribs, false);
 
@@ -350,7 +350,7 @@
 void out_EndLoadSection(void)
 {
 	if (!currentLoadSection)
-		yyerror("Found `ENDL` outside of a `LOAD` block");
+		error("Found `ENDL` outside of a `LOAD` block\n");
 	currentLoadSection = NULL;
 
 	changeSection();
@@ -382,13 +382,13 @@
 
 	if (sect->org != -1) {
 		if ((sym_GetPCValue() - offset) % (1 << alignment))
-			yyerror("Section's fixed address fails required alignment (PC = $%04" PRIx32 ")",
-				sym_GetPCValue());
+			error("Section's fixed address fails required alignment (PC = $%04"
+				PRIx32 ")\n", sym_GetPCValue());
 	} else if (sect->align != 0) {
 		if ((((sect->alignOfs + curOffset) % (1 << sect->align))
 						- offset) % (1 << alignment)) {
-			yyerror("Section's alignment fails required alignment (offset from section start = $%04" PRIx32 ")",
-				curOffset);
+			error("Section's alignment fails required alignment (offset from section start = $%04"
+				PRIx32 ")\n", curOffset);
 		} else if (alignment > sect->align) {
 			sect->align = alignment;
 			sect->alignOfs =
@@ -438,13 +438,13 @@
 void sect_StartUnion(void)
 {
 	if (!pCurrentSection)
-		fatalerror("UNIONs must be inside a SECTION");
+		fatalerror("UNIONs must be inside a SECTION\n");
 	if (sect_HasData(pCurrentSection->type))
-		fatalerror("Cannot use UNION inside of ROM0 or ROMX sections");
+		fatalerror("Cannot use UNION inside of ROM0 or ROMX sections\n");
 	struct UnionStackEntry *entry = malloc(sizeof(*entry));
 
 	if (!entry)
-		fatalerror("Failed to allocate new union stack entry: %s", strerror(errno));
+		fatalerror("Failed to allocate new union stack entry: %s\n", strerror(errno));
 	entry->start = curOffset;
 	entry->size = 0;
 	entry->next = unionStack;
@@ -463,7 +463,7 @@
 void sect_NextUnionMember(void)
 {
 	if (!unionStack)
-		fatalerror("Found NEXTU outside of a UNION construct");
+		fatalerror("Found NEXTU outside of a UNION construct\n");
 	endUnionMember();
 }
 
@@ -470,7 +470,7 @@
 void sect_EndUnion(void)
 {
 	if (!unionStack)
-		fatalerror("Found ENDU outside of a UNION construct");
+		fatalerror("Found ENDU outside of a UNION construct\n");
 	endUnionMember();
 	curOffset += unionStack->size;
 	struct UnionStackEntry *next = unionStack->next;
@@ -482,7 +482,7 @@
 void sect_CheckUnionClosed(void)
 {
 	if (unionStack)
-		fatalerror("Unterminated UNION construct!");
+		fatalerror("Unterminated UNION construct!\n");
 }
 
 /*
@@ -514,7 +514,7 @@
 	reserveSpace(skip);
 
 	if (!ds && sect_HasData(pCurrentSection->type))
-		warning(WARNING_EMPTY_DATA_DIRECTIVE, "db/dw/dl directive without data in ROM");
+		warning(WARNING_EMPTY_DATA_DIRECTIVE, "db/dw/dl directive without data in ROM\n");
 
 	if (!sect_HasData(pCurrentSection->type)) {
 		growSection(skip);
@@ -636,7 +636,7 @@
 			offset = sym_GetValue(sym) - (sym_GetValue(pc) + 1);
 
 		if (offset < -128 || offset > 127) {
-			yyerror("jr target out of reach (expected -129 < %" PRId16 " < 128)",
+			error("jr target out of reach (expected -129 < %" PRId16 " < 128)\n",
 				offset);
 			writebyte(0);
 		} else {
@@ -652,8 +652,7 @@
 void out_BinaryFile(char const *s, int32_t startPos)
 {
 	if (startPos < 0) {
-		yyerror("Start position cannot be negative (%" PRId32 ")",
-			startPos);
+		error("Start position cannot be negative (%" PRId32 ")\n", startPos);
 		startPos = 0;
 	}
 
@@ -664,8 +663,7 @@
 			oFailedOnMissingInclude = true;
 			return;
 		}
-		fatalerror("Error opening INCBIN file '%s': %s", s,
-			   strerror(errno));
+		fatalerror("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
 	}
 
 	int32_t fsize = -1;
@@ -676,7 +674,7 @@
 		fsize = ftell(f);
 
 		if (startPos >= fsize) {
-			yyerror("Specified start position is greater than length of file");
+			error("Specified start position is greater than length of file\n");
 			return;
 		}
 
@@ -684,7 +682,7 @@
 		reserveSpace(fsize - startPos);
 	} else {
 		if (errno != ESPIPE)
-			yyerror("Error determining size of INCBIN file '%s': %s",
+			error("Error determining size of INCBIN file '%s': %s\n",
 				s, strerror(errno));
 		/* The file isn't seekable, so we'll just skip bytes */
 		while (startPos--)
@@ -698,8 +696,7 @@
 	}
 
 	if (ferror(f))
-		yyerror("Error reading INCBIN file '%s': %s", s,
-			strerror(errno));
+		error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
 
 	fclose(f);
 }
@@ -707,14 +704,12 @@
 void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
 {
 	if (start_pos < 0) {
-		yyerror("Start position cannot be negative (%" PRId32 ")",
-			start_pos);
+		error("Start position cannot be negative (%" PRId32 ")\n", start_pos);
 		start_pos = 0;
 	}
 
 	if (length < 0) {
-		yyerror("Number of bytes to read cannot be negative (%" PRId32 ")",
-			length);
+		error("Number of bytes to read cannot be negative (%" PRId32 ")\n", length);
 		length = 0;
 	}
 	if (length == 0) /* Don't even bother with 0-byte slices */
@@ -727,8 +722,7 @@
 			oFailedOnMissingInclude = true;
 			return;
 		}
-		fatalerror("Error opening INCBIN file '%s': %s", s,
-			   strerror(errno));
+		fatalerror("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
 	}
 
 	checkcodesection();
@@ -740,17 +734,17 @@
 		fsize = ftell(f);
 
 		if (start_pos >= fsize) {
-			yyerror("Specified start position is greater than length of file");
+			error("Specified start position is greater than length of file\n");
 			return;
 		}
 
 		if ((start_pos + length) > fsize)
-			fatalerror("Specified range in INCBIN is out of bounds");
+			fatalerror("Specified range in INCBIN is out of bounds\n");
 
 		fseek(f, start_pos, SEEK_SET);
 	} else {
 		if (errno != ESPIPE)
-			yyerror("Error determining size of INCBIN file '%s': %s",
+			error("Error determining size of INCBIN file '%s': %s\n",
 				s, strerror(errno));
 		/* The file isn't seekable, so we'll just skip bytes */
 		while (start_pos--)
@@ -765,10 +759,9 @@
 		if (byte != EOF) {
 			writebyte(byte);
 		} else if (ferror(f)) {
-			yyerror("Error reading INCBIN file '%s': %s", s,
-				strerror(errno));
+			error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
 		} else {
-			yyerror("Premature end of file (%" PRId32 " bytes left to read)",
+			error("Premature end of file (%" PRId32 " bytes left to read)\n",
 				todo + 1);
 		}
 	}
@@ -785,7 +778,7 @@
 
 	sect = malloc(sizeof(struct SectionStackEntry));
 	if (sect == NULL)
-		fatalerror("No memory for section stack");
+		fatalerror("No memory for section stack: %s<\n",  strerror(errno));
 
 	sect->pSection = pCurrentSection;
 	sect->pScope = sym_GetCurrentSymbolScope();
@@ -798,10 +791,10 @@
 void out_PopSection(void)
 {
 	if (pSectionStack == NULL)
-		fatalerror("No entries in the section stack");
+		fatalerror("No entries in the section stack\n");
 
 	if (currentLoadSection)
-		fatalerror("Cannot change the section within a `LOAD` block!");
+		fatalerror("Cannot change the section within a `LOAD` block!\n");
 
 	struct SectionStackEntry *sect;
 
--- a/src/asm/symbol.c
+++ b/src/asm/symbol.c
@@ -114,8 +114,7 @@
 {
 	if (snprintf(sym->fileName, _MAX_PATH + 1, "%s",
 		     tzCurrentFileName) > _MAX_PATH)
-		fatalerror("%s: File name is too long: '%s'", __func__,
-			   tzCurrentFileName);
+		fatalerror("%s: File name is too long: '%s'\n", __func__, tzCurrentFileName);
 	sym->fileLine = fstk_GetLine();
 }
 
@@ -127,10 +126,10 @@
 	struct Symbol *symbol = malloc(sizeof(*symbol));
 
 	if (!symbol)
-		fatalerror("Failed to create symbol: %s", strerror(errno));
+		fatalerror("Failed to create symbol '%s': %s\n", s, strerror(errno));
 
 	if (snprintf(symbol->name, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN)
-		warning(WARNING_LONG_STR, "Symbol name is too long: '%s'", s);
+		warning(WARNING_LONG_STR, "Symbol name is too long: '%s'\n", s);
 
 	symbol->isExported = false;
 	symbol->isBuiltin = false;
@@ -155,8 +154,7 @@
 	int n = snprintf(output, outputSize, "%s%s", parent->name, localName);
 
 	if (n >= (int)outputSize)
-		fatalerror("Symbol name is too long: '%s%s'", parent->name,
-			   localName);
+		fatalerror("Symbol name is too long: '%s%s'\n", parent->name, localName);
 }
 
 /*
@@ -174,8 +172,7 @@
 	char const *separator = strchr(s, '.');
 
 	if (separator && strchr(separator + 1, '.'))
-		fatalerror("'%s' is a nonsensical reference to a nested local symbol",
-			   s);
+		fatalerror("'%s' is a nonsensical reference to a nested local symbol\n", s);
 
 	return hash_GetElement(symbols, s);
 }
@@ -202,12 +199,11 @@
 	struct Symbol *symbol = findsymbol(symName, scope);
 
 	if (!symbol) {
-		yyerror("'%s' not defined", symName);
+		error("'%s' not defined\n", symName);
 	} else if (symbol->isBuiltin) {
-		yyerror("Built-in symbol '%s' cannot be purged", symName);
+		error("Built-in symbol '%s' cannot be purged\n", symName);
 	} else if (isReferenced(symbol)) {
-		yyerror("Symbol \"%s\" is referenced and thus cannot be purged",
-			symName);
+		error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName);
 	} else {
 		hash_RemoveElement(symbols, symbol->name);
 		if (symbol->type == SYM_MACRO)
@@ -221,9 +217,9 @@
 	struct Section const *sect = sect_GetSymbolSection();
 
 	if (!sect)
-		yyerror("PC has no value outside a section");
+		error("PC has no value outside a section\n");
 	else if (sect->org == -1)
-		yyerror("Expected constant PC but section is not fixed");
+		error("Expected constant PC but section is not fixed\n");
 	else
 		return CallbackPC();
 	return 0;
@@ -237,11 +233,11 @@
 	struct Symbol const *sym = sym_FindSymbol(s);
 
 	if (sym == NULL)
-		yyerror("'%s' not defined", s);
+		error("'%s' not defined\n", s);
 	else if (sym == PCSymbol)
 		return sym_GetPCValue();
 	else if (!sym_IsConstant(sym))
-		yyerror("\"%s\" does not have a constant value", s);
+		error("\"%s\" does not have a constant value\n", s);
 	else
 		return sym_GetValue(sym);
 
@@ -256,9 +252,9 @@
 	struct Symbol const *sym = sym_FindSymbol(s);
 
 	if (sym == NULL || !sym_IsDefined(sym))
-		yyerror("'%s' not defined", s);
+		error("'%s' not defined\n", s);
 	else if (!sym_IsNumeric(sym))
-		yyerror("'%s' is a macro or string symbol", s);
+		error("'%s' is a macro or string symbol\n", s);
 	else
 		return sym_GetValue(sym);
 
@@ -287,7 +283,7 @@
 	if (!symbol)
 		symbol = createsymbol(symbolName);
 	else if (sym_IsDefined(symbol))
-		yyerror("'%s' already defined at %s(%" PRIu32 ")", symbolName,
+		error("'%s' already defined at %s(%" PRIu32 ")\n", symbolName,
 			symbol->fileName, symbol->fileLine);
 
 	return symbol;
@@ -326,7 +322,7 @@
 	char *string = malloc(len + 1);
 
 	if (string == NULL)
-		fatalerror("No memory for string equate");
+		fatalerror("No memory for string equate: %s\n", strerror(errno));
 	strcpy(string, value);
 
 	sym->type = SYM_EQUS;
@@ -347,7 +343,7 @@
 	if (sym == NULL)
 		sym = createsymbol(symName);
 	else if (sym_IsDefined(sym) && sym->type != SYM_SET)
-		yyerror("'%s' already defined as %s at %s(%" PRIu32 ")",
+		error("'%s' already defined as %s at %s(%" PRIu32 ")\n",
 			symName, sym->type == SYM_LABEL ? "label" : "constant",
 			sym->fileName, sym->fileLine);
 	else
@@ -367,7 +363,7 @@
 struct Symbol *sym_AddLocalReloc(char const *symName)
 {
 	if (!symbolScope) {
-		yyerror("Local label '%s' in main scope", symName);
+		error("Local label '%s' in main scope\n", symName);
 		return NULL;
 	}
 
@@ -387,7 +383,7 @@
 
 	if (localPtr != NULL) {
 		if (!symbolScope) {
-			yyerror("Local label in main scope");
+			error("Local label in main scope\n");
 			return NULL;
 		}
 
@@ -395,12 +391,11 @@
 		uint32_t parentLen = localPtr - symName;
 
 		if (strchr(localPtr + 1, '.') != NULL)
-			fatalerror("'%s' is a nonsensical reference to a nested local symbol",
+			fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
 				   symName);
 		else if (strlen(scope->name) != parentLen
 			|| strncmp(symName, scope->name, parentLen) != 0)
-			yyerror("Not currently in the scope of '%.*s'",
-				parentLen, symName);
+			error("Not currently in the scope of '%.*s'\n", parentLen, symName);
 	}
 
 	struct Symbol *sym = findsymbol(symName, scope);
@@ -408,8 +403,8 @@
 	if (!sym)
 		sym = createsymbol(symName);
 	else if (sym_IsDefined(sym))
-		yyerror("'%s' already defined in %s(%" PRIu32 ")", symName,
-			sym->fileName, sym->fileLine);
+		error("'%s' already defined in %s(%" PRIu32 ")\n",
+			symName, sym->fileName, sym->fileLine);
 	/* If the symbol already exists as a ref, just "take over" it */
 
 	sym->type = SYM_LABEL;
@@ -423,8 +418,7 @@
 	sym->section = sect_GetSymbolSection();
 	/* Labels need to be assigned a section, except PC */
 	if (!sym->section && strcmp(symName, "@"))
-		yyerror("Label \"%s\" created outside of a SECTION",
-			symName);
+		error("Label \"%s\" created outside of a SECTION\n", symName);
 
 	updateSymbolFilename(sym);
 
@@ -481,12 +475,9 @@
 
 		if (symName[0] == '.') {
 			if (!symbolScope)
-				fatalerror("Local label reference '%s' in main scope",
-					   symName);
-			scope = symbolScope->scope ? symbolScope->scope
-						   : symbolScope;
-			fullSymbolName(fullname, sizeof(fullname), symName,
-				       symbolScope);
+				fatalerror("Local label reference '%s' in main scope\n", symName);
+			scope = symbolScope->scope ? symbolScope->scope : symbolScope;
+			fullSymbolName(fullname, sizeof(fullname), symName, symbolScope);
 			symName = fullname;
 		}
 
--- a/src/asm/util.c
+++ b/src/asm/util.c
@@ -35,7 +35,7 @@
 
 	for (i = 0, state = 0;; i++) {
 		if (decode(&state, &codep, (uint8_t)src[i]) == 1)
-			fatalerror("invalid UTF-8 character");
+			fatalerror("invalid UTF-8 character\n");
 
 		dest[i] = src[i];
 
--- a/src/asm/warning.c
+++ b/src/asm/warning.c
@@ -201,12 +201,11 @@
 	fstk_Dump();
 	fprintf(stderr, flag ? ": [-Werror=%s]\n    " : ":\n    ", flag);
 	vfprintf(stderr, fmt, args);
-	fputc('\n', stderr);
 	fstk_DumpStringExpansions();
 	nbErrors++;
 }
 
-void yyerror(const char *fmt, ...)
+void error(const char *fmt, ...)
 {
 	va_list args;
 
@@ -254,7 +253,6 @@
 	fstk_Dump();
 	fprintf(stderr, ": [-W%s]\n    ", flag);
 	vfprintf(stderr, fmt, args);
-	fputc('\n', stderr);
 	fstk_DumpStringExpansions();
 
 	va_end(args);
--- a/test/asm/overflow.err
+++ b/test/asm/overflow.err
@@ -1,7 +1,7 @@
 warning: overflow.asm(24): [-Wdiv]
-    Division of min value by -1
+    Division of -2147483648 by -1 yields -2147483648
 warning: overflow.asm(25): [-Wdiv]
-    Division of min value by -1
+    Division of -2147483648 by -1 yields -2147483648
 warning: overflow.asm(39): [-Wlarge-constant]
     Integer constant '4294967296' is too large
 warning: overflow.asm(42): [-Wlarge-constant]