changeset 378:1c31e9005ff7

Brought forward lwlink, lwar, and lwobjdump along with some misc junk
author lost@starbug
date Mon, 26 Apr 2010 19:30:44 -0600
parents 55ed7d06b136
children 85b592c8b8f6
files .hgignore ChangeLog Makefile.am configure.ac extra/README extra/ar extra/as extra/ld lwar/Makefile.am lwar/add.c lwar/extract.c lwar/list.c lwar/lwar.c lwar/lwar.h lwar/main.c lwar/remove.c lwar/replace.c lwar/util.c lwar/util.h lwlink/Makefile.am lwlink/expr.c lwlink/expr.h lwlink/link.c lwlink/lwlink.c lwlink/lwlink.h lwlink/main.c lwlink/map.c lwlink/objdump.c lwlink/output.c lwlink/readfiles.c lwlink/script.c lwlink/util.c lwlink/util.h
diffstat 33 files changed, 4437 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Mon Apr 26 18:37:06 2010 -0600
+++ b/.hgignore	Mon Apr 26 19:30:44 2010 -0600
@@ -2,6 +2,9 @@
 *~
 *.o
 lwasm/lwasm
+lwlink/lwlink
+lwlink/lwobjdump
+lwar/lwar
 lwlib/liblw.a
 link-warning.h
 
--- a/ChangeLog	Mon Apr 26 18:37:06 2010 -0600
+++ b/ChangeLog	Mon Apr 26 19:30:44 2010 -0600
@@ -15,4 +15,4 @@
 
 Version 3.0
 
-[*] Completely new architecture
+[*] Completely new architecture for lwasm
--- a/Makefile.am	Mon Apr 26 18:37:06 2010 -0600
+++ b/Makefile.am	Mon Apr 26 19:30:44 2010 -0600
@@ -1,4 +1,4 @@
 ACLOCAL_AMFLAGS = -I m4
-SUBDIRS = lib lwlib lwasm
-DIST_SUBDIRS = lib lwlib lwasm
+SUBDIRS = lib lwlib lwasm lwlink lwar
+DIST_SUBDIRS = lib lwlib lwasm lwlink lwar
 EXTRA_DIST = m4/gnulib-cache.m4
--- a/configure.ac	Mon Apr 26 18:37:06 2010 -0600
+++ b/configure.ac	Mon Apr 26 19:30:44 2010 -0600
@@ -10,5 +10,7 @@
 	lib/Makefile
 	lwlib/Makefile
 	lwasm/Makefile
