shithub: rgbds

Download patch

ref: 80e2129f2219d6a458591117c91320a8a4fb4c8d
parent: af70f03933abf02f218044ccc954417eb0e94663
parent: 25efb00769cb1a16fe3ab643e4b63d33402a5bcc
author: stag019 <[email protected]>
date: Sat Nov 1 21:00:20 EDT 2014

Merge https://github.com/bentley/rgbds

Conflicts:
	include/lib/types.h
	src/asm/symbol.c

--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
-rgbasm, rgblink, and rgblib are derived from Justin Lloyd's RGBDS, which
-is released under the following license:
+rgbasm and rgblink are derived from Justin Lloyd's RGBDS, which is
+released under the following license:
 
                       DO WHATEVER PUBLIC LICENSE*
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@@ -17,3 +17,9 @@
 
 rgbfix was rewritten from scratch by Anthony J. Bentley, and is released
 under the ISC license; see the source file for the text of the license.
+
+extern/err.c is derived from the Musl C library, http://www.musl-libc.org,
+and is released under the MIT license.
+
+extern/strl.c is derived from the OpenBSD Project, http://www.openbsd.org,
+and is released under the BSD license.
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,7 @@
-CFLAGS +=	-Wall -Iinclude -Iinclude/asm/gameboy -g -std=c99
+.POSIX:
 
+REALCFLAGS =	${CFLAGS} -Wall -Iinclude -Iinclude/asm/gameboy -g -std=c99
+
 # User-defined variables
 PREFIX =	/usr/local
 BINPREFIX =	${PREFIX}/bin
@@ -13,7 +15,6 @@
 	src/asm/gameboy/yaccprt4.y
 
 rgbasm_obj := \
-	src/asm/alloca.o \
 	src/asm/asmy.o \
 	src/asm/charmap.o \
 	src/asm/fstack.o \
@@ -24,12 +25,11 @@
 	src/asm/output.o \
 	src/asm/rpn.o \
 	src/asm/symbol.o \
-	src/asm/gameboy/locallex.o
+	src/asm/gameboy/locallex.o \
+	src/extern/err.o \
+	src/extern/strlcpy.o \
+	src/extern/strlcat.o
 
-rgblib_obj := \
-	src/lib/library.o \
-	src/lib/main.o
-
 rgblink_obj := \
 	src/link/assign.o \
 	src/link/library.o \
@@ -38,61 +38,53 @@
 	src/link/object.o \
 	src/link/output.o \
 	src/link/patch.o \
-	src/link/symbol.o
+	src/link/symbol.o \
+	src/extern/err.o
 
 rgbfix_obj := \
-	src/fix/main.o
+	src/fix/main.o \
+	src/extern/err.o
 
-all: rgbasm rgblib rgblink rgbfix
+all: rgbasm rgblink rgbfix
 
 clean:
-	${Q}rm -rf rgbds.html
-	${Q}rm -rf rgbasm rgbasm.exe ${rgbasm_obj} rgbasm.html
-	${Q}rm -rf rgblib rgblib.exe ${rgblib_obj} rgblib.html
-	${Q}rm -rf rgblink rgblink.exe ${rgblink_obj} rgblink.html
-	${Q}rm -rf rgbfix rgbfix.exe ${rgbfix_obj} rgbfix.html
-	${Q}rm -rf src/asm/asmy.c src/asm/asmy.h src/asm/asmy.y
+	$Qrm -rf rgbds.html
+	$Qrm -rf rgbasm rgbasm.exe ${rgbasm_obj} rgbasm.html
+	$Qrm -rf rgblink rgblink.exe ${rgblink_obj} rgblink.html
+	$Qrm -rf rgbfix rgbfix.exe ${rgbfix_obj} rgbfix.html
+	$Qrm -rf src/asm/asmy.c src/asm/asmy.h src/asm/asmy.y
 
 install: all
-	${Q}install -s -m 555 rgbasm ${BINPREFIX}/rgbasm
-	${Q}install -s -m 555 rgbfix ${BINPREFIX}/rgbfix
-	${Q}install -s -m 555 rgblink ${BINPREFIX}/rgblink
-	${Q}install -s -m 555 rgblib ${BINPREFIX}/rgblib
-	${Q}install -m 444 src/rgbds.7 ${MANPREFIX}/man7/rgbds.7 || \
-		(echo Installing manpages to ${MANPREFIX} failed. >&2 && \
-		 echo Check where your manpages are installed and set the \
-		      proper directory >&2 && \
-		 echo with, e.g., make install MANPREFIX=/usr/share/man \
-		 >&2 ; false)
-	${Q}install -m 444 src/asm/rgbasm.1 \
-		${MANPREFIX}/man1/rgbasm.1
-	${Q}install -m 444 src/fix/rgbfix.1 \
-		${MANPREFIX}/man1/rgbfix.1
-	${Q}install -m 444 src/link/rgblink.1 \
-		${MANPREFIX}/man1/rgblink.1
-	${Q}install -m 444 src/lib/rgblib.1 \
-		${MANPREFIX}/man1/rgblib.1
+	$Qmkdir -p ${BINPREFIX}
+	$Qinstall -s -m 555 rgbasm ${BINPREFIX}/rgbasm
+	$Qinstall -s -m 555 rgbfix ${BINPREFIX}/rgbfix
+	$Qinstall -s -m 555 rgblink ${BINPREFIX}/rgblink
+	$Qmkdir -p ${MANPREFIX}/man1 ${MANPREFIX}/man7
+	$Qinstall -m 444 src/rgbds.7 ${MANPREFIX}/man7/rgbds.7
+	$Qinstall -m 444 src/asm/rgbasm.1 ${MANPREFIX}/man1/rgbasm.1
+	$Qinstall -m 444 src/fix/rgbfix.1 ${MANPREFIX}/man1/rgbfix.1
+	$Qinstall -m 444 src/link/rgblink.1 ${MANPREFIX}/man1/rgblink.1
 
 rgbasm: ${rgbasm_obj}
-	${Q}${CC} ${CFLAGS} -o $@ ${rgbasm_obj} -lm
+	$Q${CC} ${REALCFLAGS} -o $@ ${rgbasm_obj} -lm
 
-rgblib: ${rgblib_obj}
-	${Q}${CC} ${CFLAGS} -o $@ ${rgblib_obj}
-
 rgblink: ${rgblink_obj}
-	${Q}${CC} ${CFLAGS} -o $@ ${rgblink_obj}
+	$Q${CC} ${REALCFLAGS} -o $@ ${rgblink_obj}
 
 rgbfix: ${rgbfix_obj}
-	${Q}${CC} ${CFLAGS} -o $@ ${rgbfix_obj}
+	$Q${CC} ${REALCFLAGS} -o $@ ${rgbfix_obj}
 
+.y.c:
+	$Q${YACC} -d ${YFLAGS} -o $@ $<
+
 .c.o:
-	${Q}${CC} ${CFLAGS} -c -o $@ $<
+	$Q${CC} ${REALCFLAGS} -c -o $@ $<
 
-src/asm/asmy.c: src/asm/asmy.y
-	${Q}${YACC} -d -o $@ $<
+src/asm/gameboy/locallex.o src/asm/globlex.o src/asm/lexer.o: src/asm/asmy.h
+src/asm/asmy.h: src/asm/asmy.c
 
 src/asm/asmy.y: ${yacc_pre}
-	${Q}cat ${yacc_pre} > $@
+	$Qcat ${yacc_pre} > $@
 
 
 # Below is a target for the project maintainer to easily create win32 exes.
@@ -100,19 +92,23 @@
 # If you're building on Windows with Cygwin or Mingw, just follow the Unix
 # install instructions instead.
 mingw:
-	${Q}env PATH=/usr/local/mingw32/bin:/bin:/usr/bin:/usr/local/bin make CC=gcc CFLAGS="-I/usr/local/mingw32/include ${CFLAGS}"
-	${Q}mv rgbasm rgbasm.exe
-	${Q}mv rgblib rgblib.exe
-	${Q}mv rgblink rgblink.exe
-	${Q}mv rgbfix rgbfix.exe
+	$Qenv PATH=/usr/local/mingw32/bin:/bin:/usr/bin:/usr/local/bin \
+		make CC=gcc CFLAGS="-I/usr/local/mingw32/include \
+			-D__progname=\\\"\\\" ${CFLAGS}"
+	$Qmv rgbasm rgbasm.exe
+	$Qmv rgblink rgblink.exe
+	$Qmv rgbfix rgbfix.exe
 
 # Below is a target for the project maintainer to easily create web manuals.
 # It relies on mandoc: http://mdocml.bsd.lv
-MANDOC =	-Thtml -Oman=/rgbds/manual/%N/ -Ostyle=/rgbds/manual/manual.css -Ios=General
+MANDOC =	-Thtml -Ios=General -Oman=/rgbds/manual/%N/ \
+			-Ostyle=/rgbds/manual/manual.css
 
 wwwman:
-	${Q}mandoc ${MANDOC} src/rgbds.7 | sed s/OpenBSD/General/ > rgbds.html
-	${Q}mandoc ${MANDOC} src/asm/rgbasm.1 | sed s/OpenBSD/General/ > rgbasm.html
-	${Q}mandoc ${MANDOC} src/fix/rgbfix.1 | sed s/OpenBSD/General/ > rgbfix.html
-	${Q}mandoc ${MANDOC} src/lib/rgblib.1 | sed s/OpenBSD/General/ > rgblib.html
-	${Q}mandoc ${MANDOC} src/link/rgblink.1 | sed s/OpenBSD/General/ > rgblink.html
+	$Qmandoc ${MANDOC} src/rgbds.7 | sed s/OpenBSD/General/ > rgbds.html
+	$Qmandoc ${MANDOC} src/asm/rgbasm.1 | sed s/OpenBSD/General/ > \
+		rgbasm.html
+	$Qmandoc ${MANDOC} src/fix/rgbfix.1 | sed s/OpenBSD/General/ > \
+		rgbfix.html
+	$Qmandoc ${MANDOC} src/link/rgblink.1 | sed s/OpenBSD/General/ > \
+		rgblink.html
--- a/README
+++ b/README
@@ -7,7 +7,6 @@
 
   - rgbasm  (assembler)
   - rgblink (linker)
-  - rgblib  (library manager)
   - rgbfix  (checksum/header fixer)
 
 rgbds-linux is a fork of the original RGBDS which aims to make the programs
--- a/doc/index.htm
+++ b/doc/index.htm
@@ -12,7 +12,6 @@
 	<li><a href="geninfo.htm">ASMotor General Information</a>
 	<li><a href="asm.htm">xASM Documentation</a>
 	<li><a href="link.htm">xLink Documentation</a>
-	<li><a href="lib.htm">xLib Documentation</a>
 	<li><a href="fix.htm">RGBFix Documentation</a>
 	<li><a href="rgb0.htm">The RGB0-2 ObjectFileFormat</a>
 </ul>
--- a/doc/lib.htm
+++ /dev/null
@@ -1,47 +1,0 @@
-<!DOCTYPE HTML PUBliC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html>
-<head>
-	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
-	<title>xLib</title>
-        <link rel="stylesheet" type="text/css" href="./style.css">
-</head>
-<body>
-<h1>xLib Documentation</h1>
-<h2>Table of Contents</h2>
-<ul>
-<li><a href="#history"> History</A>
-<li><a href="#usage"> Usage</A>
-<li><a href="#commands"> The commands</A>
-</ul>
-<hr>
-<h2 id="history">History</h2>
-<table>
-	<caption>The history of xLib</caption>
-<thead>
-<tr>
-	<th scope="col">Version</th>
-	<th scope="col">Dated</th>
-	<th scope="col">Release notes</th>
-</tr>
-</thead>
-<tr>
-	<td>1.0</td>
-	<td>21 Sep. 1997</td>
-	<td>First release</td>
-</tr>
-</table>
-<h2 id="usage">Usage</h2>
-<pre>    xlib library command [module1 module2 ... modulen]</pre>
-<h2 id="commands">The Commands</h2>
-<p>The <b>command</b> specified after <b>library</b> on the <a href="#Usage">commandline</a> tells xLib what to do.
-<p>The following commands are available:
-<ul>
-	<li>a — Adds (or replaces if already present) the modules to the library
-	<li>d — Deletes the modules specified from the library
-	<li>l — Lists the library contents
-	<li>x — Extracts the modules from the library
-</ul>
-<hr>
-<p>Last updated 21 September 1997 by <a href="mailto:[email protected]">Carsten Sorensen</a></p>
-</body>
-</html>
--- a/include/asm/asm.h
+++ b/include/asm/asm.h
@@ -17,8 +17,6 @@
 
 #include "localasm.h"
 
-#include "asmotor.h"
-
 extern SLONG nLineNo;
 extern ULONG nTotalLines;
 extern ULONG nPC;
--- a/include/asm/fstack.h
+++ b/include/asm/fstack.h
@@ -9,6 +9,8 @@
 #ifndef ASMOTOR_ASM_FSTACK_H
 #define ASMOTOR_ASM_FSTACK_H
 
+#include <stdio.h>
+
 #include "asm/asm.h"
 #include "asm/types.h"
 #include "asm/lexer.h"
@@ -27,14 +29,17 @@
 	ULONG nREPTBlockSize;
 };
 
-extern ULONG fstk_RunInclude(char *s);
+void
+fstk_RunInclude(char *);
 extern void fstk_RunMacroArg(SLONG s);
-extern ULONG fstk_Init(char *s);
+void
+fstk_Init(char *);
 extern void fstk_Dump(void);
 extern void fstk_AddIncludePath(char *s);
 extern ULONG fstk_RunMacro(char *s);
 extern void fstk_RunRept(ULONG count);
-extern void fstk_FindFile(char *s);
+FILE *
+fstk_FindFile(char *);
 
 extern int yywrap(void);
 
--- a/include/asm/lexer.h
+++ b/include/asm/lexer.h
@@ -5,7 +5,8 @@
 
 #include "asm/types.h"
 
-#define LEXHASHSIZE	512
+#define LEXHASHSIZE (1 << 11)
+#define MAXSTRLEN 255
 
 struct sLexInitString {
 	char *tzName;
@@ -18,7 +19,9 @@
 };
 
 struct yy_buffer_state {
-	char *pBufferStart;
+	char *pBufferRealStart; // actual starting address
+	char *pBufferStart; // address where the data is initially written
+	                    // after the "safety margin"
 	char *pBuffer;
 	ULONG nBufferSize;
 	ULONG oAtLineStart;
--- a/include/asm/symbol.h
+++ b/include/asm/symbol.h
@@ -3,7 +3,7 @@
 
 #include "asm/types.h"
 
-#define HASHSIZE 73
+#define HASHSIZE (1 << 16)
 #define MAXSYMLEN 256
 
 struct sSymbol {
@@ -35,6 +35,7 @@
 #define SYMF_CONST		0x200	/* symbol has a constant value, will
 					 * not be changed during linking */
 
+ULONG calchash(char *s);
 void sym_PrepPass1(void);
 void sym_PrepPass2(void);
 void sym_AddLocalReloc(char *tzSym);
--- a/include/asmotor.h
+++ /dev/null
@@ -1,21 +1,0 @@
-/*	asmotor.h
- *
- *	Contains defines for every program in the ASMotor package
- *
- *	Copyright 1997 Carsten Sorensen
- *
- */
-
-#ifndef	ASMOTOR_ASMOTOR_H
-#define	ASMOTOR_ASMOTOR_H
-
-#define	ASMOTOR
-
-#define	ASMOTOR_VERSION	"1.10-linux"
-
-#define	ASM_VERSION		"1.08c"
-#define	LINK_VERSION	"1.06c"
-#define	RGBFIX_VERSION	"1.02"
-#define	LIB_VERSION		"1.00"
-
-#endif
--- /dev/null
+++ b/include/extern/err.h
@@ -1,0 +1,63 @@
+/*
+ * Copyright © 2005-2013 Rich Felker, et al.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef EXTERN_ERR_H
+#define EXTERN_ERR_H
+
+#ifdef ERR_IN_LIBC
+#include <err.h>
+#else
+
+#include <stdarg.h>
+
+#define warn rgbds_warn
+#define vwarn rgbds_vwarn
+#define warnx rgbds_warnx
+#define vwarnx rgbds_vwarnx
+
+#define err rgbds_err
+#define verr rgbds_verr
+#define errx rgbds_errx
+#define verrx rgbds_verrx
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void warn(const char *, ...);
+void vwarn(const char *, va_list);
+void warnx(const char *, ...);
+void vwarnx(const char *, va_list);
+
+void err(int, const char *, ...);
+void verr(int, const char *, va_list);
+void errx(int, const char *, ...);
+void verrx(int, const char *, va_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+#endif
--- /dev/null
+++ b/include/extern/strl.h
@@ -1,0 +1,13 @@
+#ifndef STRL_H
+#define STRL_H
+
+#ifdef STRL_IN_LIBC
+#include <string.h>
+#else
+#define strlcpy rgbds_strlcpy
+#define strlcat rgbds_strlcat
+size_t strlcpy(char *, const char *, size_t);
+size_t strlcat(char *, const char *, size_t);
+#endif
+
+#endif
--- a/include/lib/library.h
+++ /dev/null
@@ -1,13 +1,0 @@
-#ifndef ASMOTOR_LIB_LIBRARY_H
-#define ASMOTOR_LIB_LIBRARY_H
-
-#include "lib/libwrap.h"
-
-extern sLibrary *lib_Read(char *filename);
-extern BBOOL lib_Write(sLibrary * lib, char *filename);
-extern sLibrary *lib_AddReplace(sLibrary * lib, char *filename);
-extern void lib_Free(sLibrary * lib);
-extern sLibrary *lib_DeleteModule(sLibrary * lib, char *filename);
-extern sLibrary *lib_Find(sLibrary * lib, char *filename);
-
-#endif
--- a/include/lib/libwrap.h
+++ /dev/null
@@ -1,19 +1,0 @@
-#ifndef ASMOTOR_LIB_LIBWRAP_H
-#define ASMOTOR_LIB_LIBWRAP_H
-
-#include "lib/types.h"
-
-#define MAXNAMELENGTH	256
-
-struct LibraryWrapper {
-	char tName[MAXNAMELENGTH];
-	UWORD uwTime;
-	UWORD uwDate;
-	SLONG nByteLength;
-	UBYTE *pData;
-	struct LibraryWrapper *pNext;
-};
-
-typedef struct LibraryWrapper sLibrary;
-
-#endif
--- a/include/lib/types.h
+++ /dev/null
@@ -1,16 +1,0 @@
-#ifndef ASMOTOR_LIB_TYPES_H
-#define ASMOTOR_LIB_TYPES_H
-
-#ifndef _MAX_PATH
-#define	_MAX_PATH	512
-#endif
-
-typedef unsigned char UBYTE;
-typedef signed char SBYTE;
-typedef unsigned short UWORD;
-typedef signed short SWORD;
-typedef unsigned long ULONG;
-typedef signed long SLONG;
-typedef signed char BBOOL;
-
-#endif
--- a/include/link/object.h
+++ b/include/link/object.h
@@ -2,6 +2,5 @@
 #define	ASMOTOR_LINK_OBJECT_H
 
 extern void obj_Readfile(char *tzObjectfile);
-extern void lib_Readfile(char *tzLibfile);
 
 #endif
--- a/src/asm/alloca.c
+++ /dev/null
@@ -1,466 +1,0 @@
-/* alloca.c -- allocate automatically reclaimed memory
-   (Mostly) portable public-domain implementation -- D A Gwyn
-
-   This implementation of the PWB library alloca function,
-   which is used to allocate space off the run-time stack so
-   that it is automatically reclaimed upon procedure exit,
-   was inspired by discussions with J. Q. Johnson of Cornell.
-   J.Otto Tennant <[email protected]> contributed the Cray support.
-
-   There are some preprocessor constants that can
-   be defined when compiling for your specific system, for
-   improved efficiency; however, the defaults should be okay.
-
-   The general concept of this implementation is to keep
-   track of all alloca-allocated blocks, and reclaim any
-   that are found to be deeper in the stack than the current
-   invocation.  This heuristic does not reclaim storage as
-   soon as it becomes invalid, but it will do so eventually.
-
-   As a special case, alloca(0) reclaims storage without
-   allocating any.  It is a good idea to use alloca(0) in
-   your main control loop, etc. to force garbage collection.  */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifdef emacs
-#include "blockinput.h"
-#endif
-
-/* If compiling with GCC 2, this file's not needed.  */
-#if !defined (__GNUC__) || __GNUC__ < 2
-
-/* If someone has defined alloca as a macro,
-   there must be some other way alloca is supposed to work.  */
-#ifndef alloca
-
-#ifdef emacs
-#ifdef static
-/* actually, only want this if static is defined as ""
-   -- this is for usg, in which emacs must undefine static
-   in order to make unexec workable
-   */
-#ifndef STACK_DIRECTION
-you lose-- must know STACK_DIRECTION at compile - time
-#endif				/* STACK_DIRECTION undefined */
-#endif				/* static */
-#endif				/* emacs */
-/* If your stack is a linked list of frames, you have to
-   provide an "address metric" ADDRESS_FUNCTION macro.  */
-#if defined (CRAY) && defined (CRAY_STACKSEG_END)
-long i00afunc();
-#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
-#else
-#define ADDRESS_FUNCTION(arg) &(arg)
-#endif
-#if __STDC__
-     typedef void *pointer;
-#else
-     typedef char *pointer;
-#endif
-
-#define NULL	0
-
-/* Different portions of Emacs need to call different versions of
-   malloc.  The Emacs executable needs alloca to call xmalloc, because
-   ordinary malloc isn't protected from input signals.  On the other
-   hand, the utilities in lib-src need alloca to call malloc; some of
-   them are very simple, and don't have an xmalloc routine.
-
-   Non-Emacs programs expect this to call use xmalloc.
-
-   Callers below should use malloc.  */
-
-/*	Carsten Sorensen 09/09/97
- *	Commented out the following, I want malloc!
-#ifndef emacs
-#define malloc xmalloc
-#endif
-extern pointer malloc ();
-	And added the following line:
- */
-#include <stdlib.h>
-
-/* Define STACK_DIRECTION if you know the direction of stack
-   growth for your system; otherwise it will be automatically
-   deduced at run-time.
-
-   STACK_DIRECTION > 0 => grows toward higher addresses
-   STACK_DIRECTION < 0 => grows toward lower addresses
-   STACK_DIRECTION = 0 => direction of growth unknown  */
-
-#ifndef STACK_DIRECTION
-#define STACK_DIRECTION	0	/* Direction unknown.  */
-#endif
-
-#if STACK_DIRECTION != 0
-
-#define STACK_DIR	STACK_DIRECTION	/* Known at compile-time.  */
-
-#else				/* STACK_DIRECTION == 0; need run-time code.  */
-
-static int stack_dir;		/* 1 or -1 once known.  */
-#define STACK_DIR	stack_dir
-
-static void 
-find_stack_direction()
-{
-	static char *addr = NULL;	/* Address of first `dummy', once
-					 * known.  */
-	auto char dummy;	/* To get stack address.  */
-
-	if (addr == NULL) {	/* Initial entry.  */
-		addr = ADDRESS_FUNCTION(dummy);
-
-		find_stack_direction();	/* Recurse once.  */
-	} else {
-		/* Second entry.  */
-		if (ADDRESS_FUNCTION(dummy) > addr)
-			stack_dir = 1;	/* Stack grew upward.  */
-		else
-			stack_dir = -1;	/* Stack grew downward.  */
-	}
-}
-#endif				/* STACK_DIRECTION == 0 */
-
-/* An "alloca header" is used to:
-   (a) chain together all alloca'ed blocks;
-   (b) keep track of stack depth.
-
-   It is very important that sizeof(header) agree with malloc
-   alignment chunk size.  The following default should work okay.  */
-
-#ifndef ALIGN_SIZE
-#define ALIGN_SIZE	sizeof(double)
-#endif
-
-typedef union hdr {
-	char align[ALIGN_SIZE];	/* To force sizeof(header).  */
-	struct {
-		union hdr *next;/* For chaining headers.  */
-		char *deep;	/* For stack depth measure.  */
-	}      h;
-}   header;
-
-static header *last_alloca_header = NULL;	/* -> last alloca header.  */
-
-/* Return a pointer to at least SIZE bytes of storage,
-   which will be automatically reclaimed upon exit from
-   the procedure that called alloca.  Originally, this space
-   was supposed to be taken from the current stack frame of the
-   caller, but that method cannot be made to work for some
-   implementations of C, for example under Gould's UTX/32.  */
-
-pointer 
-alloca(size)
-	unsigned size;
-{
-	auto char probe;	/* Probes stack depth: */
-	register char *depth = ADDRESS_FUNCTION(probe);
-
-#if STACK_DIRECTION == 0
-	if (STACK_DIR == 0)	/* Unknown growth direction.  */
-		find_stack_direction();
-#endif
-
-	/* Reclaim garbage, defined as all alloca'd storage that was allocated
-	 * from deeper in the stack than currently. */
-
-	{
-		register header *hp;	/* Traverses linked list.  */
-
-#ifdef emacs
-		BLOCK_INPUT;
-#endif
-
-		for (hp = last_alloca_header; hp != NULL;)
-			if ((STACK_DIR > 0 && hp->h.deep > depth)
-			    || (STACK_DIR < 0 && hp->h.deep < depth)) {
-				register header *np = hp->h.next;
-
-				free((pointer) hp);	/* Collect garbage.  */
-
-				hp = np;	/* -> next header.  */
-			} else
-				break;	/* Rest are not deeper.  */
-
-		last_alloca_header = hp;	/* -> last valid storage.  */
-
-#ifdef emacs
-		UNBLOCK_INPUT;
-#endif
-	}
-
-	if (size == 0)
-		return NULL;	/* No allocation required.  */
-
-	/* Allocate combined header + user data storage.  */
-
-	{
-		register pointer new = malloc(sizeof(header) + size);
-		/* Address of header.  */
-
-		((header *) new)->h.next = last_alloca_header;
-		((header *) new)->h.deep = depth;
-
-		last_alloca_header = (header *) new;
-
-		/* User storage begins just after header.  */
-
-		return (pointer) ((char *) new + sizeof(header));
-	}
-}
-#if defined (CRAY) && defined (CRAY_STACKSEG_END)
-
-#ifdef DEBUG_I00AFUNC
-#include <stdio.h>
-#endif
-
-#ifndef CRAY_STACK
-#define CRAY_STACK
-#ifndef CRAY2
-/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
-struct stack_control_header {
-	long shgrow:32;		/* Number of times stack has grown.  */
-	long shaseg:32;		/* Size of increments to stack.  */
-	long shhwm:32;		/* High water mark of stack.  */
-	long shsize:32;		/* Current size of stack (all segments).  */
-};
-/* The stack segment linkage control information occurs at
-   the high-address end of a stack segment.  (The stack
-   grows from low addresses to high addresses.)  The initial
-   part of the stack segment linkage control information is
-   0200 (octal) words.  This provides for register storage
-   for the routine which overflows the stack.  */
-
-struct stack_segment_linkage {
-	long ss[0200];		/* 0200 overflow words.  */
-	long sssize:32;		/* Number of words in this segment.  */
-	long ssbase:32;		/* Offset to stack base.  */
-	long:32;
-	long sspseg:32;		/* Offset to linkage control of previous
-				 * segment of stack.  */
-	long:32;
-	long sstcpt:32;		/* Pointer to task common address block.  */
-	long sscsnm;		/* Private control structure number for
-				 * microtasking.  */
-	long ssusr1;		/* Reserved for user.  */
-	long ssusr2;		/* Reserved for user.  */
-	long sstpid;		/* Process ID for pid based multi-tasking.  */
-	long ssgvup;		/* Pointer to multitasking thread giveup.  */
-	long sscray[7];		/* Reserved for Cray Research.  */
-	long ssa0;
-	long ssa1;
-	long ssa2;
-	long ssa3;
-	long ssa4;
-	long ssa5;
-	long ssa6;
-	long ssa7;
-	long sss0;
-	long sss1;
-	long sss2;
-	long sss3;
-	long sss4;
-	long sss5;
-	long sss6;
-	long sss7;
-};
-#else				/* CRAY2 */
-/* The following structure defines the vector of words
-   returned by the STKSTAT library routine.  */
-struct stk_stat {
-	long now;		/* Current total stack size.  */
-	long maxc;		/* Amount of contiguous space which would be
-				 * required to satisfy the maximum stack
-				 * demand to date.  */
-	long high_water;	/* Stack high-water mark.  */
-	long overflows;		/* Number of stack overflow ($STKOFEN) calls.  */
-	long hits;		/* Number of internal buffer hits.  */
-	long extends;		/* Number of block extensions.  */
-	long stko_mallocs;	/* Block allocations by $STKOFEN.  */
-	long underflows;	/* Number of stack underflow calls ($STKRETN).  */
-	long stko_free;		/* Number of deallocations by $STKRETN.  */
-	long stkm_free;		/* Number of deallocations by $STKMRET.  */
-	long segments;		/* Current number of stack segments.  */
-	long maxs;		/* Maximum number of stack segments so far.  */
-	long pad_size;		/* Stack pad size.  */
-	long current_address;	/* Current stack segment address.  */
-	long current_size;	/* Current stack segment size.  This number is
-				 * actually corrupted by STKSTAT to include
-				 * the fifteen word trailer area.  */
-	long initial_address;	/* Address of initial segment.  */
-	long initial_size;	/* Size of initial segment.  */
-};
-/* The following structure describes the data structure which trails
-   any stack segment.  I think that the description in 'asdef' is
-   out of date.  I only describe the parts that I am sure about.  */
-
-struct stk_trailer {
-	long this_address;	/* Address of this block.  */
-	long this_size;		/* Size of this block (does not include this
-				 * trailer).  */
-	long unknown2;
-	long unknown3;
-	long link;		/* Address of trailer block of previous
-				 * segment.  */
-	long unknown5;
-	long unknown6;
-	long unknown7;
-	long unknown8;
-	long unknown9;
-	long unknown10;
-	long unknown11;
-	long unknown12;
-	long unknown13;
-	long unknown14;
-};
-#endif				/* CRAY2 */
-#endif				/* not CRAY_STACK */
-
-#ifdef CRAY2
-/* Determine a "stack measure" for an arbitrary ADDRESS.
-   I doubt that "lint" will like this much. */
-
-static long 
-i00afunc(long *address)
-{
-	struct stk_stat status;
-	struct stk_trailer *trailer;
-	long *block, size;
-	long result = 0;
-
-	/* We want to iterate through all of the segments.  The first step is
-	 * to get the stack status structure.  We could do this more quickly
-	 * and more directly, perhaps, by referencing the $LM00 common block,
-	 * but I know that this works.  */
-
-	STKSTAT(&status);
-
-	/* Set up the iteration.  */
-
-	trailer = (struct stk_trailer *) (status.current_address
-	    + status.current_size - 15);
-
-	/* There must be at least one stack segment.  Therefore it is a fatal
-	 * error if "trailer" is null.  */
-
-	if (trailer == 0)
-		abort();
-
-	/* Discard segments that do not contain our argument address.  */
-
-	while (trailer != 0) {
-		block = (long *) trailer->this_address;
-		size = trailer->this_size;
-		if (block == 0 || size == 0)
-			abort();
-		trailer = (struct stk_trailer *) trailer->link;
-		if ((block <= address) && (address < (block + size)))
-			break;
-	}
-
-	/* Set the result to the offset in this segment and add the sizes of
-	 * all predecessor segments.  */
-
-	result = address - block;
-
-	if (trailer == 0) {
-		return result;
-	}
-	do {
-		if (trailer->this_size <= 0)
-			abort();
-		result += trailer->this_size;
-		trailer = (struct stk_trailer *) trailer->link;
-	}
-	while (trailer != 0);
-
-	/* We are done.  Note that if you present a bogus address (one not in
-	 * any segment), you will get a different number back, formed from
-	 * subtracting the address of the first block.  This is probably not
-	 * what you want.  */
-
-	return (result);
-}
-#else				/* not CRAY2 */
-/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
-   Determine the number of the cell within the stack,
-   given the address of the cell.  The purpose of this
-   routine is to linearize, in some sense, stack addresses
-   for alloca.  */
-
-static long 
-i00afunc(long address)
-{
-	long stkl = 0;
-
-	long size, pseg, this_segment, stack;
-	long result = 0;
-
-	struct stack_segment_linkage *ssptr;
-
-	/* Register B67 contains the address of the end of the current stack
-	 * segment.  If you (as a subprogram) store your registers on the
-	 * stack and find that you are past the contents of B67, you have
-	 * overflowed the segment.
-	 * 
-	 * B67 also points to the stack segment linkage control area, which is
-	 * what we are really interested in.  */
-
-	stkl = CRAY_STACKSEG_END();
-	ssptr = (struct stack_segment_linkage *) stkl;
-
-	/* If one subtracts 'size' from the end of the segment, one has the
-	 * address of the first word of the segment.
-	 * 
-	 * If this is not the first segment, 'pseg' will be nonzero.  */
-
-	pseg = ssptr->sspseg;
-	size = ssptr->sssize;
-
-	this_segment = stkl - size;
-
-	/* It is possible that calling this routine itself caused a stack
-	 * overflow.  Discard stack segments which do not contain the target
-	 * address.  */
-
-	while (!(this_segment <= address && address <= stkl)) {
-#ifdef DEBUG_I00AFUNC
-		fprintf(stderr, "%011o %011o %011o\n", this_segment, address,
-		    stkl);
-#endif
-		if (pseg == 0)
-			break;
-		stkl = stkl - pseg;
-		ssptr = (struct stack_segment_linkage *) stkl;
-		size = ssptr->sssize;
-		pseg = ssptr->sspseg;
-		this_segment = stkl - size;
-	}
-
-	result = address - this_segment;
-
-	/* If you subtract pseg from the current end of the stack, you get the
-	 * address of the previous stack segment's end. This seems a little
-	 * convoluted to me, but I'll bet you save a cycle somewhere.  */
-
-	while (pseg != 0) {
-#ifdef DEBUG_I00AFUNC
-		fprintf(stderr, "%011o %011o\n", pseg, size);
-#endif
-		stkl = stkl - pseg;
-		ssptr = (struct stack_segment_linkage *) stkl;
-		size = ssptr->sssize;
-		pseg = ssptr->sspseg;
-		result += size;
-	}
-	return (result);
-}
-#endif				/* not CRAY2 */
-#endif				/* CRAY */
-
-#endif				/* no alloca */
-#endif				/* not GCC version 2 */
--- a/src/asm/fstack.c
+++ b/src/asm/fstack.c
@@ -5,6 +5,8 @@
  *
  */
 
+#include <errno.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -14,7 +16,13 @@
 #include "asm/types.h"
 #include "asm/main.h"
 #include "asm/lexer.h"
+#include "extern/err.h"
+#include "extern/strl.h"
 
+#ifndef PATH_MAX
+#define PATH_MAX 256
+#endif
+
 /*
  * RGBAsm - FSTACK.C (FileStack routines)
  *
@@ -62,8 +70,7 @@
 	while (*ppFileStack)
 		ppFileStack = &((*ppFileStack)->pNext);
 
-	if ((*ppFileStack =
-		(struct sContext *) malloc(sizeof(struct sContext))) != NULL) {
+	if ((*ppFileStack = malloc(sizeof(struct sContext))) != NULL) {
 		(*ppFileStack)->FlexHandle = CurrentFlexHandle;
 		(*ppFileStack)->pNext = NULL;
 		strcpy((char *) (*ppFileStack)->tzFileName,
@@ -195,28 +202,33 @@
 	strcpy(IncludePaths[NextIncPath++], s);
 }
 
-void 
-fstk_FindFile(char *s)
+FILE *
+fstk_FindFile(char *fname)
 {
-	char t[_MAX_PATH + 1];
-	SLONG i = -1;
+	char path[PATH_MAX];
+	int i;
+	FILE *f;
 
-	strcpy(t, s);
+	if ((f = fopen(fname, "rb")) != NULL || errno != ENOENT) {
+		return f;
+	}
 
-	while (i < NextIncPath) {
-		FILE *f;
-
-		if ((f = fopen(t, "rb")) != NULL) {
-			fclose(f);
-			strcpy(s, t);
-			return;
+	for (i = 0; i < NextIncPath; ++i) {
+		if (strlcpy(path, IncludePaths[i], sizeof path) >= 
+		    sizeof path) {
+			continue;
 		}
-		i += 1;
-		if (i < NextIncPath) {
-			strcpy(t, IncludePaths[i]);
-			strcat(t, s);
+		if (strlcat(path, fname, sizeof path) >= sizeof path) {
+			continue;
 		}
+
+		if ((f = fopen(path, "rb")) != NULL || errno != ENOENT) {
+			return f;
+		}
 	}
+
+	errno = ENOENT;
+	return NULL;
 }
 /*
  * RGBAsm - FSTACK.C (FileStack routines)
@@ -225,33 +237,30 @@
  *
  */
 
-ULONG 
+void
 fstk_RunInclude(char *tzFileName)
 {
 	FILE *f;
 
-	//printf("INCLUDE: %s\n", s);
+	f = fstk_FindFile(tzFileName);
 
-	fstk_FindFile(tzFileName);
-	//printf("INCLUDING: %s\n", tzFileName);
+	if (f == NULL) {
+		err(1, "Unable to open included file '%s'",
+		    tzFileName);
+	}
 
-	if ((f = fopen(tzFileName, "r")) != NULL) {
-		pushcontext();
-		nLineNo = 1;
-		nCurrentStatus = STAT_isInclude;
-		strcpy(tzCurrentFileName, tzFileName);
-		pCurrentFile = f;
-		CurrentFlexHandle = yy_create_buffer(pCurrentFile);
-		yy_switch_to_buffer(CurrentFlexHandle);
+	pushcontext();
+	nLineNo = 1;
+	nCurrentStatus = STAT_isInclude;
+	strcpy(tzCurrentFileName, tzFileName);
+	pCurrentFile = f;
+	CurrentFlexHandle = yy_create_buffer(pCurrentFile);
+	yy_switch_to_buffer(CurrentFlexHandle);
 
-		//Dirty hack to give the INCLUDE directive a linefeed
+	//Dirty hack to give the INCLUDE directive a linefeed
 
-		    yyunput('\n');
-		nLineNo -= 1;
-
-		return (1);
-	} else
-		return (0);
+	yyunput('\n');
+	nLineNo -= 1;
 }
 /*
  * RGBAsm - FSTACK.C (FileStack routines)
@@ -360,7 +369,7 @@
  *
  */
 
-ULONG 
+void
 fstk_Init(char *s)
 {
 	char tzFileName[_MAX_PATH + 1];
@@ -368,17 +377,16 @@
 	sym_AddString("__FILE__", s);
 
 	strcpy(tzFileName, s);
-	fstk_FindFile(tzFileName);
-
 	pFileStack = NULL;
-	if ((pCurrentFile = fopen(tzFileName, "r")) != NULL) {
-		nMacroCount = 0;
-		nCurrentStatus = STAT_isInclude;
-		strcpy(tzCurrentFileName, tzFileName);
-		CurrentFlexHandle = yy_create_buffer(pCurrentFile);
-		yy_switch_to_buffer(CurrentFlexHandle);
-		nLineNo = 1;
-		return (1);
-	} else
-		return (0);
+	pCurrentFile = fopen(tzFileName, "rb");
+	if (pCurrentFile == NULL) {
+		err(1, "Unable to open file '%s'", tzFileName);
+	}
+
+	nMacroCount = 0;
+	nCurrentStatus = STAT_isInclude;
+	strcpy(tzCurrentFileName, tzFileName);
+	CurrentFlexHandle = yy_create_buffer(pCurrentFile);
+	yy_switch_to_buffer(CurrentFlexHandle);
+	nLineNo = 1;
 }
--- a/src/asm/gameboy/yaccprt4.y
+++ b/src/asm/gameboy/yaccprt4.y
@@ -290,6 +290,8 @@
 
 z80_ld_hl		:	T_Z80_LD T_MODE_HL comma '[' T_MODE_SP const_8bit ']'
 					{ out_AbsByte(0xF8); out_RelByte(&$6); }
+				|	T_Z80_LD T_MODE_HL comma T_MODE_SP const_8bit
+					{ out_AbsByte(0xF8); out_RelByte(&$5); }
 				|	T_Z80_LD T_MODE_HL comma const_16bit
 					{ out_AbsByte(0x01|(REG_HL<<4)); out_RelWord(&$4); }
 ;
--- a/src/asm/globlex.c
+++ b/src/asm/globlex.c
@@ -208,10 +208,14 @@
 	char *s;
 
 	yyskipbytes(size);
-	if ((s = sym_FindMacroArg(src[1] - '0')) != NULL) {
-		yyunputstr(s);
+	if ((size == 2 && src[1] >= '1' && src[1] <= '9')) {
+		if ((s = sym_FindMacroArg(src[1] - '0')) != NULL) {
+			yyunputstr(s);
+		} else {
+			yyerror("Macro argument not defined");
+		}
 	} else {
-		yyerror("Macro argument not defined");
+		yyerror("Invalid macro argument");
 	}
 	return (0);
 }
@@ -219,9 +223,15 @@
 ULONG 
 PutUniqueArg(char *src, ULONG size)
 {
+	char *s;
+
 	src = src;
 	yyskipbytes(size);
-	yyunputstr(sym_FindMacroArg(-1));
+	if ((s = sym_FindMacroArg(-1)) != NULL) {
+		yyunputstr(s);
+	} else {
+		yyerror("Macro unique label string not defined");
+	}
 	return (0);
 }
 
@@ -388,7 +398,7 @@
 
 	    id = lex_FloatAlloc(&tMacroArgToken);
 	lex_FloatAddFirstRange(id, '\\', '\\');
-	lex_FloatAddSecondRange(id, '0', '9');
+	lex_FloatAddSecondRange(id, '1', '9');
 	id = lex_FloatAlloc(&tMacroUniqueToken);
 	lex_FloatAddFirstRange(id, '\\', '\\');
 	lex_FloatAddSecondRange(id, '@', '@');
--- a/src/asm/lexer.c
+++ b/src/asm/lexer.c
@@ -11,6 +11,7 @@
 #include "asm/main.h"
 #include "asm/rpn.h"
 #include "asm/fstack.h"
+#include "extern/err.h"
 
 #include "asmy.h"
 
@@ -20,18 +21,18 @@
 	ULONG nNameLength;
 	struct sLexString *pNext;
 };
-#define pLexBuffer		(pCurrentBuffer->pBuffer)
-#define nLexBufferLeng	(pCurrentBuffer->nBufferSize)
+#define pLexBufferRealStart (pCurrentBuffer->pBufferRealStart)
+#define pLexBuffer		    (pCurrentBuffer->pBuffer)
+#define AtLineStart	        (pCurrentBuffer->oAtLineStart)
 
 #define SAFETYMARGIN	1024
 
-extern ULONG symvaluetostring(char *dest, char *s);
+extern size_t symvaluetostring(char *dest, size_t maxLength, char *sym);
 
 struct sLexFloat tLexFloat[32];
 struct sLexString *tLexHash[LEXHASHSIZE];
 YY_BUFFER_STATE pCurrentBuffer;
-ULONG yyleng;
-ULONG nLexMaxLeng;
+ULONG nLexMaxLength; // max length of all keywords and operators
 
 ULONG tFloatingSecondChar[256];
 ULONG tFloatingFirstChar[256];
@@ -39,8 +40,6 @@
 ULONG nFloating;
 enum eLexerState lexerstate = LEX_STATE_NORMAL;
 
-#define AtLineStart	pCurrentBuffer->oAtLineStart
-
 #ifdef __GNUC__
 void 
 strupr(char *s)
@@ -75,6 +74,9 @@
 void 
 yyunput(char c)
 {
+	if (pLexBuffer <= pLexBufferRealStart)
+		fatalerror("Buffer safety margin exceeded");
+
 	*(--pLexBuffer) = c;
 }
 
@@ -81,12 +83,15 @@
 void 
 yyunputstr(char *s)
 {
-	SLONG i;
+	int i, len;
 
-	i = strlen(s) - 1;
+	len = strlen(s);
 
-	while (i >= 0)
-		yyunput(s[i--]);
+	if (pLexBuffer - len < pLexBufferRealStart)
+		fatalerror("Buffer safety margin exceeded");
+
+	for (i = len - 1; i >= 0; i--)
+		*(--pLexBuffer) = s[i];
 }
 
 void 
@@ -113,13 +118,11 @@
 {
 	YY_BUFFER_STATE pBuffer;
 
-	if ((pBuffer =
-		(YY_BUFFER_STATE) malloc(sizeof(struct yy_buffer_state))) !=
-	    NULL) {
-		if ((pBuffer->pBuffer = pBuffer->pBufferStart =
-			(char *) malloc(size + 1 + SAFETYMARGIN)) != NULL) {
-			pBuffer->pBuffer += SAFETYMARGIN;
-			pBuffer->pBufferStart += SAFETYMARGIN;
+	if ((pBuffer = malloc(sizeof(struct yy_buffer_state))) != NULL) {
+		if ((pBuffer->pBufferRealStart =
+		    malloc(size + 1 + SAFETYMARGIN)) != NULL) {
+			pBuffer->pBufferStart = pBuffer->pBufferRealStart + SAFETYMARGIN;
+			pBuffer->pBuffer = pBuffer->pBufferRealStart + SAFETYMARGIN;
 			memcpy(pBuffer->pBuffer, mem, size);
 			pBuffer->nBufferSize = size;
 			pBuffer->oAtLineStart = 1;
@@ -136,9 +139,7 @@
 {
 	YY_BUFFER_STATE pBuffer;
 
-	if ((pBuffer =
-		(YY_BUFFER_STATE) malloc(sizeof(struct yy_buffer_state))) !=
-	    NULL) {
+	if ((pBuffer = malloc(sizeof(struct yy_buffer_state))) != NULL) {
 		ULONG size;
 
 		fseek(f, 0, SEEK_END);
@@ -145,13 +146,13 @@
 		size = ftell(f);
 		fseek(f, 0, SEEK_SET);
 
-		if ((pBuffer->pBuffer = pBuffer->pBufferStart =
-			(char *) malloc(size + 2 + SAFETYMARGIN)) != NULL) {
+		if ((pBuffer->pBufferRealStart =
+			malloc(size + 2 + SAFETYMARGIN)) != NULL) {
 			char *mem;
 			ULONG instring = 0;
 
-			pBuffer->pBuffer += SAFETYMARGIN;
-			pBuffer->pBufferStart += SAFETYMARGIN;
+			pBuffer->pBufferStart = pBuffer->pBufferRealStart + SAFETYMARGIN;
+			pBuffer->pBuffer = pBuffer->pBufferRealStart + SAFETYMARGIN;
 
 			size = fread(pBuffer->pBuffer, sizeof(UBYTE), size, f);
 
@@ -165,11 +166,14 @@
 				if (*mem == '\"')
 					instring = 1 - instring;
 
-				if (instring) {
+				if (mem[0] == '\\' &&
+				    (mem[1] == '\"' || mem[1] == '\\')) {
+					mem += 2;
+				} else if (instring) {
 					mem += 1;
 				} else {
 					if ((mem[0] == 10 && mem[1] == 13)
-					    || (mem[0] == 13 && mem[1] == 10)) {
+					 || (mem[0] == 13 && mem[1] == 10)) {
 						mem[0] = ' ';
 						mem[1] = '\n';
 						mem += 2;
@@ -176,17 +180,12 @@
 					} else if (mem[0] == 10 || mem[0] == 13) {
 						mem[0] = '\n';
 						mem += 1;
-					} else if (mem[0] == '\n'
-					    && mem[1] == '*') {
+					} else if (mem[0] == '\n' && mem[1] == '*') {
 						mem += 1;
-						while (!
-						    (*mem == '\n'
-							|| *mem == '\0'))
+						while (!(*mem == '\n' || *mem == '\0'))
 							*mem++ = ' ';
 					} else if (*mem == ';') {
-						while (!
-						    (*mem == '\n'
-							|| *mem == '\0'))
+						while (!(*mem == '\n' || *mem == '\0'))
 							*mem++ = ' ';
 					} else
 						mem += 1;
@@ -201,17 +200,32 @@
 	return (NULL);
 }
 
-ULONG 
-lex_FloatAlloc(struct sLexFloat * tok)
+ULONG
+lex_FloatAlloc(struct sLexFloat *token)
 {
-	tLexFloat[nFloating] = (*tok);
+	tLexFloat[nFloating] = *token;
 
 	return (1 << (nFloating++));
 }
 
+/*
+ * Make sure that only non-zero ASCII characters are used. Also, check if the
+ * start is greater than the end of the range.
+ */
+void
+lex_CheckCharacterRange(UWORD start, UWORD end)
+{
+	if (start > end || start < 1 || end > 127) {
+		errx(1, "Invalid character range (start: %u, end: %u)",
+		        start, end);
+	}
+}
+
 void 
 lex_FloatDeleteRange(ULONG id, UWORD start, UWORD end)
 {
+	lex_CheckCharacterRange(start, end);
+
 	while (start <= end) {
 		tFloatingChars[start] &= ~id;
 		start += 1;
@@ -221,6 +235,8 @@
 void 
 lex_FloatAddRange(ULONG id, UWORD start, UWORD end)
 {
+	lex_CheckCharacterRange(start, end);
+
 	while (start <= end) {
 		tFloatingChars[start] |= id;
 		start += 1;
@@ -230,6 +246,8 @@
 void 
 lex_FloatDeleteFirstRange(ULONG id, UWORD start, UWORD end)
 {
+	lex_CheckCharacterRange(start, end);
+
 	while (start <= end) {
 		tFloatingFirstChar[start] &= ~id;
 		start += 1;
@@ -239,6 +257,8 @@
 void 
 lex_FloatAddFirstRange(ULONG id, UWORD start, UWORD end)
 {
+	lex_CheckCharacterRange(start, end);
+
 	while (start <= end) {
 		tFloatingFirstChar[start] |= id;
 		start += 1;
@@ -248,6 +268,8 @@
 void 
 lex_FloatDeleteSecondRange(ULONG id, UWORD start, UWORD end)
 {
+	lex_CheckCharacterRange(start, end);
+
 	while (start <= end) {
 		tFloatingSecondChar[start] &= ~id;
 		start += 1;
@@ -257,6 +279,8 @@
 void 
 lex_FloatAddSecondRange(ULONG id, UWORD start, UWORD end)
 {
+	lex_CheckCharacterRange(start, end);
+
 	while (start <= end) {
 		tFloatingSecondChar[start] |= id;
 		start += 1;
@@ -264,32 +288,32 @@
 }
 
 struct sLexFloat *
-lexgetfloat(ULONG id)
+lexgetfloat(ULONG nFloatMask)
 {
-	ULONG r = 0, mask = 1;
+	if (nFloatMask == 0) {
+		fatalerror("Internal error in lexgetfloat");
+	}
 
-	if (id == 0)
-		return (NULL);
+	int i = 0;
 
-	while ((id & mask) == 0) {
-		mask <<= 1;
-		r += 1;
+	while ((nFloatMask & 1) == 0) {
+		nFloatMask >>= 1;
+		i++;
 	}
 
-	return (&tLexFloat[r]);
+	return (&tLexFloat[i]);
 }
 
 ULONG 
 lexcalchash(char *s)
 {
-	ULONG r = 0;
+	ULONG hash = 0;
 
 	while (*s) {
-		r = ((r << 1) + (toupper(*s))) % LEXHASHSIZE;
-		s += 1;
+		hash = (hash * 283) ^ toupper(*s++);
 	}
 
-	return (r);
+	return (hash % LEXHASHSIZE);
 }
 
 void 
@@ -297,17 +321,17 @@
 {
 	ULONG i;
 
-	for (i = 0; i < LEXHASHSIZE; i += 1) {
+	for (i = 0; i < LEXHASHSIZE; i++) {
 		tLexHash[i] = NULL;
 	}
 
-	for (i = 0; i < 256; i += 1) {
+	for (i = 0; i < 256; i++) {
 		tFloatingFirstChar[i] = 0;
 		tFloatingSecondChar[i] = 0;
 		tFloatingChars[i] = 0;
 	}
 
-	nLexMaxLeng = 0;
+	nLexMaxLength = 0;
 	nFloating = 0;
 }
 
@@ -322,11 +346,7 @@
 		while (*ppHash)
 			ppHash = &((*ppHash)->pNext);
 
-		//printf("%s has hashvalue %d\n", lex->tzName, hash);
-
-		if (((*ppHash) =
-			(struct sLexString *) malloc(sizeof(struct sLexString))) !=
-		    NULL) {
+		if (((*ppHash) = malloc(sizeof(struct sLexString))) != NULL) {
 			if (((*ppHash)->tzName =
 				(char *) strdup(lex->tzName)) != NULL) {
 				(*ppHash)->nNameLength = strlen(lex->tzName);
@@ -335,8 +355,8 @@
 
 				strupr((*ppHash)->tzName);
 
-				if ((*ppHash)->nNameLength > nLexMaxLeng)
-					nLexMaxLeng = (*ppHash)->nNameLength;
+				if ((*ppHash)->nNameLength > nLexMaxLength)
+					nLexMaxLength = (*ppHash)->nNameLength;
 
 			} else
 				fatalerror("Out of memory!");
@@ -347,458 +367,390 @@
 	}
 }
 
-ULONG 
-yylex(void)
+/*
+ * Gets the "float" mask and "float" length.
+ * "Float" refers to the token type of a token that is not a keyword.
+ * The character classes floatingFirstChar, floatingSecondChar, and
+ * floatingChars are defined separately for each token type.
+ * It uses bit masks to match against a set of simple regular expressions
+ * of the form /[floatingFirstChar]([floatingSecondChar][floatingChars]*)?/.
+ * The token types with the longest match from the current position in the
+ * buffer will have their bits set in the float mask.
+ */
+void
+yylex_GetFloatMaskAndFloatLen(ULONG *pnFloatMask, ULONG *pnFloatLen)
 {
-	ULONG hash, maxlen;
-	char *s;
-	struct sLexString *pLongestFixed = NULL;
-	ULONG nFloatMask, nOldFloatMask, nFloatLen;
-	ULONG linestart = AtLineStart;
+	// Note that '\0' should always have a bit mask of 0 in the "floating"
+	// tables, so it doesn't need to be checked for separately.
 
-	switch (lexerstate) {
-	case LEX_STATE_NORMAL:
-		AtLineStart = 0;
+	char *s = pLexBuffer;
+	ULONG nOldFloatMask = 0;
+	ULONG nFloatMask = tFloatingFirstChar[(int)*s];
 
-scanagain:
+	if (nFloatMask != 0) {
+		s++;
+		nOldFloatMask = nFloatMask;
+		nFloatMask &= tFloatingSecondChar[(int)*s];
 
-		while (*pLexBuffer == ' ' || *pLexBuffer == '\t') {
-			linestart = 0;
-			pLexBuffer += 1;
+		while (nFloatMask != 0) {
+			s++;
+			nOldFloatMask = nFloatMask;
+			nFloatMask &= tFloatingChars[(int)*s];
 		}
+	}
 
-		if (*pLexBuffer == 0) {
-			if (yywrap() == 0) {
-				linestart = AtLineStart;
-				AtLineStart = 0;
-				goto scanagain;
+	*pnFloatMask = nOldFloatMask;
+	*pnFloatLen = (ULONG)(s - pLexBuffer);
+}
+
+/*
+ * Gets the longest keyword/operator from the current position in the buffer.
+ */
+struct sLexString *
+yylex_GetLongestFixed()
+{
+	struct sLexString *pLongestFixed = NULL;
+	char *s = pLexBuffer;
+	ULONG hash = 0;
+	ULONG length = 0;
+
+	while (length < nLexMaxLength && *s) {
+		hash = (hash * 283) ^ toupper(*s);
+		s++;
+		length++;
+
+		struct sLexString *lex = tLexHash[hash % LEXHASHSIZE];
+
+		while (lex) {
+			if (lex->nNameLength == length
+			 && strncasecmp(pLexBuffer, lex->tzName, length) == 0) {
+				pLongestFixed = lex;
+				break;
 			}
+			lex = lex->pNext;
 		}
-		s = pLexBuffer;
-		nOldFloatMask = nFloatLen = 0;
-		nFloatMask = tFloatingFirstChar[(int) *s++];
-		while (nFloatMask && nFloatLen < nLexBufferLeng) {
-			nFloatLen += 1;
-			nOldFloatMask = nFloatMask;
-			if (nFloatLen == 1)
-				nFloatMask &= tFloatingSecondChar[(int) *s++];
-			else
-				nFloatMask &= tFloatingChars[(int) *s++];
+	}
+
+	return pLongestFixed;
+}
+
+size_t
+CopyMacroArg(char *dest, size_t maxLength, char c)
+{
+	int i;
+	char *s;
+	int argNum;
+	
+	switch (c) {
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+		argNum = c - '0';
+		break;
+	case '@':
+		argNum = -1;
+		break;
+	default:
+		return 0;
+	}
+
+	if ((s = sym_FindMacroArg(argNum)) == NULL)
+		fatalerror("Macro argument not defined");
+
+	for (i = 0; s[i] != 0; i++) {
+		if (i >= maxLength) {
+			fatalerror("Macro argument too long to fit buffer");
 		}
+		dest[i] = s[i];
+	}
 
-		maxlen = nLexBufferLeng;
-		if (nLexMaxLeng < maxlen)
-			maxlen = nLexMaxLeng;
+	return i;
+}
 
-		yyleng = 0;
-		hash = 0;
-		s = pLexBuffer;
-		while (yyleng < nLexMaxLeng) {
-			/* XXX: Kludge warning! The dereference of s below
-			 * may go beyond the end of the buffer. We use the
-			 * following test to stop that from happening,
-			 * without really understanding what the rest of
-			 * the code is doing. This may not be the correct
-			 * fix! */
-			if (!*s)
+static inline void
+yylex_StringWriteChar(char *s, size_t index, char c)
+{
+	if (index >= MAXSTRLEN) {
+		fatalerror("String too long");
+	}
+
+	s[index] = c;
+}
+
+static inline void
+yylex_SymbolWriteChar(char *s, size_t index, char c)
+{
+	if (index >= MAXSYMLEN) {
+		fatalerror("Symbol too long");
+	}
+
+	s[index] = c;
+}
+
+/*
+ * Trims white space at the end of a string.
+ * The index parameter is the index of the 0 at the end of the string.
+ */
+void yylex_TrimEnd(char *s, size_t index)
+{
+	int i;
+
+	for (i = (int)index - 1; i >= 0 && (s[i] == ' ' || s[i] == '\t'); i--)
+		s[i] = 0;
+}
+
+size_t
+yylex_ReadBracketedSymbol(char *dest, size_t index)
+{
+	char sym[MAXSYMLEN + 1];
+	char ch;
+	size_t i = 0;
+	size_t length, maxLength;
+
+	for (ch = *pLexBuffer;
+	     ch != '}' && ch != '"' && ch != '\n';
+		 ch = *(++pLexBuffer)) {
+		if (ch == '\\') {
+			ch = *(++pLexBuffer);
+			maxLength = MAXSYMLEN - i;
+			length = CopyMacroArg(&sym[i], maxLength, ch);
+
+			if (length != 0)
+				i += length;
+			else
+				fatalerror("Illegal character escape '%c'", ch);
+		} else
+			yylex_SymbolWriteChar(sym, i++, ch);
+	}
+
+	yylex_SymbolWriteChar(sym, i, 0);
+
+	maxLength = MAXSTRLEN - index; // it's assumed we're writing to a T_STRING
+	length = symvaluetostring(&dest[index], maxLength, sym);
+
+	if (*pLexBuffer == '}')
+		pLexBuffer++;
+	else
+		yyerror("Missing }");
+
+	return length;
+}
+
+void
+yylex_ReadQuotedString()
+{
+	size_t index = 0;
+	size_t length, maxLength;
+
+	while (*pLexBuffer != '"' && *pLexBuffer != '\n') {
+		char ch = *pLexBuffer++;
+
+		if (ch == '\\') {
+			ch = *pLexBuffer++;
+
+			switch (ch) {
+			case 'n':
+				ch = '\n';
 				break;
+			case 't':
+				ch = '\t';
+				break;
+			case '\\':
+				ch = '\\';
+				break;
+			case '"':
+				ch = '"';
+				break;
+			default:
+				maxLength = MAXSTRLEN - index;
+				length = CopyMacroArg(&yylval.tzString[index], maxLength, ch);
 
-			yyleng += 1;
-			hash = ((hash << 1) + (toupper(*s))) % LEXHASHSIZE;
-			s += 1;
-			if (tLexHash[hash]) {
-				struct sLexString *lex;
+				if (length != 0)
+					index += length;
+				else
+					fatalerror("Illegal character escape '%c'", ch);
 
-				lex = tLexHash[hash];
-				while (lex) {
-					if (lex->nNameLength == yyleng) {
-						if (strncasecmp
-						    (pLexBuffer, lex->tzName,
-							yyleng) == 0) {
-							pLongestFixed = lex;
-						}
-					}
-					lex = lex->pNext;
-				}
+				ch = 0;
+				break;
 			}
+		} else if (ch == '{') {
+			// Get bracketed symbol within string.
+			index += yylex_ReadBracketedSymbol(yylval.tzString, index);
+			ch = 0;
 		}
 
-		if (nFloatLen == 0 && pLongestFixed == NULL) {
-			if (*pLexBuffer == '"') {
-				ULONG index = 0;
+		if (ch)
+			yylex_StringWriteChar(yylval.tzString, index++, ch);
+	}
 
-				pLexBuffer += 1;
-				while ((*pLexBuffer != '"')
-				    && (*pLexBuffer != '\n')) {
-					char ch, *marg;
+	yylex_StringWriteChar(yylval.tzString, index, 0);
 
-					if ((ch = *pLexBuffer++) == '\\') {
-						switch (ch = (*pLexBuffer++)) {
-						case 'n':
-							ch = '\n';
-							break;
-						case 't':
-							ch = '\t';
-							break;
-						case '0':
-						case '1':
-						case '2':
-						case '3':
-						case '4':
-						case '5':
-						case '6':
-						case '7':
-						case '8':
-						case '9':
-							if ((marg =
-								sym_FindMacroArg(ch
-								    -
-								    '0'))
-							    != NULL) {
-								while (*marg)
-									yylval.
-									    tzString
-									    [index++]
-									    =
-									    *marg++;
-								ch = 0;
-							}
-							break;
-						case '@':
-							if ((marg =
-								sym_FindMacroArg
-								(-1)) != NULL) {
-								while (*marg)
-									yylval.
-									    tzString
-									    [index++]
-									    =
-									    *marg++;
-								ch = 0;
-							}
-							break;
-						}
-					} else if (ch == '{') {
-						char sym[MAXSYMLEN];
-						int i = 0;
+	if (*pLexBuffer == '"')
+		pLexBuffer++;
+	else
+		yyerror("Unterminated string");
+}
 
-						while ((*pLexBuffer != '}')
-						    && (*pLexBuffer != '"')
-						    && (*pLexBuffer !=
-							'\n')) {
-							if ((ch =
-								*pLexBuffer++) ==
-							    '\\') {
-								switch (ch =
-								    (*pLexBuffer++)) {
-								case '0':
-								case '1':
-								case '2':
-								case '3':
-								case '4':
-								case '5':
-								case '6':
-								case '7':
-								case '8':
-								case '9':
-									if ((marg = sym_FindMacroArg(ch - '0')) != NULL) {
-										while
-										    (*marg)
-											sym[i++] = *marg++;
-										ch = 0;
-									}
-									break;
-								case '@':
-									if ((marg = sym_FindMacroArg(-1)) != NULL) {
-										while
-										    (*marg)
-											sym[i++] = *marg++;
-										ch = 0;
-									}
-									break;
-								}
-							} else
-								sym[i++] = ch;
-						}
+ULONG
+yylex_NORMAL()
+{
+	struct sLexString *pLongestFixed = NULL;
+	ULONG nFloatMask, nFloatLen;
+	ULONG linestart = AtLineStart;
 
-						sym[i] = 0;
-						index +=
-						    symvaluetostring(&yylval.
-						    tzString
-						    [index],
-						    sym);
-						if (*pLexBuffer == '}')
-							pLexBuffer += 1;
-						else
-							yyerror("Missing }");
-						ch = 0;
-					}
-					if (ch)
-						yylval.tzString[index++] = ch;
-				}
+	AtLineStart = 0;
 
-				yylval.tzString[index++] = 0;
+scanagain:
+	while (*pLexBuffer == ' ' || *pLexBuffer == '\t') {
+		linestart = 0;
+		pLexBuffer++;
+	}
 
-				if (*pLexBuffer == '\n')
-					yyerror("Unterminated string");
-				else
-					pLexBuffer += 1;
+	if (*pLexBuffer == 0) {
+		// Reached the end of a file, macro, or rept.
+		if (yywrap() == 0) {
+			linestart = AtLineStart;
+			AtLineStart = 0;
+			goto scanagain;
+		}
+	}
 
-				return (T_STRING);
-			} else if (*pLexBuffer == '{') {
-				char sym[MAXSYMLEN], ch, *marg;
-				int i = 0;
+	// Try to match an identifier, macro argument (e.g. \1),
+	// or numeric literal.
+	yylex_GetFloatMaskAndFloatLen(&nFloatMask, &nFloatLen);
 
-				pLexBuffer += 1;
+	// Try to match a keyword or operator.
+	pLongestFixed = yylex_GetLongestFixed();
 
-				while ((*pLexBuffer != '}')
-				    && (*pLexBuffer != '\n')) {
-					if ((ch = *pLexBuffer++) == '\\') {
-						switch (ch = (*pLexBuffer++)) {
-						case '0':
-						case '1':
-						case '2':
-						case '3':
-						case '4':
-						case '5':
-						case '6':
-						case '7':
-						case '8':
-						case '9':
-							if ((marg =
-								sym_FindMacroArg(ch
-								    -
-								    '0'))
-							    != NULL) {
-								while (*marg)
-									sym[i++]
-									    =
-									    *marg++;
-								ch = 0;
-							}
-							break;
-						case '@':
-							if ((marg =
-								sym_FindMacroArg
-								(-1)) != NULL) {
-								while (*marg)
-									sym[i++]
-									    =
-									    *marg++;
-								ch = 0;
-							}
-							break;
-						}
-					} else
-						sym[i++] = ch;
-				}
-				sym[i] = 0;
-				symvaluetostring(yylval.tzString, sym);
-				if (*pLexBuffer == '}')
-					pLexBuffer += 1;
-				else
-					yyerror("Missing }");
+	if (nFloatLen == 0 && pLongestFixed == NULL) {
+		// No keyword, identifier, operator, or numerical literal matches.
 
-				return (T_STRING);
-			} else {
-				if (*pLexBuffer == '\n')
-					AtLineStart = 1;
+		if (*pLexBuffer == '"') {
+			pLexBuffer++;
+			yylex_ReadQuotedString();
+			return T_STRING;
+		} else if (*pLexBuffer == '{') {
+			pLexBuffer++;
+			yylex_ReadBracketedSymbol(yylval.tzString, 0);
+			return T_STRING;
+		} else {
+			// It's not a keyword, operator, identifier, macro argument,
+			// numeric literal, string, or bracketed symbol, so just return
+			// the ASCII character.
+			if (*pLexBuffer == '\n')
+				AtLineStart = 1;
 
-				yyleng = 1;
-				return (*pLexBuffer++);
-			}
+			return *pLexBuffer++;
 		}
-		if (nFloatLen == 0) {
-			yyleng = pLongestFixed->nNameLength;
-			pLexBuffer += yyleng;
-			return (pLongestFixed->nToken);
-		}
-		if (pLongestFixed == NULL) {
-			struct sLexFloat *tok;
+	}
 
-			tok = lexgetfloat(nOldFloatMask);
-			yyleng = nFloatLen;
-			if (tok->Callback) {
-				if (tok->Callback(pLexBuffer, yyleng) == 0)
-					goto scanagain;
-			}
-			if (tok->nToken == T_ID && linestart) {
-				pLexBuffer += yyleng;
-				return (T_LABEL);
-			} else {
-				pLexBuffer += yyleng;
-				return (tok->nToken);
-			}
+	if (pLongestFixed == NULL || nFloatLen > pLongestFixed->nNameLength) {
+		// Longest match was an identifier, macro argument, or numeric literal.
+		struct sLexFloat *token = lexgetfloat(nFloatMask);
+
+		if (token->Callback) {
+			int done = token->Callback(pLexBuffer, nFloatLen);
+			if (!done)
+				goto scanagain;
 		}
-		if (nFloatLen > pLongestFixed->nNameLength) {
-			struct sLexFloat *tok;
 
-			tok = lexgetfloat(nOldFloatMask);
-			yyleng = nFloatLen;
-			if (tok->Callback) {
-				if (tok->Callback(pLexBuffer, yyleng) == 0)
-					goto scanagain;
-			}
-			if (tok->nToken == T_ID && linestart) {
-				pLexBuffer += yyleng;
-				return (T_LABEL);
-			} else {
-				pLexBuffer += yyleng;
-				return (tok->nToken);
-			}
+		pLexBuffer += nFloatLen;
+
+		if (token->nToken == T_ID && linestart) {
+			return T_LABEL;
 		} else {
-			yyleng = pLongestFixed->nNameLength;
-			pLexBuffer += yyleng;
-			return (pLongestFixed->nToken);
+			return token->nToken;
 		}
-		break;
+	}
 
-	case LEX_STATE_MACROARGS:
-		{
-			ULONG index = 0;
+	// Longest match was a keyword or operator.
+	pLexBuffer += pLongestFixed->nNameLength;
+	return pLongestFixed->nToken;
+}
 
-			while (*pLexBuffer == ' ' || *pLexBuffer == '\t') {
-				linestart = 0;
-				pLexBuffer += 1;
-			}
+ULONG
+yylex_MACROARGS()
+{
+	size_t index = 0;
+	size_t length, maxLength;
 
-			while ((*pLexBuffer != ',')
-			    && (*pLexBuffer != '\n')) {
-				char ch, *marg;
+	while (*pLexBuffer == ' ' || *pLexBuffer == '\t') {
+		pLexBuffer++;
+	}
 
-				if ((ch = *pLexBuffer++) == '\\') {
-					switch (ch = (*pLexBuffer++)) {
-					case 'n':
-						ch = '\n';
-						break;
-					case 't':
-						ch = '\t';
-						break;
-					case '0':
-					case '1':
-					case '2':
-					case '3':
-					case '4':
-					case '5':
-					case '6':
-					case '7':
-					case '8':
-					case '9':
-						if ((marg =
-							sym_FindMacroArg(ch -
-							    '0')) !=
-						    NULL) {
-							while (*marg)
-								yylval.
-								    tzString
-								    [index++] =
-								    *marg++;
-							ch = 0;
-						}
-						break;
-					case '@':
-						if ((marg =
-							sym_FindMacroArg(-1)) !=
-						    NULL) {
-							while (*marg)
-								yylval.
-								    tzString
-								    [index++] =
-								    *marg++;
-							ch = 0;
-						}
-						break;
-					}
-				} else if (ch == '{') {
-					char sym[MAXSYMLEN];
-					int i = 0;
+	while (*pLexBuffer != ',' && (*pLexBuffer != '\n')) {
+		char ch = *pLexBuffer++;
 
-					while ((*pLexBuffer != '}')
-					    && (*pLexBuffer != '"')
-					    && (*pLexBuffer != '\n')) {
-						if ((ch =
-							*pLexBuffer++) == '\\') {
-							switch (ch =
-							    (*pLexBuffer++)) {
-							case '0':
-							case '1':
-							case '2':
-							case '3':
-							case '4':
-							case '5':
-							case '6':
-							case '7':
-							case '8':
-							case '9':
-								if ((marg =
-									sym_FindMacroArg
-									(ch -
-									    '0')) !=
-								    NULL) {
-									while
-									    (*marg)
-										sym[i++] = *marg++;
-									ch = 0;
-								}
-								break;
-							case '@':
-								if ((marg =
-									sym_FindMacroArg
-									(-1)) !=
-								    NULL) {
-									while
-									    (*marg)
-										sym[i++] = *marg++;
-									ch = 0;
-								}
-								break;
-							}
-						} else
-							sym[i++] = ch;
-					}
-					sym[i] = 0;
-					index +=
-					    symvaluetostring(&yylval.
-					    tzString[index],
-					    sym);
-					if (*pLexBuffer == '}')
-						pLexBuffer += 1;
-					else
-						yyerror("Missing }");
-					ch = 0;
-				}
-				if (ch)
-					yylval.tzString[index++] = ch;
-			}
+		if (ch == '\\') {
+			ch = *pLexBuffer++;
 
-			if (index) {
-				yyleng = index;
-				yylval.tzString[index] = 0;
-				if (*pLexBuffer == '\n') {
-					while (yylval.tzString[--index] == ' ') {
-						yylval.tzString[index] = 0;
-						yyleng -= 1;
-					}
-				}
-				return (T_STRING);
-			} else if (*pLexBuffer == '\n') {
-				pLexBuffer += 1;
-				AtLineStart = 1;
-				yyleng = 1;
-				return ('\n');
-			} else if (*pLexBuffer == ',') {
-				pLexBuffer += 1;
-				yyleng = 1;
-				return (',');
-			} else {
-				yyerror("INTERNAL ERROR IN YYLEX");
-				return (0);
+			switch (ch) {
+			case 'n':
+				ch = '\n';
+				break;
+			case 't':
+				ch = '\t';
+				break;
+			case '\\':
+				ch = '\\';
+				break;
+			default:
+				maxLength = MAXSTRLEN - index;
+				length = CopyMacroArg(&yylval.tzString[index], maxLength, ch);
+
+				if (length != 0)
+					index += length;
+				else
+					fatalerror("Illegal character escape '%c'", ch);
+				
+				ch = 0;
+				break;
 			}
+		} else if (ch == '{') {
+			index += yylex_ReadBracketedSymbol(yylval.tzString, index);
+			ch = 0;
 		}
+		if (ch)
+			yylex_StringWriteChar(yylval.tzString, index++, ch);
+	}
 
-		break;
+	if (index) {
+		yylex_StringWriteChar(yylval.tzString, index, 0);
+
+		// trim trailing white space at the end of the line
+		if (*pLexBuffer == '\n')
+			yylex_TrimEnd(yylval.tzString, index);
+
+		return T_STRING;
+	} else if (*pLexBuffer == '\n') {
+		pLexBuffer++;
+		AtLineStart = 1;
+		return '\n';
+	} else if (*pLexBuffer == ',') {
+		pLexBuffer++;
+		return ',';
 	}
 
-	yyerror("INTERNAL ERROR IN YYLEX");
-	return (0);
+	fatalerror("Internal error in yylex_MACROARGS");
+	return 0;
+}
+
+ULONG 
+yylex(void)
+{
+	switch (lexerstate) {
+	case LEX_STATE_NORMAL:
+		return yylex_NORMAL();
+	case LEX_STATE_MACROARGS:
+		return yylex_MACROARGS();
+	}
+
+	fatalerror("Internal error in yylex");
+	return 0;
 }
--- a/src/asm/main.c
+++ b/src/asm/main.c
@@ -19,6 +19,7 @@
 #include "asm/fstack.h"
 #include "asm/output.h"
 #include "asm/main.h"
+#include "extern/err.h"
 
 int yyparse(void);
 void setuplex(void);
@@ -133,9 +134,8 @@
 			newopt.gbgfx[2] = s[3];
 			newopt.gbgfx[3] = s[4];
 		} else {
-			fprintf(stderr, "Must specify exactly 4 characters "
-			    "for option 'g'\n");
-			exit(1);
+			errx(1, "Must specify exactly 4 characters for "
+			    "option 'g'");
 		}
 		break;
 	case 'b':
@@ -143,9 +143,8 @@
 			newopt.binary[0] = s[1];
 			newopt.binary[1] = s[2];
 		} else {
-			fprintf(stderr, "Must specify exactly 2 characters "
-			    "for option 'b'\n");
-			exit(1);
+			errx(1, "Must specify exactly 2 characters for option "
+			    "'b'");
 		}
 		break;
 	case 'z':
@@ -154,12 +153,10 @@
 
 			result = sscanf(&s[1], "%lx", &newopt.fillchar);
 			if (!((result == EOF) || (result == 1))) {
-				fprintf(stderr,
-				    "Invalid argument for option 'z'\n");
-				exit(1);
+				errx(1, "Invalid argument for option 'z'");
 			}
 		} else {
-			fprintf(stderr, "Invalid argument for option 'z'\n");
+			errx(1, "Invalid argument for option 'z'");
 			exit(1);
 		}
 		break;
@@ -176,9 +173,7 @@
 {
 	struct sOptionStackEntry *pOpt;
 
-	if ((pOpt =
-		(struct sOptionStackEntry *)
-		malloc(sizeof(struct sOptionStackEntry))) != NULL) {
+	if ((pOpt = malloc(sizeof(struct sOptionStackEntry))) != NULL) {
 		pOpt->Options = CurrentOptions;
 		pOpt->pNext = pOptionStack;
 		pOptionStack = pOpt;
@@ -245,8 +240,6 @@
 void 
 PrintUsage(void)
 {
-	printf("RGBAsm v" ASM_VERSION " (part of ASMotor " ASMOTOR_VERSION
-	    ")\n\n");
 	printf("Usage: rgbasm [-v] [-h] [-b chars] [-g chars] [-i path] [-o outfile] [-p pad_value]\n"
 	    "              file\n");
 	exit(1);
@@ -295,9 +288,8 @@
 				newopt.binary[0] = optarg[1];
 				newopt.binary[1] = optarg[2];
 			} else {
-				fprintf(stderr, "Must specify exactly "
-				    "2 characters for option 'b'\n");
-				exit(1);
+				errx(1, "Must specify exactly 2 characters for "
+				    "option 'b'");
 			}
 			break;
 		case 'g':
@@ -307,9 +299,8 @@
 				newopt.gbgfx[2] = optarg[3];
 				newopt.gbgfx[3] = optarg[4];
 			} else {
-				fprintf(stderr, "Must specify exactly "
-				    "4 characters for option 'g'\n");
-				exit(1);
+				errx(1, "Must specify exactly 4 characters for "
+				    "option 'g'");
 			}
 			break;
 		case 'h':
@@ -324,14 +315,11 @@
 		case 'p':
 			newopt.fillchar = strtoul(optarg, &ep, 0);
 			if (optarg[0] == '\0' || *ep != '\0') {
-				fprintf(stderr,
-				    "Invalid argument for option 'p'\n");
-				exit(1);
+				errx(1, "Invalid argument for option 'p'");
 			}
 			if (newopt.fillchar < 0 || newopt.fillchar > 0xFF) {
-				fprintf(stderr, "Argument for option 'p' "
-				    "must be between 0 and 0xFF\n");
-				exit(1);
+				errx(1, "Argument for option 'p' must be "
+				    "between 0 and 0xFF");
 			}
 			break;
 		case 'v':
@@ -348,7 +336,6 @@
 
 	DefaultOptions = CurrentOptions;
 
-	/* tzMainfile=argv[argn++]; argc-=1; */
 	tzMainfile = argv[argc - 1];
 
 	setuplex();
@@ -366,75 +353,67 @@
 	nPass = 1;
 	nErrors = 0;
 	sym_PrepPass1();
-	if (fstk_Init(tzMainfile)) {
-		if (CurrentOptions.verbose) {
-			printf("Pass 1...\n");
-		}
+	fstk_Init(tzMainfile);
+	if (CurrentOptions.verbose) {
+		printf("Pass 1...\n");
+	}
 
-		yy_set_state(LEX_STATE_NORMAL);
-		opt_SetCurrentOptions(&DefaultOptions);
+	yy_set_state(LEX_STATE_NORMAL);
+	opt_SetCurrentOptions(&DefaultOptions);
 
-		if (yyparse() == 0 && nErrors == 0) {
-			if (nIFDepth == 0) {
-				nTotalLines = 0;
-				nLineNo = 1;
-				nIFDepth = 0;
-				nPC = 0;
-				nPass = 2;
-				nErrors = 0;
-				sym_PrepPass2();
-				out_PrepPass2();
-				fstk_Init(tzMainfile);
-				yy_set_state(LEX_STATE_NORMAL);
-				opt_SetCurrentOptions(&DefaultOptions);
+	if (yyparse() == 0 && nErrors == 0) {
+		if (nIFDepth == 0) {
+			nTotalLines = 0;
+			nLineNo = 1;
+			nIFDepth = 0;
+			nPC = 0;
+			nPass = 2;
+			nErrors = 0;
+			sym_PrepPass2();
+			out_PrepPass2();
+			fstk_Init(tzMainfile);
+			yy_set_state(LEX_STATE_NORMAL);
+			opt_SetCurrentOptions(&DefaultOptions);
 
-				if (CurrentOptions.verbose) {
-					printf("Pass 2...\n");
-				}
+			if (CurrentOptions.verbose) {
+				printf("Pass 2...\n");
+			}
 
-				if (yyparse() == 0 && nErrors == 0) {
-					double timespent;
+			if (yyparse() == 0 && nErrors == 0) {
+				double timespent;
 
-					nEndClock = clock();
-					timespent =
-					    ((double) (nEndClock - nStartClock))
-					    / (double) CLOCKS_PER_SEC;
-					if (CurrentOptions.verbose) {
-						printf
-						    ("Success! %ld lines in %d.%02d seconds ",
-						    nTotalLines, (int) timespent,
-						    ((int) (timespent * 100.0)) % 100);
-						if (timespent == 0)
-							printf
-							    ("(INFINITY lines/minute)\n");
-						else
-							printf("(%d lines/minute)\n",
-							    (int) (60 / timespent *
-								nTotalLines));
-					}
-					out_WriteObject();
-				} else {
+				nEndClock = clock();
+				timespent =
+				    ((double) (nEndClock - nStartClock))
+				    / (double) CLOCKS_PER_SEC;
+				if (CurrentOptions.verbose) {
 					printf
-					    ("Assembly aborted in pass 2 (%ld errors)!\n",
-					    nErrors);
-					//sym_PrintSymbolTable();
-					exit(5);
+					    ("Success! %ld lines in %d.%02d seconds ",
+					    nTotalLines, (int) timespent,
+					    ((int) (timespent * 100.0)) % 100);
+					if (timespent == 0)
+						printf
+						    ("(INFINITY lines/minute)\n");
+					else
+						printf("(%d lines/minute)\n",
+						    (int) (60 / timespent *
+							nTotalLines));
 				}
+				out_WriteObject();
 			} else {
-				fprintf(stderr,
-				    "Unterminated IF construct (%ld levels)!\n",
-				    nIFDepth);
-				exit(1);
+				printf
+				    ("Assembly aborted in pass 2 (%ld errors)!\n",
+				    nErrors);
+				//sym_PrintSymbolTable();
+				exit(5);
 			}
 		} else {
-			fprintf(stderr,
-			    "Assembly aborted in pass 1 (%ld errors)!\n",
-			    nErrors);
-			exit(1);
+			errx(1, "Unterminated IF construct (%ld levels)!",
+			    nIFDepth);
 		}
 	} else {
-		printf("File '%s' not found\n", tzMainfile);
-		exit(5);
+		errx(1, "Assembly aborted in pass 1 (%ld errors)!",
+		    nErrors);
 	}
-	return (0);
+	return 0;
 }
--- a/src/asm/output.c
+++ b/src/asm/output.c
@@ -18,6 +18,7 @@
 #include "asm/main.h"
 #include "asm/rpn.h"
 #include "asm/fstack.h"
+#include "extern/err.h"
 
 #define SECTIONCHUNK	0x4000
 
@@ -44,6 +45,7 @@
 	ULONG ID;
 	struct sSymbol *pSymbol;
 	struct PatchSymbol *pNext;
+	struct PatchSymbol *pBucketNext; // next symbol in hash table bucket
 };
 
 struct SectionStackEntry {
@@ -57,9 +59,11 @@
  *
  */
 
+struct PatchSymbol *tHashedPatchSymbols[HASHSIZE];
 struct Section *pSectionList = NULL, *pCurrentSection = NULL;
 struct PatchSymbol *pPatchSymbols = NULL;
-char tzObjectname[_MAX_PATH];
+struct PatchSymbol **ppPatchSymbolsTail = &pPatchSymbols;
+char *tzObjectname;
 struct SectionStackEntry *pSectionStack = NULL;
 
 /*
@@ -74,9 +78,7 @@
 {
 	struct SectionStackEntry *pSect;
 
-	if ((pSect =
-		(struct SectionStackEntry *)
-		malloc(sizeof(struct SectionStackEntry))) != NULL) {
+	if ((pSect = malloc(sizeof(struct SectionStackEntry))) != NULL) {
 		pSect->pSection = pCurrentSection;
 		pSect->pNext = pSectionStack;
 		pSectionStack = pSect;
@@ -328,30 +330,30 @@
 addsymbol(struct sSymbol * pSym)
 {
 	struct PatchSymbol *pPSym, **ppPSym;
-	ULONG ID = 0;
+	static ULONG nextID = 0;
+	ULONG hash;
 
-	pPSym = pPatchSymbols;
-	ppPSym = &(pPatchSymbols);
+	hash = calchash(pSym->tzName);
+	ppPSym = &(tHashedPatchSymbols[hash]);
 
-	while (pPSym) {
-		if (pSym == pPSym->pSymbol)
-			return (pPSym->ID);
-		ppPSym = &(pPSym->pNext);
-		pPSym = pPSym->pNext;
-		ID += 1;
+	while ((*ppPSym) != NULL) {
+		if (pSym == (*ppPSym)->pSymbol)
+			return (*ppPSym)->ID;
+		ppPSym = &((*ppPSym)->pBucketNext);
 	}
 
-	if ((*ppPSym = pPSym =
-		(struct PatchSymbol *) malloc(sizeof(struct PatchSymbol))) !=
-	    NULL) {
+	if ((*ppPSym = pPSym = malloc(sizeof(struct PatchSymbol))) != NULL) {
 		pPSym->pNext = NULL;
+		pPSym->pBucketNext = NULL;
 		pPSym->pSymbol = pSym;
-		pPSym->ID = ID;
-		return (ID);
+		pPSym->ID = nextID++;
 	} else
 		fatalerror("No memory for patchsymbol");
 
-	return ((ULONG) - 1);
+	*ppPatchSymbolsTail = pPSym;
+	ppPatchSymbolsTail = &(pPSym->pNext);
+
+	return pPSym->ID;
 }
 /*
  * RGBAsm - OUTPUT.C - Outputs an objectfile
@@ -386,24 +388,17 @@
 struct Patch *
 allocpatch(void)
 {
-	struct Patch *pPatch, **ppPatch;
+	struct Patch *pPatch;
 
-	pPatch = pCurrentSection->pPatches;
-	ppPatch = &(pCurrentSection->pPatches);
-
-	while (pPatch) {
-		ppPatch = &(pPatch->pNext);
-		pPatch = pPatch->pNext;
-	}
-
-	if ((*ppPatch = pPatch =
-		(struct Patch *) malloc(sizeof(struct Patch))) != NULL) {
-		pPatch->pNext = NULL;
+	if ((pPatch = malloc(sizeof(struct Patch))) != NULL) {
+		pPatch->pNext = pCurrentSection->pPatches;
 		pPatch->nRPNSize = 0;
 		pPatch->pRPN = NULL;
 	} else
 		fatalerror("No memory for patch");
 
+	pCurrentSection->pPatches = pPatch;
+
 	return (pPatch);
 }
 /*
@@ -473,7 +468,7 @@
 			break;
 		}
 	}
-	if ((pPatch->pRPN = (UBYTE *) malloc(rpnptr)) != NULL) {
+	if ((pPatch->pRPN = malloc(rpnptr)) != NULL) {
 		memcpy(pPatch->pRPN, rpnexpr, rpnptr);
 		pPatch->nRPNSize = rpnptr;
 	}
@@ -505,9 +500,12 @@
 checkcodesection(SLONG size)
 {
 	checksection();
-	if ((pCurrentSection->nType == SECT_ROM0
-		|| pCurrentSection->nType == SECT_ROMX)
-	    && (pCurrentSection->nPC + size <= MAXSECTIONSIZE)) {
+	if (pCurrentSection->nType != SECT_ROM0 &&
+	    pCurrentSection->nType != SECT_ROMX) {
+		errx(1, "Section '%s' cannot contain code or data (not a "
+		    "ROM0 or ROMX)", pCurrentSection->pzName);
+	}
+	if (pCurrentSection->nPC + size <= MAXSECTIONSIZE) {
 		if (((pCurrentSection->nPC % SECTIONCHUNK) >
 			((pCurrentSection->nPC + size) % SECTIONCHUNK))
 		    && (pCurrentSection->nType == SECT_ROM0
@@ -524,8 +522,7 @@
 		}
 		return;
 	} else
-		fatalerror
-		    ("Section can't contain initialized data or section limit exceeded");
+		errx(1, "Section '%s' is too big", pCurrentSection->pzName);
 }
 /*
  * RGBAsm - OUTPUT.C - Outputs an objectfile
@@ -594,7 +591,7 @@
 void 
 out_SetFileName(char *s)
 {
-	strcpy(tzObjectname, s);
+	tzObjectname = s;
 	if (CurrentOptions.verbose) {
 		printf("Output filename %s\n", s);
 	}
@@ -632,11 +629,8 @@
 		pSect = pSect->pNext;
 	}
 
-	if ((*ppSect =
-		(pSect =
-		    (struct Section *) malloc(sizeof(struct Section)))) != NULL) {
-		if ((pSect->pzName =
-			(char *) malloc(strlen(pzName) + 1)) != NULL) {
+	if ((*ppSect = (pSect = malloc(sizeof(struct Section)))) != NULL) {
+		if ((pSect->pzName = malloc(strlen(pzName) + 1)) != NULL) {
 			strcpy(pSect->pzName, pzName);
 			pSect->nType = secttype;
 			pSect->nPC = 0;
@@ -647,8 +641,7 @@
 			pSect->charmap = NULL;
 			pPatchSymbols = NULL;
 
-			if ((pSect->tData =
-				(UBYTE *) malloc(SECTIONCHUNK)) != NULL) {
+			if ((pSect->tData = malloc(SECTIONCHUNK)) != NULL) {
 				return (pSect);
 			} else
 				fatalerror("Not enough memory for section");
@@ -918,30 +911,30 @@
 {
 	FILE *f;
 
-	fstk_FindFile(s);
+	f = fstk_FindFile(s);
+	if (f == NULL) {
+		err(1, "Unable to open incbin file '%s'", s);
+	}
 
-	if ((f = fopen(s, "rb")) != NULL) {
-		SLONG fsize;
+	SLONG fsize;
 
-		fseek(f, 0, SEEK_END);
-		fsize = ftell(f);
-		fseek(f, 0, SEEK_SET);
+	fseek(f, 0, SEEK_END);
+	fsize = ftell(f);
+	fseek(f, 0, SEEK_SET);
 
-		checkcodesection(fsize);
+	checkcodesection(fsize);
 
-		if (nPass == 2) {
-			SLONG dest = nPC;
-			SLONG todo = fsize;
+	if (nPass == 2) {
+		SLONG dest = nPC;
+		SLONG todo = fsize;
 
-			while (todo--)
-				pCurrentSection->tData[dest++] = fgetc(f);
-		}
-		pCurrentSection->nPC += fsize;
-		nPC += fsize;
-		pPCSymbol->nValue += fsize;
-		fclose(f);
-	} else
-		fatalerror("Could not open file '%s': %s", s, strerror(errno));
+		while (todo--)
+			pCurrentSection->tData[dest++] = fgetc(f);
+	}
+	pCurrentSection->nPC += fsize;
+	nPC += fsize;
+	pPCSymbol->nValue += fsize;
+	fclose(f);
 }
 
 void 
@@ -955,36 +948,36 @@
 	if (length < 0)
 		fatalerror("Number of bytes to read must be greater than zero");
 
-	fstk_FindFile(s);
+	f = fstk_FindFile(s);
+	if (f == NULL) {
+		err(1, "Unable to open included file '%s'", s);
+	}
 
-	if ((f = fopen(s, "rb")) != NULL) {
-		SLONG fsize;
+	SLONG fsize;
 
-		fseek(f, 0, SEEK_END);
-		fsize = ftell(f);
+	fseek(f, 0, SEEK_END);
+	fsize = ftell(f);
 
-		if (start_pos >= fsize)
-			fatalerror("Specified start position is greater than length of file");
+	if (start_pos >= fsize)
+		fatalerror("Specified start position is greater than length of file");
 
-		if ((start_pos + length) > fsize)
-			fatalerror("Specified range in INCBIN is out of bounds");
+	if ((start_pos + length) > fsize)
+		fatalerror("Specified range in INCBIN is out of bounds");
 
-		fseek(f, start_pos, SEEK_SET);
+	fseek(f, start_pos, SEEK_SET);
 
-		checkcodesection(length);
+	checkcodesection(length);
 
-		if (nPass == 2) {
-			SLONG dest = nPC;
-			SLONG todo = length;
+	if (nPass == 2) {
+		SLONG dest = nPC;
+		SLONG todo = length;
 
-			while (todo--)
-				pCurrentSection->tData[dest++] = fgetc(f);
-		}
-		pCurrentSection->nPC += length;
-		nPC += length;
-		pPCSymbol->nValue += length;
+		while (todo--)
+			pCurrentSection->tData[dest++] = fgetc(f);
+	}
+	pCurrentSection->nPC += length;
+	nPC += length;
+	pPCSymbol->nValue += length;
 
-		fclose(f);
-	} else
-		fatalerror("Could not open file '%s': %s", s, strerror(errno));
+	fclose(f);
 }
--- a/src/asm/rgbasm.1
+++ b/src/asm/rgbasm.1
@@ -60,7 +60,6 @@
 .Sh SEE ALSO
 .Xr rgbds 7 ,
 .Xr rgbfix 1 ,
-.Xr rgblib 1 ,
 .Xr rgblink 1 ,
 .Xr gbz80 7
 .Sh HISTORY
--- a/src/asm/symbol.c
+++ b/src/asm/symbol.c
@@ -6,6 +6,7 @@
  */
 
 #define _XOPEN_SOURCE 500
+#include <assert.h>
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
@@ -27,7 +28,6 @@
 struct sSymbol *pScope = NULL;
 struct sSymbol *pPCSymbol = NULL;
 struct sSymbol *p_NARGSymbol = NULL;
-struct sSymbol *p__LINE__Symbol = NULL;
 char *currentmacroargs[MAXMACROARGS + 1];
 char *newmacroargs[MAXMACROARGS + 1];
 char SavedTIME[256];
@@ -45,12 +45,6 @@
 	return (i);
 }
 
-SLONG 
-Callback__LINE__(struct sSymbol * sym)
-{
-	sym = sym;
-	return (nLineNo);
-}
 /*
  * RGBAsm - SYMBOL.C - Symboltable stuff
  *
@@ -76,10 +70,10 @@
 ULONG 
 calchash(char *s)
 {
-	ULONG hash = 0;
+	ULONG hash = 5381;
 
 	while (*s != 0)
-		hash += (*s++);
+		hash = (hash * 33) ^ (*s++);
 
 	return (hash % HASHSIZE);
 }
@@ -102,8 +96,7 @@
 	while ((*ppsym) != NULL)
 		ppsym = &((*ppsym)->pNext);
 
-	if (((*ppsym) =
-		(struct sSymbol *) malloc(sizeof(struct sSymbol))) != NULL) {
+	if (((*ppsym) = malloc(sizeof(struct sSymbol))) != NULL) {
 		strcpy((*ppsym)->tzName, s);
 		(*ppsym)->nValue = 0;
 		(*ppsym)->nType = 0;
@@ -440,6 +433,8 @@
 	if (i == -1)
 		i = MAXMACROARGS + 1;
 
+	assert(i-1 >= 0 &&
+	    i-1 < sizeof currentmacroargs / sizeof *currentmacroargs);
 	return (currentmacroargs[i - 1]);
 }
 
@@ -578,8 +573,7 @@
 		nsym = createsymbol(tzSym);
 
 	if (nsym) {
-		if ((nsym->pMacro =
-			(char *) malloc(strlen(tzValue) + 1)) != NULL)
+		if ((nsym->pMacro = malloc(strlen(tzValue) + 1)) != NULL)
 			strcpy(nsym->pMacro, tzValue);
 		else
 			fatalerror("No memory for stringequate");
@@ -849,6 +843,10 @@
 	sym_AddString("__TIME__", SavedTIME);
 	sym_AddString("__DATE__", SavedDATE);
 	sym_AddSet("_RS", 0);
+
+	sym_AddEqu("_NARG", 0);
+	p_NARGSymbol = findsymbol("_NARG", NULL);
+	p_NARGSymbol->Callback = Callback_NARG;
 }
 /*
  * RGBAsm - SYMBOL.C - Symboltable stuff
@@ -876,11 +874,7 @@
 	sym_AddEqu("_NARG", 0);
 	p_NARGSymbol = findsymbol("_NARG", NULL);
 	p_NARGSymbol->Callback = Callback_NARG;
-	sym_AddEqu("__LINE__", 0);
-	p__LINE__Symbol = findsymbol("__LINE__", NULL);
-	p__LINE__Symbol->Callback = Callback__LINE__;
 
-	sym_AddEqu("__ASM__", (SLONG) (atof(ASM_VERSION) * 65536));
 	sym_AddSet("_RS", 0);
 
 	if (time(&tod) != -1) {
--- a/src/asm/yaccprt1.y
+++ b/src/asm/yaccprt1.y
@@ -24,14 +24,38 @@
 char	*tzNewMacro;
 ULONG	ulNewMacroSize;
 
-ULONG	symvaluetostring( char *dest, char *sym )
+size_t symvaluetostring(char *dest, size_t maxLength, char *sym)
 {
-	if( sym_isString(sym) )
-		strcpy( dest, sym_GetStringValue(sym) );
-	else
-		sprintf( dest, "$%lX", sym_GetConstantValue(sym) );
+	size_t length;
 
-	return( strlen(dest) );
+	if (sym_isString(sym)) {
+		char *src = sym_GetStringValue(sym);
+		size_t i;
+
+		for (i = 0; src[i] != 0; i++) {
+			if (i >= maxLength) {
+				fatalerror("Symbol value too long to fit buffer");
+			}
+			dest[i] = src[i];
+		}
+
+		length = i;
+	} else {
+		ULONG value = sym_GetConstantValue(sym);
+		int fullLength = snprintf(dest, maxLength + 1, "$%lX", value);
+
+		if (fullLength < 0) {
+			fatalerror("snprintf encoding error");
+		} else {
+			length = (size_t)fullLength;
+
+			if (length > maxLength) {
+				fatalerror("Symbol value too long to fit buffer");
+			}
+		}
+	}
+
+	return length;
 }
 
 ULONG	str2int( char *s )
@@ -124,8 +148,7 @@
 	src=pCurrentBuffer->pBuffer;
 	ulNewMacroSize=len;
 
-	if( (tzNewMacro=(char *)malloc(ulNewMacroSize+1))!=NULL )
-	{
+	if ((tzNewMacro = malloc(ulNewMacroSize + 1)) != NULL) {
 		ULONG i;
 
 		tzNewMacro[ulNewMacroSize]=0;
@@ -134,8 +157,7 @@
 			if( (tzNewMacro[i]=src[i])=='\n' )
 				nLineNo+=1;
 		}
-	}
-	else
+	} else
 		fatalerror( "No mem for REPT block" );
 
 	yyskipbytes( ulNewMacroSize+4 );
@@ -353,8 +375,8 @@
 
 %union
 {
-    char tzSym[MAXSYMLEN+1];
-    char tzString[256];
+    char tzSym[MAXSYMLEN + 1];
+    char tzString[MAXSTRLEN + 1];
     struct Expression sVal;
     SLONG nConstValue;
 }
--- a/src/asm/yaccprt3.y
+++ b/src/asm/yaccprt3.y
@@ -266,10 +266,7 @@
 
 include			:	T_POP_INCLUDE string
 					{
-						if( !fstk_RunInclude($2) )
-						{
-							yyerror("Could not open file '%s' : %s\n", $2, strerror(errno));
-						}
+						fstk_RunInclude($2);
 					}
 ;
 
--- /dev/null
+++ b/src/extern/err.c
@@ -1,0 +1,92 @@
+/*
+ * Copyright © 2005-2013 Rich Felker, et al.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include "extern/err.h"
+
+#ifndef __MINGW32__
+char *__progname;
+#endif
+
+void rgbds_vwarn(const char *fmt, va_list ap)
+{
+	fprintf (stderr, "%s: ", __progname);
+	if (fmt) {
+		vfprintf(stderr, fmt, ap);
+		fputs (": ", stderr);
+	}
+	perror(0);
+}
+
+void rgbds_vwarnx(const char *fmt, va_list ap)
+{
+	fprintf (stderr, "%s: ", __progname);
+	if (fmt) vfprintf(stderr, fmt, ap);
+	putc('\n', stderr);
+}
+
+void rgbds_verr(int status, const char *fmt, va_list ap)
+{
+	vwarn(fmt, ap);
+	exit(status);
+}
+
+void rgbds_verrx(int status, const char *fmt, va_list ap)
+{
+	vwarnx(fmt, ap);
+	exit(status);
+}
+
+void rgbds_warn(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	vwarn(fmt, ap);
+	va_end(ap);
+}
+
+void rgbds_warnx(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	vwarnx(fmt, ap);
+	va_end(ap);
+}
+
+void rgbds_err(int status, const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	verr(status, fmt, ap);
+	va_end(ap);
+}
+
+void rgbds_errx(int status, const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	verrx(status, fmt, ap);
+	va_end(ap);
+}
--- /dev/null
+++ b/src/extern/strlcat.c
@@ -1,0 +1,55 @@
+/*	$OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left).  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+rgbds_strlcat(char *dst, const char *src, size_t siz)
+{
+	char *d = dst;
+	const char *s = src;
+	size_t n = siz;
+	size_t dlen;
+
+	/* Find the end of dst and adjust bytes left but don't go past end */
+	while (n-- != 0 && *d != '\0')
+		d++;
+	dlen = d - dst;
+	n = siz - dlen;
+
+	if (n == 0)
+		return(dlen + strlen(s));
+	while (*s != '\0') {
+		if (n != 1) {
+			*d++ = *s;
+			n--;
+		}
+		s++;
+	}
+	*d = '\0';
+
+	return(dlen + (s - src));	/* count does not include NUL */
+}
--- /dev/null
+++ b/src/extern/strlcpy.c
@@ -1,0 +1,51 @@
+/*	$OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+rgbds_strlcpy(char *dst, const char *src, size_t siz)
+{
+	char *d = dst;
+	const char *s = src;
+	size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0) {
+		while (--n != 0) {
+			if ((*d++ = *s++) == '\0')
+				break;
+		}
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src */
+	if (n == 0) {
+		if (siz != 0)
+			*d = '\0';		/* NUL-terminate dst */
+		while (*s++)
+			;
+	}
+
+	return(s - src - 1);	/* count does not include NUL */
+}
--- a/src/fix/main.c
+++ b/src/fix/main.c
@@ -22,6 +22,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "extern/err.h"
+
 static void
 usage(void)
 {
@@ -47,9 +49,7 @@
 		usage();
 
 	if ((rom = fopen(argv[argc - 1], "rb+")) == NULL) {
-		fprintf(stderr, "Error opening file %s: \n", argv[argc - 1]);
-		perror(NULL);
-		exit(1);
+		err(1, "Error opening file %s", argv[argc - 1]);
 	}
 
 	/*
@@ -93,9 +93,8 @@
 			setid = true;
 
 			if (strlen(optarg) != 4) {
-				fprintf(stderr, "Game ID %s must be exactly 4 "
-				    "characters\n", optarg);
-				exit(1);
+				errx(1, "Game ID %s must be exactly 4 "
+				    "characters", optarg);
 			}
 
 			id = optarg;
@@ -107,10 +106,8 @@
 			setnewlicensee = true;
 
 			if (strlen(optarg) != 2) {
-				fprintf(stderr,
-				    "New licensee code %s is not the correct "
-				    "length of 2 characters\n", optarg);
-				exit(1);
+				errx(1, "New licensee code %s is not the "
+				    "correct length of 2 characters", optarg);
 			}
 
 			newlicensee = optarg;
@@ -120,15 +117,11 @@
 
 			licensee = strtoul(optarg, &ep, 0);
 			if (optarg[0] == '\0' || *ep != '\0') {
-				fprintf(stderr,
-				    "Invalid argument for option 'l'\n");
-				exit(1);
+				errx(1, "Invalid argument for option 'l'");
 			}
 			if (licensee < 0 || licensee > 0xFF) {
-				fprintf(stderr,
-				    "Argument for option 'l' must be "
-				    "between 0 and 255\n");
-				exit(1);
+				errx(1, "Argument for option 'l' must be "
+				    "between 0 and 255");
 			}
 			break;
 		case 'm':
@@ -136,15 +129,11 @@
 
 			cartridge = strtoul(optarg, &ep, 0);
 			if (optarg[0] == '\0' || *ep != '\0') {
-				fprintf(stderr,
-				    "Invalid argument for option 'm'\n");
-				exit(1);
+				errx(1, "Invalid argument for option 'm'");
 			}
 			if (cartridge < 0 || cartridge > 0xFF) {
-				fprintf(stderr,
-				    "Argument for option 'm' must be "
-				    "between 0 and 255\n");
-				exit(1);
+				errx(1, "Argument for option 'm' must be "
+				    "between 0 and 255");
 			}
 			break;
 		case 'n':
@@ -152,15 +141,11 @@
 
 			version = strtoul(optarg, &ep, 0);
 			if (optarg[0] == '\0' || *ep != '\0') {
-				fprintf(stderr,
-				    "Invalid argument for option 'n'\n");
-				exit(1);
+				errx(1, "Invalid argument for option 'n'");
 			}
 			if (version < 0 || version > 0xFF) {
-				fprintf(stderr,
-				    "Argument for option 'n' must be "
-				    "between 0 and 255\n");
-				exit(1);
+				errx(1, "Argument for option 'n' must be "
+				    "between 0 and 255");
 			}
 			break;
 		case 'p':
@@ -168,15 +153,11 @@
 
 			padvalue = strtoul(optarg, &ep, 0);
 			if (optarg[0] == '\0' || *ep != '\0') {
-				fprintf(stderr,
-				    "Invalid argument for option 'p'\n");
-				exit(1);
+				errx(1, "Invalid argument for option 'p'");
 			}
 			if (padvalue < 0 || padvalue > 0xFF) {
-				fprintf(stderr,
-				    "Argument for option 'p' must be "
-				    "between 0 and 255\n");
-				exit(1);
+				errx(1, "Argument for option 'p' must be "
+				    "between 0 and 255");
 			}
 			break;
 		case 'r':
@@ -184,14 +165,11 @@
 
 			ramsize = strtoul(optarg, &ep, 0);
 			if (optarg[0] == '\0' || *ep != '\0') {
-				fprintf(stderr,
-				    "Invalid argument for option 'r'\n");
+				errx(1, "Invalid argument for option 'r'");
 			}
 			if (ramsize < 0 || ramsize > 0xFF) {
-				fprintf(stderr,
-				    "Argument for option 'r' must be "
-				    "between 0 and 255\n");
-				exit(1);
+				errx(1, "Argument for option 'r' must be "
+				    "between 0 and 255");
 			}
 			break;
 		case 's':
@@ -201,15 +179,13 @@
 			settitle = true;
 
 			if (strlen(optarg) > 16) {
-				fprintf(stderr, "Title %s is greater than the "
-				    "maximum of 16 characters\n", optarg);
-				exit(1);
+				errx(1, "Title %s is greater than the "
+				    "maximum of 16 characters", optarg);
 			}
 
 			if (strlen(optarg) == 16)
-				fprintf(stderr,
-				    "Title %s is 16 chars, it is best "
-				    "to keep it to 15 or fewer\n", optarg);
+				warnx("Title %s is 16 chars, it is best to "
+				    "keep it to 15 or fewer", optarg);
 
 			title = optarg;
 			break;
@@ -221,8 +197,6 @@
 			/* NOTREACHED */
 		}
 	}
-	argc -= optind;
-	argv += optind;
 
 	/*
 	 * Write changes to ROM
@@ -315,8 +289,7 @@
 			byte |= 1 << 6;
 
 		if (byte & 0x3F)
-			fprintf(stderr,
-			    "Color flag conflicts with game title\n");
+			warnx("Color flag conflicts with game title");
 
 		fseek(rom, 0x143, SEEK_SET);
 		fputc(byte, rom);
@@ -353,9 +326,8 @@
 		 */
 
 		if (!setlicensee)
-			fprintf(stderr,
-			    "You should probably set both '-s' and "
-			    "'-l 0x33'\n");
+			warnx("You should probably set both '-s' and "
+			    "'-l 0x33'");
 
 		fseek(rom, 0x146, SEEK_SET);
 		fputc(3, rom);
@@ -380,6 +352,7 @@
 
 		/* We will pad the ROM to match the size given in the header. */
 		int romsize, newsize, headbyte;
+		uint8_t *buf;
 		fseek(rom, 0, SEEK_END);
 		romsize = ftell(rom);
 		newsize = 0x8000;
@@ -390,14 +363,17 @@
 			headbyte++;
 		}
 
-		while (newsize != ftell(rom)) /* ROM needs resizing */
-			fputc(padvalue, rom);
-
 		if (newsize > 0x800000) /* ROM is bigger than 8MiB */
-			fprintf(stderr, "ROM size is bigger than 8MiB\n");
+			warnx("ROM size is bigger than 8MiB");
 
+		buf = malloc(newsize - romsize);
+		memset(buf, padvalue, newsize - romsize);
+		fwrite(buf, 1, newsize - romsize, rom);
+
 		fseek(rom, 0x148, SEEK_SET);
 		fputc(headbyte, rom);
+
+		free(buf);
 	}
 
 	if (setramsize) {
--- a/src/fix/rgbfix.1
+++ b/src/fix/rgbfix.1
@@ -130,7 +130,6 @@
 .Sh SEE ALSO
 .Xr rgbds 7 ,
 .Xr rgbasm 1 ,
-.Xr rgblib 1 ,
 .Xr rgblink 1 ,
 .Xr gbz80 7
 .Sh HISTORY
--- a/src/lib/library.c
+++ /dev/null
@@ -1,306 +1,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "lib/types.h"
-#include "lib/libwrap.h"
-
-SLONG 
-file_Length(FILE * f)
-{
-	ULONG r, p;
-
-	p = ftell(f);
-	fseek(f, 0, SEEK_END);
-	r = ftell(f);
-	fseek(f, p, SEEK_SET);
-
-	return (r);
-}
-
-SLONG 
-file_ReadASCIIz(char *b, FILE * f)
-{
-	SLONG r = 0;
-
-	while ((*b++ = fgetc(f)) != 0)
-		r += 1;
-
-	return (r + 1);
-}
-
-void 
-file_WriteASCIIz(char *b, FILE * f)
-{
-	while (*b)
-		fputc(*b++, f);
-
-	fputc(0, f);
-}
-
-UWORD 
-file_ReadWord(FILE * f)
-{
-	UWORD r;
-
-	r = fgetc(f);
-	r |= fgetc(f) << 8;
-
-	return (r);
-}
-
-void 
-file_WriteWord(UWORD w, FILE * f)
-{
-	fputc(w, f);
-	fputc(w >> 8, f);
-}
-
-ULONG 
-file_ReadLong(FILE * f)
-{
-	ULONG r;
-
-	r = fgetc(f);
-	r |= fgetc(f) << 8;
-	r |= fgetc(f) << 16;
-	r |= fgetc(f) << 24;
-
-	return (r);
-}
-
-void 
-file_WriteLong(UWORD w, FILE * f)
-{
-	fputc(w, f);
-	fputc(w >> 8, f);
-	fputc(w >> 16, f);
-	fputc(w >> 24, f);
-}
-
-sLibrary *
-lib_ReadLib0(FILE * f, SLONG size)
-{
-	if (size) {
-		sLibrary *l = NULL, *first = NULL;
-
-		while (size > 0) {
-			if (l == NULL) {
-				l = malloc(sizeof *l);
-				if (!l) {
-					fprintf(stderr, "Out of memory\n");
-					exit(1);
-				}
-
-				first = l;
-			} else {
-				l->pNext = malloc(sizeof *l->pNext);
-				if (!l->pNext) {
-					fprintf(stderr, "Out of memory\n");
-					exit(1);
-				}
-
-				l = l->pNext;
-			}
-
-			size -= file_ReadASCIIz(l->tName, f);
-			l->uwTime = file_ReadWord(f);
-			size -= 2;
-			l->uwDate = file_ReadWord(f);
-			size -= 2;
-			l->nByteLength = file_ReadLong(f);
-			size -= 4;
-			if ((l->pData = malloc(l->nByteLength))) {
-				fread(l->pData, sizeof(UBYTE), l->nByteLength,
-				    f);
-				size -= l->nByteLength;
-			} else {
-				fprintf(stderr, "Out of memory\n");
-				exit(1);
-			}
-
-			l->pNext = NULL;
-		}
-		return (first);
-	}
-	return (NULL);
-}
-
-sLibrary *
-lib_Read(char *filename)
-{
-	FILE *f;
-
-	if ((f = fopen(filename, "rb"))) {
-		SLONG size;
-		char ID[5];
-
-		size = file_Length(f);
-		if (size == 0) {
-			fclose(f);
-			return (NULL);
-		}
-		fread(ID, sizeof(char), 4, f);
-		ID[4] = 0;
-		size -= 4;
-
-		if (strcmp(ID, "XLB0") == 0) {
-			sLibrary *r;
-
-			r = lib_ReadLib0(f, size);
-			fclose(f);
-			printf("Library '%s' opened\n", filename);
-			return (r);
-		} else {
-			fclose(f);
-			fprintf(stderr, "Not a valid xLib library\n");
-			exit(1);
-		}
-	} else {
-		printf
-		    ("Library '%s' not found, it will be created if necessary\n",
-		    filename);
-		return (NULL);
-	}
-}
-
-BBOOL 
-lib_Write(sLibrary * lib, char *filename)
-{
-	FILE *f;
-
-	if ((f = fopen(filename, "wb"))) {
-		fwrite("XLB0", sizeof(char), 4, f);
-		while (lib) {
-			file_WriteASCIIz(lib->tName, f);
-			file_WriteWord(lib->uwTime, f);
-			file_WriteWord(lib->uwDate, f);
-			file_WriteLong(lib->nByteLength, f);
-			fwrite(lib->pData, sizeof(UBYTE), lib->nByteLength, f);
-			lib = lib->pNext;
-		}
-
-		fclose(f);
-		printf("Library '%s' closed\n", filename);
-		return (1);
-	}
-	return (0);
-}
-
-sLibrary *
-lib_Find(sLibrary * lib, char *filename)
-{
-	if (strlen(filename) >= MAXNAMELENGTH) {
-		fprintf(stderr, "Module name too long: %s\n", filename);
-		exit(1);
-	}
-
-	while (lib) {
-		if (strcmp(lib->tName, filename) == 0)
-			break;
-
-		lib = lib->pNext;
-	}
-
-	return (lib);
-}
-
-sLibrary *
-lib_AddReplace(sLibrary * lib, char *filename)
-{
-	FILE *f;
-
-	if ((f = fopen(filename, "rb"))) {
-		sLibrary *module;
-
-		if (strlen(filename) >= MAXNAMELENGTH) {
-			fprintf(stderr, "Module name too long: %s\n",
-			    filename);
-			exit(1);
-		}
-
-		if ((module = lib_Find(lib, filename)) == NULL) {
-			module = malloc(sizeof *module);
-			if (!module) {
-				fprintf(stderr, "Out of memory\n");
-				exit(1);
-			}
-
-			module->pNext = lib;
-			lib = module;
-		} else {
-			/* Module already exists */
-			free(module->pData);
-		}
-
-		module->nByteLength = file_Length(f);
-		strcpy(module->tName, filename);
-		module->pData = malloc(module->nByteLength);
-		if (!module->pData) {
-			fprintf(stderr, "Out of memory\n");
-			exit(1);
-		}
-
-		fread(module->pData, sizeof(UBYTE), module->nByteLength, f);
-
-		printf("Added module '%s'\n", filename);
-
-		fclose(f);
-	}
-	return (lib);
-}
-
-sLibrary *
-lib_DeleteModule(sLibrary * lib, char *filename)
-{
-	sLibrary **pp, **first;
-	BBOOL found = 0;
-
-	pp = &lib;
-	first = pp;
-
-	if (strlen(filename) >= MAXNAMELENGTH) {
-		fprintf(stderr, "Module name too long: %s\n", filename);
-		exit(1);
-	}
-
-	while ((*pp) && (!found)) {
-		if (strcmp((*pp)->tName, filename) == 0) {
-			sLibrary *t;
-
-			t = *pp;
-
-			if (t->pData)
-				free(t->pData);
-
-			*pp = t->pNext;
-
-			free(t);
-			found = 1;
-		}
-		pp = &((*pp)->pNext);
-	}
-
-	if (!found) {
-		fprintf(stderr, "Module not found\n");
-		exit(1);
-	} else
-		printf("Module '%s' deleted from library\n", filename);
-
-	return (*first);
-}
-
-void 
-lib_Free(sLibrary * lib)
-{
-	while (lib) {
-		sLibrary *l;
-
-		if (lib->pData)
-			free(lib->pData);
-
-		l = lib;
-		lib = lib->pNext;
-		free(l);
-	}
-}
--- a/src/lib/main.c
+++ /dev/null
@@ -1,120 +1,0 @@
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "asmotor.h"
-
-#include "lib/types.h"
-#include "lib/library.h"
-
-/*
- * Print the usagescreen
- *
- */
-
-static void 
-usage(void)
-{
-	printf("RGBLib v" LIB_VERSION " (part of ASMotor " ASMOTOR_VERSION ")\n\n");
-	printf("usage: rgblib file [add | delete | extract | list] [module ...]\n");
-	exit(1);
-}
-/*
- * The main routine
- *
- */
-
-int 
-main(int argc, char *argv[])
-{
-	SLONG argn = 0;
-	char *libname;
-
-	argc -= 1;
-	argn += 1;
-
-	if (argc >= 2) {
-		sLibrary *lib;
-
-		lib = lib_Read(libname = argv[argn++]);
-		argc -= 1;
-
-		if (strcmp(argv[argn], "add") == 0) {
-			argn += 1;
-			argc -= 1;
-
-			while (argc) {
-				lib = lib_AddReplace(lib, argv[argn++]);
-				argc -= 1;
-			}
-			lib_Write(lib, libname);
-			lib_Free(lib);
-		} else if (strcmp(argv[argn], "delete") == 0) {
-			argn += 1;
-			argc -= 1;
-
-			while (argc) {
-				lib =
-				    lib_DeleteModule(lib, argv[argn++]);
-				argc -= 1;
-			}
-			lib_Write(lib, libname);
-			lib_Free(lib);
-		} else if (strcmp(argv[argn], "extract") == 0) {
-			argn += 1;
-			argc -= 1;
-
-			while (argc) {
-				sLibrary *l;
-
-				l = lib_Find(lib, argv[argn]);
-				if (l) {
-					FILE *f;
-
-					if ((f = fopen(argv[argn], "wb"))) {
-						fwrite(l->pData,
-						    sizeof(UBYTE),
-						    l->nByteLength,
-						    f);
-						fclose(f);
-						printf
-						    ("Extracted module '%s'\n",
-						    argv[argn]);
-					} else {
-						fprintf(stderr,
-						    "Unable to write module '%s': ", argv[argn]);
-						perror(NULL);
-						exit(1);
-					}
-				} else {
-					fprintf(stderr, "Module not found\n");
-					exit(1);
-				}
-
-				argn += 1;
-				argc -= 1;
-			}
-			lib_Free(lib);
-		} else if (strcmp(argv[argn], "list") == 0) {
-			argn += 1;
-			argc -= 1;
-
-			sLibrary *l;
-
-			l = lib;
-
-			while (l) {
-				printf("%10ld %s\n",
-				    l->nByteLength,
-				    l->tName);
-				l = l->pNext;
-			}
-		} else
-			usage();
-	} else
-		usage();
-
-	return (0);
-}
--- a/src/lib/rgblib.1
+++ /dev/null
@@ -1,36 +1,0 @@
-.Dd $Mdocdate$
-.Dt RGBLIB 1
-.Os RGBDS Manual
-.Sh NAME
-.Nm rgblib
-.Nd Game Boy library manager
-.Sh SYNOPSIS
-.Nm rgblib
-.Ar library
-.Op add | delete | extract | list
-.Ar module ...
-.Sh DESCRIPTION
-The
-.Nm
-program manages libraries for use with
-.Xr rgblink 1 .
-.Bl -tag -width Ds
-.It add
-Add the given modules to the library.
-.It delete
-Delete the given modules from the library.
-.It extract
-Extract the given modules from the library.
-.It list
-List all the modules in the library.
-.El
-.Sh SEE ALSO
-.Xr rgbds 7 ,
-.Xr rgbasm 1 ,
-.Xr rgbfix 1 ,
-.Xr rgblink 1 ,
-.Xr gbz80 7
-.Sh HISTORY
-.Nm
-was originally released by Carsten S\(/orensen as part of the ASMotor package,
-and was later packaged in RGBDS by Justin Lloyd.
--- a/src/link/assign.c
+++ b/src/link/assign.c
@@ -1,6 +1,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "extern/err.h"
 #include "link/mylink.h"
 #include "link/main.h"
 #include "link/symbol.h"
@@ -63,7 +64,6 @@
 					struct sFreeArea *pNewArea;
 
 					if ((pNewArea =
-						(struct sFreeArea *)
 						malloc(sizeof(struct sFreeArea)))
 					    != NULL) {
 						*pNewArea = *pArea;
@@ -77,9 +77,7 @@
 
 						return (org);
 					} else {
-						fprintf(stderr,
-						    "Out of memory!\n");
-						exit(1);
+						err(1, NULL);
 					}
 				}
 			}
@@ -310,9 +308,7 @@
 			pSection->oAssigned = 1;
 			DOMAXVBANK(pSection->nBank);
 		} else {
-			fprintf(stderr,
-			    "Unable to place VRAM section anywhere\n");
-			exit(1);
+			errx(1, "Unable to place VRAM section anywhere");
 		}
 	}
 }
@@ -331,9 +327,7 @@
 			pSection->oAssigned = 1;
 			DOMAXSBANK(pSection->nBank);
 		} else {
-			fprintf(stderr,
-			    "Unable to place SRAM section anywhere\n");
-			exit(1);
+			errx(1, "Unable to place SRAM section anywhere");
 		}
 	}
 }
@@ -352,9 +346,7 @@
 			pSection->oAssigned = 1;
 			DOMAXWBANK(pSection->nBank);
 		} else {
-			fprintf(stderr,
-			    "Unable to place WRAMX section anywhere\n");
-			exit(1);
+			errx(1, "Unable to place WRAMX section anywhere");
 		}
 	}
 }
@@ -373,9 +365,7 @@
 			pSection->oAssigned = 1;
 			DOMAXBANK(pSection->nBank);
 		} else {
-			fprintf(stderr,
-			    "Unable to place ROMX section anywhere\n");
-			exit(1);
+			errx(1, "Unable to place ROMX section anywhere");
 		}
 	}
 }
@@ -397,8 +387,7 @@
 		BankFree[i] = malloc(sizeof *BankFree[i]);
 
 		if (!BankFree[i]) {
-			fprintf(stderr, "Out of memory!\n");
-			exit(1);
+			err(1, NULL);
 		}
 
 		if (i == 0) {
@@ -472,10 +461,9 @@
 				if (area_AllocAbs
 				    (&BankFree[BANK_WRAM0], pSection->nOrg,
 					pSection->nByteSize) != pSection->nOrg) {
-					fprintf(stderr,
-					    "Unable to load fixed WRAM0 section "
-					    "at $%lX\n", pSection->nOrg);
-					exit(1);
+					errx(1,
+					    "Unable to load fixed WRAM0 "
+					    "section at $%lX", pSection->nOrg);
 				}
 				pSection->oAssigned = 1;
 				pSection->nBank = BANK_WRAM0;
@@ -484,10 +472,8 @@
 				if (area_AllocAbs
 				    (&BankFree[BANK_HRAM], pSection->nOrg,
 					pSection->nByteSize) != pSection->nOrg) {
-					fprintf(stderr, "Unable to load fixed "
-					    "HRAM section at $%lX\n",
-					    pSection->nOrg);
-					exit(1);
+					errx(1, "Unable to load fixed HRAM "
+					    "section at $%lX", pSection->nOrg);
 				}
 				pSection->oAssigned = 1;
 				pSection->nBank = BANK_HRAM;
@@ -530,17 +516,15 @@
 							    pSection->nOrg,
 							    pSection->nByteSize)
 							    != pSection->nOrg) {
-								fprintf(stderr,
-"Unable to load fixed SRAM section at $%lX in bank $%02lX\n", pSection->nOrg, pSection->nBank);
-								exit(1);
+								errx(1,
+"Unable to load fixed SRAM section at $%lX in bank $%02lX", pSection->nOrg, pSection->nBank);
 							}
 							DOMAXVBANK(pSection->
 							    nBank);
 							pSection->oAssigned = 1;
 						} else {
-							fprintf(stderr,
-"Unable to load fixed SRAM section at $%lX in bank $%02lX\n", pSection->nOrg, pSection->nBank);
-							exit(1);
+							errx(1,
+"Unable to load fixed SRAM section at $%lX in bank $%02lX", pSection->nOrg, pSection->nBank);
 						}
 					}
 				}
@@ -583,17 +567,15 @@
 							    pSection->nOrg,
 							    pSection->nByteSize)
 							    != pSection->nOrg) {
-								fprintf(stderr,
-"Unable to load fixed WRAMX section at $%lX in bank $%02lX\n", pSection->nOrg, pSection->nBank);
-								exit(1);
+								errx(1,
+"Unable to load fixed WRAMX section at $%lX in bank $%02lX", pSection->nOrg, pSection->nBank);
 							}
 							DOMAXWBANK(pSection->
 							    nBank);
 							pSection->oAssigned = 1;
 						} else {
-							fprintf(stderr,
-"Unable to load fixed WRAMX section at $%lX in bank $%02lX\n", pSection->nOrg, pSection->nBank);
-							exit(1);
+							errx(1,
+"Unable to load fixed WRAMX section at $%lX in bank $%02lX", pSection->nOrg, pSection->nBank);
 						}
 					}
 				}
@@ -636,17 +618,15 @@
 							    pSection->nOrg,
 							    pSection->nByteSize)
 							    != pSection->nOrg) {
-								fprintf(stderr,
-"Unable to load fixed VRAM section at $%lX in bank $%02lX\n", pSection->nOrg, pSection->nBank);
-								exit(1);
+								errx(1,
+"Unable to load fixed VRAM section at $%lX in bank $%02lX", pSection->nOrg, pSection->nBank);
 							}
 							DOMAXVBANK(pSection->
 							    nBank);
 							pSection->oAssigned = 1;
 						} else {
-							fprintf(stderr,
-"Unable to load fixed VRAM section at $%lX in bank $%02lX\n", pSection->nOrg, pSection->nBank);
-							exit(1);
+							errx(1,
+"Unable to load fixed VRAM section at $%lX in bank $%02lX", pSection->nOrg, pSection->nBank);
 						}
 					}
 				}
@@ -655,10 +635,8 @@
 				if (area_AllocAbs
 				    (&BankFree[BANK_ROM0], pSection->nOrg,
 					pSection->nByteSize) != pSection->nOrg) {
-					fprintf(stderr, "Unable to load fixed "
-					    "ROM0 section at $%lX\n",
-					    pSection->nOrg);
-					exit(1);
+					errx(1, "Unable to load fixed ROM0 "
+					    "section at $%lX", pSection->nOrg);
 				}
 				pSection->oAssigned = 1;
 				pSection->nBank = BANK_ROM0;
@@ -703,15 +681,15 @@
 								pSection->
 								nByteSize) !=
 							    pSection->nOrg) {
-								fprintf(stderr, "Unable to load fixed ROMX section at $%lX in bank $%02lX\n", pSection->nOrg, pSection->nBank);
-								exit(1);
+								errx(1,
+								    "Unable to load fixed ROMX section at $%lX in bank $%02lX", pSection->nOrg, pSection->nBank);
 							}
 							DOMAXBANK(pSection->
 							    nBank);
 							pSection->oAssigned = 1;
 						} else {
-							fprintf(stderr, "Unable to load fixed ROMX section at $%lX in bank $%02lX\n", pSection->nOrg, pSection->nBank);
-							exit(1);
+							errx(1,
+							"Unable to load fixed ROMX section at $%lX in bank $%02lX", pSection->nOrg, pSection->nBank);
 						}
 					}
 
@@ -737,14 +715,13 @@
 				if ((pSection->nOrg =
 					area_Alloc(&BankFree[pSection->nBank],
 					    pSection->nByteSize)) == -1) {
-					fprintf(stderr, "Unable to load fixed ROMX section into bank $%02lX\n", pSection->nBank);
-					exit(1);
+					errx(1,
+					"Unable to load fixed ROMX section into bank $%02lX", pSection->nBank);
 				}
 				pSection->oAssigned = 1;
 				DOMAXBANK(pSection->nBank);
 			} else {
-				fprintf(stderr, "Unable to load fixed ROMX section into bank $%02lX\n", pSection->nBank);
-				exit(1);
+				errx(1, "Unable to load fixed ROMX section into bank $%02lX", pSection->nBank);
 			}
 		} else if (pSection->oAssigned == 0
 		    && pSection->Type == SECT_SRAM
@@ -755,14 +732,12 @@
 				if ((pSection->nOrg =
 					area_Alloc(&BankFree[pSection->nBank],
 					    pSection->nByteSize)) == -1) {
-					fprintf(stderr, "Unable to load fixed SRAM section into bank $%02lX\n", pSection->nBank);
-					exit(1);
+					errx(1, "Unable to load fixed SRAM section into bank $%02lX", pSection->nBank);
 				}
 				pSection->oAssigned = 1;
 				DOMAXSBANK(pSection->nBank);
 			} else {
-				fprintf(stderr, "Unable to load fixed VRAM section into bank $%02lX\n", pSection->nBank);
-				exit(1);
+				errx(1, "Unable to load fixed VRAM section into bank $%02lX", pSection->nBank);
 			}
 		} else if (pSection->oAssigned == 0
 		    && pSection->Type == SECT_VRAM
@@ -773,14 +748,12 @@
 				if ((pSection->nOrg =
 					area_Alloc(&BankFree[pSection->nBank],
 					    pSection->nByteSize)) == -1) {
-					fprintf(stderr, "Unable to load fixed VRAM section into bank $%02lX\n", pSection->nBank);
-					exit(1);
+					errx(1, "Unable to load fixed VRAM section into bank $%02lX", pSection->nBank);
 				}
 				pSection->oAssigned = 1;
 				DOMAXVBANK(pSection->nBank);
 			} else {
-				fprintf(stderr, "Unable to load fixed VRAM section into bank $%02lX\n", pSection->nBank);
-				exit(1);
+				errx(1, "Unable to load fixed VRAM section into bank $%02lX", pSection->nBank);
 			}
 		} else if (pSection->oAssigned == 0
 		    && pSection->Type == SECT_WRAMX
@@ -791,14 +764,12 @@
 				if ((pSection->nOrg =
 					area_Alloc(&BankFree[pSection->nBank],
 					    pSection->nByteSize)) == -1) {
-					fprintf(stderr, "Unable to load fixed WRAMX section into bank $%02lX\n", pSection->nBank - BANK_WRAMX);
-					exit(1);
+					errx(1, "Unable to load fixed WRAMX section into bank $%02lX", pSection->nBank - BANK_WRAMX);
 				}
 				pSection->oAssigned = 1;
 				DOMAXWBANK(pSection->nBank);
 			} else {
-				fprintf(stderr, "Unable to load fixed WRAMX section into bank $%02lX\n", pSection->nBank - BANK_WRAMX);
-				exit(1);
+				errx(1, "Unable to load fixed WRAMX section into bank $%02lX", pSection->nBank - BANK_WRAMX);
 			}
 		}
 		pSection = pSection->pNext;