+	lwlink/Makefile
+	lwar/Makefile
 ])
 AC_OUTPUT
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/README	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,43 @@
+These files are extra utility type scripts that can be used for various
+purposes.
+
+as
+
+This is a sort of front-end script that makes lwasm look approximately like
+gnu as which is useful for using lwasm as a backend to gcc. You will
+probably need to edit it to make it work fully. Simply put this in place
+of whatever gcc6809 installed for "as" (in the "m6809/bin" folder in
+"--prefix") after editing it to point to the real location of the "lwasm"
+binary.
+
+
+ld
+
+Similar to the "as" script above except for lwlink.
+
+
+ar
+
+Similar to the "as" script above except for lwar.
+
+
+To use these scripts, you really need to understand how to build a gcc as a
+cross compiler. The basics are that you put the as, ld, and ar scripts
+whereever you plan to put your cross-development binaries. Then, when
+building the cross compiler, you tell it where the scripts are.
+
+You should probably name them m6809-unknown-none-{ar,as,ld} or similar
+depending on your gcc build target. Then you'll want to get the gcc6809
+patch and patch the correct gcc source code. Then use a configure line
+similar to the following:
+
+configure --enable-languages=c --target=m6809-coco
+--program-prefix=m6809-coco-lwos- --enable-obsolete
+--srcdir=/home/lost/gcc6809/src/gcc-4.3.3 --disable-threads --disable-nls
+--disable-libssp --prefix=/usr/local/coco --with-as=/usr/local/coco/bin/as
+--with-ld=/usr/local/coco/bin/ld --with-sysroot=/usr/local/coco
+
+Obviously adjust various paths to match what you're doing.
+
+The exact mechanics of configuring and getting gcc to install correctly is
+left as an exercise to the dedicated masochist.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/ar	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+#
+# Copyright 2009 by William Astle <lost@l-w.ca>
+#
+#This file is part of LWTOOLS.
+#
+#LWTOOLS is free software: you can redistribute it and/or modify it under the
+#terms of the GNU General Public License as published by the Free Software
+#Foundation, either version 3 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful, but WITHOUT
+#ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+#FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+#more details.
+#
+#You should have received a copy of the GNU General Public License along with
+#this program. If not, see <http://www.gnu.org/licenses/>.
+
+# this was based somewhat on the "ar" script from gcc6809
+
+# This script is a frontend to the lwar library manager, to make it
+# look more like GNU ar.  Not all ar features are supported here.
+# It basically translates ar style options into lwar format.
+
+# Parse and translate command-line options
+
+# ar options cheatsheet:
+# r: insert (with replace)
+# c: create archive
+# u: only replace newer files
+# v: verbose mode
+# x: extract files from archive
+
+options=$1; shift
+case $options in
+	rc|cru|-rc|-cru)
+		options="--replace --create"
+		;;
+	rv)
+		options="--replace"
+		;;
+	x|-x)
+		options="--extract"
+		;;
+	-C|--cache)
+		exit 0
+		;;
+	*)
+		options="--replace --create $options"
+		if [ "$libname" = "" ]; then
+			libname=$options
+		fi
+		;;
+esac
+
+if [ "x$options" = "x" ]; then
+	echo "ar (m6809): no options given"
+	exit 1
+fi
+
+# Run the real lwar with translated options.
+exec lwar $options $*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/as	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,144 @@
+#!/bin/sh
+#
+# Copyright 2009 by William Astle <lost@l-w.ca>
+#
+#This file is part of LWASM.
+#
+#LWASM is free software: you can redistribute it and/or modify it under the
+#terms of the GNU General Public License as published by the Free Software
+#Foundation, either version 3 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful, but WITHOUT
+#ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+#FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+#more details.
+#
+#You should have received a copy of the GNU General Public License along with
+#this program. If not, see <http://www.gnu.org/licenses/>.
+
+# this was based somewhat on the "as" script from gcc6809
+
+#echo "LWASM-as $0 $*"
+
+show_version () {
+cat <<END
+LWASM (GNU assembler frontend) 2.3
+This program is free software; you may redistribute it under the terms of
+the GNU General Public License.  This program has absolutely no warranty.
+END
+}
+
+fatal_error () {
+	echo $* 1>&2
+	exit 1
+}
+
+# Assume nothing.
+
+input_file=
+output_file=
+list_file=
+options=
+list_file_enabled=n
+
+# Parse the command-line options.  See the GNU 'as' man page for
+# an explanation of all these options.  Our goal is to translate
+# them into lwasm form.
+
+while [ "$1" != "" ]; do
+	arg=$1; shift
+	case $arg in
+		-m6809)
+			true
+			;;
+		-gn)
+			# Generate NoICE debug symbols
+			# ignored - no output formats support debugging symbols
+			;;
+		-gs)
+			# Generate SDCC debug symbols
+			# ignored - no output formats supprt debugging symbols
+			;;
+#		--globalize-symbols)
+#			# Make all symbols global
+#			# lwasm does not support globalizing everything by default
+#			;;
+		-m*)
+			fatal_error "invalid CPU option '$arg'"
+			;;
+		--)
+			fatal_error "standard input not supported"
+			;;
+#		-a*)
+#			options="${options}lc"
+#			list_file_enabled=y
+#			;;
+		-I*)
+			#include_file=${arg#-I}
+			#echo "warning: include path '$include_file' ignored"
+			;;
+		-MD)
+			fatal_error "assembler option '$arg' not supported"
+			;;
+		-o)
+			output_file=$1; shift
+			;;
+		-v|-version)
+			show_version
+			;;
+		--version)
+			show_version
+			exit 0
+			;;
+		-D|-f|-K|--traditional-format|-w|-x|-Z|-W|--no-warn)
+			# These options are accepted but ignored by GNU as.
+			true
+			;;
+#		=*)
+#			# Set the name of the listing file
+#			list_file=${arg#=}
+#			;;
+		-*)
+			echo "as (m6809): unrecognized option $arg"
+			exit 1
+			;;
+		*)
+			input_file=$arg
+			;;
+	esac
+done
+
+# Complain if no input files given.  We don't support redirecting
+# from standard input.
+
+if [ "x$input_file" = "x" ]; then
+	fatal_error "no input file specified"
+fi
+
+# Invoke the real (lwasm) assembler.
+# The -o option specifies the output file name
+# --obj creates object files
+# --pragma=undefextern causes undefined symbols to be assumed external
+# --pragma=cescapes allows C escape syntax in strings
+#echo lwasm -o "$output_file" $options --obj --pragma=undefextern --pragma=cescapes $input_file
+lwasm -o "$output_file" $options --obj --pragma=undefextern --pragma=cescapes --pragma=importundefexport $input_file
+rc=$?
+
+# OK, see if the assembler succeeded or not.
+# If it failed, the source is copied to /tmp/as6809_error.s
+# so that it can be inspected.  GCC will normally delete any
+# temporary .s files that it generates.  This makes debugging
+# the compiler easier.
+#
+# lwasm does not create an output file if it errors out but it also doesn't
+# remove an existing file if it fails so we remove it anyway...
+
+if [ "$rc" != "0" ]; then
+	cp -p $input_file /tmp/as6809_error.s
+	rm -f $asoutput_file
+	exit $rc
+fi
+
+# we don't need anything fancy here since lwasm supports specifying output
+# file names....
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/ld	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,127 @@
+#!/bin/sh
+#
+#
+# Copyright 2009 by William Astle <lost@l-w.ca>
+#
+#This file is part of LWTOOLS.
+#
+#LWTOOLS is free software: you can redistribute it and/or modify it under the
+#terms of the GNU General Public License as published by the Free Software
+#Foundation, either version 3 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful, but WITHOUT
+#ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+#FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+#more details.
+#
+#You should have received a copy of the GNU General Public License along with
+#this program. If not, see <http://www.gnu.org/licenses/>.
+
+# this was based somewhat on the "as" script from gcc6809
+
+echo "LWTOOLS-as $0 $*"
+path_to_lwlink=lwlink
+
+# Set defaults.  Some are based on the target type, which is 
+# determined by the name by which the program was invoked.
+output_file=a.out
+libpaths=
+libs=
+verbose=
+case $0 in
+	*m6809-coco-*)
+		options="--format=decb"
+#		options="-b .text=0x2000 -b .data=0x7000 -b .bss=0x7C00 -b .ctors=0x7F00 -b .dtors=0x7F80 -b vector=0x7FF0"
+#		aslink_options="-nwxst"
+#		exe_suffix=.bin
+		;;
+	*)
+		options="--format=decb"
+#		options="-b .text=0x8000 -b .data=0x1000 -b .bss=0x0100 -b .ctors=0xFD00 -b .dtors=0xFE00 -b vector=0xFFF0"
+#		aslink_options="-nwxso"
+#		exe_suffix=.s19
+		;;
+esac
+
+
+while [ "$1" != "" ]; do
+	arg=$1; shift
+	case $arg in
+		-gn)
+			# Generate NoICE debug file
+			# ignored because debugging not supported by targets
+			;;
+		-gs)
+			# Generate SDCC debug file
+			# ignored because debugging not supported by targets
+			;;
+		-o)
+			output_file=$1; shift
+			;;
+		-L*)
+			arg=${arg#-L}
+			libpaths="$libpaths --library-path=$arg"
+			;;
+		-l*)
+			arg=${arg#-l}
+			libs="$libs --library=$arg"
+			;;
+		--section-start)
+			section_value=$1; shift
+			options="$options --section-start=$section_value"
+			;;
+		-Tbss)
+			options="$options --section-start=.bss=$1"; shift
+			;;
+		-Tdata)
+			options="$options --section-start=.data=$1"; shift
+			;;
+		-Ttext|-Tcode)
+			options="$options --section-start=.text=$1"; shift
+			;;
+		-v|--verbose)
+			verbose=1
+			;;
+		*crt0.o)
+			startup_files=$arg
+			;;
+		-g)
+			# Ignored by GNU ld; we should do the same
+			true
+			;;
+		-h|--help)
+			echo "ld (m6809)"
+			exit 0
+			;;
+		-T)
+			echo "-T scripts not supported";
+			exit 1;
+			;;
+		--format-lwex)
+			options="$options --format=lwex"
+			;;
+
+		-*)
+			echo "ld (m6809): unknown option $arg"
+			exit 1
+			;;
+		*)
+			input_files="$input_files $arg"
+			;;
+	esac
+done
+
+options="$options -o $output_file"
+
+if [ "$verbose" = "1" ]; then
+	echo "$path_to_lwlink $options $input_files $startup_files $libpaths $libs"
+fi
+
+$path_to_lwlink $options $input_files $startup_files $libpaths $libs
+rc=$?
+
+if [ "$rc" != "0" ]; then
+	rm -f $output_file
+	exit $rc
+fi
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/Makefile.am	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,5 @@
+AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib
+bin_PROGRAMS = lwar
+lwar_SOURCES = main.c util.c lwar.c list.c add.c remove.c replace.c extract.c
+lwar_LDADD = -L$(top_builddir)/lib -L$(top_srcdir)/lib -lgnu
+EXTRA_DIST = lwar.h util.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/add.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,195 @@
+/*
+add.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+Implements the program startup code
+
+*/
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lwar.h"
+
+void do_add(void)
+{
+	FILE *f;
+	unsigned char buf[8];
+	long l;
+	int c;
+	FILE *f2;
+	int i;
+	
+	f = fopen(archive_file, "r+");
+	if (!f)
+	{
+		if (errno == ENOENT)
+		{
+			f = fopen(archive_file, "w");
+			if (f)
+			{
+				fputs("LWAR1V", f);
+				goto doadd;
+			}
+		}
+		perror("Cannot open archive file");
+	}
+	
+	fread(buf, 1, 6, f);
+	if (memcmp("LWAR1V", buf, 6))
+	{
+		fprintf(stderr, "%s is not a valid archive file.\n", archive_file);
+		exit(1);
+	}
+
+	for (;;)
+	{
+		c = fgetc(f);
+		if (c == EOF && ferror(f))
+		{
+			perror("Reading archive file");
+			exit(1);
+		}
+		if (c == EOF)
+			goto doadd;
+		
+		if (!c)
+		{
+			fseek(f, -1, SEEK_CUR);
+			goto doadd;
+		}
+		
+		// find the end of the file name
+		while (c)
+		{
+			c = fgetc(f);
+			if (c == EOF || ferror(f))
+			{
+				fprintf(stderr, "Bad archive file\n");
+				exit(1);
+			}
+		}
+		
+		// get length of archive member
+		l = 0;
+		c = fgetc(f);
+		l = c << 24;
+		c = fgetc(f);
+		l |= c << 16;
+		c = fgetc(f);
+		l |= c << 8;
+		c = fgetc(f);
+		l |= c;
+		
+		fseek(f, l, SEEK_CUR);
+	}
+	// back up to the NUL byte at the end of the file
+	fseek(f, -1, SEEK_CUR);
+doadd:
+	for (i = 0; i < nfiles; i++)
+	{
+		f2 = fopen(files[i], "r");
+		if (!f2)
+		{
+			fprintf(stderr, "Cannot open file %s:", files[i]);
+			perror("");
+			exit(1);
+		}
+		fread(buf, 1, 6, f2);
+		if (mergeflag && !memcmp("LWAR1V", buf, 6))
+		{
+			// add archive contents...
+			for (;;)
+			{
+				c = fgetc(f2);
+				if (c == EOF || ferror(f2))
+				{
+					perror("Reading input archive file");
+					exit(1);
+				}
+				if (c == EOF)
+					break;
+		
+				if (!c)
+				{
+					break;
+				}
+		
+				// find the end of the file name
+				while (c)
+				{
+					fputc(c, f);
+					c = fgetc(f2);
+					if (c == EOF || ferror(f))
+					{
+						fprintf(stderr, "Bad input archive file\n");
+						exit(1);
+					}
+				}
+				fputc(0, f);
+				
+				// get length of archive member
+				l = 0;
+				c = fgetc(f2);
+				fputc(c, f);
+				l = c << 24;
+				c = fgetc(f2);
+				fputc(c, f);
+				l |= c << 16;
+				c = fgetc(f2);
+				fputc(c, f);
+				l |= c << 8;
+				c = fgetc(f2);
+				fputc(c, f);
+				l |= c;
+		
+				while (l)
+				{
+					c = fgetc(f2);
+					fputc(c, f);
+					l--;
+				}
+			}
+			
+			fclose(f2);
+			continue;
+		}
+		fseek(f2, 0, SEEK_END);
+		l = ftell(f2);
+		fseek(f2, 0, SEEK_SET);
+		fputs(files[i], f);
+		fputc(0, f);
+		fputc(l >> 24, f);
+		fputc((l >> 16) & 0xff, f);
+		fputc((l >> 8) & 0xff, f);
+		fputc(l & 0xff, f);
+		while (l)
+		{
+			c = fgetc(f2);
+			fputc(c, f);
+			l--;
+		}
+	}
+	
+	// flag end of file
+	fputc(0, f);	
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/extract.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,123 @@
+/*
+extract.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwar.h"
+
+void do_extract(void)
+{
+	FILE *f;
+	char buf[8];
+	long l;
+	int c;
+	char fnbuf[1024];
+	int i;
+	FILE *nf;
+	
+	f = fopen(archive_file, "r");
+	if (!f)
+	{
+		perror("Opening archive file");
+		exit(1);
+	}
+	
+	fread(buf, 1, 6, f);
+	if (memcmp("LWAR1V", buf, 6))
+	{
+		fprintf(stderr, "%s is not a valid archive file.\n", archive_file);
+		exit(1);
+	}
+
+	for (;;)
+	{
+		c = fgetc(f);
+		if (ferror(f))
+		{
+			perror("Reading archive file");
+			exit(1);
+		}
+		if (c == EOF)
+			return;
+		
+		
+		// find the end of the file name
+		if (!c)
+			return;
+		
+		i = 0;
+		while (c)
+		{
+			fnbuf[i++] = c;
+			c = fgetc(f);
+			if (c == EOF || ferror(f))
+			{
+				fprintf(stderr, "Bad archive file\n");
+				exit(1);
+			}
+		}
+		fnbuf[i] = 0;
+		
+		// get length of archive member
+		l = 0;
+		c = fgetc(f);
+		l = c << 24;
+		c = fgetc(f);
+		l |= c << 16;
+		c = fgetc(f);
+		l |= c << 8;
+		c = fgetc(f);
+		l |= c;
+		
+		for (i = 0; i < nfiles; i++)
+		{
+			if (!strcmp(files[i], fnbuf))
+				break;
+		}
+		if (i < nfiles || nfiles == 0)
+		{
+			// extract the file
+			nf = fopen(fnbuf, "w");
+			if (!nf)
+			{
+				fprintf(stderr, "Cannot extract '%s': %s\n", fnbuf, strerror(errno));
+				exit(1);
+			}
+			while (l)
+			{
+				c = fgetc(f);
+				fputc(c, nf);
+				l--;
+			}
+			fclose(nf);
+		}
+		else
+		{
+			// skip the file
+			fseek(f, l, SEEK_CUR);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/list.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,95 @@
+/*
+list.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+Implements the program startup code
+
+*/
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwar.h"
+
+void do_list(void)
+{
+	FILE *f;
+	char buf[8];
+	long l;
+	int c;
+		
+	f = fopen(archive_file, "r");
+	if (!f)
+	{
+		perror("Opening archive file");
+		exit(1);
+	}
+	
+	fread(buf, 1, 6, f);
+	if (memcmp("LWAR1V", buf, 6))
+	{
+		fprintf(stderr, "%s is not a valid archive file.\n", archive_file);
+		exit(1);
+	}
+
+	for (;;)
+	{
+		c = fgetc(f);
+		if (ferror(f))
+		{
+			perror("Reading archive file");
+			exit(1);
+		}
+		if (c == EOF)
+			return;
+		
+		
+		// find the end of the file name
+		if (!c)
+			return;
+
+		while (c)
+		{
+			putchar(c);
+			c = fgetc(f);
+			if (c == EOF || ferror(f))
+			{
+				fprintf(stderr, "Bad archive file\n");
+				exit(1);
+			}
+		}
+		
+		// get length of archive member
+		l = 0;
+		c = fgetc(f);
+		l = c << 24;
+		c = fgetc(f);
+		l |= c << 16;
+		c = fgetc(f);
+		l |= c << 8;
+		c = fgetc(f);
+		l |= c;
+		printf(": %04lx bytes\n", l);
+		fseek(f, l, SEEK_CUR);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/lwar.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,55 @@
+/*
+lwar.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+Implements the program startup code
+
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define __lwar_c_seen__
+#include "lwar.h"
+#include "util.h"
+
+typedef struct
+{
+	FILE *f;
+} arhandle_real;
+
+int debug_level = 0;
+int operation = 0;
+int nfiles = 0;
+char *archive_file = NULL;
+int mergeflag = 0;
+
+char **files = NULL;
+
+void add_file_name(char *fn)
+{
+	files = lw_realloc(files, sizeof(char *) * (nfiles + 1));
+	files[nfiles] = fn;
+	nfiles++;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/lwar.h	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,62 @@
+/*
+lwar.h
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+Contains the main defs used by the linker
+*/
+
+
+#define LWAR_OP_LIST	1
+#define LWAR_OP_ADD		2
+#define LWAR_OP_REMOVE	3
+#define LWAR_OP_CREATE	4
+#define LWAR_OP_EXTRACT	5
+#define LWAR_OP_REPLACE	6
+
+#ifndef __lwar_h_seen__
+#define __lwar_h_seen__
+
+#ifndef __lwar_c_seen__
+
+extern char *archive_file;
+extern int debug_level;
+extern int operation;
+extern int nfiles;
+extern char **files;
+extern int mergeflag;
+
+//typedef void * ARHANDLE;
+
+#define AR_MODE_RD		1
+#define AR_MODE_WR		2
+#define AR_MODE_RW		3
+#define AR_MODE_CREATE	4
+
+
+#define __lwar_E__ extern
+#else
+#define __lwar_E__
+#endif // __lwar_c_seen__
+
+__lwar_E__ void add_file_name(char *fn);
+
+//__lwar_E__ ARHANDLE open_archive(char *fn, int mode);
+
+#undef __lwar_E__
+
+#endif //__lwar_h_seen__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/main.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,208 @@
+/*
+main.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+Implements the program startup code
+
+*/
+
+#include <config.h>
+
+#include <argp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "lwar.h"
+
+// command line option handling
+const char *argp_program_version = "LWAR from " PACKAGE_STRING;
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+char *program_name;
+
+static error_t parse_opts(int key, char *arg, struct argp_state *state)
+{
+	switch (key)
+	{
+	case 'd':
+		// debug
+		debug_level++;
+		break;
+	
+	case 'a':
+		// add members
+		operation = LWAR_OP_ADD;
+		break;
+	
+	case 'c':
+		// create archive
+		operation = LWAR_OP_CREATE;
+		break;
+	
+	case 'm':
+		mergeflag = 1;
+		break;
+
+	case 'r':
+		// replace members
+		operation = LWAR_OP_REPLACE;
+		break;
+	
+	case 'l':
+		// list members
+		operation = LWAR_OP_LIST;
+		break;
+	
+	case 'x':
+		// extract members
+		operation = LWAR_OP_EXTRACT;
+		break;
+
+	case ARGP_KEY_ARG:
+		if (archive_file)
+		{
+			// add archive member to list
+			add_file_name(arg);
+		}
+		else
+			archive_file = arg;
+		break;
+		
+	default:
+		return ARGP_ERR_UNKNOWN;
+	}
+	return 0;
+}
+
+static struct argp_option options[] =
+{
+	{ "replace",	'r',	0,		0,
+				"Add or replace archive members" },
+	{ "extract",	'x',	0,		0,
+				"Extract members from the archive" },
+	{ "add",		'a',	0,		0,
+				"Add members to the archive" },
+	{ "list",		'l',	0,		0,
+				"List the contents of the archive" },
+	{ "create",		'c',	0,		0,
+				"Create new archive (or truncate existing one)" },
+	{ "merge",		'm',	0,		0,
+				"Add the contents of archive arguments instead of the archives themselves" },
+	{ "debug",		'd',	0,		0,
+				"Set debug mode"},
+	{ 0 }
+};
+
+static struct argp argp =
+{
+	options,
+	parse_opts,
+	"<archive> [<file> ...]",
+	"LWAR, a library file manager for LWLINK"
+};
+
+extern void do_list(void);
+extern void do_add(void);
+extern void do_remove(void);
+extern void do_replace(void);
+extern void do_extract(void);
+
+// main function; parse command line, set up assembler state, and run the
+// assembler on the first file
+int main(int argc, char **argv)
+{
+	program_name = argv[0];
+	argp_parse(&argp, argc, argv, 0, 0, NULL);
+	if (archive_file == NULL)
+	{
+		fprintf(stderr, "You must specify an archive file.\n");
+		exit(1);
+	}
+
+	if (operation == 0)
+	{
+		fprintf(stderr, "You must specify an operation.\n");
+		exit(1);
+	}
+
+	if (operation == LWAR_OP_LIST || operation == LWAR_OP_REMOVE || operation == LWAR_OP_EXTRACT)
+	{
+		struct stat stbuf;
+		// make sure the archive exists
+		if (stat(archive_file, &stbuf) < 0)
+		{
+			fprintf(stderr, "Cannot open archive file %s:\n", archive_file);
+			perror("");
+			exit(2);
+		}
+	}
+	if (operation == LWAR_OP_CREATE)
+	{
+		struct stat stbuf;
+		// check if the archive exists
+		if (stat(archive_file, &stbuf) < 0)
+		{
+			if (errno != ENOENT)
+			{
+				fprintf(stderr, "Cannot create archive file %s:\n", archive_file);
+				perror("");
+				exit(2);
+			}
+		}
+		else
+		{
+			if (unlink(archive_file) < 0)
+			{
+				fprintf(stderr, "Cannot create archive file %s:\n", archive_file);
+				perror("");
+				exit(2);
+			}
+				
+		}
+	}
+	
+	switch (operation)
+	{
+	case LWAR_OP_LIST:
+		do_list();
+		break;
+	
+	case LWAR_OP_ADD:
+	case LWAR_OP_CREATE:
+		do_add();
+		break;
+	
+	case LWAR_OP_REMOVE:
+		do_remove();
+		break;
+	
+	case LWAR_OP_REPLACE:
+		do_replace();
+		break;
+	
+	case LWAR_OP_EXTRACT:
+		do_extract();
+		break;
+	}
+
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/remove.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,31 @@
+/*
+remove.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+Implements the program startup code
+
+*/
+
+#include <config.h>
+
+#include "lwar.h"
+
+void do_remove(void)
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/replace.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,243 @@
+/*
+replace.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwar.h"
+
+void do_replace(void)
+{
+	FILE *f;
+	FILE *nf;
+	unsigned char buf[8];
+	long l;
+	int c;
+	FILE *f2;
+	int i;
+	char fnbuf[1024];
+	char fnbuf2[1024];
+		
+	sprintf(fnbuf, "%s.tmp", archive_file);
+	
+	f = fopen(archive_file, "r+");
+	if (!f)
+	{
+		if (errno == ENOENT)
+		{
+			nf = fopen(fnbuf, "w");
+			if (nf)
+			{
+				fputs("LWAR1V", nf);
+				goto doadd;
+			}
+		}
+		perror("Cannot open archive file");
+	}
+	
+	fread(buf, 1, 6, f);
+	if (memcmp("LWAR1V", buf, 6))
+	{
+		fprintf(stderr, "%s is not a valid archive file.\n", archive_file);
+		exit(1);
+	}
+
+	nf = fopen(fnbuf, "w");
+	if (!nf)
+	{
+		perror("Cannot create temp archive file");
+		exit(1);
+	}
+
+	fputs("LWAR1V", nf);
+
+	for (;;)
+	{
+		c = fgetc(f);
+		if (c == EOF && ferror(f))
+		{
+			perror("Reading archive file");
+			exit(1);
+		}
+		if (c == EOF)
+			goto doadd;
+		
+		if (!c)
+		{
+			goto doadd;
+		}
+		
+		// find the end of the file name
+		i = 0;
+		while (c)
+		{
+			fnbuf2[i++] = c;
+			c = fgetc(f);
+			if (c == EOF || ferror(f))
+			{
+				fprintf(stderr, "Bad archive file\n");
+				exit(1);
+			}
+		}
+		fnbuf2[i] = 0;
+		
+		// get length of archive member
+		l = 0;
+		c = fgetc(f);
+		l = c << 24;
+		c = fgetc(f);
+		l |= c << 16;
+		c = fgetc(f);
+		l |= c << 8;
+		c = fgetc(f);
+		l |= c;
+		
+		// is it a file we are replacing? if so, do not copy it
+		for (i = 0; i < nfiles; i++)
+		{
+			if (!strcmp(files[i], fnbuf2))
+				break;
+		}
+		if (i < nfiles)
+		{
+			fseek(f, l, SEEK_CUR);
+		}
+		else
+		{
+			// otherwise, copy it
+			fprintf(nf, "%s", fnbuf2);
+			fputc(0, nf);
+			fputc(l >> 24, nf);
+			fputc((l >> 16) & 0xff, nf);
+			fputc((l >> 8) & 0xff, nf);
+			fputc(l & 0xff, nf);
+			while (l)
+			{
+				c = fgetc(f);
+				fputc(c, nf);
+				l--;
+			}
+		}
+	}
+	
+	// done with the original file
+	fclose(f);
+doadd:
+	for (i = 0; i < nfiles; i++)
+	{
+		f2 = fopen(files[i], "r");
+		if (!f2)
+		{
+			fprintf(stderr, "Cannot open file %s:", files[i]);
+			perror("");
+			exit(1);
+		}
+		fread(buf, 1, 6, f2);
+		if (mergeflag && !memcmp("LWAR1V", buf, 6))
+		{
+			// add archive contents...
+			for (;;)
+			{
+				c = fgetc(f2);
+				if (c == EOF || ferror(f2))
+				{
+					perror("Reading input archive file");
+					exit(1);
+				}
+				if (c == EOF)
+					break;
+		
+				if (!c)
+				{
+					break;
+				}
+		
+				// find the end of the file name
+				while (c)
+				{
+					fputc(c, nf);
+					c = fgetc(f2);
+					if (c == EOF || ferror(f))
+					{
+						fprintf(stderr, "Bad input archive file\n");
+						exit(1);
+					}
+				}
+				fputc(0, nf);
+				
+				// get length of archive member
+				l = 0;
+				c = fgetc(f2);
+				fputc(c, nf);
+				l = c << 24;
+				c = fgetc(f2);
+				fputc(c, nf);
+				l |= c << 16;
+				c = fgetc(f2);
+				fputc(c, nf);
+				l |= c << 8;
+				c = fgetc(f2);
+				fputc(c, nf);
+				l |= c;
+		
+				while (l)
+				{
+					c = fgetc(f2);
+					fputc(c, nf);
+					l--;
+				}
+			}
+			
+			fclose(f2);
+			continue;
+		}
+		fseek(f2, 0, SEEK_END);
+		l = ftell(f2);
+		fseek(f2, 0, SEEK_SET);
+		fputs(files[i], nf);
+		fputc(0, nf);
+		fputc(l >> 24, nf);
+		fputc((l >> 16) & 0xff, nf);
+		fputc((l >> 8) & 0xff, nf);
+		fputc(l & 0xff, nf);
+		while (l)
+		{
+			c = fgetc(f2);
+			fputc(c, nf);
+			l--;
+		}
+	}
+	
+	// flag end of file
+	fputc(0, nf);
+	
+	fclose(nf);
+	
+	if (rename(fnbuf, archive_file) < 0)
+	{
+		perror("Cannot replace old archive file");
+		unlink(fnbuf);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/util.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,88 @@
+/*
+util.c
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+Utility functions
+*/
+
+#define __util_c_seen__
+#include <config.h>
+
+#include <malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void *lw_malloc(int size)
+{
+	void *ptr;
+	
+	ptr = malloc(size);
+	if (!ptr)
+	{
+		// bail out; memory allocation error
+		fprintf(stderr, "lw_malloc(): Memory allocation error\n");
+		exit(1);
+	}
+	return ptr;
+}
+
+void *lw_realloc(void *optr, int size)
+{
+	void *ptr;
+	
+	if (size == 0)
+	{
+		lw_free(optr);
+		return;
+	}
+	
+	ptr = realloc(optr, size);
+	if (!ptr)
+	{
+		fprintf(stderr, "lw_realloc(): memory allocation error\n");
+		exit(1);
+	}
+}
+
+void lw_free(void *ptr)
+{
+	if (ptr)
+		free(ptr);
+}
+
+char *lw_strdup(const char *s)
+{
+	char *d;
+	
+	if (!s)
+		return NULL;
+
+	d = strdup(s);
+	if (!d)
+	{
+		fprintf(stderr, "lw_strdup(): memory allocation error\n");
+		exit(1);
+	}
+	
+	return d;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwar/util.h	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,44 @@
+/*
+util.h
+Copyright © 2009 William Astle
+
+This file is part of LWAR.
+
+LWAR is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+Utility functions
+*/
+
+#ifndef __util_h_seen__
+#define __util_h_seen__
+
+#ifndef __util_c_seen__
+#define __util_E__ extern
+#else
+#define __util_E__
+#endif
+
+// allocate memory
+__util_E__ void *lw_malloc(int size);
+__util_E__ void lw_free(void *ptr);
+__util_E__ void *lw_realloc(void *optr, int size);
+
+// string stuff
+__util_E__ char *lw_strdup(const char *s);
+
+#undef __util_E__
+
+#endif // __util_h_seen__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/Makefile.am	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,7 @@
+AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib
+bin_PROGRAMS = lwlink lwobjdump
+lwlink_SOURCES = main.c lwlink.c util.c readfiles.c expr.c script.c link.c output.c map.c
+lwlink_LDADD = -L$(top_builddir)/lib -L$(top_srcdir)/lib -lgnu
+lwobjdump_SOURCES = objdump.c util.c
+lwobjdump_LDADD = -L$(top_builddir)/lib -L$(top_srcdir)/lib -lgnu
+EXTRA_DIST = lwlink.h util.h expr.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/expr.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,370 @@
+/*
+expr.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+This file contains the actual expression evaluator
+*/
+
+#define __expr_c_seen__
+#include <config.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "expr.h"
+#include "util.h"
+
+lw_expr_stack_t *lw_expr_stack_create(void)
+{
+	lw_expr_stack_t *s;
+	
+	s = lw_malloc(sizeof(lw_expr_stack_t));
+	s -> head = NULL;
+	s -> tail = NULL;
+	return s;
+}
+
+void lw_expr_stack_free(lw_expr_stack_t *s)
+{
+	while (s -> head)
+	{
+		s -> tail = s -> head;
+		s -> head = s -> head -> next;
+		lw_expr_term_free(s -> tail -> term);
+		lw_free(s -> tail);
+	}
+	lw_free(s);
+}
+
+lw_expr_stack_t *lw_expr_stack_dup(lw_expr_stack_t *s)
+{
+	lw_expr_stack_node_t *t;
+	lw_expr_stack_t *s2;
+		
+	s2 = lw_expr_stack_create();	
+	for (t = s -> head; t; t = t -> next)
+	{
+		lw_expr_stack_push(s2, t -> term);
+	}
+	return s2;
+}
+
+void lw_expr_term_free(lw_expr_term_t *t)
+{
+	if (t)
+	{
+		if (t -> term_type == LW_TERM_SYM)
+			lw_free(t -> symbol);
+		lw_free(t);
+	}
+}
+
+lw_expr_term_t *lw_expr_term_create_oper(int oper)
+{
+	lw_expr_term_t *t;
+
+	t = lw_malloc(sizeof(lw_expr_term_t));
+	t -> term_type = LW_TERM_OPER;
+	t -> value = oper;
+	return t;
+}
+
+lw_expr_term_t *lw_expr_term_create_int(int val)
+{
+	lw_expr_term_t *t;
+	
+	t = lw_malloc(sizeof(lw_expr_term_t));
+	t -> term_type = LW_TERM_INT;
+	t -> value = val;
+	return t;
+}
+
+lw_expr_term_t *lw_expr_term_create_sym(char *sym, int symtype)
+{
+	lw_expr_term_t *t;
+	
+	t = lw_malloc(sizeof(lw_expr_term_t));
+	t -> term_type = LW_TERM_SYM;
+	t -> symbol = lw_strdup(sym);
+	t -> value = symtype;
+	return t;
+}
+
+lw_expr_term_t *lw_expr_term_dup(lw_expr_term_t *t)
+{
+	switch (t -> term_type)
+	{
+	case LW_TERM_INT:
+		return lw_expr_term_create_int(t -> value);
+		
+	case LW_TERM_OPER:
+		return lw_expr_term_create_oper(t -> value);
+		
+	case LW_TERM_SYM:
+		return lw_expr_term_create_sym(t -> symbol, t -> value);
+	
+	default:
+		exit(1);
+	}
+// can't get here
+}
+
+void lw_expr_stack_push(lw_expr_stack_t *s, lw_expr_term_t *t)
+{
+	lw_expr_stack_node_t *n;
+
+	if (!s)
+	{
+		exit(1);
+	}
+	
+	n = lw_malloc(sizeof(lw_expr_stack_node_t));
+	n -> next = NULL;
+	n -> prev = s -> tail;
+	n -> term = lw_expr_term_dup(t);
+	
+	if (s -> head)
+	{
+		s -> tail -> next = n;
+		s -> tail = n;
+	}
+	else
+	{
+		s -> head = n;
+		s -> tail = n;
+	}
+}
+
+lw_expr_term_t *lw_expr_stack_pop(lw_expr_stack_t *s)
+{
+	lw_expr_term_t *t;
+	lw_expr_stack_node_t *n;
+	
+	if (!(s -> tail))
+		return NULL;
+	
+	n = s -> tail;
+	s -> tail = n -> prev;
+	if (!(n -> prev))
+	{
+		s -> head = NULL;
+	}
+	
+	t = n -> term;
+	n -> term = NULL;
+	
+	lw_free(n);
+	
+	return t;
+}
+
+/*
+take an expression stack s and scan for operations that can be completed
+
+return -1 on error, 0 on no error
+
+possible errors are: division by zero or unknown operator
+
+theory of operation:
+
+scan the stack for an operator which has two constants preceding it (binary)
+or 1 constant preceding it (unary) and if found, perform the calculation
+and replace the operator and its operands with the result
+
+repeat the scan until no futher simplications are found or if there are no
+further operators or only a single term remains
+
+*/
+int lw_expr_reval(lw_expr_stack_t *s, lw_expr_stack_t *(*sfunc)(char *sym, int stype, void *state), void *state)
+{
+	lw_expr_stack_node_t *n, *n2;
+	lw_expr_stack_t *ss;
+	int c;
+	
+next_iter_sym:
+	// resolve symbols
+	// symbols that do not resolve to an expression are left alone
+	for (c = 0, n = s -> head; n; n = n -> next)
+	{
+		if (n -> term -> term_type == LW_TERM_SYM)
+		{
+			ss = sfunc(n -> term -> symbol, n -> term -> value, state);
+			if (ss)
+			{
+				c++;
+				// splice in the result stack
+				if (n -> prev)
+				{
+					n -> prev -> next = ss -> head;
+				}
+				else
+				{
+					s -> head = ss -> head;
+				}
+				ss -> head -> prev = n -> prev;
+				ss -> tail -> next = n -> next;
+				if (n -> next)
+				{
+					n -> next -> prev = ss -> tail;
+				}
+				else
+				{
+					s -> tail = ss -> tail;
+				}
+				lw_expr_term_free(n -> term);
+				lw_free(n);
+				n = ss -> tail;
+				
+				ss -> head = NULL;
+				ss -> tail = NULL;
+				lw_expr_stack_free(ss);
+			}
+		}
+	}
+	if (c)
+		goto next_iter_sym;
+
+next_iter:	
+	// a single term
+	if (s -> head == s -> tail)
+		return 0;
+	
+	// search for an operator
+	for (n = s -> head; n; n = n -> next)
+	{
+		if (n -> term -> term_type == LW_TERM_OPER)
+		{
+			if (n -> term -> value == LW_OPER_NEG
+				|| n -> term -> value == LW_OPER_COM
+				)
+			{
+				// unary operator
+				if (n -> prev && n -> prev -> term -> term_type == LW_TERM_INT)
+				{
+					// a unary operator we can resolve
+					// we do the op then remove the term "n" is pointing at
+					if (n -> term -> value == LW_OPER_NEG)
+					{
+						n -> prev -> term -> value = -(n -> prev -> term -> value);
+					}
+					else if (n -> term -> value == LW_OPER_COM)
+					{
+						n -> prev -> term -> value = ~(n -> prev -> term -> value);
+					}
+					n -> prev -> next = n -> next;
+					if (n -> next)
+						n -> next -> prev = n -> prev;
+					else
+						s -> tail = n -> prev;	
+					
+					lw_expr_term_free(n -> term);
+					lw_free(n);
+					break;
+				}
+			}
+			else
+			{
+				// binary operator
+				if (n -> prev && n -> prev -> prev && n -> prev -> term -> term_type == LW_TERM_INT && n -> prev -> prev -> term -> term_type == LW_TERM_INT)
+				{
+					// a binary operator we can resolve
+					switch (n -> term -> value)
+					{
+					case LW_OPER_PLUS:
+						n -> prev -> prev -> term -> value += n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_MINUS:
+						n -> prev -> prev -> term -> value -= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_TIMES:
+						n -> prev -> prev -> term -> value *= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_DIVIDE:
+						if (n -> prev -> term -> value == 0)
+							return -1;
+						n -> prev -> prev -> term -> value /= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_MOD:
+						if (n -> prev -> term -> value == 0)
+							return -1;
+						n -> prev -> prev -> term -> value %= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_INTDIV:
+						if (n -> prev -> term -> value == 0)
+							return -1;
+						n -> prev -> prev -> term -> value /= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_BWAND:
+						n -> prev -> prev -> term -> value &= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_BWOR:
+						n -> prev -> prev -> term -> value |= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_BWXOR:
+						n -> prev -> prev -> term -> value ^= n -> prev -> term -> value;
+						break;
+
+					case LW_OPER_AND:
+						n -> prev -> prev -> term -> value = (n -> prev -> term -> value && n -> prev -> prev -> term -> value) ? 1 : 0;
+						break;
+
+					case LW_OPER_OR:
+						n -> prev -> prev -> term -> value = (n -> prev -> term -> value || n -> prev -> prev -> term -> value) ? 1 : 0;
+						break;
+
+					default:
+						// return error if unknown operator!
+						return -1;
+					}
+
+					// now remove the two unneeded entries from the stack
+					n -> prev -> prev -> next = n -> next;
+					if (n -> next)
+						n -> next -> prev = n -> prev -> prev;
+					else
+						s -> tail = n -> prev -> prev;	
+					
+					lw_expr_term_free(n -> term);
+					lw_expr_term_free(n -> prev -> term);
+					lw_free(n -> prev);
+					lw_free(n);
+					break;
+				}
+			}
+		}
+	}
+	// note for the terminally confused about dynamic memory and pointers:
+	// n will not be NULL even after the lw_free calls above so
+	// this test will still work (n will be a dangling pointer)
+	// (n will only be NULL if we didn't find any operators to simplify)
+	if (n)
+		goto next_iter;
+	
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/expr.h	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,108 @@
+/*
+expr.h
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+Definitions for expression evaluator
+*/
+
+#ifndef __expr_h_seen__
+#define __expr_h_seen__
+
+#ifndef __expr_c_seen__
+#define __expr_E__ extern
+#else
+#define __expr_E__
+#endif
+
+// term types
+#define LW_TERM_NONE		0
+#define LW_TERM_OPER		1	// an operator
+#define LW_TERM_INT		2	// 32 bit signed integer
+#define LW_TERM_SYM		3	// symbol reference
+
+// operator types
+#define LW_OPER_NONE		0
+#define LW_OPER_PLUS		1	// +
+#define LW_OPER_MINUS	2	// -
+#define LW_OPER_TIMES	3	// *
+#define LW_OPER_DIVIDE	4	// /
+#define LW_OPER_MOD		5	// %
+#define LW_OPER_INTDIV	6	// \ (don't end line with \)
+#define LW_OPER_BWAND	7	// bitwise AND
+#define LW_OPER_BWOR		8	// bitwise OR
+#define LW_OPER_BWXOR	9	// bitwise XOR
+#define LW_OPER_AND		10	// boolean AND
+#define LW_OPER_OR		11	// boolean OR
+#define LW_OPER_NEG		12	// - unary negation (2's complement)
+#define LW_OPER_COM		13	// ^ unary 1's complement
+
+
+// term structure
+typedef struct lw_expr_term_s
+{
+	int term_type;		// type of term (see above)
+	char *symbol;		// name of a symbol
+	int value;			// value of the term (int) or operator number (OPER)
+} lw_expr_term_t;
+
+// type for an expression evaluation stack
+typedef struct lw_expr_stack_node_s lw_expr_stack_node_t;
+struct lw_expr_stack_node_s
+{
+	lw_expr_term_t		*term;
+	lw_expr_stack_node_t	*prev;
+	lw_expr_stack_node_t	*next;	
+};
+
+typedef struct lw_expr_stack_s
+{
+	lw_expr_stack_node_t *head;
+	lw_expr_stack_node_t *tail;
+} lw_expr_stack_t;
+
+__expr_E__ void lw_expr_term_free(lw_expr_term_t *t);
+__expr_E__ lw_expr_term_t *lw_expr_term_create_oper(int oper);
+__expr_E__ lw_expr_term_t *lw_expr_term_create_sym(char *sym, int symtype);
+__expr_E__ lw_expr_term_t *lw_expr_term_create_int(int val);
+__expr_E__ lw_expr_term_t *lw_expr_term_dup(lw_expr_term_t *t);
+
+__expr_E__ void lw_expr_stack_free(lw_expr_stack_t *s);
+__expr_E__ lw_expr_stack_t *lw_expr_stack_create(void);
+__expr_E__ lw_expr_stack_t *lw_expr_stack_dup(lw_expr_stack_t *s);
+
+__expr_E__ void lw_expr_stack_push(lw_expr_stack_t *s, lw_expr_term_t *t);
+__expr_E__ lw_expr_term_t *lw_expr_stack_pop(lw_expr_stack_t *s);
+
+// simplify expression
+__expr_E__ int lw_expr_reval(lw_expr_stack_t *s, lw_expr_stack_t *(*sfunc)(char *sym, int symtype, void *state), void *state);
+
+// useful macros
+// is the expression "simple" (one term)?
+#define lw_expr_is_simple(s) ((s) -> head == (s) -> tail)
+
+// is the expression constant?
+#define lw_expr_is_constant(s) (lw_expr_is_simple(s) && (!((s) -> head) || (s) -> head -> term -> term_type == LW_TERM_INT))
+
+// get the constant value of an expression or 0 if not constant
+#define lw_expr_get_value(s) (lw_expr_is_constant(s) ? ((s) -> head ? (s) -> head -> term -> value : 0) : 0)
+
+#undef __expr_E__
+
+#endif // __expr_h_seen__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/link.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,462 @@
+/*
+link.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+Resolve section and symbol addresses; handle incomplete references
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "expr.h"
+#include "lwlink.h"
+#include "util.h"
+
+struct section_list *sectlist = NULL;
+int nsects = 0;
+static int nforced = 0;
+
+void check_section_name(char *name, int *base, fileinfo_t *fn)
+{
+	int sn;
+
+	if (fn -> forced == 0)
+		return;
+
+	for (sn = 0; sn < fn -> nsections; sn++)
+	{
+		if (!strcmp(name, fn -> sections[sn].name))
+		{
+			// we have a match
+			sectlist = lw_realloc(sectlist, sizeof(struct section_list) * (nsects + 1));
+			sectlist[nsects].ptr = &(fn -> sections[sn]);
+					
+			fn -> sections[sn].processed = 1;
+			fn -> sections[sn].loadaddress = *base;
+			*base += fn -> sections[sn].codesize;
+			nsects++;
+		}
+	}
+	for (sn = 0; sn < fn -> nsubs; sn++)
+	{
+		check_section_name(name, base, fn -> subs[sn]);
+	}
+}
+
+void add_matching_sections(char *name, int yesflags, int noflags, int *base);
+void check_section_flags(int yesflags, int noflags, int *base, fileinfo_t *fn)
+{
+	int sn;
+
+	if (fn -> forced == 0)
+		return;
+
+	for (sn = 0; sn < fn -> nsections; sn++)
+	{
+		// ignore if the noflags tell us to
+		if (noflags && (fn -> sections[sn].flags & noflags))
+			continue;
+		// ignore unless the yesflags tell us not to
+		if (yesflags && (fn -> sections[sn].flags & yesflags == 0))
+			continue;
+		// ignore it if already processed
+		if (fn -> sections[sn].processed)
+			continue;
+
+		// we have a match - now collect *all* sections of the same name!
+		add_matching_sections(fn -> sections[sn].name, 0, 0, base);
+		
+		// and then continue looking for sections
+	}
+	for (sn = 0; sn < fn -> nsubs; sn++)
+	{
+		check_section_flags(yesflags, noflags, base, fn -> subs[sn]);
+	}
+}
+
+
+
+void add_matching_sections(char *name, int yesflags, int noflags, int *base)
+{
+	int fn;
+	if (name)
+	{
+		// named section
+		// look for all instances of a section by the specified name
+		// and resolve base addresses and add to the list
+		for (fn = 0; fn < ninputfiles; fn++)
+		{
+			check_section_name(name, base, inputfiles[fn]);
+		}
+	}
+	else
+	{
+		// wildcard section
+		// named section
+		// look for all instances of a section by the specified name
+		// and resolve base addresses and add to the list
+		for (fn = 0; fn < ninputfiles; fn++)
+		{
+			check_section_flags(yesflags, noflags, base, inputfiles[fn]);
+		}
+	}
+}
+
+// work out section load order and resolve base addresses for each section
+// make a list of sections to load in order
+void resolve_sections(void)
+{
+	int laddr = 0;
+	int ln, sn, fn;
+	
+	for (ln = 0; ln < linkscript.nlines; ln++)
+	{
+		if (linkscript.lines[ln].loadat >= 0)
+			laddr = linkscript.lines[ln].loadat;
+		add_matching_sections(linkscript.lines[ln].sectname, linkscript.lines[ln].yesflags, linkscript.lines[ln].noflags, &laddr);
+		
+		if (linkscript.lines[ln].sectname)
+		{
+		}
+		else
+		{
+			// wildcard section
+			// look for all sections not yet processed that match flags
+
+			int f = 0;
+			int fn0, sn0;
+			char *sname;
+			
+			// named section
+			// look for all instances of a section by the specified name
+			// and resolve base addresses and add to the list
+			for (fn0 = 0; fn0 < ninputfiles; fn0++)
+			{
+				for (sn0 = 0; sn0 < inputfiles[fn0] -> nsections; sn0++)
+				{
+					// ignore if the "no flags" bit says to
+					if (linkscript.lines[ln].noflags && (inputfiles[fn0] -> sections[sn0].flags & linkscript.lines[ln].noflags))
+						continue;
+					// ignore unless the yes flags tell us not to
+					if (linkscript.lines[ln].yesflags && (inputfiles[fn0] -> sections[sn0].flags & linkscript.lines[ln].yesflags == 0))
+						continue;
+					if (inputfiles[fn0] -> sections[sn0].processed == 0)
+					{
+						sname = inputfiles[fn0] -> sections[sn0].name;
+						for (fn = 0; fn < ninputfiles; fn++)
+						{
+							for (sn = 0; sn < inputfiles[fn] -> nsections; sn++)
+							{
+								if (!strcmp(sname, inputfiles[fn] -> sections[sn].name))
+								{
+									// we have a match
+									sectlist = lw_realloc(sectlist, sizeof(struct section_list) * (nsects + 1));
+									sectlist[nsects].ptr = &(inputfiles[fn] -> sections[sn]);
+						
+									inputfiles[fn] -> sections[sn].processed = 1;
+									if (!f && linkscript.lines[ln].loadat >= 0)
+									{
+										f = 1;
+										sectlist[nsects].forceaddr = 1;
+										laddr = linkscript.lines[ln].loadat;
+									}
+									else
+									{
+										sectlist[nsects].forceaddr = 0;
+									}
+									inputfiles[fn] -> sections[sn].loadaddress = laddr;
+									laddr += inputfiles[fn] -> sections[sn].codesize;
+									nsects++;
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	
+	// theoretically, all the base addresses are set now
+}
+
+lw_expr_stack_t *find_external_sym_recurse(char *sym, fileinfo_t *fn)
+{
+	int sn;
+	lw_expr_stack_t *r;
+	lw_expr_term_t *term;
+	symtab_t *se;
+	int val;
+	
+	for (sn = 0; sn < fn -> nsections; sn++)
+	{
+		for (se = fn -> sections[sn].exportedsyms; se; se = se -> next)
+		{
+			if (!strcmp(sym, se -> sym))
+			{
+				if (!(fn -> forced))
+				{
+					fn -> forced = 1;
+					nforced = 1;
+				}
+				val = se -> offset + fn -> sections[sn].loadaddress;
+				r = lw_expr_stack_create();
+				term = lw_expr_term_create_int(val & 0xffff);
+				lw_expr_stack_push(r, term);
+				lw_expr_term_free(term);
+				return r;
+			}
+		}
+	}
+		
+	for (sn = 0; sn < fn -> nsubs; sn++)
+	{
+		r = find_external_sym_recurse(sym, fn -> subs[sn]);
+		if (r)
+		{
+			if (!(fn -> forced))
+			{
+				nforced = 1;
+				fn -> forced = 1;
+			}
+			return r;
+		}
+	}
+	return NULL;
+}
+
+// resolve all incomplete references now
+// anything that is unresolvable at this stage will throw an error
+// because we know the load address of every section now
+lw_expr_stack_t *resolve_sym(char *sym, int symtype, void *state)
+{
+	section_t *sect = state;
+	lw_expr_term_t *term;
+	int val = 0, i, fn;
+	lw_expr_stack_t *s;
+	symtab_t *se;
+	fileinfo_t *fp;
+
+	if (symtype == 1)
+	{
+		// local symbol
+		if (!sym)
+		{
+			val = sect -> loadaddress;
+			goto out;
+		}
+		
+		// start with this section
+		for (se = sect -> localsyms; se; se = se -> next)
+		{
+			if (!strcmp(se -> sym, sym))
+			{
+				val = se -> offset + sect -> loadaddress;
+				goto out;
+			}
+		}
+		// not in this section - check all sections in this file
+		for (i = 0; i < sect -> file -> nsections; i++)
+		{
+			for (se = sect -> file -> sections[i].localsyms; se; se = se -> next)
+			{
+				if (!strcmp(se -> sym, sym))
+				{
+					val = se -> offset + sect -> file -> sections[i].loadaddress;
+					goto out;
+				}
+			}
+		}
+		// not found
+		symerr = 1;
+		fprintf(stderr, "Local symbol %s not found in %s:%s\n", sym, sect -> file -> filename, sect -> name);
+		goto outerr;
+	}
+	else
+	{
+		// external symbol
+		// read all files in order until found (or not found)
+		if (sect)
+		{
+			for (fp = sect -> file; fp; fp = fp -> parent)
+			{
+				s = find_external_sym_recurse(sym, fp);
+				if (s)
+					return s;
+			}
+		}
+
+		for (fn = 0; fn < ninputfiles; fn++)
+		{
+			s = find_external_sym_recurse(sym, inputfiles[fn]);
+			if (s)
+				return s;
+		}
+		if (sect)
+		{
+			fprintf(stderr, "External symbol %s not found in %s:%s\n", sym, sect -> file -> filename, sect -> name);
+		}
+		else
+		{
+			fprintf(stderr, "External symbol %s not found\n", sym);
+		}
+		symerr = 1;
+		goto outerr;
+	}
+	fprintf(stderr, "Shouldn't ever get here!!!\n");
+	exit(88);
+out:
+	s = lw_expr_stack_create();
+	term = lw_expr_term_create_int(val & 0xffff);
+	lw_expr_stack_push(s, term);
+	lw_expr_term_free(term);
+	return s;
+outerr:
+	return NULL;
+}
+
+void resolve_references(void)
+{
+	int sn;
+	reloc_t *rl;
+	int rval;
+
+	// resolve entry point if required
+	// this must resolve to an *exported* symbol and will resolve to the
+	// first instance of that symbol
+	if (linkscript.execsym)
+	{
+		lw_expr_stack_t *s;
+		
+		s = resolve_sym(linkscript.execsym, 0, NULL);
+		if (!s)
+		{
+			fprintf(stderr, "Cannot resolve exec address '%s'\n", linkscript.execsym);
+			symerr = 1;
+		}
+		else
+		{
+			linkscript.execaddr = lw_expr_get_value(s);
+			lw_expr_stack_free(s);
+		}
+	}
+	
+	for (sn = 0; sn < nsects; sn++)
+	{
+		for (rl = sectlist[sn].ptr -> incompletes; rl; rl = rl -> next)
+		{
+			// do a "simplify" on the expression
+			rval = lw_expr_reval(rl -> expr, resolve_sym, sectlist[sn].ptr);
+
+			// is it constant? error out if not
+			if (rval != 0 || !lw_expr_is_constant(rl -> expr))
+			{
+				fprintf(stderr, "Incomplete reference at %s:%s+%02X\n", sectlist[sn].ptr -> file -> filename, sectlist[sn].ptr -> name, rl -> offset);
+				symerr = 1;
+			}
+			else
+			{
+				// put the value into the relocation address
+				rval = lw_expr_get_value(rl -> expr);
+				if (rl -> flags & RELOC_8BIT)
+				{
+					sectlist[sn].ptr -> code[rl -> offset] = rval & 0xff;
+				}
+				else
+				{
+					sectlist[sn].ptr -> code[rl -> offset] = (rval >> 8) & 0xff;
+					sectlist[sn].ptr -> code[rl -> offset + 1] = rval & 0xff;
+				}
+			}
+		}
+	}
+	
+	if (symerr)
+		exit(1);
+}
+
+/*
+This is just a pared down version of the algo for resolving references.
+*/
+void resolve_files(void)
+{
+	int sn;
+	int fn;
+	reloc_t *rl;
+	lw_expr_stack_t *te;
+	
+	int rval;
+
+	// resolve entry point if required
+	// this must resolve to an *exported* symbol and will resolve to the
+	// first instance of that symbol
+	if (linkscript.execsym)
+	{
+		lw_expr_stack_t *s;
+		
+		s = resolve_sym(linkscript.execsym, 0, NULL);
+		if (!s)
+		{
+			fprintf(stderr, "Cannot resolve exec address '%s'\n", linkscript.execsym);
+			symerr = 1;
+		}
+	}
+	
+	do
+	{
+		nforced = 0;
+		for (fn = 0; fn < ninputfiles; fn++)
+		{
+			if (inputfiles[fn] -> forced == 0)
+				continue;
+	
+			for (sn = 0; sn < inputfiles[fn] -> nsections; sn++)
+			{
+				for (rl = inputfiles[fn] -> sections[sn].incompletes; rl; rl = rl -> next)
+				{
+					// do a "simplify" on the expression
+					te = lw_expr_stack_dup(rl -> expr);
+					rval = lw_expr_reval(te, resolve_sym, &(inputfiles[fn] -> sections[sn]));
+					
+					// is it constant? error out if not
+					if (rval != 0 || !lw_expr_is_constant(te))
+					{
+						fprintf(stderr, "Incomplete reference at %s:%s+%02X\n", inputfiles[fn] -> filename, inputfiles[fn] -> sections[sn].name, rl -> offset);
+						symerr = 1;
+					}
+					lw_expr_stack_free(te);
+				}
+			}
+		}
+	}
+	while (nforced == 1);
+
+	if (symerr)
+		exit(1);
+	
+	// theoretically, all files referenced by other files now have "forced" set to 1
+	for (fn = 0; fn < ninputfiles; fn++)
+	{
+		if (inputfiles[fn] -> forced == 1)
+			continue;
+		
+		fprintf(stderr, "Warning: %s (%d) does not resolve any symbols\n", inputfiles[fn] -> filename, fn);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/lwlink.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,107 @@
+/*
+lwlink.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+*/
+
+#include <config.h>
+
+#define __lwlink_c_seen__
+
+#include <argp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwlink.h"
+#include "util.h"
+
+int debug_level = 0;
+int outformat = OUTPUT_DECB;
+char *outfile = NULL;
+char *scriptfile = NULL;
+int symerr = 0;
+char *map_file = NULL;
+
+fileinfo_t **inputfiles = NULL;
+int ninputfiles = 0;
+
+int nlibdirs = 0;
+char **libdirs = NULL;
+
+int nscriptls = 0;
+char **scriptls = NULL;
+
+void add_input_file(char *fn)
+{
+	inputfiles = lw_realloc(inputfiles, sizeof(fileinfo_t *) * (ninputfiles + 1));
+	inputfiles[ninputfiles] = lw_malloc(sizeof(fileinfo_t));
+	memset(inputfiles[ninputfiles], 0, sizeof(fileinfo_t));
+	inputfiles[ninputfiles] -> forced = 1;
+	inputfiles[ninputfiles++] -> filename = lw_strdup(fn);
+}
+
+void add_input_library(char *libname)
+{
+	inputfiles = lw_realloc(inputfiles, sizeof(fileinfo_t *) * (ninputfiles + 1));
+	inputfiles[ninputfiles] = lw_malloc(sizeof(fileinfo_t));
+	memset(inputfiles[ninputfiles], 0, sizeof(fileinfo_t));
+	inputfiles[ninputfiles] -> islib = 1;
+	inputfiles[ninputfiles] -> forced = 0;
+	inputfiles[ninputfiles++] -> filename = lw_strdup(libname);	
+}
+
+void add_library_search(char *libdir)
+{
+	libdirs = lw_realloc(libdirs, sizeof(char*) * (nlibdirs + 1));
+	libdirs[nlibdirs] = lw_strdup(libdir);
+	nlibdirs++;
+}
+
+void add_section_base(char *sectspec)
+{
+	char *base;
+	int baseaddr;
+	char *t;
+	int l;
+	
+	base = strchr(sectspec, '=');
+	if (!base)
+	{
+		l = strlen(sectspec);
+		baseaddr = 0;
+	}
+	else
+	{
+		baseaddr = strtol(base + 1, NULL, 16);
+		l = base - sectspec;
+		*base = '\0';
+	}
+	baseaddr = baseaddr & 0xffff;
+	
+	t = lw_malloc(l + 25);
+	sprintf(t, "section %s load %04X", sectspec, baseaddr);
+	if (base)
+		*base = '=';
+	
+	scriptls = lw_realloc(scriptls, sizeof(char *) * (nscriptls + 1));
+	scriptls[nscriptls++] = t;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/lwlink.h	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,155 @@
+/*
+lwlink.h
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+Contains the main defs used by the linker
+*/
+
+
+#ifndef __lwlink_h_seen__
+#define __lwlink_h_seen__
+
+#include "expr.h"
+
+#define OUTPUT_DECB		0	// DECB multirecord format
+#define OUTPUT_RAW		1	// raw sequence of bytes
+#define OUTPUT_LWEX0	2	// LWOS LWEX binary version 0
+
+typedef struct symtab_s symtab_t;
+struct symtab_s
+{
+	unsigned char *sym;		// symbol name
+	int offset;				// local offset
+//	int realval;			// resolved value
+	symtab_t *next;			// next symbol
+};
+
+#define RELOC_NORM	0		// all default (16 bit)
+#define RELOC_8BIT	1		// only use the low 8 bits for the reloc
+typedef struct reloc_s reloc_t;
+struct reloc_s
+{
+	int offset;				// where in the section
+	int flags;				// flags for the relocation
+	lw_expr_stack_t *expr;	// the expression to calculate it
+	reloc_t *next;			// ptr to next relocation
+};
+
+typedef struct fileinfo_s fileinfo_t;
+
+#define SECTION_BSS		1
+typedef struct
+{
+	unsigned char *name;	// name of the section
+	int flags;				// section flags
+	int codesize;			// size of the code
+	unsigned char *code;	// pointer to the code
+	int loadaddress;		// the actual load address of the section
+	int processed;			// was the section processed yet?
+		
+	symtab_t *localsyms;	// local symbol table
+	symtab_t *exportedsyms;	// exported symbols table
+	
+	reloc_t *incompletes;	// table of incomplete references
+	
+	fileinfo_t *file;		// the file we are in
+} section_t;
+
+struct fileinfo_s
+{
+	char *filename;
+	unsigned char *filedata;
+	long filesize;
+	section_t *sections;
+	int nsections;
+	int islib;				// set to true if the file is a "-l" option
+
+	int forced;				// set to true if the file is a "forced" include
+
+	// "sub" files (like in archives or libraries)
+	int nsubs;
+	fileinfo_t **subs;
+	fileinfo_t *parent;
+};
+
+struct section_list
+{
+	section_t *ptr;		// ptr to section structure
+	int forceaddr;		// was this force to an address by the link script?
+};
+
+#ifndef __link_c_seen__
+extern struct section_list *sectlist;
+extern int nsects;
+#endif
+
+
+#ifndef __lwlink_c_seen__
+
+extern int debug_level;
+extern int outformat;
+extern char *outfile;
+extern int ninputfiles;
+extern fileinfo_t **inputfiles;
+extern char *scriptfile;
+
+extern int nlibdirs;
+extern char **libdirs;
+
+extern int nscriptls;
+extern char **scriptls;
+
+extern int symerr;
+
+extern char *map_file;
+
+#define __lwlink_E__ extern
+#else
+#define __lwlink_E__
+#endif // __lwlink_c_seen__
+
+__lwlink_E__ void add_input_file(char *fn);
+__lwlink_E__ void add_input_library(char *fn);
+__lwlink_E__ void add_library_search(char *fn);
+__lwlink_E__ void add_section_base(char *fn);
+
+#undef __lwlink_E__
+
+struct scriptline_s
+{
+	char *sectname;				// name of section, NULL for wildcard
+	int loadat;					// address to load at (or -1)
+	int noflags;				// flags to NOT have
+	int yesflags;				// flags to HAVE
+};
+
+typedef struct
+{
+	int nlines;					// number of lines in the script
+	struct scriptline_s *lines;	// the parsed script lines (section)
+	int padsize;				// the size to pad to, -1 for none
+	char *execsym;				// entry point symbol
+	int execaddr;				// execution address (entry point)
+	int stacksize;				// stack size
+} linkscript_t;
+
+#ifndef __script_c_seen__
+extern linkscript_t linkscript;
+#endif
+
+#endif //__lwlink_h_seen__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/main.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,196 @@
+/*
+main.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+Implements the program startup code
+
+*/
+
+#include <config.h>
+
+#include <argp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "lwlink.h"
+
+char *program_name;
+
+// command line option handling
+const char *argp_program_version = "LWLINK from " PACKAGE_STRING;
+const char *argp_program_bug_address = PACKAGE_BUGREPORT;
+
+static error_t parse_opts(int key, char *arg, struct argp_state *state)
+{
+	switch (key)
+	{
+	case 'o':
+		// output
+		outfile = arg;
+		break;
+	
+	case 's':
+		// script file
+		scriptfile = arg;
+		break;
+
+	case 'd':
+		// debug
+		debug_level++;
+		break;
+	
+	case 'b':
+		// decb output
+		outformat = OUTPUT_DECB;
+		break;
+	
+	case 'r':
+		// raw binary output
+		outformat = OUTPUT_RAW;
+		break;
+	
+	case 'f':
+		// output format
+		if (!strcasecmp(arg, "decb"))
+			outformat = OUTPUT_DECB;
+		else if (!strcasecmp(arg, "raw"))
+			outformat = OUTPUT_RAW;
+		else if (!strcasecmp(arg, "lwex0") || !strcasecmp(arg, "lwex"))
+			outformat = OUTPUT_LWEX0;
+		else
+		{
+			fprintf(stderr, "Invalid output format: %s\n", arg);
+			exit(1);
+		}
+		break;
+	case ARGP_KEY_END:
+		// done; sanity check
+		if (!outfile)
+			outfile = "a.out";
+		break;
+	
+	case 'l':
+		add_input_library(arg);
+		break;
+	
+	case 'L':
+		add_library_search(arg);
+		break;
+	
+	case 0x100:
+		add_section_base(arg);
+		break;
+	
+	case 'm':
+		map_file = arg;
+		break;
+	
+	case ARGP_KEY_ARG:
+		add_input_file(arg);
+		break;
+		
+	default:
+		return ARGP_ERR_UNKNOWN;
+	}
+	return 0;
+}
+
+static struct argp_option options[] =
+{
+	{ "output",		'o',	"FILE",	0,
+				"Output to FILE"},
+	{ "debug",		'd',	0,		0,
+				"Set debug mode"},
+	{ "format",		'f',	"TYPE",	0,
+				"Select output format: decb, raw, lwex"},
+	{ "decb",		'b',	0,		0,
+				"Generate DECB .bin format output, equivalent of --format=decb"},
+	{ "raw",		'r',	0,		0,
+				"Generate raw binary format output, equivalent of --format=raw"},
+	{ "script",		's',	"FILE",		0,
+				"Specify the linking script (overrides the built in defaults)"},
+	{ "library",	'l',	"LIBSPEC",	0,
+				"Read library libLIBSPEC.a from the search path" },
+	{ "library-path", 'L',	"DIR",		0,
+				"Add DIR to the library search path" },
+	{ "section-base", 0x100,	"SECT=BASE",	0,
+				"Load section SECT at BASE" },
+	{ "map",		'm',	"FILE",		0,
+				"Output informaiton about the link" },
+	{ 0 }
+};
+
+static struct argp argp =
+{
+	options,
+	parse_opts,
+	"<input file> ...",
+	"LWLINK, a HD6309 and MC6809 cross-linker"
+};
+
+extern void read_files(void);
+extern void setup_script(void);
+extern void resolve_files(void);
+extern void resolve_sections(void);
+extern void resolve_references(void);
+extern void do_output(void);
+extern void display_map(void);
+
+// main function; parse command line, set up assembler state, and run the
+// assembler on the first file
+int main(int argc, char **argv)
+{
+	program_name = argv[0];
+
+	argp_parse(&argp, argc, argv, 0, 0, NULL);
+	if (ninputfiles == 0)
+	{
+		fprintf(stderr, "No input files\n");
+		exit(1);
+	}
+
+	unlink(outfile);
+
+	// handle the linker script
+	setup_script();
+
+	// read the input files
+	read_files();
+
+	// trace unresolved references and determine which non-forced
+	// objects must be included
+	resolve_files();
+	
+	// resolve section bases and section order
+	resolve_sections();
+	
+	// resolve incomplete references
+	resolve_references();
+	
+	// do the actual output
+	do_output();
+
+	// display/output the link map
+	if (map_file)
+		display_map();
+
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/map.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,115 @@
+/*
+map.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+Output information about the linking process
+*/
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lwlink.h"
+#include "util.h"
+
+struct symliste
+{
+	char *name;
+	char *fn;
+	int addr;
+	int ext;
+	struct symliste *next;
+};
+
+void display_map(void)
+{
+	FILE *of;
+	int sn;
+	int std = 0;
+	struct symliste *slist = NULL;
+	struct symliste *ce, *pe, *ne;
+	symtab_t *sym;
+	int i;
+	
+	if (!strcmp(map_file, "-"))
+	{
+		std = 1;
+		of = stdout;
+	}
+	else
+	{
+		of = fopen(map_file, "w");
+		if (!of)
+		{
+			fprintf(stderr, "Cannot open map file - using stdout\n");
+			std = 1;
+			of = stdout;
+		}
+	}
+
+	// display section list
+	for (sn = 0; sn < nsects; sn++)
+	{
+		fprintf(of, "Section: %s (%s) load at %04X, length %04X\n",
+				sectlist[sn].ptr -> name,
+				sectlist[sn].ptr -> file -> filename,
+				sectlist[sn].ptr -> loadaddress,
+				sectlist[sn].ptr -> codesize
+			);
+	}
+
+	// generate a sorted list of symbols and display it
+	for (sn = 0; sn < nsects; sn++)
+	{
+		for (sym = sectlist[sn].ptr -> localsyms; sym; sym = sym -> next)
+		{
+			for (pe = NULL, ce = slist; ce; ce = ce -> next)
+			{
+				i = strcmp(ce -> name, sym -> sym);
+				if (i == 0)
+				{
+					i = strcmp(ce -> fn, sectlist[sn].ptr -> file -> filename);
+				}
+				if (i > 0)
+					break;
+				pe = ce;
+			}
+			ne = lw_malloc(sizeof(struct symliste));
+			ne -> ext = 0;
+			ne -> addr = sym -> offset + sectlist[sn].ptr -> loadaddress;
+			ne -> next = ce;
+			ne -> name = sym -> sym;
+			ne -> fn = sectlist[sn].ptr -> file -> filename;
+			if (pe)
+				pe -> next = ne;
+			else
+				slist = ne;
+		}
+	}
+	
+	for (ce = slist; ce; ce = ce -> next)
+	{
+		fprintf(of, "Symbol: %s (%s) = %04X\n", ce -> name, ce -> fn, ce -> addr);
+	}
+
+	if (!std)
+		fclose(of);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/objdump.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,311 @@
+/*
+objdump.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+A standalone program to dump an object file in a text form to stdout
+
+*/
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+void read_lwobj16v0(unsigned char *filedata, long filesize);
+char *program_name;
+
+/*
+The logic of reading the entire file into memory is simple. All the symbol
+names in the file are NUL terminated strings and can be used directly without
+making additional copies.
+*/
+int main(int argc, char **argv)
+{
+	int i;
+	long size;
+	FILE *f;
+	long bread;
+	unsigned char *filedata;
+
+	program_name = argv[0];	
+	if (argc != 2)
+	{
+		fprintf(stderr, "Must specify exactly one input file.\n");
+		exit(1);
+	}
+
+	f = fopen(argv[1], "rb");
+	if (!f)
+	{
+		fprintf(stderr, "Can't open file %s:", argv[1]);
+		perror("");
+		exit(1);
+	}
+	fseek(f, 0, SEEK_END);
+	size = ftell(f);
+	rewind(f);
+		
+	filedata = lw_malloc(size);
+		
+	bread = fread(filedata, 1, size, f);
+	if (bread < size)
+	{
+		fprintf(stderr, "Short read on file %s (%ld/%ld):", argv[1], bread, size);
+		perror("");
+		exit(1);
+	}
+			
+	fclose(f);
+		
+	if (!memcmp(filedata, "LWOBJ16", 8))
+	{
+		// read v0 LWOBJ16 file
+		read_lwobj16v0(filedata, size);
+	}
+	else
+	{
+		fprintf(stderr, "%s: unknown file format\n", argv[1]);
+		exit(1);
+	}
+	exit(0);
+}
+
+// this macro is used to bail out if we run off the end of the file data
+// while parsing - it keeps the code below cleaner
+#define NEXTBYTE()	do { cc++; if (cc > filesize) { fprintf(stderr, "***invalid file format\n"); exit(1); } } while (0)
+// this macro is used to refer to the current byte in the stream
+#define CURBYTE()	(filedata[cc < filesize ? cc : filesize - 1])
+// this one will leave the input pointer past the trailing NUL
+#define CURSTR()	read_lwobj16v0_str(&cc, &filedata, filesize)
+unsigned char *read_lwobj16v0_str(long *cc1, unsigned char **filedata1, long filesize)
+{
+	int cc = *cc1;
+	unsigned char *filedata = *filedata1;
+	unsigned char *fp;
+	fp = &CURBYTE();
+	while (CURBYTE())
+		NEXTBYTE();
+	NEXTBYTE();
+	*cc1 = cc;
+	*filedata1 = filedata;
+	return fp;
+}
+// the function below can be switched to dealing with data coming from a
+// source other than an in-memory byte pool by adjusting the input data
+// in "fn" and the above two macros
+void read_lwobj16v0(unsigned char *filedata, long filesize)
+{
+	unsigned char *fp;
+	long cc;
+	int val;
+	int bss;
+
+	static char *opernames[] = {
+		"?",
+		"PLUS",
+		"MINUS",
+		"TIMES",
+		"DIVIDE",
+		"MOD",
+		"INTDIV",
+		"BWAND",
+		"BWOR",
+		"BWXOR",
+		"AND",
+		"OR",
+		"NEG",
+		"COM"
+	};
+	static const int numopers = 13;
+		
+	// start reading *after* the magic number
+	cc = 8;
+	
+	while (1)
+	{
+		bss = 0;
+		
+		// bail out if no more sections
+		if (!(CURBYTE()))
+			break;
+		
+		fp = CURSTR();
+		
+		printf("SECTION %s\n", fp);
+		
+		// read flags
+		while (CURBYTE())
+		{
+			switch (CURBYTE())
+			{
+			case 0x01:
+				printf("    FLAG: BSS\n");
+				bss = 1;
+				break;
+
+			default:
+				printf("    FLAG: %02X (unknown)\n", CURBYTE());
+				break;
+			}
+			NEXTBYTE();
+		}
+		// skip NUL terminating flags
+		NEXTBYTE();
+		
+		printf("    Local symbols:\n");
+		// now parse the local symbol table
+		while (CURBYTE())
+		{
+			fp = CURSTR();
+
+			// fp is the symbol name
+			val = (CURBYTE()) << 8;
+			NEXTBYTE();
+			val |= (CURBYTE());
+			NEXTBYTE();
+			// val is now the symbol value
+			
+			printf("        %s=%04X\n", fp, val);
+			
+		}
+		// skip terminating NUL
+		NEXTBYTE();
+
+		printf("    Exported symbols\n");
+				
+		// now parse the exported symbol table
+		while (CURBYTE())
+		{
+			fp = CURSTR();
+
+			// fp is the symbol name
+			val = (CURBYTE()) << 8;
+			NEXTBYTE();
+			val |= (CURBYTE());
+			NEXTBYTE();
+			// val is now the symbol value
+			
+			printf("        %s=%04X\n", fp, val);
+		}
+		// skip terminating NUL
+		NEXTBYTE();
+		
+		// now parse the incomplete references and make a list of
+		// external references that need resolution
+		printf("    Incomplete references\n");
+		while (CURBYTE())
+		{
+			printf("        (");
+			// parse the expression
+			while (CURBYTE())
+			{
+				int tt = CURBYTE();
+				NEXTBYTE();
+				switch (tt)
+				{
+				case 0x01:
+					// 16 bit integer
+					tt = CURBYTE() << 8;
+					NEXTBYTE();
+					tt |= CURBYTE();
+					NEXTBYTE();
+					// normalize for negatives...
+					if (tt > 0x7fff)
+						tt -= 0x10000;
+					printf(" I16=%d", tt);
+					break;
+				
+				case 0x02:
+					// external symbol reference
+					printf(" ES=%s", CURSTR());
+					break;
+					
+				case 0x03:
+					// internal symbol reference
+					printf(" IS=%s", CURSTR());
+					break;
+				
+				case 0x04:
+					// operator
+					if (CURBYTE() > 0 && CURBYTE() <= numopers)
+						printf(" OP=%s", opernames[CURBYTE()]);
+					else
+						printf(" OP=?");
+					NEXTBYTE();
+					break;
+
+				case 0x05:
+					// section base reference (NULL internal reference is
+					// the section base address
+					printf(" SB");
+					break;
+				
+				case 0xFF:
+					// section flags
+					printf(" FLAGS=%02X", CURBYTE());
+					NEXTBYTE();
+					break;
+					
+				default:
+					printf(" ERR");
+				}
+			}
+			// skip the NUL
+			NEXTBYTE();
+			
+			// fetch the offset
+			val = CURBYTE() << 8;
+			NEXTBYTE();
+			val |= CURBYTE() & 0xff;
+			NEXTBYTE();
+			printf(" ) @ %04X\n", val);
+		}
+		// skip the NUL terminating the relocations
+		NEXTBYTE();
+				
+		// now set code location and size and verify that the file
+		// contains data going to the end of the code (if !SECTION_BSS)
+		val = CURBYTE() << 8;
+		NEXTBYTE();
+		val |= CURBYTE();
+		NEXTBYTE();
+		
+		printf("    CODE %04X bytes", val);
+		
+		// skip the code if we're not in a BSS section
+		if (!bss)
+		{
+			int i;
+			for (i = 0; i < val; i++)
+			{
+				if (! (i % 16))
+				{
+					printf("\n    %04X ", i);
+				}
+				printf("%02X", CURBYTE());
+				NEXTBYTE();
+			}
+		}
+		printf("\n");
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/output.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,217 @@
+/*
+output.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+Actually output the binary
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwlink.h"
+
+// this prevents warnings about not using the return value of fwrite()
+// and, theoretically, can be replaced with a function that handles things
+// better in the future
+#define writebytes(s, l, c, f)	do { int r; r = fwrite((s), (l), (c), (f)); } while (0)
+
+void do_output_decb(FILE *of);
+void do_output_raw(FILE *of);
+void do_output_lwex0(FILE *of);
+
+void do_output(void)
+{
+	FILE *of;
+	
+	of = fopen(outfile, "wb");
+	if (!of)
+	{
+		fprintf(stderr, "Cannot open output file %s: ", outfile);
+		perror("");
+		exit(1);
+	}
+	
+	switch (outformat)
+	{
+	case OUTPUT_DECB:
+		do_output_decb(of);
+		break;
+	
+	case OUTPUT_RAW:
+		do_output_raw(of);
+		break;
+
+	case OUTPUT_LWEX0:
+		do_output_lwex0(of);
+		break;
+	
+	default:
+		fprintf(stderr, "Unknown output format doing output!\n");
+		exit(111);
+	}
+	
+	fclose(of);
+}
+
+void do_output_decb(FILE *of)
+{
+	int sn, sn2;
+	int cloc, olen;
+	unsigned char buf[5];
+	
+	for (sn = 0; sn < nsects; sn++)
+	{
+		if (sectlist[sn].ptr -> flags & SECTION_BSS)
+		{
+			// no output for a BSS section
+			continue;
+		}
+		if (sectlist[sn].ptr -> codesize == 0)
+		{
+			// don't generate output for a zero size section
+			continue;
+		}
+		
+		// calculate the length of this output block
+		cloc = sectlist[sn].ptr -> loadaddress;
+		olen = 0;
+		for (sn2 = sn; sn2 < nsects; sn2++)
+		{
+			// ignore BSS sections
+			if (sectlist[sn2].ptr -> flags & SECTION_BSS)
+				continue;
+			// ignore zero length sections
+			if (sectlist[sn2].ptr -> codesize == 0)
+				continue;
+			if (cloc != sectlist[sn2].ptr -> loadaddress)
+				break;
+			olen += sectlist[sn2].ptr -> codesize;
+			cloc += sectlist[sn2].ptr -> codesize;
+		}
+		
+		// write a preamble
+		buf[0] = 0x00;
+		buf[1] = olen >> 8;
+		buf[2] = olen & 0xff;
+		buf[3] = sectlist[sn].ptr -> loadaddress >> 8;
+		buf[4] = sectlist[sn].ptr -> loadaddress & 0xff;
+		writebytes(buf, 1, 5, of);
+		for (; sn < sn2; sn++)
+		{
+			if (sectlist[sn].ptr -> flags & SECTION_BSS)
+				continue;
+			if (sectlist[sn].ptr -> codesize == 0)
+				continue;
+			writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of);
+		}
+		sn--;
+	}
+	// write a postamble
+	buf[0] = 0xff;
+	buf[1] = 0x00;
+	buf[2] = 0x00;
+	buf[3] = linkscript.execaddr >> 8;
+	buf[4] = linkscript.execaddr & 0xff;
+	writebytes(buf, 1, 5, of);
+}
+
+void do_output_raw(FILE *of)
+{
+	int nskips = 0;		// used to output blanks for BSS inline
+	int sn;
+	
+	for (sn = 0; sn < nsects; sn++)
+	{
+		if (sectlist[sn].ptr -> flags & SECTION_BSS)
+		{
+			// no output for a BSS section
+			nskips += sectlist[sn].ptr -> codesize;
+			continue;
+		}
+		while (nskips > 0)
+		{
+			// the "" is not an error - it turns into a single NUL byte!
+			writebytes("", 1, 1, of);
+			nskips--;
+		}
+		writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of);
+	}
+}
+
+void do_output_lwex0(FILE *of)
+{
+	int nskips = 0;		// used to output blanks for BSS inline
+	int sn;
+	int codedatasize = 0;
+	unsigned char buf[32];
+	
+	// calculate items for the file header
+	for (sn = 0; sn < nsects; sn++)
+	{
+		if (sectlist[sn].ptr -> flags & SECTION_BSS)
+		{
+			// no output for a BSS section
+			nskips += sectlist[sn].ptr -> codesize;
+			continue;
+		}
+		codedatasize += nskips;
+		nskips = 0;
+		codedatasize += sectlist[sn].ptr -> codesize;
+	}
+
+	// output the file header
+	buf[0] = 'L';
+	buf[1] = 'W';
+	buf[2] = 'E';
+	buf[3] = 'X';
+	buf[4] = 0;		// version 0
+	buf[5] = 0;		// low stack
+	buf[6] = linkscript.stacksize / 256;
+	buf[7] = linkscript.stacksize & 0xff;
+	buf[8] = nskips / 256;
+	buf[9] = nskips & 0xff;
+	buf[10] = codedatasize / 256;
+	buf[11] = codedatasize & 0xff;
+	buf[12] = linkscript.execaddr / 256;
+	buf[13] = linkscript.execaddr & 0xff;
+	memset(buf + 14, 0, 18);
+	
+	writebytes(buf, 1, 32, of);
+	// output the data
+	// NOTE: disjoint load addresses will not work correctly!!!!!
+	for (sn = 0; sn < nsects; sn++)
+	{
+		if (sectlist[sn].ptr -> flags & SECTION_BSS)
+		{
+			// no output for a BSS section
+			nskips += sectlist[sn].ptr -> codesize;
+			continue;
+		}
+		while (nskips > 0)
+		{
+			// the "" is not an error - it turns into a single NUL byte!
+			writebytes("", 1, 1, of);
+			nskips--;
+		}
+		writebytes(sectlist[sn].ptr -> code, 1, sectlist[sn].ptr -> codesize, of);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/readfiles.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,425 @@
+/*
+readfiles.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+Reads input files
+
+*/
+
+#include <config.h>
+
+#include <argp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwlink.h"
+#include "util.h"
+
+void read_lwobj16v0(fileinfo_t *fn);
+void read_lwar1v(fileinfo_t *fn);
+
+/*
+The logic of reading the entire file into memory is simple. All the symbol
+names in the file are NUL terminated strings and can be used directly without
+making additional copies.
+*/
+void read_file(fileinfo_t *fn)
+{
+	if (!memcmp(fn -> filedata, "LWOBJ16", 8))
+		{
+			// read v0 LWOBJ16 file
+			read_lwobj16v0(fn);
+		}
+		else if (!memcmp(fn -> filedata, "LWAR1V", 6))
+		{
+			// archive file
+			read_lwar1v(fn);
+		}
+		else
+		{
+			fprintf(stderr, "%s: unknown file format\n", fn -> filename);
+			exit(1);
+		}
+}
+
+void read_files(void)
+{
+	int i;
+	long size;
+	FILE *f;
+	long bread;
+	for (i = 0; i < ninputfiles; i++)
+	{
+		if (inputfiles[i] -> islib)
+		{
+			char *tf;
+			int s;
+			int j;
+			
+			f = NULL;
+			
+			for (j = 0; j < nlibdirs; j++)
+			{
+				s = strlen(libdirs[j]) + 7 + strlen(inputfiles[i] -> filename);
+				tf = lw_malloc(s + 1);
+				sprintf(tf, "%s/lib%s.a", libdirs[j], inputfiles[i] -> filename);
+				f = fopen(tf, "rb");
+				if (!f)
+				{
+					free(tf);
+					continue;
+				}
+				free(tf);
+				break;
+			}
+			if (!f)
+			{
+				fprintf(stderr, "Can't open library: -l%s\n", inputfiles[i] -> filename);
+				exit(1);
+			}
+		}
+		else
+		{
+			f = fopen(inputfiles[i] -> filename, "rb");
+			if (!f)
+			{
+				fprintf(stderr, "Can't open file %s:", inputfiles[i] -> filename);
+				perror("");
+				exit(1);
+			}
+		}
+		fseek(f, 0, SEEK_END);
+		size = ftell(f);
+		rewind(f);
+		
+		inputfiles[i] -> filedata = lw_malloc(size);
+		inputfiles[i] -> filesize = size;
+		
+		bread = fread(inputfiles[i] -> filedata, 1, size, f);
+		if (bread < size)
+		{
+			fprintf(stderr, "Short read on file %s (%ld/%ld):", inputfiles[i] -> filename, bread, size);
+			perror("");
+			exit(1);
+		}
+			
+		fclose(f);
+		
+		read_file(inputfiles[i]);
+	}
+}
+
+// this macro is used to bail out if we run off the end of the file data
+// while parsing - it keeps the code below cleaner
+#define NEXTBYTE()	do { cc++; if (cc > fn -> filesize) { fprintf(stderr, "%s: invalid file format\n", fn -> filename); exit(1); } } while (0)
+// this macro is used to refer to the current byte in the stream
+#define CURBYTE()	(fn -> filedata[cc < fn -> filesize ? cc : fn -> filesize - 1])
+// this one will leave the input pointer past the trailing NUL
+#define CURSTR()	read_lwobj16v0_str(&cc, fn)
+unsigned char *read_lwobj16v0_str(long *cc1, fileinfo_t *fn)
+{
+	int cc = *cc1;
+	unsigned char *fp;
+	fp = &CURBYTE();
+	while (CURBYTE())
+		NEXTBYTE();
+	NEXTBYTE();
+	*cc1 = cc;
+	return fp;
+}
+// the function below can be switched to dealing with data coming from a
+// source other than an in-memory byte pool by adjusting the input data
+// in "fn" and the above two macros
+
+void read_lwobj16v0(fileinfo_t *fn)
+{
+	unsigned char *fp;
+	long cc;
+	section_t *s;
+	int val;
+	symtab_t *se;
+	
+	// start reading *after* the magic number
+	cc = 8;
+	
+	// init data
+	fn -> sections = NULL;
+	fn -> nsections = 0;
+
+	while (1)
+	{
+//		NEXTBYTE();
+		// bail out if no more sections
+		if (!(CURBYTE()))
+			break;
+		
+		fp = CURSTR();
+		
+		// we now have a section name in fp
+		// create new section entry
+		fn -> sections = lw_realloc(fn -> sections, sizeof(section_t) * (fn -> nsections + 1));
+		s = &(fn -> sections[fn -> nsections]);
+		fn -> nsections += 1;
+		
+		s -> localsyms = NULL;
+		s -> flags = 0;
+		s -> codesize = 0;
+		s -> name = fp;
+		s -> loadaddress = 0;
+		s -> localsyms = NULL;
+		s -> exportedsyms = NULL;
+		s -> incompletes = NULL;
+		s -> processed = 0;
+		s -> file = fn;
+		
+		// read flags
+		while (CURBYTE())
+		{
+			switch (CURBYTE())
+			{
+			case 0x01:
+				s -> flags |= SECTION_BSS;
+				break;
+
+			default:
+				fprintf(stderr, "%s (%s): unrecognized section flag %02X\n", fn -> filename, s -> name, (int)(CURBYTE()));
+				exit(1);
+			}
+			NEXTBYTE();
+		}
+		// skip NUL terminating flags
+		NEXTBYTE();
+		
+		// now parse the local symbol table
+		while (CURBYTE())
+		{
+			fp = CURSTR();
+
+			// fp is the symbol name
+			val = (CURBYTE()) << 8;
+			NEXTBYTE();
+			val |= (CURBYTE());
+			NEXTBYTE();
+			// val is now the symbol value
+			
+			// create symbol table entry
+			se = lw_malloc(sizeof(symtab_t));
+			se -> next = s -> localsyms;
+			s -> localsyms = se;
+			se -> sym = fp;
+			se -> offset = val;
+		}
+		// skip terminating NUL
+		NEXTBYTE();
+		
+		// now parse the exported symbol table
+		while (CURBYTE())
+		{
+			fp = CURSTR();
+
+			// fp is the symbol name
+			val = (CURBYTE()) << 8;
+			NEXTBYTE();
+			val |= (CURBYTE());
+			NEXTBYTE();
+			// val is now the symbol value
+			
+			// create symbol table entry
+			se = lw_malloc(sizeof(symtab_t));
+			se -> next = s -> exportedsyms;
+			s -> exportedsyms = se;
+			se -> sym = fp;
+			se -> offset = val;
+		}
+		// skip terminating NUL
+		NEXTBYTE();
+		
+		// now parse the incomplete references and make a list of
+		// external references that need resolution
+		while (CURBYTE())
+		{
+			reloc_t *rp;
+			lw_expr_term_t *term;
+			
+			// we have a reference
+			rp = lw_malloc(sizeof(reloc_t));
+			rp -> next = s -> incompletes;
+			s -> incompletes = rp;
+			rp -> offset = 0;
+			rp -> expr = lw_expr_stack_create();
+			rp -> flags = RELOC_NORM;
+			
+			// parse the expression
+			while (CURBYTE())
+			{
+				int tt = CURBYTE();
+				NEXTBYTE();
+				switch (tt)
+				{
+				case 0xFF:
+					// a flag specifier
+					tt = CURBYTE();
+					rp -> flags = tt;
+					NEXTBYTE();
+					term = NULL;
+					break;
+					
+				case 0x01:
+					// 16 bit integer
+					tt = CURBYTE() << 8;
+					NEXTBYTE();
+					tt |= CURBYTE();
+					NEXTBYTE();
+					// normalize for negatives...
+					if (tt > 0x7fff)
+						tt -= 0x10000;
+					term = lw_expr_term_create_int(tt);
+					break;
+				
+				case 0x02:
+					// external symbol reference
+					term = lw_expr_term_create_sym(CURSTR(), 0);
+					break;
+					
+				case 0x03:
+					// internal symbol reference
+					term = lw_expr_term_create_sym(CURSTR(), 1);
+					break;
+				
+				case 0x04:
+					// operator
+					term = lw_expr_term_create_oper(CURBYTE());
+					NEXTBYTE();
+					break;
+
+				case 0x05:
+					// section base reference (NULL internal reference is
+					// the section base address
+					term = lw_expr_term_create_sym(NULL, 1);
+					break;
+					
+				default:
+					fprintf(stderr, "%s (%s): bad relocation expression (%02X)\n", fn -> filename, s -> name, tt);
+					exit(1);
+				}
+				if (term)
+				{
+					lw_expr_stack_push(rp -> expr, term);
+					lw_expr_term_free(term);
+				}
+			}
+			// skip the NUL
+			NEXTBYTE();
+			
+			// fetch the offset
+			rp -> offset = CURBYTE() << 8;
+			NEXTBYTE();
+			rp -> offset |= CURBYTE() & 0xff;
+			NEXTBYTE();
+		}
+		// skip the NUL terminating the relocations
+		NEXTBYTE();
+				
+		// now set code location and size and verify that the file
+		// contains data going to the end of the code (if !SECTION_BSS)
+		s -> codesize = CURBYTE() << 8;
+		NEXTBYTE();
+		s -> codesize |= CURBYTE();
+		NEXTBYTE();
+		
+		s -> code = &(CURBYTE());
+		
+		// skip the code if we're not in a BSS section
+		if (!(s -> flags & SECTION_BSS))
+		{
+			int i;
+			for (i = 0; i < s -> codesize; i++)
+				NEXTBYTE();
+		}
+	}
+}
+
+/*
+Read an archive file - this will create a "sub" record and farm out the
+parsing of the sub files to the regular file parsers
+
+The archive file format consists of the 6 byte magic number followed by a
+series of records as follows:
+
+- NUL terminated file name
+- 32 bit file length in big endian order
+- the file data
+
+An empty file name indicates the end of the file.
+
+*/
+void read_lwar1v(fileinfo_t *fn)
+{
+	unsigned long cc = 6;
+	unsigned long flen;
+	unsigned long l;
+	for (;;)
+	{
+		if (cc >= fn -> filesize || !(fn -> filedata[cc]))
+			return;
+
+		for (l = cc; cc < fn -> filesize && fn -> filedata[cc]; cc++)
+			/* do nothing */ ;
+
+		cc++;
+
+		if (cc >= fn -> filesize)
+		{
+			fprintf(stderr, "Malformed archive file %s.\n", fn -> filename);
+			exit(1);
+		}
+
+		if (cc + 4 > fn -> filesize)
+			return;
+
+		flen = (fn -> filedata[cc++] << 24);
+		flen |= (fn -> filedata[cc++] << 16);
+		flen |= (fn -> filedata[cc++] << 8);
+		flen |= (fn -> filedata[cc++]);
+
+		if (flen == 0)
+			return;
+		
+		if (cc + flen > fn -> filesize)
+		{
+			fprintf(stderr, "Malformed archive file %s.\n", fn -> filename);
+			exit(1);
+		}
+		
+		// add the "sub" input file
+		fn -> subs = lw_realloc(fn -> subs, sizeof(fileinfo_t *) * (fn -> nsubs + 1));
+		fn -> subs[fn -> nsubs] = lw_malloc(sizeof(fileinfo_t));
+		memset(fn -> subs[fn -> nsubs], 0, sizeof(fileinfo_t));
+		fn -> subs[fn -> nsubs] -> filedata = fn -> filedata + cc;
+		fn -> subs[fn -> nsubs] -> filesize = flen;
+		fn -> subs[fn -> nsubs] -> filename = lw_strdup(fn -> filedata + l);
+		fn -> subs[fn -> nsubs] -> parent = fn;
+		fn -> subs[fn -> nsubs] -> forced = fn -> forced;		
+		read_file(fn -> subs[fn -> nsubs]);
+		fn -> nsubs++;
+		cc += flen;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/script.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,297 @@
+/*
+script.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+Read and parse linker scripts
+*/
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lwlink.h"
+#include "util.h"
+
+// the built-in DECB target linker script
+static char *decb_script =
+	"section init load 2000\n"
+	"section code\n"
+	"section *,!bss\n"
+	"section *,bss\n"
+	"entry 2000\n"
+	;
+
+// the built-in RAW target linker script
+static char *raw_script = 
+	"section init load 0000\n"
+	"section code\n"
+	"section *,!bss\n"
+	"section *,bss\n"
+	;
+
+static char *lwex0_script =
+	"section init load 0100\n"
+	"section .text\n"
+	"section .data\n"
+	"section *,!bss\n"
+	"section *,bss\n"
+	"entry __start\n"
+	"stacksize 0100\n"		// default 256 byte stack
+	;
+
+// the "simple" script
+static char *simple_script = 
+	"section *,!bss\n"
+	"section *,bss\n"
+	;
+
+linkscript_t linkscript = { 0, NULL, -1 };
+
+void setup_script()
+{
+	char *script, *oscript;
+	long size;
+
+	// read the file if needed
+	if (scriptfile)
+	{
+		FILE *f;
+		long bread;
+		
+		f = fopen(scriptfile, "rb");
+		if (!f)
+		{
+			fprintf(stderr, "Can't open file %s:", scriptfile);
+			perror("");
+			exit(1);
+		}
+		fseek(f, 0, SEEK_END);
+		size = ftell(f);
+		rewind(f);
+		
+		script = lw_malloc(size + 2);
+		
+		bread = fread(script, 1, size, f);
+		if (bread < size)
+		{
+			fprintf(stderr, "Short read on file %s (%ld/%ld):", scriptfile, bread, size);
+			perror("");
+			exit(1);
+		}
+		fclose(f);
+		
+		script[size] = '\n';
+		script[size + 1] = '\0';
+	}
+	else
+	{
+		// fetch defaults based on output mode
+		switch (outformat)
+		{
+		case OUTPUT_RAW:
+			script = raw_script;
+			break;
+		
+		case OUTPUT_DECB:
+			script = decb_script;
+			break;
+		
+		case OUTPUT_LWEX0:
+			script = lwex0_script;
+			break;
+			
+		default:
+			script = simple_script;
+			break;
+		}
+		
+		size = strlen(script);
+		if (nscriptls)
+		{
+			char *rscript;
+			int i;
+			// prepend the "extra" script lines
+			for (i = 0; i < nscriptls; i++)
+				size += strlen(scriptls[i]) + 1;
+			
+			rscript = lw_malloc(size + 1);
+			oscript = rscript;
+			for (i = 0; i < nscriptls; i++)
+			{
+				oscript += sprintf(oscript, "%s\n", scriptls[i]);
+			}
+			strcpy(oscript, script);
+			script = rscript;
+		}
+	}
+
+	if (outformat == OUTPUT_LWEX0)
+		linkscript.stacksize = 0x100;
+
+	oscript = script;
+	// now parse the script file
+	while (*script)
+	{
+		char *ptr, *ptr2, *line;
+
+		for (ptr = script; *ptr && *ptr != '\n' && *ptr != '\r'; ptr++)
+			/* do nothing */ ;
+		
+		line = lw_malloc(ptr - script + 1);
+		memcpy(line, script, ptr - script);
+		line[ptr - script] = '\0';
+
+		// skip line terms
+		for (script = ptr + 1; *script == '\n' || *script == '\r'; script++)
+			/* do nothing */ ;
+		
+		// ignore leading whitespace
+		for (ptr = line; *ptr && isspace(*ptr); ptr++)
+			/* do nothing */ ;
+		
+		// ignore blank lines
+		if (!*ptr)
+			continue;
+		
+		for (ptr = line; *ptr && !isspace(*ptr); ptr++)
+			/* do nothing */ ;
+		
+		// now ptr points to the char past the first word
+		// NUL it out
+		if (*ptr)
+			*ptr++ = '\0';
+		
+		// skip spaces after the first word
+		for ( ; *ptr && isspace(*ptr); ptr++)
+			/* do nothing */ ;
+		
+		if (!strcmp(line, "pad"))
+		{
+			// padding
+			// parse the hex number and stow it
+			linkscript.padsize = strtol(ptr, NULL, 16);
+			if (linkscript.padsize < 0)
+				linkscript.padsize = 0;
+		}
+		else if (!strcmp(line, "stacksize"))
+		{
+			// stack size for targets that support it
+			// parse the hex number and stow it
+			linkscript.padsize = strtol(ptr, NULL, 16);
+			if (linkscript.stacksize < 0)
+				linkscript.stacksize = 0x100;
+		}
+		else if (!strcmp(line, "entry"))
+		{
+			int eaddr;
+			
+			eaddr = strtol(ptr, &ptr2, 16);
+			if (*ptr2)
+			{
+				linkscript.execaddr = -1;
+				linkscript.execsym = lw_strdup(ptr);
+			}
+			else
+			{
+				linkscript.execaddr = eaddr;
+				linkscript.execsym = NULL;
+			}
+		}
+		else if (!strcmp(line, "section"))
+		{
+			// section
+			// parse out the section name and flags
+			for (ptr2 = ptr; *ptr2 && !isspace(*ptr2); ptr2++)
+				/* do nothing */ ;
+			
+			if (*ptr2)
+				*ptr2++ = '\0';
+			
+			while (*ptr2 && isspace(*ptr2))
+				ptr2++;
+				
+			// ptr now points to the section name and flags and ptr2
+			// to the first non-space character following
+			
+			// then look for "load <addr>" clause
+			if (*ptr2)
+			{
+				if (!strncmp(ptr2, "load", 4))
+				{
+					ptr2 += 4;
+					while (*ptr2 && isspace(*ptr2))
+						ptr2++;
+					
+				}
+				else
+				{
+					fprintf(stderr, "%s: bad script\n", scriptfile);
+					exit(1);
+				}
+			}
+			
+			// now ptr2 points to the load address if there is one
+			// or NUL if not
+			linkscript.lines = lw_realloc(linkscript.lines, sizeof(struct scriptline_s) * (linkscript.nlines + 1));
+
+			linkscript.lines[linkscript.nlines].noflags = 0;
+			linkscript.lines[linkscript.nlines].yesflags = 0;
+			if (*ptr2)
+				linkscript.lines[linkscript.nlines].loadat = strtol(ptr2, NULL, 16);
+			else
+				linkscript.lines[linkscript.nlines].loadat = -1;
+			for (ptr2 = ptr; *ptr2 && *ptr2 != ','; ptr2++)
+				/* do nothing */ ;
+			if (*ptr2)
+			{
+				*ptr2++ = '\0';
+				if (!strcmp(ptr2, "!bss"))
+				{
+					linkscript.lines[linkscript.nlines].noflags = SECTION_BSS;
+				}
+				else if (!strcmp(ptr2, "bss"))
+				{
+					linkscript.lines[linkscript.nlines].yesflags = SECTION_BSS;
+				}
+				else
+				{
+					fprintf(stderr, "%s: bad script\n", scriptfile);
+					exit(1);
+				}
+			}
+			if (ptr[0] == '*' && ptr[1] == '\0')
+				linkscript.lines[linkscript.nlines].sectname = NULL;
+			else
+				linkscript.lines[linkscript.nlines].sectname = lw_strdup(ptr);
+			linkscript.nlines++;
+		}
+		else
+		{
+			fprintf(stderr, "%s: bad script\n", scriptfile);
+			exit(1);
+		}
+		lw_free(line);
+	}
+	
+	if (scriptfile || nscriptls)
+		lw_free(oscript);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/util.c	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,88 @@
+/*
+util.c
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+Utility functions
+*/
+
+#define __util_c_seen__
+#include <config.h>
+
+#include <malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void *lw_malloc(int size)
+{
+	void *ptr;
+	
+	ptr = malloc(size);
+	if (!ptr)
+	{
+		// bail out; memory allocation error
+		fprintf(stderr, "lw_malloc(): Memory allocation error\n");
+		exit(1);
+	}
+	return ptr;
+}
+
+void *lw_realloc(void *optr, int size)
+{
+	void *ptr;
+	
+	if (size == 0)
+	{
+		lw_free(optr);
+		return;
+	}
+	
+	ptr = realloc(optr, size);
+	if (!ptr)
+	{
+		fprintf(stderr, "lw_realloc(): memory allocation error\n");
+		exit(1);
+	}
+}
+
+void lw_free(void *ptr)
+{
+	if (ptr)
+		free(ptr);
+}
+
+char *lw_strdup(const char *s)
+{
+	char *d;
+	
+	if (!s)
+		return NULL;
+
+	d = strdup(s);
+	if (!d)
+	{
+		fprintf(stderr, "lw_strdup(): memory allocation error\n");
+		exit(1);
+	}
+	
+	return d;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lwlink/util.h	Mon Apr 26 19:30:44 2010 -0600
@@ -0,0 +1,44 @@
+/*
+util.h
+Copyright © 2009 William Astle
+
+This file is part of LWLINK.
+
+LWLINK is free software: you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the Free Software
+Foundation, either version 3 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+Utility functions
+*/
+
+#ifndef __util_h_seen__
+#define __util_h_seen__
+
+#ifndef __util_c_seen__
+#define __util_E__ extern
+#else
+#define __util_E__
+#endif
+
+// allocate memory
+__util_E__ void *lw_malloc(int size);
+__util_E__ void lw_free(void *ptr);
+__util_E__ void *lw_realloc(void *optr, int size);
+
+// string stuff
+__util_E__ char *lw_strdup(const char *s);
+
+#undef __util_E__
+
+#endif // __util_h_seen__