@@ -820,8 +791,7 @@
 				area_AllocAbsROMXAnyBank(pSection->nOrg,
 				    pSection->nByteSize)) ==
 			    -1) {
-				fprintf(stderr, "Unable to load fixed ROMX section at $%lX into any bank\n", pSection->nOrg);
-				exit(1);
+				errx(1, "Unable to load fixed ROMX section at $%lX into any bank", pSection->nOrg);
 			}
 			pSection->oAssigned = 1;
 			DOMAXBANK(pSection->nBank);
@@ -834,8 +804,7 @@
 				area_AllocAbsVRAMAnyBank(pSection->nOrg,
 				    pSection->nByteSize)) ==
 			    -1) {
-				fprintf(stderr, "Unable to load fixed VRAM section at $%lX into any bank\n", pSection->nOrg);
-				exit(1);
+				errx(1, "Unable to load fixed VRAM section at $%lX into any bank", pSection->nOrg);
 			}
 			pSection->oAssigned = 1;
 			DOMAXVBANK(pSection->nBank);
@@ -848,8 +817,7 @@
 				area_AllocAbsSRAMAnyBank(pSection->nOrg,
 				    pSection->nByteSize)) ==
 			    -1) {
-				fprintf(stderr, "Unable to load fixed SRAM section at $%lX into any bank\n", pSection->nOrg);
-				exit(1);
+				errx(1, "Unable to load fixed SRAM section at $%lX into any bank", pSection->nOrg);
 			}
 			pSection->oAssigned = 1;
 			DOMAXSBANK(pSection->nBank);
@@ -862,8 +830,7 @@
 				area_AllocAbsWRAMAnyBank(pSection->nOrg,
 				    pSection->nByteSize)) ==
 			    -1) {
-				fprintf(stderr, "Unable to load fixed WRAMX section at $%lX into any bank\n", pSection->nOrg);
-				exit(1);
+				errx(1, "Unable to load fixed WRAMX section at $%lX into any bank", pSection->nOrg);
 			}
 			pSection->oAssigned = 1;
 			DOMAXWBANK(pSection->nBank);
@@ -885,8 +852,7 @@
 				if ((pSection->nOrg =
 					area_Alloc(&BankFree[BANK_WRAM0],
 					    pSection->nByteSize)) == -1) {
-					fprintf(stderr, "WRAM0 section too large\n");
-					exit(1);
+					errx(1, "WRAM0 section too large");
 				}
 				pSection->nBank = BANK_WRAM0;
 				pSection->oAssigned = 1;
@@ -895,8 +861,7 @@
 				if ((pSection->nOrg =
 					area_Alloc(&BankFree[BANK_HRAM],
 					    pSection->nByteSize)) == -1) {
-					fprintf(stderr, "HRAM section too large\n");
-					exit(1);
+					errx(1, "HRAM section too large");
 				}
 				pSection->nBank = BANK_HRAM;
 				pSection->oAssigned = 1;
@@ -911,8 +876,7 @@
 				if ((pSection->nOrg =
 					area_Alloc(&BankFree[BANK_ROM0],
 					    pSection->nByteSize)) == -1) {
-					fprintf(stderr, "ROM0 section too large\n");
-					exit(1);
+					errx(1, "ROM0 section too large");
 				}
 				pSection->nBank = BANK_ROM0;
 				pSection->oAssigned = 1;
@@ -920,8 +884,7 @@
 			case SECT_ROMX:
 				break;
 			default:
-				fprintf(stderr, "(INTERNAL) Unknown section type!\n");
-				exit(1);
+				errx(1, "(INTERNAL) Unknown section type!");
 				break;
 			}
 		}
--- a/src/link/library.c
+++ b/src/link/library.c
@@ -2,6 +2,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "extern/err.h"
 #include "link/types.h"
 #include "link/mylink.h"
 #include "link/main.h"
@@ -91,9 +92,8 @@
 	}
 	if (options & OPT_SMART_C_LINK) {
 		if (!addmodulecontaining(smartlinkstartsymbol)) {
-			fprintf(stderr, "Can't find start symbol '%s'\n",
+			errx(1, "Can't find start symbol '%s'",
 			    smartlinkstartsymbol);
-			exit(1);
 		} else
 			printf("Smart linking with symbol '%s'\n",
 			    smartlinkstartsymbol);
--- a/src/link/main.c
+++ b/src/link/main.c
@@ -4,8 +4,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#include "asmotor.h"
-
+#include "extern/err.h"
 #include "link/object.h"
 #include "link/output.h"
 #include "link/assign.h"
@@ -34,9 +33,7 @@
 static void 
 usage(void)
 {
-	printf("RGBLink v" LINK_VERSION " (part of ASMotor " ASMOTOR_VERSION
-	    ")\n\n");
-	printf("usage: rgblink [-t] [-l library] [-m mapfile] [-n symfile] [-o outfile]\n");
+	printf("usage: rgblink [-t] [-m mapfile] [-n symfile] [-o outfile]\n");
 	printf("\t    [-s symbol] [-z pad_value] objectfile [...]\n");
 
 	exit(1);
@@ -58,9 +55,6 @@
 
 	while ((ch = getopt(argc, argv, "l:m:n:o:p:s:t")) != -1) {
 		switch (ch) {
-		case 'l':
-			lib_Readfile(optarg);
-			break;
 		case 'm':
 			SetMapfileName(optarg);
 			break;
@@ -73,8 +67,7 @@
 		case 'p':
 			fillchar = strtoul(optarg, &ep, 0);
 			if (optarg[0] == '\0' || *ep != '\0') {
-				fprintf(stderr, "Invalid argument for option 'p'\n");
-				exit(1);
+				errx(1, "Invalid argument for option 'p'");
 			}
 			if (fillchar < 0 || fillchar > 0xFF) {
 				fprintf(stderr, "Argument for option 'p' must be between 0 and 0xFF");
--- a/src/link/mapfile.c
+++ b/src/link/mapfile.c
@@ -3,8 +3,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "asmotor.h"
-
+#include "extern/err.h"
 #include "link/main.h"
 #include "link/mylink.h"
 #include "link/assign.h"
@@ -20,9 +19,7 @@
 	mf = fopen(name, "w");
 
 	if (mf == NULL) {
-		fprintf(stderr, "Cannot open mapfile '%s': ", name);
-		perror(NULL);
-		exit(1);
+		err(1, "Cannot open mapfile '%s'", name);
 	}
 }
 
@@ -32,11 +29,10 @@
 	sf = fopen(name, "w");
 
 	if (sf == NULL) {
-		fprintf(stderr, "Cannot open symfile '%s'\n", name);
-		exit(1);
+		err(1, "Cannot open symfile '%s'", name);
 	}
 
-	fprintf(sf, ";File generated by xLink v" LINK_VERSION "\n\n");
+	fprintf(sf, ";File generated by rgblink\n\n");
 }
 
 void 
--- a/src/link/object.c
+++ b/src/link/object.c
@@ -8,6 +8,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "extern/err.h"
 #include "link/mylink.h"
 #include "link/main.h"
 
@@ -80,8 +81,7 @@
 
 	*ppSections = malloc(sizeof **ppSections);
 	if (!*ppSections) {
-		fprintf(stderr, "Out of memory!\n");
-		exit(1);
+		err(1, NULL);
 	}
 	(*ppSections)->tSymbols = tSymbols;
 	(*ppSections)->pNext = NULL;
@@ -102,15 +102,13 @@
 
 	pSym = malloc(sizeof *pSym);
 	if (!pSym) {
-		fprintf(stderr, "Out of memory!\n");
-		exit(1);
+		err(1, NULL);
 	}
 
 	readasciiz(s, f);
 	pSym->pzName = malloc(strlen(s) + 1);
 	if (!pSym->pzName) {
-		fprintf(stderr, "Out of memory!\n");
-		exit(1);
+		err(1, NULL);
 	}
 
 	strcpy(pSym->pzName, s);
@@ -150,7 +148,7 @@
 		if (pSection->nByteSize) {
 			pSection->pData = malloc(pSection->nByteSize);
 			if (!pSection->pData) {
-				fprintf(stderr, "Out of memory!\n");
+				err(1, NULL);
 			}
 
 			SLONG nNumberOfPatches;
@@ -169,7 +167,7 @@
 			while (nNumberOfPatches--) {
 				pPatch = malloc(sizeof *pPatch);
 				if (!pPatch) {
-					fprintf(stderr, "Out of memory!\n");
+					err(1, NULL);
 				}
 
 				*ppPatch = pPatch;
@@ -177,7 +175,7 @@
 
 				pPatch->pzFilename = malloc(strlen(s) + 1);
 				if (!pPatch->pzFilename) {
-					fprintf(stderr, "Out of memory!\n");
+					err(1, NULL);
 				}
 
 				strcpy(pPatch->pzFilename, s);
@@ -193,8 +191,7 @@
 				if ((pPatch->nRPNSize = readlong(f)) > 0) {
 					pPatch->pRPN = malloc(pPatch->nRPNSize);
 					if (!pPatch->pRPN) {
-						fprintf(stderr, "Out of memory!\n");
-						exit(1);
+						err(1, NULL);
 					}
 
 					fread(pPatch->pRPN, sizeof(UBYTE),
@@ -228,8 +225,7 @@
 	if (nNumberOfSymbols) {
 		tSymbols = malloc(nNumberOfSymbols * sizeof(struct sSymbol *));
 		if (!tSymbols) {
-			fprintf(stderr, "Out of memory!\n");
-			exit(1);
+			err(1, NULL);
 		}
 
 		for (i = 0; i < nNumberOfSymbols; i += 1)
@@ -305,8 +301,7 @@
 		if (pSection->nByteSize) {
 			pSection->pData = malloc(pSection->nByteSize);
 			if (!pSection->pData) {
-				fprintf(stderr, "Out of memory!\n");
-				exit(1);
+				err(1, NULL);
 			}
 
 			SLONG nNumberOfPatches;
@@ -325,7 +320,7 @@
 			while (nNumberOfPatches--) {
 				pPatch = malloc(sizeof *pPatch);
 				if (!pPatch) {
-					fprintf(stderr, "Out of memory!\n");
+					err(1, NULL);
 				}
 
 				*ppPatch = pPatch;
@@ -332,7 +327,7 @@
 				readasciiz(s, f);
 				pPatch->pzFilename = malloc(strlen(s) + 1);
 				if (!pPatch->pzFilename) {
-					fprintf(stderr, "Out of memory!\n");
+					err(1, NULL);
 				}
 
 				strcpy(pPatch->pzFilename, s);
@@ -342,7 +337,7 @@
 				if ((pPatch->nRPNSize = readlong(f)) > 0) {
 					pPatch->pRPN = malloc(pPatch->nRPNSize);
 					if (!pPatch->pRPN) {
-						fprintf(stderr, "Out of memory!\n");
+						err(1, NULL);
 					}
 
 					fread(pPatch->pRPN, sizeof(UBYTE),
@@ -376,7 +371,7 @@
 	if (nNumberOfSymbols) {
 		tSymbols = malloc(nNumberOfSymbols * sizeof *tSymbols);
 		if (!tSymbols) {
-			fprintf(stderr, "Out of memory!\n");
+			err(1, NULL);
 		}
 
 		for (i = 0; i < nNumberOfSymbols; i += 1)
@@ -440,14 +435,10 @@
 			    obj_ReadRGB1(pObjfile);
 			break;
 		default:
-			fprintf(stderr, "'%s' is an unsupported version",
-			    tzObjectfile);
-			exit(1);
-			break;
+			errx(1, "'%s' is an unsupported version", tzObjectfile);
 		}
 	} else {
-		fprintf(stderr, "'%s' is not a valid object\n", tzObjectfile);
-		exit(1);
+		errx(1, "'%s' is not a valid object", tzObjectfile);
 	}
 }
 
@@ -463,10 +454,7 @@
 
 	pObjfile = fopen(tzObjectfile, "rb");
 	if (pObjfile == NULL) {
-		fprintf(stderr, "Unable to open object '%s': ",
-		    tzObjectfile);
-		perror(NULL);
-		exit(1);
+		err(1, "Unable to open object '%s'", tzObjectfile);
 	}
 	obj_ReadOpenFile(pObjfile, tzObjectfile);
 	fclose(pObjfile);
@@ -505,35 +493,4 @@
 		size -= 4;
 		obj_ReadOpenFile(f, name);
 	}
-}
-
-void 
-lib_Readfile(char *tzLibfile)
-{
-	FILE *pObjfile;
-
-	oReadLib = 1;
-
-	pObjfile = fopen(tzLibfile, "rb");
-	if (pObjfile == NULL) {
-		fprintf(stderr, "Unable to open object '%s': ", tzLibfile);
-		perror(NULL);
-		exit(1);
-	}
-	if (!pObjfile) {
-		fprintf(stderr, "Unable to open '%s'\n", tzLibfile);
-		exit(1);
-	}
-	char tzHeader[5];
-
-	fread(tzHeader, sizeof(char), 4, pObjfile);
-	tzHeader[4] = 0;
-	if (strcmp(tzHeader, "XLB0") == 0)
-		lib_ReadXLB0(pObjfile);
-	else {
-		fprintf(stderr, "'%s' is an invalid library\n",
-		    tzLibfile);
-		exit(1);
-	}
-	fclose(pObjfile);
 }
--- a/src/link/output.c
+++ b/src/link/output.c
@@ -7,8 +7,7 @@
 #include "link/main.h"
 #include "link/assign.h"
 
-char tzOutname[_MAX_PATH];
-BBOOL oOutput = 0;
+char *tzOutname;
 
 void 
 writehome(FILE * f)
@@ -71,8 +70,7 @@
 void 
 out_Setname(char *tzOutputfile)
 {
-	strcpy(tzOutname, tzOutputfile);
-	oOutput = 1;
+	tzOutname = tzOutputfile;
 }
 
 void 
--- a/src/link/patch.c
+++ b/src/link/patch.c
@@ -2,6 +2,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "extern/err.h"
 #include "link/mylink.h"
 #include "link/symbol.h"
 #include "link/main.h"
@@ -46,8 +47,7 @@
 	default:
 		break;
 	}
-	fprintf(stderr, "*INTERNAL* UNKNOWN SYMBOL TYPE\n");
-	exit(1);
+	errx(1, "*INTERNAL* UNKNOWN SYMBOL TYPE");
 }
 
 SLONG 
@@ -64,8 +64,7 @@
 	default:
 		break;
 	}
-	fprintf(stderr, "*INTERNAL* UNKNOWN SYMBOL TYPE\n");
-	exit(1);
+	errx(1, "*INTERNAL* UNKNOWN SYMBOL TYPE");
 }
 
 SLONG 
@@ -159,10 +158,9 @@
 			t = rpnpop();
 			rpnpush(t & 0xFF);
 			if (t < 0 || (t > 0xFF && t < 0xFF00) || t > 0xFFFF) {
-				fprintf(stderr,
-				    "%s(%ld) : Value must be in the HRAM area\n",
+				errx(1,
+				    "%s(%ld) : Value must be in the HRAM area",
 				    pPatch->pzFilename, pPatch->nLineNo);
-				exit(1);
 			}
 			break;
 		case RPN_PCEZP:
@@ -169,10 +167,9 @@
 			t = rpnpop();
 			rpnpush(t & 0xFF);
 			if (t < 0x2000 || t > 0x20FF) {
-				fprintf(stderr,
-				    "%s(%ld) : Value must be in the ZP area\n",
+				errx(1,
+				    "%s(%ld) : Value must be in the ZP area",
 				    pPatch->pzFilename, pPatch->nLineNo);
-				exit(1);
 			}
 			break;
 		case RPN_CONST:
@@ -217,11 +214,10 @@
 				high |= (*rpn++) << 24;
 				t = rpnpop();
 				if (t < low || t > high) {
-					fprintf(stderr,
-					    "%s(%ld) : Value must be in the range [%ld;%ld]\n",
+					errx(1,
+					    "%s(%ld) : Value must be in the range [%ld;%ld]",
 					    pPatch->pzFilename,
 					    pPatch->nLineNo, low, high);
-					exit(1);
 				}
 				rpnpush(t);
 				size -= 8;
@@ -255,11 +251,10 @@
 					pSect->pData[pPatch->nOffset] =
 					    (UBYTE) t;
 				} else {
-					fprintf(stderr,
-					    "%s(%ld) : Value must be 8-bit\n",
+					errx(1,
+					    "%s(%ld) : Value must be 8-bit",
 					    pPatch->pzFilename,
 					    pPatch->nLineNo);
-					exit(1);
 				}
 				break;
 			case PATCH_WORD_L:
@@ -280,11 +275,10 @@
 						    1] = t & 0xFF;
 					}
 				} else {
-					fprintf(stderr,
-					    "%s(%ld) : Value must be 16-bit\n",
+					errx(1,
+					    "%s(%ld) : Value must be 16-bit",
 					    pPatch->pzFilename,
 					    pPatch->nLineNo);
-					exit(1);
 				}
 				break;
 			case PATCH_LONG_L:
--- a/src/link/rgblink.1
+++ b/src/link/rgblink.1
@@ -29,12 +29,6 @@
 .Pp
 The arguments are as follows:
 .Bl -tag -width Ds
-.It Fl l Ar library
-Include a referenced library module created with
-.Xr rgblib 1 .
-Note that specified libraries will be included only if needed\(emthat is, if
-a SECTION from a library is referenced by an object file.
-Only the relevant SECTION will be included, rather than the entire module.
 .It Fl m Ar mapfile
 Write a mapfile to the given filename.
 .It Fl n Ar symfile
@@ -70,7 +64,6 @@
 .Xr rgbds 7 ,
 .Xr rgbasm 1 ,
 .Xr rgbfix 1 ,
-.Xr rgblib 1 ,
 .Xr gbz80 7
 .Sh HISTORY
 .Nm
--- a/src/link/symbol.c
+++ b/src/link/symbol.c
@@ -2,6 +2,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "extern/err.h"
 #include "link/main.h"
 #include "link/patch.h"
 #include "link/types.h"
@@ -53,8 +54,7 @@
 			}
 		}
 
-		fprintf(stderr, "Unknown symbol '%s'\n", tzName);
-		exit(1);
+		errx(1, "Unknown symbol '%s'", tzName);
 	}
 }
 
@@ -72,8 +72,7 @@
 		}
 	}
 
-	fprintf(stderr, "Unknown symbol '%s'\n", tzName);
-	exit(1);
+	errx(1, "Unknown symbol '%s'", tzName);
 }
 
 void 
@@ -93,10 +92,7 @@
 			if (nBank == -1)
 				return;
 
-			fprintf(stderr,
-			    "Symbol '%s' defined more than once\n",
-			    tzName);
-			exit(1);
+			errx(1, "Symbol '%s' defined more than once", tzName);
 		}
 	}
 
--- a/src/rgbds.7
+++ b/src/rgbds.7
@@ -13,7 +13,6 @@
 .Sh SEE ALSO
 .Xr rgbasm 1 ,
 .Xr rgbfix 1 ,
-.Xr rgblib 1 ,
 .Xr rgblink 1 ,
 .Xr gbz80 7
 .Sh HISTORY